import assert from "node:assert/strict"; import { randomUUID, randomBytes } from "node:crypto"; import { mkdir, writeFile } from "node:fs/promises"; import { createClient } from "@supabase/supabase-js"; process.loadEnvFile(".env.local"); const origin = process.argv[2] || "https://minegeo.vercel.app"; const db = createClient( process.env.NEXT_PUBLIC_SUPABASE_URL, process.env.SUPABASE_SECRET_KEY, { auth: { persistSession: false, autoRefreshToken: false } }, ); const accounts = [], rooms = []; const checked = (r) => { if (r.error) throw new Error(r.error.message); return r.data; }; await mkdir(".data/discord-check", { recursive: true }); const journal = () => writeFile( ".data/discord-check/http-fixtures.json", JSON.stringify({ accounts, rooms }), ); function session() { const cookies = new Map(); return async ( path, body, method = body === undefined ? "GET" : "POST", expected = 200, ) => { const response = await fetch(`${origin}/api/v1/${path}`, { method, headers: { origin, "content-type": "application/json", cookie: [...cookies] .map(([key, value]) => `${key}=${value}`) .join("; "), }, ...(body === undefined ? {} : { body: JSON.stringify(body) }), signal: AbortSignal.timeout(60000), }); for (const cookie of response.headers.getSetCookie()) { const part = cookie.split(";")[0], at = part.indexOf("="); cookies.set(part.slice(0, at), part.slice(at + 1)); } const data = await response.json(); assert.equal(response.status, expected, `${path}: ${JSON.stringify(data)}`); assert.match(response.headers.get("cache-control") ?? "", /no-store/); return data; }; } async function account(role) { const email = `discord-check-${randomUUID()}@example.com`, password = randomBytes(24).toString("base64url"); const { user } = checked( await db.auth.admin.createUser({ email, password, email_confirm: true, user_metadata: { display_name: `Discord ${role} fixture` }, }), ); accounts.push(user.id); await journal(); checked( await db .from("profiles") .insert({ id: user.id, role, display_name: `Discord ${role} fixture` }), ); const api = session(); await api("auth", { action: "login", email, password, next: "/moderation" }); return api; } try { const admin = await account("admin"), moderator = await account("moderator"), visitor = session(); const status = await admin("moderation/discord"); assert.equal(typeof status.botConfigured, "boolean"); assert.equal(typeof status.pending, "number"); assert.ok( !Object.keys(status).some((key) => /token|secret|channel_?id/i.test(key)), ); await visitor("moderation/discord", undefined, "GET", 401); for (const [body, method] of [ [undefined, "GET"], [{ action: "check" }, "POST"], [{ action: "retry" }, "POST"], ]) await moderator("moderation/discord", body, method, 403); if ( !status.botConfigured || !status.publicUrlConfigured || (!status.mapsConfigured && !status.partiesConfigured) ) await admin("moderation/discord", { action: "check" }, "POST", 400); const dispatch = await fetch(`${origin}/api/integrations/discord/dispatch`); assert.equal(dispatch.status, 401); console.log( "Discord HTTP: admin-only setup, secret-free status, disabled configuration and private dispatch verified.", ); const dataset = checked( await db .from("datasets") .select("id") .eq("state", "published") .is("parent_dataset_id", null) .is("imported_from_id", null) .limit(1) .single(), ); const host = session(), friend = session(), outsider = session(); const created = await host("rooms", { datasetId: dataset.id, difficulty: "normal", displayName: "Discord host fixture", playerLimit: 4, }); rooms.push(created.id); await journal(); const room = await host(`rooms/${created.id}`); assert.equal(typeof room.discord.available, "boolean"); assert.equal(room.discord.state, null); await outsider(`rooms/${created.id}/discord`, {}, "POST", 403); await friend(`rooms/${created.id}/join`, { displayName: "Discord friend fixture", }); assert.equal((await friend(`rooms/${created.id}`)).discord, undefined); await friend(`rooms/${created.id}/discord`, {}, "POST", 403); await host( `rooms/${created.id}/discord`, { channelId: "123456789012345678" }, "POST", 400, ); if ( !status.botConfigured || !status.publicUrlConfigured || !status.partiesConfigured ) { assert.equal(room.discord.available, false); await host(`rooms/${created.id}/discord`, {}, "POST", 503); } assert.equal( checked( await db .from("discord_announcements") .select("id") .eq("kind", "party") .eq("target_id", created.id), ).length, 0, "disabled and unauthorized actions never queue a notification", ); await host(`rooms/${created.id}/close`, {}); console.log( "Discord HTTP passed: guests and members cannot publish, destination overrides denied, unconfigured hosts get an actionable error, no test Discord messages sent.", ); } finally { for (const id of rooms) { checked( await db .from("discord_announcements") .delete() .eq("kind", "party") .eq("target_id", id), ); checked(await db.from("multiplayer_rooms").delete().eq("id", id)); } for (const id of accounts) { checked(await db.from("profiles").delete().eq("id", id)); checked(await db.auth.admin.deleteUser(id)); } await writeFile( ".data/discord-check/http-fixtures.json", JSON.stringify({ accounts: [], rooms: [] }), ); console.log("Discord HTTP fixtures removed."); }