Code coverage for matrix log in & guild settings

This commit is contained in:
Cadence Ember
2025-02-08 16:05:35 +13:00
parent a90d3b9055
commit b2078620be
8 changed files with 284 additions and 50 deletions

View File

@@ -1,7 +1,7 @@
extends includes/template.pug
block body
if !session.data.user_id
if !session.data.userID
.s-empty-state.wmx4.p48
!= icons.Spots.SpotEmptyXL
p You need to log in to manage your servers.

View File

@@ -2,13 +2,19 @@
const assert = require("assert/strict")
const {z} = require("zod")
const {defineEventHandler, useSession, createError, readValidatedBody, getRequestHeader, setResponseHeader, sendRedirect} = require("h3")
const {defineEventHandler, useSession, createError, readValidatedBody, getRequestHeader, setResponseHeader, sendRedirect, H3Event} = require("h3")
const {as, db, sync, select} = require("../../passthrough")
const {reg} = require("../../matrix/read-registration")
/** @type {import("../../d2m/actions/create-space")} */
const createSpace = sync.require("../../d2m/actions/create-space")
/**
* @param {H3Event} event
* @returns {import("../../d2m/actions/create-space")}
*/
function getCreateSpace(event) {
/* c8 ignore next */
return event.context.createSpace || sync.require("../../d2m/actions/create-space")
}
/** @type {["invite", "link", "directory"]} */
const levels = ["invite", "link", "directory"]
@@ -48,6 +54,7 @@ as.router.post("/api/privacy-level", defineEventHandler(async event => {
const session = await useSession(event, {password: reg.as_token})
if (!(session.data.managedGuilds || []).concat(session.data.matrixGuilds || []).includes(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)

View File

@@ -0,0 +1,83 @@
// @ts-check
const tryToCatch = require("try-to-catch")
const {router, test} = require("../../../test/web")
const {select} = require("../../passthrough")
const {MatrixServerError} = require("../../matrix/mreq")
test("web autocreate: checks permissions", async t => {
const [error] = await tryToCatch(() => router.test("post", "/api/autocreate", {
body: {
guild_id: "66192955777486848"
}
}))
t.equal(error.data, "Can't change settings for a guild you don't have Manage Server permissions in")
})
test("web autocreate: turns off autocreate and does htmx page refresh when guild not linked", async t => {
const event = {}
await router.test("post", "/api/autocreate", {
sessionData: {
managedGuilds: ["66192955777486848"]
},
body: {
guild_id: "66192955777486848",
// autocreate is false
},
headers: {
"hx-request": "true"
},
event
})
t.equal(event.node.res.getHeader("hx-refresh"), "true")
t.equal(select("guild_active", "autocreate", {guild_id: "66192955777486848"}).pluck().get(), 0)
})
test("web autocreate: turns on autocreate and issues 302 when not using htmx", async t => {
const event = {}
await router.test("post", "/api/autocreate", {
sessionData: {
managedGuilds: ["66192955777486848"]
},
body: {
guild_id: "66192955777486848",
autocreate: "yes"
},
event
})
t.equal(event.node.res.getHeader("location"), "")
t.equal(select("guild_active", "autocreate", {guild_id: "66192955777486848"}).pluck().get(), 1)
})
test("web privacy level: checks permissions", async t => {
const [error] = await tryToCatch(() => router.test("post", "/api/privacy-level", {
body: {
guild_id: "112760669178241024",
level: "directory"
}
}))
t.equal(error.data, "Can't change settings for a guild you don't have Manage Server permissions in")
})
test("web privacy level: updates privacy level", async t => {
let called = 0
await router.test("post", "/api/privacy-level", {
sessionData: {
managedGuilds: ["112760669178241024"]
},
body: {
guild_id: "112760669178241024",
level: "directory"
},
createSpace: {
async syncSpaceFully(guildID) {
called++
t.equal(guildID, "112760669178241024")
return ""
}
}
})
t.equal(called, 1)
t.equal(select("guild_space", "privacy_level", {guild_id: "112760669178241024"}).pluck().get(), 2) // directory = 2
})

View File

@@ -11,27 +11,27 @@ test("web guild: access denied when not logged in", async t => {
sessionData: {
},
})
t.match(html, /You need to log in to manage your servers./)
t.has(html, "You need to log in to manage your servers.")
})
test("web guild: asks to select guild if not selected", async t => {
const html = await router.test("get", "/guild", {
sessionData: {
user_id: "1",
userID: "1",
managedGuilds: []
},
})
t.match(html, /Select a server from the top right corner to continue./)
t.has(html, "Select a server from the top right corner to continue.")
})
test("web guild: access denied when guild id messed up", async t => {
const html = await router.test("get", "/guild?guild_id=1", {
sessionData: {
user_id: "1",
userID: "1",
managedGuilds: []
},
})
t.match(html, /the selected server doesn't exist/)
t.has(html, "the selected server doesn't exist")
})
test("web invite: access denied with invalid nonce", async t => {
@@ -44,7 +44,6 @@ test("web invite: access denied with invalid nonce", async t => {
test("web guild: can view unbridged guild", async t => {
const html = await router.test("get", "/guild?guild_id=66192955777486848", {
sessionData: {
user_id: "1",
managedGuilds: ["66192955777486848"]
}
})
@@ -54,7 +53,6 @@ test("web guild: can view unbridged guild", async t => {
test("web guild: unbridged self-service guild prompts log in to matrix", async t => {
const html = await router.test("get", "/guild?guild_id=665289423482519565", {
sessionData: {
user_id: "1",
managedGuilds: ["665289423482519565"]
}
})
@@ -66,7 +64,6 @@ test("web guild: unbridged self-service guild asks to be invited", async t => {
const html = await router.test("get", "/guild?guild_id=665289423482519565", {
sessionData: {
mxid: "@user:example.org",
user_id: "1",
managedGuilds: ["665289423482519565"]
}
})
@@ -77,7 +74,6 @@ test("web guild: unbridged self-service guild shows available spaces", async t =
const html = await router.test("get", "/guild?guild_id=665289423482519565", {
sessionData: {
mxid: "@cadence:cadence.moe",
user_id: "1",
managedGuilds: ["665289423482519565"]
}
})

View File

@@ -21,7 +21,6 @@ test("web link space: access denied when not logged in to Discord", async t => {
test("web link space: access denied when not logged in to Matrix", async t => {
const [error] = await tryToCatch(() => router.test("post", "/api/link-space", {
sessionData: {
user_id: "1",
managedGuilds: ["665289423482519565"]
},
body: {
@@ -35,7 +34,6 @@ test("web link space: access denied when not logged in to Matrix", async t => {
test("web link space: access denied when bot was invited by different user", async t => {
const [error] = await tryToCatch(() => router.test("post", "/api/link-space", {
sessionData: {
user_id: "1",
managedGuilds: ["665289423482519565"],
mxid: "@user:example.org"
},
@@ -50,7 +48,6 @@ test("web link space: access denied when bot was invited by different user", asy
test("web link space: access denied when guild is already in use", async t => {
const [error] = await tryToCatch(() => router.test("post", "/api/link-space", {
sessionData: {
user_id: "1",
managedGuilds: ["112760669178241024"],
mxid: "@cadence:cadence.moe"
},
@@ -66,7 +63,6 @@ test("web link space: check that OOYE is joined", async t => {
let called = 0
const [error] = await tryToCatch(() => router.test("post", "/api/link-space", {
sessionData: {
user_id: "1",
managedGuilds: ["665289423482519565"],
mxid: "@cadence:cadence.moe"
},
@@ -92,7 +88,6 @@ test("web link space: check that OOYE has PL 100 (not missing)", async t => {
let called = 0
const [error] = await tryToCatch(() => router.test("post", "/api/link-space", {
sessionData: {
user_id: "1",
managedGuilds: ["665289423482519565"],
mxid: "@cadence:cadence.moe"
},
@@ -121,7 +116,6 @@ test("web link space: check that OOYE has PL 100 (not users_default)", async t =
let called = 0
const [error] = await tryToCatch(() => router.test("post", "/api/link-space", {
sessionData: {
user_id: "1",
managedGuilds: ["665289423482519565"],
mxid: "@cadence:cadence.moe"
},
@@ -151,7 +145,6 @@ test("web link space: check that OOYE has PL 100 (not 50)", async t => {
let called = 0
const [error] = await tryToCatch(() => router.test("post", "/api/link-space", {
sessionData: {
user_id: "1",
managedGuilds: ["665289423482519565"],
mxid: "@cadence:cadence.moe"
},
@@ -181,7 +174,6 @@ test("web link space: check that inviting user has PL 50", async t => {
let called = 0
const [error] = await tryToCatch(() => router.test("post", "/api/link-space", {
sessionData: {
user_id: "1",
managedGuilds: ["665289423482519565"],
mxid: "@cadence:cadence.moe"
},
@@ -211,7 +203,6 @@ test("web link space: successfully adds entry to database and loads page", async
let called = 0
await router.test("post", "/api/link-space", {
sessionData: {
user_id: "1",
managedGuilds: ["665289423482519565"],
mxid: "@cadence:cadence.moe"
},
@@ -241,7 +232,6 @@ test("web link space: successfully adds entry to database and loads page", async
// check that the guild info page now loads
const html = await router.test("get", "/guild?guild_id=665289423482519565", {
sessionData: {
user_id: "1",
managedGuilds: ["665289423482519565"],
mxid: "@cadence:cadence.moe"
},
@@ -278,7 +268,6 @@ test("web link room: access denied when not logged in to Discord", async t => {
test("web link room: check that guild exists", async t => {
const [error] = await tryToCatch(() => router.test("post", "/api/link", {
sessionData: {
user_id: "1",
managedGuilds: ["1"]
},
body: {
@@ -293,7 +282,6 @@ test("web link room: check that guild exists", async t => {
test("web link room: check that channel exists", async t => {
const [error] = await tryToCatch(() => router.test("post", "/api/link", {
sessionData: {
user_id: "1",
managedGuilds: ["665289423482519565"]
},
body: {
@@ -308,7 +296,6 @@ test("web link room: check that channel exists", async t => {
test("web link room: check that channel is part of guild", async t => {
const [error] = await tryToCatch(() => router.test("post", "/api/link", {
sessionData: {
user_id: "1",
managedGuilds: ["665289423482519565"]
},
body: {
@@ -323,7 +310,6 @@ test("web link room: check that channel is part of guild", async t => {
test("web link room: check that channel is not already linked", async t => {
const [error] = await tryToCatch(() => router.test("post", "/api/link", {
sessionData: {
user_id: "1",
managedGuilds: ["112760669178241024"]
},
body: {
@@ -339,7 +325,6 @@ test("web link room: checks the autocreate setting if the space doesn't exist ye
let called = 0
const [error] = await tryToCatch(() => router.test("post", "/api/link", {
sessionData: {
user_id: "1",
managedGuilds: ["665289423482519565"]
},
body: {
@@ -366,7 +351,6 @@ test("web link room: check that room is part of space (event missing)", async t
let called = 0
const [error] = await tryToCatch(() => router.test("post", "/api/link", {
sessionData: {
user_id: "1",
managedGuilds: ["665289423482519565"]
},
body: {
@@ -392,7 +376,6 @@ test("web link room: check that room is part of space (event empty)", async t =>
let called = 0
const [error] = await tryToCatch(() => router.test("post", "/api/link", {
sessionData: {
user_id: "1",
managedGuilds: ["665289423482519565"]
},
body: {
@@ -418,7 +401,6 @@ test("web link room: check that bridge is joined to room", async t => {
let called = 0
const [error] = await tryToCatch(() => router.test("post", "/api/link", {
sessionData: {
user_id: "1",
managedGuilds: ["665289423482519565"]
},
body: {
@@ -449,7 +431,6 @@ test("web link room: check that bridge has PL 100 in target room (event missing)
let called = 0
const [error] = await tryToCatch(() => router.test("post", "/api/link", {
sessionData: {
user_id: "1",
managedGuilds: ["665289423482519565"]
},
body: {
@@ -484,7 +465,6 @@ test("web link room: check that bridge has PL 100 in target room (users default)
let called = 0
const [error] = await tryToCatch(() => router.test("post", "/api/link", {
sessionData: {
user_id: "1",
managedGuilds: ["665289423482519565"]
},
body: {
@@ -519,7 +499,6 @@ test("web link room: successfully calls createRoom", async t => {
let called = 0
await router.test("post", "/api/link", {
sessionData: {
user_id: "1",
managedGuilds: ["665289423482519565"]
},
body: {
@@ -578,7 +557,6 @@ test("web unlink room: access denied if not logged in to Discord", async t => {
test("web unlink room: checks that guild exists", async t => {
const [error] = await tryToCatch(() => router.test("post", "/api/unlink", {
sessionData: {
user_id: "1",
managedGuilds: ["2"]
},
body: {
@@ -592,7 +570,6 @@ test("web unlink room: checks that guild exists", async t => {
test("web unlink room: checks that the channel is part of the guild", async t => {
const [error] = await tryToCatch(() => router.test("post", "/api/unlink", {
sessionData: {
user_id: "1",
managedGuilds: ["665289423482519565"]
},
body: {
@@ -607,7 +584,6 @@ test("web unlink room: successfully calls unbridgeDeletedChannel when the channe
let called = 0
await router.test("post", "/api/unlink", {
sessionData: {
user_id: "1",
managedGuilds: ["665289423482519565"]
},
body: {
@@ -628,7 +604,6 @@ test("web unlink room: successfully calls unbridgeDeletedChannel when the channe
let called = 0
await router.test("post", "/api/unlink", {
sessionData: {
user_id: "1",
managedGuilds: ["112760669178241024"]
},
body: {
@@ -649,7 +624,6 @@ test("web unlink room: checks that the channel is bridged", async t => {
db.prepare("DELETE FROM channel_room WHERE channel_id = '665310973967597573'").run()
const [error] = await tryToCatch(() => router.test("post", "/api/unlink", {
sessionData: {
user_id: "1",
managedGuilds: ["665289423482519565"]
},
body: {

View File

@@ -2,25 +2,16 @@
const {z} = require("zod")
const {randomUUID} = require("crypto")
const {defineEventHandler, getValidatedQuery, sendRedirect, readValidatedBody, useSession, createError, getRequestHeader} = require("h3")
const {SnowTransfer} = require("snowtransfer")
const DiscordTypes = require("discord-api-types/v10")
const fetch = require("node-fetch")
const getRelativePath = require("get-relative-path")
const {defineEventHandler, getValidatedQuery, sendRedirect, readValidatedBody, useSession, createError, getRequestHeader, H3Event} = require("h3")
const {LRUCache} = require("lru-cache")
const {as, db, select, from} = require("../../passthrough")
const {id} = require("../../../addbot")
const {as, db} = require("../../passthrough")
const {reg} = require("../../matrix/read-registration")
const {sync} = require("../../passthrough")
const assert = require("assert").strict
/** @type {import("../pug-sync")} */
const pugSync = sync.require("../pug-sync")
/** @type {import("../../matrix/api")} */
const api = sync.require("../../matrix/api")
const redirect_uri = `${reg.ooye.bridge_origin}/oauth`
const schema = {
form: z.object({
@@ -31,6 +22,15 @@ const schema = {
})
}
/**
* @param {H3Event} event
* @returns {import("../../matrix/api")}
*/
function getAPI(event) {
/* c8 ignore next */
return event.context.api || sync.require("../../matrix/api")
}
/** @type {LRUCache<string, string>} token to mxid */
const validToken = new LRUCache({max: 200})
@@ -67,6 +67,7 @@ as.router.get("/log-in-with-matrix", defineEventHandler(async event => {
}))
as.router.post("/api/log-in-with-matrix", defineEventHandler(async event => {
const api = getAPI(event)
const {mxid} = await readValidatedBody(event, schema.form.parse)
let roomID = null

View File

@@ -0,0 +1,171 @@
// @ts-check
const tryToCatch = require("try-to-catch")
const {router, test} = require("../../../test/web")
const {MatrixServerError} = require("../../matrix/mreq")
// ***** first request *****
test("log in with matrix: shows web page with form on first request", async t => {
const html = await router.test("get", "/log-in-with-matrix", {
})
t.has(html, `hx-post="/api/log-in-with-matrix"`)
})
// ***** second request *****
let token
test("log in with matrix: sends message when there is no m.direct data", async t => {
const event = {}
let called = 0
await router.test("post", "/api/log-in-with-matrix", {
body: {
mxid: "@cadence:cadence.moe"
},
api: {
async getAccountData(type) {
called++
t.equal(type, "m.direct")
throw new MatrixServerError({errcode: "M_NOT_FOUND"})
},
async createRoom() {
called++
return "!created:cadence.moe"
},
async setAccountData(type, content) {
called++
t.equal(type, "m.direct")
t.deepEqual(content, {"@cadence:cadence.moe": ["!created:cadence.moe"]})
},
async sendEvent(roomID, type, content) {
called++
t.equal(roomID, "!created:cadence.moe")
t.equal(type, "m.room.message")
token = content.body.match(/log-in-with-matrix\?token=([a-f0-9-]+)/)[1]
t.ok(token, "log in token not issued")
return ""
}
},
event
})
t.match(event.node.res.getHeader("location"), /Please check your inbox on Matrix/)
t.equal(called, 4)
})
test("log in with matrix: does not send another message when a log in is in progress", async t => {
const event = {}
await router.test("post", "/api/log-in-with-matrix", {
body: {
mxid: "@cadence:cadence.moe"
},
event
})
t.match(event.node.res.getHeader("location"), /We already sent you a link on Matrix/)
})
test("log in with matrix: reuses room from m.direct", async t => {
const event = {}
let called = 0
await router.test("post", "/api/log-in-with-matrix", {
body: {
mxid: "@user1:example.org"
},
api: {
async getAccountData(type) {
called++
t.equal(type, "m.direct")
return {"@user1:example.org": ["!existing:cadence.moe"]}
},
async getStateEvent(roomID, type, key) {
called++
t.equal(roomID, "!existing:cadence.moe")
t.equal(type, "m.room.member")
t.equal(key, "@user1:example.org")
return {membership: "join"}
},
async sendEvent(roomID) {
called++
t.equal(roomID, "!existing:cadence.moe")
return ""
}
},
event
})
t.match(event.node.res.getHeader("location"), /Please check your inbox on Matrix/)
t.equal(called, 3)
})
test("log in with matrix: reuses room from m.direct, reinviting if user has left", async t => {
const event = {}
let called = 0
await router.test("post", "/api/log-in-with-matrix", {
body: {
mxid: "@user2:example.org"
},
api: {
async getAccountData(type) {
called++
t.equal(type, "m.direct")
return {"@user2:example.org": ["!existing:cadence.moe"]}
},
async getStateEvent(roomID, type, key) {
called++
t.equal(roomID, "!existing:cadence.moe")
t.equal(type, "m.room.member")
t.equal(key, "@user2:example.org")
throw new MatrixServerError({errcode: "M_NOT_FOUND"})
},
async inviteToRoom(roomID, mxid) {
called++
t.equal(roomID, "!existing:cadence.moe")
t.equal(mxid, "@user2:example.org")
},
async sendEvent(roomID) {
called++
t.equal(roomID, "!existing:cadence.moe")
return ""
}
},
event
})
t.match(event.node.res.getHeader("location"), /Please check your inbox on Matrix/)
t.equal(called, 4)
})
// ***** third request *****
test("log in with matrix: does not use up token when requested by Synapse URL previewer", async t => {
const event = {}
const [error] = await tryToCatch(() => router.test("get", `/log-in-with-matrix?token=${token}`, {
headers: {
"user-agent": "Synapse (bot; +https://github.com/matrix-org/synapse)"
},
event
}))
t.equal(error.data, "Sorry URL previewer, you can't have this URL.")
})
test("log in with matrix: does not use up token when requested by Discord URL previewer", async t => {
const event = {}
const [error] = await tryToCatch(() => router.test("get", `/log-in-with-matrix?token=${token}`, {
headers: {
"user-agent": "Mozilla/5.0 (compatible; Discordbot/2.0; +https://discordapp.com)"
},
event
}))
t.equal(error.data, "Sorry URL previewer, you can't have this URL.")
})
test("log in with matrix: successful request when using valid token", async t => {
const event = {}
await router.test("get", `/log-in-with-matrix?token=${token}`, {event})
t.equal(event.node.res.getHeader("location"), "./")
})
test("log in with matrix: won't log in again if token has been used", async t => {
const event = {}
await router.test("get", `/log-in-with-matrix?token=${token}`, {event})
t.equal(event.node.res.getHeader("location"), "https://bridge.example.org/log-in-with-matrix")
})

View File

@@ -157,5 +157,7 @@ file._actuallyUploadDiscordFileToMxc = function(url, res) { throw new Error(`Not
require("../src/web/routes/download-discord.test")
require("../src/web/routes/download-matrix.test")
require("../src/web/routes/guild.test")
require("../src/web/routes/guild-settings.test")
require("../src/web/routes/link.test")
require("../src/web/routes/log-in-with-matrix.test")
})()