Add MineGeo gameplay systems and interface
This commit is contained in:
184
scripts/test-discord-http.mjs
Normal file
184
scripts/test-discord-http.mjs
Normal file
@@ -0,0 +1,184 @@
|
||||
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.");
|
||||
}
|
||||
Reference in New Issue
Block a user