Files
MineGeo/scripts/test-discord-http.mjs
pavel444-byte 1f254b29a2
All checks were successful
Deploy MineGeo to Dokploy / deploy (push) Successful in 5m57s
Update MineGeo domain and harden process supervision
2026-10-15 20:35:39 +05:00

188 lines
5.8 KiB
JavaScript

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.site";
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 = await account("contributor"),
friend = session(),
outsider = session();
const created = await host("rooms", {
datasetId: dataset.id,
difficulty: "normal",
playerLimit: 4,
});
rooms.push(created.id);
await journal();
const room = await host(`rooms/${created.id}`);
assert.equal(
room.players.find((p) => p.id === room.hostId).name,
"Discord contributor fixture",
);
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.");
}