Code coverage for web settings
This commit is contained in:
		
							parent
							
								
									21c7b35136
								
							
						
					
					
						commit
						62be5f7091
					
				
					 5 changed files with 92 additions and 74 deletions
				
			
		|  | @ -20,77 +20,75 @@ function getCreateSpace(event) { | |||
| 	return event.context.createSpace || sync.require("../../d2m/actions/create-space") | ||||
| } | ||||
| 
 | ||||
| /** @type {["invite", "link", "directory"]} */ | ||||
| const levels = ["invite", "link", "directory"] | ||||
| const schema = { | ||||
| 	autocreate: z.object({ | ||||
| 		guild_id: z.string(), | ||||
| 		autocreate: z.string().optional() | ||||
| 	}), | ||||
| 	urlPreview: z.object({ | ||||
| 		guild_id: z.string(), | ||||
| 		url_preview: z.string().optional() | ||||
| 	}), | ||||
| 	presence: z.object({ | ||||
| 		guild_id: z.string(), | ||||
| 		presence: z.string().optional() | ||||
| 	}), | ||||
| 	privacyLevel: z.object({ | ||||
| 		guild_id: z.string(), | ||||
| 		level: z.enum(levels) | ||||
| /** | ||||
|  * @typedef Options | ||||
|  * @prop {(value: string?) => number} transform | ||||
|  * @prop {(event: H3Event, guildID: string) => any} [after] | ||||
|  * @prop {keyof import("../../db/orm-defs").Models} table | ||||
|  */ | ||||
| 
 | ||||
| /** | ||||
|  * @template {string} T | ||||
|  * @param {T} key | ||||
|  * @param {Partial<Options>} [inputOptions] | ||||
|  */ | ||||
| function defineToggle(key, inputOptions) { | ||||
| 	/** @type {Options} */ | ||||
| 	const options = { | ||||
| 		transform: x => +!!x, // convert toggle to 0 or 1
 | ||||
| 		table: "guild_space" | ||||
| 	} | ||||
| 	Object.assign(options, inputOptions) | ||||
| 	return defineEventHandler(async event => { | ||||
| 		const bodySchema = z.object({ | ||||
| 			guild_id: z.string(), | ||||
| 			[key]: z.string().optional() | ||||
| 		}) | ||||
| 		/** @type {Record<T, string?> & Record<"guild_id", string> & Record<string, unknown>} */ // @ts-ignore
 | ||||
| 		const parsedBody = await readValidatedBody(event, bodySchema.parse) | ||||
| 		const managed = await auth.getManagedGuilds(event) | ||||
| 		if (!managed.has(parsedBody.guild_id)) throw createError({status: 403, message: "Forbidden", data: "Can't change settings for a guild you don't have Manage Server permissions in"}) | ||||
| 
 | ||||
| 		const value = options.transform(parsedBody[key]) | ||||
| 		assert(typeof value === "number") | ||||
| 		db.prepare(`UPDATE ${options.table} SET ${key} = ? WHERE guild_id = ?`).run(value, parsedBody.guild_id) | ||||
| 
 | ||||
| 		return (options.after && await options.after(event, parsedBody.guild_id)) || null | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| as.router.post("/api/autocreate", defineEventHandler(async event => { | ||||
| 	const parsedBody = await readValidatedBody(event, schema.autocreate.parse) | ||||
| 	const managed = await auth.getManagedGuilds(event) | ||||
| 	if (!managed.has(parsedBody.guild_id)) throw createError({status: 403, message: "Forbidden", data: "Can't change settings for a guild you don't have Manage Server permissions in"}) | ||||
| 
 | ||||
| 	db.prepare("UPDATE guild_active SET autocreate = ? WHERE guild_id = ?").run(+!!parsedBody.autocreate, parsedBody.guild_id) | ||||
| 
 | ||||
| 	// If showing a partial page due to incomplete setup, need to refresh the whole page to show the alternate version
 | ||||
| 	const spaceID = select("guild_space", "space_id", {guild_id: parsedBody.guild_id}).pluck().get() | ||||
| 	if (!spaceID) { | ||||
| 		if (getRequestHeader(event, "HX-Request")) { | ||||
| 			setResponseHeader(event, "HX-Refresh", "true") | ||||
| 		} else { | ||||
| 			return sendRedirect(event, "", 302) | ||||
| as.router.post("/api/autocreate", defineToggle("autocreate", { | ||||
| 	table: "guild_active", | ||||
| 	after(event, guild_id) { | ||||
| 		// If showing a partial page due to incomplete setup, need to refresh the whole page to show the alternate version
 | ||||
| 		const spaceID = select("guild_space", "space_id", {guild_id}).pluck().get() | ||||
| 		if (!spaceID) { | ||||
| 			if (getRequestHeader(event, "HX-Request")) { | ||||
| 				setResponseHeader(event, "HX-Refresh", "true") | ||||
| 			} else { | ||||
| 				return sendRedirect(event, "", 302) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return null // 204
 | ||||
| })) | ||||
| 
 | ||||
| as.router.post("/api/url-preview", defineEventHandler(async event => { | ||||
| 	const parsedBody = await readValidatedBody(event, schema.urlPreview.parse) | ||||
| 	const managed = await auth.getManagedGuilds(event) | ||||
| 	if (!managed.has(parsedBody.guild_id)) throw createError({status: 403, message: "Forbidden", data: "Can't change settings for a guild you don't have Manage Server permissions in"}) | ||||
| as.router.post("/api/url-preview", defineToggle("url_preview")) | ||||
| 
 | ||||
| 	db.prepare("UPDATE guild_space SET url_preview = ? WHERE guild_id = ?").run(+!!parsedBody.url_preview, parsedBody.guild_id) | ||||
| 
 | ||||
| 	return null // 204
 | ||||
| as.router.post("/api/presence", defineToggle("presence", { | ||||
| 	after() { | ||||
| 		setPresence.guildPresenceSetting.update() | ||||
| 	} | ||||
| })) | ||||
| 
 | ||||
| as.router.post("/api/presence", defineEventHandler(async event => { | ||||
| 	const parsedBody = await readValidatedBody(event, schema.presence.parse) | ||||
| 	const managed = await auth.getManagedGuilds(event) | ||||
| 	if (!managed.has(parsedBody.guild_id)) throw createError({status: 403, message: "Forbidden", data: "Can't change settings for a guild you don't have Manage Server permissions in"}) | ||||
| 
 | ||||
| 	db.prepare("UPDATE guild_space SET presence = ? WHERE guild_id = ?").run(+!!parsedBody.presence, parsedBody.guild_id) | ||||
| 	setPresence.guildPresenceSetting.update() | ||||
| 
 | ||||
| 	return null // 204
 | ||||
| })) | ||||
| 
 | ||||
| as.router.post("/api/privacy-level", defineEventHandler(async event => { | ||||
| 	const parsedBody = await readValidatedBody(event, schema.privacyLevel.parse) | ||||
| 	const managed = await auth.getManagedGuilds(event) | ||||
| 	if (!managed.has(parsedBody.guild_id)) throw createError({status: 403, message: "Forbidden", data: "Can't change settings for a guild you don't have Manage Server permissions in"}) | ||||
| 
 | ||||
| 	const createSpace = getCreateSpace(event) | ||||
| 	const i = levels.indexOf(parsedBody.level) | ||||
| 	assert.notEqual(i, -1) | ||||
| 	db.prepare("UPDATE guild_space SET privacy_level = ? WHERE guild_id = ?").run(i, parsedBody.guild_id) | ||||
| 	await createSpace.syncSpaceFully(parsedBody.guild_id) // this is inefficient but OK to call infrequently on user request
 | ||||
| 	return null // 204
 | ||||
| as.router.post("/api/privacy-level", defineToggle("privacy_level", { | ||||
| 	transform(value) { | ||||
| 		assert(value) | ||||
| 		const i = ["invite", "link", "directory"].indexOf(value) | ||||
| 		assert.notEqual(i, -1) | ||||
| 		return i | ||||
| 	}, | ||||
| 	async after(event, guildID) { | ||||
| 		const createSpace = getCreateSpace(event) | ||||
| 		await createSpace.syncSpaceFully(guildID) // this is inefficient but OK to call infrequently on user request
 | ||||
| 	} | ||||
| })) | ||||
|  |  | |||
|  | @ -54,7 +54,7 @@ test("web privacy level: checks permissions", async t => { | |||
| 	const [error] = await tryToCatch(() => router.test("post", "/api/privacy-level", { | ||||
| 		body: { | ||||
| 			guild_id: "112760669178241024", | ||||
| 			level: "directory" | ||||
| 			privacy_level: "directory" | ||||
| 		} | ||||
| 	})) | ||||
| 	t.equal(error.data, "Can't change settings for a guild you don't have Manage Server permissions in") | ||||
|  | @ -68,7 +68,7 @@ test("web privacy level: updates privacy level", async t => { | |||
| 		}, | ||||
| 		body: { | ||||
| 			guild_id: "112760669178241024", | ||||
| 			level: "directory" | ||||
| 			privacy_level: "directory" | ||||
| 		}, | ||||
| 		createSpace: { | ||||
| 			async syncSpaceFully(guildID) { | ||||
|  | @ -81,3 +81,16 @@ test("web privacy level: updates privacy level", async t => { | |||
| 	t.equal(called, 1) | ||||
| 	t.equal(select("guild_space", "privacy_level", {guild_id: "112760669178241024"}).pluck().get(), 2) // directory = 2
 | ||||
| }) | ||||
| 
 | ||||
| test("web presence: updates presence", async t => { | ||||
| 	await router.test("post", "/api/presence", { | ||||
| 		sessionData: { | ||||
| 			managedGuilds: ["112760669178241024"] | ||||
| 		}, | ||||
| 		body: { | ||||
| 			guild_id: "112760669178241024" | ||||
| 			// presence is on by default - turn it off
 | ||||
| 		} | ||||
| 	}) | ||||
| 	t.equal(select("guild_space", "presence", {guild_id: "112760669178241024"}).pluck().get(), 0) | ||||
| }) | ||||
|  |  | |||
|  | @ -79,10 +79,7 @@ as.router.post("/api/link-space", defineEventHandler(async event => { | |||
| 	try { | ||||
| 		await api.joinRoom(parsedBody.space_id) | ||||
| 	} catch (e) { | ||||
| 		if (e instanceof mreq.MatrixServerError) { | ||||
| 			throw createError({status: 403, message: e.errcode, data: `${e.errcode} - ${e.message}`}) | ||||
| 		} | ||||
| 		throw e | ||||
| 		throw createError({status: 403, message: e.errcode, data: `${e.errcode} - ${e.message}`}) | ||||
| 	} | ||||
| 
 | ||||
| 	// Check bridge has PL 100
 | ||||
|  | @ -148,10 +145,7 @@ as.router.post("/api/link", defineEventHandler(async event => { | |||
| 	try { | ||||
| 		await api.joinRoom(parsedBody.matrix) | ||||
| 	} catch (e) { | ||||
| 		if (e instanceof mreq.MatrixServerError) { | ||||
| 			throw createError({status: 403, message: e.errcode, data: `${e.errcode} - ${e.message}`}) | ||||
| 		} | ||||
| 		throw e | ||||
| 		throw createError({status: 403, message: e.errcode, data: `${e.errcode} - ${e.message}`}) | ||||
| 	} | ||||
| 
 | ||||
| 	// Check bridge has PL 100
 | ||||
|  |  | |||
|  | @ -518,6 +518,18 @@ test("web link room: successfully calls createRoom", async t => { | |||
| 					t.equal(roomID, "!zTMspHVUBhFLLSdmnS:cadence.moe") | ||||
| 					t.equal(key, "!NDbIqNpJyPvfKRnNcr:cadence.moe") | ||||
| 					return {via: ["cadence.moe"]} | ||||
| 				} else if (type === "m.room.name") { | ||||
| 					called++ | ||||
| 					t.equal(roomID, "!NDbIqNpJyPvfKRnNcr:cadence.moe") | ||||
| 					return {} | ||||
| 				} else if (type === "m.room.avatar") { | ||||
| 					called++ | ||||
| 					t.equal(roomID, "!NDbIqNpJyPvfKRnNcr:cadence.moe") | ||||
| 					return {} | ||||
| 				} else if (type === "m.room.topic") { | ||||
| 					called++ | ||||
| 					t.equal(roomID, "!NDbIqNpJyPvfKRnNcr:cadence.moe") | ||||
| 					return {} | ||||
| 				} | ||||
| 			}, | ||||
| 			async sendEvent(roomID, type, content) { | ||||
|  | @ -536,7 +548,7 @@ test("web link room: successfully calls createRoom", async t => { | |||
| 			} | ||||
| 		} | ||||
| 	}) | ||||
| 	t.equal(called, 5) | ||||
| 	t.equal(called, 8) | ||||
| }) | ||||
| 
 | ||||
| // *****
 | ||||
|  |  | |||
|  | @ -144,6 +144,7 @@ INSERT INTO emoji (emoji_id, name, animated, mxc_url) VALUES | |||
| ('288858540888686602', 'upstinky', 0, 'mxc://cadence.moe/mwZaCtRGAQQyOItagDeCocEO'); | ||||
| 
 | ||||
| INSERT INTO member_cache (room_id, mxid, displayname, avatar_url, power_level) VALUES | ||||
| ('!jjmvBegULiLucuWEHU:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', NULL, 50), | ||||
| ('!kLRqKKUQXcibIMtOpl:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', NULL, 0), | ||||
| ('!kLRqKKUQXcibIMtOpl:cadence.moe', '@test_auto_invite:example.org', NULL, NULL, 0), | ||||
| ('!fGgIymcYWOqjbSRUdV:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', 'mxc://cadence.moe/azCAhThKTojXSZJRoWwZmhvU', 0), | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue