Add MineGeo gameplay systems and interface

This commit is contained in:
2026-10-10 10:27:47 +05:00
parent bf01d9801d
commit 9af71c85c2
47 changed files with 9954 additions and 93 deletions

View File

@@ -0,0 +1,247 @@
import assert from "node:assert/strict";
import { randomUUID, randomBytes } from "node:crypto";
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 = [],
maps = [];
const checked = (r) => {
if (r.error) throw new Error(r.error.message);
return r.data;
};
async function request(
account,
path,
body,
method = body === undefined ? "GET" : "POST",
status = 200,
) {
const response = await fetch(`${origin}/api/v1/${path}`, {
method,
headers: {
origin,
"content-type": "application/json",
...(account?.cookie ? { cookie: account.cookie } : {}),
},
...(body === undefined ? {} : { body: JSON.stringify(body) }),
signal: AbortSignal.timeout(60000),
});
const data = await response.json();
assert.equal(response.status, status, `${path}: ${JSON.stringify(data)}`);
return data;
}
async function account(role) {
const email = `admin-permissions-${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: `Permissions ${role} fixture` },
}),
);
const fixture = { id: user.id, cookie: "" };
accounts.push(fixture);
checked(
await db.from("profiles").insert({
id: user.id,
role,
display_name: `Permissions ${role} fixture`,
}),
);
const response = await fetch(`${origin}/api/v1/auth`, {
method: "POST",
headers: { origin, "content-type": "application/json" },
body: JSON.stringify({
action: "login",
email,
password,
next: "/moderation",
}),
});
assert.equal(response.status, 200, "Fixture login failed");
fixture.cookie = response.headers
.getSetCookie()
.map((value) => value.split(";")[0])
.join("; ");
assert.ok(fixture.cookie, "Login did not set a session cookie");
return fixture;
}
try {
const player = await account("contributor"),
moderator = await account("moderator"),
admin = await account("admin"),
missing = randomUUID();
const bounds = { minX: 0, minZ: 0, maxX: 64, maxZ: 64 };
for (const state of ["review", "unpublished"]) {
const map = checked(
await db
.from("datasets")
.insert({
owner_id: player.id,
name: `Permissions ${state} fixture`,
source: "plugin",
bounds,
state,
panorama_count: 5,
})
.select("id")
.single(),
);
maps.push(map.id);
checked(
await db.from("dataset_inputs").insert({
dataset_id: map.id,
validated: true,
manifest: {
format: "minegeo-dataset/v1",
minecraftVersion: "1.21.11",
dimension: "minecraft:overworld",
source: "plugin",
name: "Permissions map fixture",
description: "Disposable permission verification",
serverName: "Fixture server",
north: "-z",
bounds,
viewpoints: Array.from({ length: 5 }, (_, i) => ({
id: `p${i}`,
x: i * 4,
y: 64,
z: i * 4,
})),
assets: [
{ path: "terrain.json.gz", sha256: "a".repeat(64), bytes: 1 },
],
},
}),
);
}
await request(player, "moderation", undefined, "GET", 403);
const queue = await request(moderator, "moderation");
assert.equal(queue.role, "moderator");
assert.deepEqual(queue.reports, []);
assert.deepEqual(queue.appeals, []);
assert.ok(
queue.maps.every(
(m) =>
m.state === "review" ||
(m.state === "published" && m.update_state === "review"),
),
);
assert.ok(queue.maps.some((m) => m.id === maps[0]));
assert.ok(!queue.maps.some((m) => m.id === maps[1]));
assert.ok(
(await request(moderator, `moderation/${maps[0]}/panoramas`)).locations
.length === 5,
);
await request(
moderator,
`moderation/${maps[1]}/panoramas`,
undefined,
"GET",
403,
);
for (const actor of [player, moderator]) {
for (const [path, body, method] of [
["moderation/users", undefined, "GET"],
[
`moderation/users/${player.id}`,
{ prefix: "Spoof", icon: "star" },
"PATCH",
],
["moderation/settings", { betaBadgeEnabled: false }, "PATCH"],
[
`moderation/appeals/${missing}`,
{ decision: "approve", reason: "A valid reason here." },
"POST",
],
[`moderation/reports/${missing}/panoramas`, undefined, "GET"],
[
`reports/${missing}`,
{ decision: "confirm", reason: "A valid reason here." },
"PATCH",
],
["moderation", { datasetId: maps[0], decision: "unpublish" }, "POST"],
])
await request(actor, path, body, method, 403);
}
await request(moderator, "moderation", {
datasetId: maps[0],
decision: "reject",
reason: "Returned by the permission verification fixture.",
});
assert.equal(
checked(
await db.from("datasets").select("state").eq("id", maps[0]).single(),
).state,
"rejected",
);
const full = await request(admin, "moderation");
assert.equal(full.role, "admin");
assert.ok(full.maps.some((m) => m.id === maps[1]));
assert.equal(
(await request(admin, `moderation/users?q=${player.id}`)).users[0].id,
player.id,
);
await request(
admin,
`moderation/users/${player.id}`,
{ prefix: "Builder", icon: "gem" },
"PATCH",
);
assert.equal(
(await request(player, "profile")).profile.custom_prefix,
"Builder",
);
await request(
admin,
`moderation/users/${player.id}`,
{ prefix: null, icon: "star" },
"PATCH",
);
await request(
admin,
"moderation/settings",
{ betaBadgeEnabled: "invalid" },
"PATCH",
400,
);
await request(
admin,
`reports/${missing}`,
{ decision: "dismiss", reason: "A valid reason here." },
"PATCH",
400,
);
await request(
admin,
`moderation/appeals/${missing}`,
{ decision: "approve", reason: "A valid reason here." },
"POST",
400,
);
for (const actor of [player, moderator, admin])
await request(actor, "profile", { role: "admin" }, "PATCH", 400);
assert.equal((await request(admin, "profile")).profile.role, "admin");
console.log(
"Production permissions passed: signed-in players denied, moderator queue restricted, pending panoramas and feedback work, admin controls available, role spoofing denied.",
);
} finally {
if (maps.length)
checked(await db.from("review_decisions").delete().in("dataset_id", maps));
for (const id of maps)
checked(await db.from("datasets").delete().eq("id", id));
for (const fixture of accounts) {
if (fixture.cookie)
await request(fixture, "auth/logout", {}).catch(() => {});
checked(await db.from("profiles").delete().eq("id", fixture.id));
checked(await db.auth.admin.deleteUser(fixture.id));
}
console.log("Disposable permission fixtures removed.");
}

View File

@@ -0,0 +1,190 @@
import assert from "node:assert/strict";
import { randomUUID } from "node:crypto";
import { readFile, readdir } from "node:fs/promises";
import { PGlite } from "../.data/sql-verification/node_modules/@electric-sql/pglite/dist/index.js";
const db = new PGlite();
try {
await db.exec(`create role anon; create role authenticated; create role service_role;
create schema auth; create table auth.users(id uuid primary key);
create function auth.uid() returns uuid language sql as $$ select null::uuid $$;
create schema storage; create table storage.buckets(id text primary key,name text,public boolean,file_size_limit bigint);`);
for (const file of (await readdir("supabase/migrations"))
.filter((f) => f.endsWith(".sql"))
.sort())
await db.exec(await readFile(`supabase/migrations/${file}`, "utf8"));
const player = randomUUID(),
moderator = randomUUID(),
admin = randomUUID(),
missing = randomUUID();
for (const [id, role] of [
[player, "contributor"],
[moderator, "moderator"],
[admin, "admin"],
]) {
await db.query("insert into auth.users values($1)", [id]);
await db.query("insert into public.profiles(id,role) values($1,$2)", [
id,
role,
]);
}
const makeMap = async () => {
const { rows } = await db.query(
`insert into public.datasets(owner_id,name,source,bounds,state,panorama_count)
values($1,'Permissions fixture','plugin','{"minX":0,"minZ":0,"maxX":64,"maxZ":64}','review',5) returning id`,
[player],
);
const id = rows[0].id;
await db.query(
"insert into public.dataset_inputs(dataset_id,manifest,validated) values($1,$2,true)",
[
id,
JSON.stringify({
name: "Permissions fixture",
description: "Role verification",
viewpoints: Array.from({ length: 5 }, (_, i) => ({
id: `p${i}`,
assetId: `p${i}`,
})),
}),
],
);
return id;
};
const id = await makeMap();
const decide = (map, reviewer, decision, reason = null) =>
db.query("select public.publish_dataset($1,$2,$3,$4)", [
map,
reviewer,
decision,
reason,
]);
await assert.rejects(
decide(id, player, "approve"),
/Moderator permission required/,
);
await decide(id, moderator, "approve");
assert.equal(
(await db.query("select state from public.datasets where id=$1", [id]))
.rows[0].state,
"published",
);
await assert.rejects(
decide(id, moderator, "unpublish"),
/Admin permission required/,
);
await decide(id, admin, "unpublish");
assert.equal(
(await db.query("select state from public.datasets where id=$1", [id]))
.rows[0].state,
"unpublished",
);
const rejected = await makeMap();
await decide(
rejected,
moderator,
"reject",
"Please fix the missing terrain.",
);
assert.equal(
(
await db.query("select state from public.datasets where id=$1", [
rejected,
])
).rows[0].state,
"rejected",
);
const updated = await makeMap();
await decide(updated, admin, "approve");
await db.query(
"update public.datasets set update_state='review' where id=$1",
[updated],
);
await decide(updated, moderator, "approve");
assert.equal(
(
await db.query("select update_state from public.datasets where id=$1", [
updated,
])
).rows[0].update_state,
null,
);
for (const actor of [player, moderator]) {
await assert.rejects(
db.query("select public.set_beta_badge($1,false)", [actor]),
/Admin permission required/,
);
await assert.rejects(
db.query("select public.set_user_prefix($1,$2,'Spoof','crown')", [
actor,
player,
]),
/Admin permission required/,
);
await assert.rejects(
db.query(
"select public.review_content_report($1,$2,'dismiss','Valid feedback here')",
[missing, actor],
),
/Admin permission required/,
);
await assert.rejects(
db.query(
"select public.review_map_appeal($1,$2,'approve','Valid feedback here')",
[missing, actor],
),
/Admin permission required/,
);
}
await db.query("select public.set_beta_badge($1,false)", [admin]);
assert.equal(
(await db.query("select beta_badge_enabled from public.site_settings"))
.rows[0].beta_badge_enabled,
false,
);
await db.query("select public.set_user_prefix($1,$2,'Builder','gem')", [
admin,
player,
]);
assert.equal(
(
await db.query("select custom_prefix from public.profiles where id=$1", [
player,
])
).rows[0].custom_prefix,
"Builder",
);
await db.exec(
"create role identity_probe bypassrls; grant select,update on public.profiles to identity_probe; grant usage on schema public to identity_probe; set role identity_probe",
);
await assert.rejects(
db.query("update public.profiles set role='admin' where id=$1", [player]),
/Moderator permission required/,
);
await db.exec("reset role");
for (const role of ["anon", "authenticated"]) {
await db.exec(`set role ${role}`);
for (const query of [
[
"select public.publish_dataset($1,$2,'unpublish',null)",
[updated, admin],
],
["select public.set_beta_badge($1,true)", [admin]],
["select public.set_user_prefix($1,$2,'Spoof','star')", [admin, player]],
[
"select public.review_content_report($1,$2,'dismiss','Valid feedback here')",
[missing, admin],
],
[
"select public.review_map_appeal($1,$2,'approve','Valid feedback here')",
[missing, admin],
],
])
await assert.rejects(db.query(...query), /permission denied/);
await db.exec("reset role");
}
console.log(
"Admin permissions SQL passed: moderators approve/reject maps and updates; administrative actions and role spoofing are blocked; admins retain full access.",
);
} finally {
await db.close();
}

View File

@@ -167,16 +167,15 @@ try {
);
const mod = randomUUID();
await db.query("insert into auth.users values($1)", [mod]);
await db.query(
"insert into public.profiles(id,role) values($1,'moderator')",
[mod],
);
await db.query("insert into public.profiles(id,role) values($1,'admin')", [
mod,
]);
await assert.rejects(
db.query("select public.set_user_prefix($1,$2,'Builder','gem')", [
account,
mod,
]),
/Moderator permission/,
/Admin permission/,
);
await db.query("select public.set_user_prefix($1,$2,'Builder','gem')", [
mod,
@@ -205,6 +204,7 @@ try {
await db.exec("set role reward_probe");
for (const column of [
"role='moderator'",
"role='admin'",
"bug_finder=false",
"custom_prefix='Spoof'",
"custom_prefix_icon='crown'",

View 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.");
}

View File

@@ -0,0 +1,391 @@
import assert from "node:assert/strict";
import { randomUUID } from "node:crypto";
import { readFile, readdir } from "node:fs/promises";
import { PGlite } from "../.data/sql-verification/node_modules/@electric-sql/pglite/dist/index.js";
const db = new PGlite();
try {
await db.exec(`create role anon;create role authenticated;create role service_role;
create schema auth;create table auth.users(id uuid primary key);
create function auth.uid() returns uuid language sql as $$select null::uuid$$;
create schema storage;create table storage.buckets(id text primary key,name text,public boolean,file_size_limit bigint);`);
for (const file of (await readdir("supabase/migrations"))
.filter((f) => f.endsWith(".sql"))
.sort())
await db.exec(await readFile(`supabase/migrations/${file}`, "utf8"));
const owner = randomUUID(),
mod = randomUUID(),
admin = randomUUID();
for (const [id, role] of [
[owner, "contributor"],
[mod, "moderator"],
[admin, "admin"],
]) {
await db.query("insert into auth.users values($1)", [id]);
await db.query("insert into public.profiles(id,role) values($1,$2)", [
id,
role,
]);
}
const manifest = {
name: "Discord fixture",
description: "Notification verification",
bounds: { minX: 0, minZ: 0, maxX: 64, maxZ: 64 },
viewpoints: Array.from({ length: 5 }, (_, i) => ({
id: `p${i}`,
x: i,
y: 64,
z: i,
})),
};
const map = (
await db.query(
`insert into public.datasets(owner_id,name,source,bounds,state,panorama_count) values($1,'Discord fixture','plugin',$2,'review',5) returning id`,
[owner, JSON.stringify(manifest.bounds)],
)
).rows[0].id;
await db.query(
"insert into public.dataset_inputs(dataset_id,manifest,validated) values($1,$2,true)",
[map, JSON.stringify(manifest)],
);
assert.equal(
(
await db.query(
"select count(*)::int as n from public.discord_announcements",
)
).rows[0].n,
0,
);
await db.query("select public.publish_dataset($1,$2,'approve',null)", [
map,
mod,
]);
assert.equal(
(
await db.query(
"select count(*)::int as n from public.discord_announcements where kind='map' and target_id=$1",
[map],
)
).rows[0].n,
1,
);
await db.query(
"update public.datasets set update_state='review' where id=$1",
[map],
);
await db.query("select public.publish_dataset($1,$2,'approve',null)", [
map,
mod,
]);
await db.query("select public.publish_dataset($1,$2,'unpublish',null)", [
map,
admin,
]);
await db.query("update public.datasets set state='review' where id=$1", [
map,
]);
await db.query("select public.publish_dataset($1,$2,'approve',null)", [
map,
admin,
]);
assert.equal(
(
await db.query(
"select count(*)::int as n from public.discord_announcements",
)
).rows[0].n,
1,
"updates and re-publication do not spam new-map announcements",
);
const hash = "a".repeat(64),
friend = "b".repeat(64),
outsider = "c".repeat(64);
const create = async () =>
(
await db.query(
"select public.create_map_room($1,'normal','Host fixture',4,$2,$3) as id",
[map, hash, owner],
)
).rows[0].id;
const room = await create();
await db.query(
"select public.multiplayer_action($1,$2,'join','{\"name\":\"Friend fixture\"}')",
[room, friend],
);
for (const secret of [friend, outsider])
await assert.rejects(
db.query("select public.publish_discord_party($1,$2)", [room, secret]),
/Only the room owner/,
);
const event = (
await db.query("select public.publish_discord_party($1,$2) as id", [
room,
hash,
])
).rows[0].id;
assert.equal(
(
await db.query("select public.publish_discord_party($1,$2) as id", [
room,
hash,
])
).rows[0].id,
event,
"repeated host clicks return the same event",
);
const claimed = (
await db.query(
"select * from public.claim_discord_announcements('first',array['party'],null)",
)
).rows;
assert.equal(claimed.length, 1);
assert.equal(claimed[0].attempts, 1);
assert.equal(
(
await db.query(
"select * from public.claim_discord_announcements('second',array['party'],null)",
)
).rows.length,
0,
"active leases prevent double sends",
);
await db.query(
"update public.discord_announcements set lease_until=clock_timestamp()-interval '1 second' where id=$1",
[event],
);
assert.equal(
(
await db.query(
"select * from public.claim_discord_announcements('second',array['party'],null)",
)
).rows[0].attempts,
2,
"expired workers recover",
);
await db.query(
"update public.discord_announcements set attempts=8,lease_until=clock_timestamp()-interval '1 second' where id=$1",
[event],
);
assert.equal(
(
await db.query(
"select * from public.claim_discord_announcements('third',array['party'],null)",
)
).rows.length,
0,
);
assert.equal(
(
await db.query(
"select state from public.discord_announcements where id=$1",
[event],
)
).rows[0].state,
"failed",
"exhausted expired leases become actionable failures",
);
await assert.rejects(
db.query("select public.retry_discord_announcements($1)", [mod]),
/Admin permission/,
);
await db.query("select public.retry_discord_announcements($1)", [admin]);
assert.equal(
(
await db.query(
"select state from public.discord_announcements where id=$1",
[event],
)
).rows[0].state,
"pending",
);
const full = await create();
for (let i = 0; i < 3; i++)
await db.query("select public.multiplayer_action($1,$2,'join',$3)", [
full,
String(i + 4).repeat(64),
JSON.stringify({ name: `Player ${i}` }),
]);
await assert.rejects(
db.query("select public.publish_discord_party($1,$2)", [full, hash]),
/room is full/,
);
const expired = await create();
await db.query(
"update public.multiplayer_rooms set created_at=clock_timestamp()-interval '25 hours' where id=$1",
[expired],
);
await assert.rejects(
db.query("select public.publish_discord_party($1,$2)", [expired, hash]),
/Only open lobbies/,
);
await db.query(
"update public.multiplayer_rooms set status='closed' where id=$1",
[room],
);
await assert.rejects(
db.query("select public.publish_discord_party($1,$2)", [room, hash]),
/Only open lobbies/,
);
await db.query(
"update public.discord_announcements set state='sent',channel_id='123456789012345678',message_id='223456789012345678' where id=$1",
[event],
);
let cleanup = (
await db.query(
"select * from public.claim_discord_cleanup('starting:first',$1)",
[room],
)
).rows;
assert.equal(
cleanup.length,
1,
"a send acknowledged after closure is cleaned up",
);
assert.ok(cleanup[0].closed_at);
assert.equal(
(
await db.query(
"select * from public.claim_discord_cleanup('starting:second',$1)",
[room],
)
).rows.length,
0,
"cleanup scheduling has a lease",
);
const deadline = (
await db.query("select public.mark_discord_party_closed($1) as deadline", [
event,
])
).rows[0].deadline;
assert.ok(
Math.abs(new Date(deadline).getTime() - Date.now() - 300000) < 5000,
"red notice gets five minutes",
);
assert.equal(
String(
(
await db.query(
"select public.mark_discord_party_closed($1) as deadline",
[event],
)
).rows[0].deadline,
),
String(deadline),
"retry never extends deletion deadline",
);
await db.query(
"update public.discord_announcements set cleanup_run_id='run_active',cleanup_lease_until=null where id=$1",
[event],
);
assert.equal(
(
await db.query(
"select * from public.claim_discord_cleanup('starting:third',$1)",
[room],
)
).rows.length,
0,
"durable workflow remains scheduled beyond the lease",
);
await db.query(
"update public.discord_announcements set deleted_at=clock_timestamp(),cleanup_run_id=null where id=$1",
[event],
);
assert.equal(
(
await db.query(
"select * from public.claim_discord_cleanup('starting:fourth',$1)",
[room],
)
).rows.length,
0,
"removed messages stay removed",
);
const closedSentRoom = await create();
const closedSentEvent = (
await db.query("select public.publish_discord_party($1,$2) as id", [
closedSentRoom,
hash,
])
).rows[0].id;
await db.query(
"update public.discord_announcements set state='sent',channel_id='123456789012345678',message_id='223456789012345679' where id=$1",
[closedSentEvent],
);
assert.equal(
(
await db.query(
"select * from public.claim_discord_cleanup('starting:open',$1)",
[closedSentRoom],
)
).rows.length,
0,
"open rooms are untouched",
);
await db.query("select public.multiplayer_action($1,$2,'leave','{}')", [
closedSentRoom,
hash,
]);
assert.equal(
(
await db.query(
"select * from public.claim_discord_cleanup('starting:leave',$1)",
[closedSentRoom],
)
).rows.length,
1,
"last-player departure queues cleanup",
);
const hiddenRoom = await create();
await db.query("select public.publish_dataset($1,$2,'unpublish',null)", [
map,
admin,
]);
await assert.rejects(
db.query("select public.publish_discord_party($1,$2)", [hiddenRoom, hash]),
/Only parties on published maps/,
);
const imported = (
await db.query(
`insert into public.datasets(owner_id,name,source,bounds,state,panorama_count,imported_from_id,imported_owner_name) values($1,'Imported fixture','plugin',$2,'draft',5,$3,'Original owner') returning id`,
[owner, JSON.stringify(manifest.bounds), map],
)
).rows[0].id;
await db.query(
"insert into public.dataset_inputs(dataset_id,manifest,validated) values($1,$2,true)",
[imported, JSON.stringify(manifest)],
);
const privateRoom = (
await db.query(
"select public.create_map_room($1,'normal','Host fixture',4,$2,$3) as id",
[imported, hash, owner],
)
).rows[0].id;
await assert.rejects(
db.query("select public.publish_discord_party($1,$2)", [privateRoom, hash]),
/Only parties on published maps/,
);
for (const role of ["anon", "authenticated"]) {
await db.exec(`set role ${role}`);
await assert.rejects(
db.query("select * from public.discord_announcements"),
/permission denied/,
);
for (const query of [
["select public.publish_discord_party($1,$2)", [room, hash]],
[
"select * from public.claim_discord_announcements('spoof',array['map'],null)",
[],
],
["select public.retry_discord_announcements($1)", [admin]],
["select * from public.claim_discord_cleanup('spoof',null)", []],
["select public.mark_discord_party_closed($1)", [event]],
])
await assert.rejects(db.query(...query), /permission denied/);
await db.exec("reset role");
}
console.log(
"Discord SQL passed: first-publication events, one post per party, host authorization, private-map isolation, channel-independent queue, leases, retries, expiry, capacity and server-only access.",
);
} finally {
await db.close();
}

View File

@@ -70,7 +70,7 @@ async function account(role) {
email_confirm: true,
user_metadata: {
display_name:
role === "moderator" ? "Prefix moderator test" : "Badge player test",
role === "admin" ? "Prefix admin test" : "Badge player test",
},
}),
);
@@ -81,7 +81,7 @@ async function account(role) {
id,
role,
display_name:
role === "moderator" ? "Prefix moderator test" : "Badge player test",
role === "admin" ? "Prefix admin test" : "Badge player test",
}),
);
const context = await browser.newContext({ locale: "en-US" });
@@ -98,7 +98,7 @@ try {
await mkdir(".data/support-deploy", { recursive: true });
await phase("starting");
const player = await account("contributor"),
mod = await account("moderator");
mod = await account("admin");
await api(player.context, "profile", { glowColor: "#54baff" }, "PATCH", 403);
await api(
player.context,
@@ -118,7 +118,7 @@ try {
await player.page.goto(origin + "/settings", { waitUntil: "networkidle" });
assert.equal(await player.page.locator("input[type=color]").count(), 0);
await mod.page.goto(origin + "/settings", { waitUntil: "networkidle" });
assert.ok((await mod.page.locator(".username-moderator svg").count()) > 0);
assert.ok((await mod.page.locator(".username-admin svg").count()) > 0);
await mod.page.goto(origin + "/moderation", { waitUntil: "networkidle" });
await mod.page
.getByLabel("Player name or account ID", { exact: true })
@@ -343,7 +343,7 @@ try {
);
await phase("passed");
console.log(
"Profile appearance production passed: moderator-only prefixes, removal, automatic badges, real webhook grant/revoke, persistent color, authenticated multiplayer/chat, guest spoof prevention and mobile layouts.",
"Profile appearance production passed: admin-only prefixes, removal, automatic badges, real webhook grant/revoke, persistent color, authenticated multiplayer/chat, guest spoof prevention and mobile layouts.",
);
} catch (error) {
for (const context of contexts)

View File

@@ -1,21 +1,21 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import { readFile, readdir } from "node:fs/promises";
import { PGlite } from "../.data/sql-verification/node_modules/@electric-sql/pglite/dist/index.js";
const db = new PGlite();
try {
await db.exec(`create role anon; create role authenticated; create role service_role bypassrls;
create table public.profiles(id uuid primary key, role text not null);
grant select on public.profiles to service_role;
insert into public.profiles values
('00000000-0000-4000-8000-000000000001', 'moderator'),
('00000000-0000-4000-8000-000000000002', 'contributor');`);
await db.exec(
await readFile(
"supabase/migrations/20261006141532_site_settings.sql",
"utf8",
),
);
create schema auth; create table auth.users(id uuid primary key);
create function auth.uid() returns uuid language sql as $$ select null::uuid $$;
create schema storage; create table storage.buckets(id text primary key,name text,public boolean,file_size_limit bigint);`);
for (const file of (await readdir("supabase/migrations"))
.filter((f) => f.endsWith(".sql"))
.sort())
await db.exec(await readFile(`supabase/migrations/${file}`, "utf8"));
await db.exec(`insert into auth.users values
('00000000-0000-4000-8000-000000000001'),('00000000-0000-4000-8000-000000000002');
insert into public.profiles(id,role) values
('00000000-0000-4000-8000-000000000001','admin'),('00000000-0000-4000-8000-000000000002','contributor');`);
const flag = async () =>
(await db.query("select beta_badge_enabled from public.site_settings"))
.rows[0].beta_badge_enabled;
@@ -39,11 +39,11 @@ try {
db.query(
"select public.set_beta_badge('00000000-0000-4000-8000-000000000002', false)",
),
/Moderator permission required/,
/Admin permission required/,
);
await assert.rejects(
db.query("select public.set_beta_badge(null, false)"),
/Moderator permission required/,
/Admin permission required/,
);
assert.equal(await flag(), true);
await db.query(
@@ -66,7 +66,7 @@ try {
/visibility is required/,
);
console.log(
"Site settings SQL passed: guests and players denied; moderators can hide and restore; updates persist and record the moderator.",
"Site settings SQL passed: guests and players denied; admins can hide and restore; updates persist and record the admin.",
);
} finally {
await db.close();