Add player leaderboard and homepage preview
This commit is contained in:
137
scripts/test-leaderboard-sql.mjs
Normal file
137
scripts/test-leaderboard-sql.mjs
Normal file
@@ -0,0 +1,137 @@
|
||||
// Isolated database only; no fixture accounts or scores enter the real board.
|
||||
import assert from "node:assert/strict";
|
||||
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((file) => file.endsWith(".sql"))
|
||||
.sort()) {
|
||||
await db.exec(await readFile(`supabase/migrations/${file}`, "utf8"));
|
||||
}
|
||||
async function board(difficulty = "all", search = "", page = 1, limit = 25) {
|
||||
return (
|
||||
await db.query("select public.player_leaderboard($1,$2,$3,$4) as board", [
|
||||
difficulty,
|
||||
search,
|
||||
page,
|
||||
limit,
|
||||
])
|
||||
).rows[0].board;
|
||||
}
|
||||
assert.deepEqual((await board()).entries, []);
|
||||
assert.equal((await board()).page, 1);
|
||||
assert.equal((await board()).pages, 1);
|
||||
|
||||
const ids = [];
|
||||
for (const name of ["Alice", "Bob", "Carol", "%_Miner"]) {
|
||||
const id = (
|
||||
await db.query(
|
||||
"insert into auth.users values(gen_random_uuid()) returning id",
|
||||
)
|
||||
).rows[0].id;
|
||||
await db.query(
|
||||
"insert into public.profiles(id,display_name) values($1,$2)",
|
||||
[id, name],
|
||||
);
|
||||
ids.push(id);
|
||||
}
|
||||
const dataset = (
|
||||
await db.query(
|
||||
`insert into public.datasets(owner_id,name,source,bounds,state,panorama_count)
|
||||
values($1,'Leaderboard fixture','plugin','{"minX":0,"minZ":0,"maxX":64,"maxZ":64}','published',5) returning id`,
|
||||
[ids[0]],
|
||||
)
|
||||
).rows[0].id;
|
||||
const revision = (
|
||||
await db.query(
|
||||
"insert into public.revisions(dataset_id,asset_root,manifest) values($1,'fixture','{}') returning id",
|
||||
[dataset],
|
||||
)
|
||||
).rows[0].id;
|
||||
async function game(
|
||||
owner,
|
||||
difficulty,
|
||||
score,
|
||||
current = 5,
|
||||
missingResult = false,
|
||||
) {
|
||||
const session = (
|
||||
await db.query(
|
||||
"insert into public.game_sessions(revision_id,owner_id,access_hash,difficulty,current_round) values($1,$2,'private-fixture-secret',$3,$4) returning id",
|
||||
[revision, owner, difficulty, current],
|
||||
)
|
||||
).rows[0].id;
|
||||
for (let round = 0; round < 5; round++) {
|
||||
await db.query(
|
||||
"insert into public.game_rounds(session_id,round_number,viewpoint,result) values($1,$2,'{}',$3)",
|
||||
[
|
||||
session,
|
||||
round,
|
||||
missingResult && round === 4
|
||||
? null
|
||||
: JSON.stringify({ score: score / 5 }),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
await game(ids[0], "easy", 19000);
|
||||
await game(ids[0], "hard", 23000);
|
||||
await game(ids[1], "easy", 21000);
|
||||
await game(ids[1], "hard", 23000);
|
||||
await game(ids[2], "easy", 20000);
|
||||
await game(ids[2], "hard", 23000);
|
||||
await game(ids[2], "hard", 23000);
|
||||
await game(ids[3], "easy", 5000);
|
||||
await game(null, "easy", 25000);
|
||||
await game(ids[0], "normal", 25000, 4);
|
||||
await game(ids[0], "normal", 25000, 5, true);
|
||||
|
||||
const all = await board();
|
||||
assert.equal(all.total_players, 4);
|
||||
assert.equal(all.total_games, 8);
|
||||
assert.deepEqual(
|
||||
all.entries.map((player) => player.display_name),
|
||||
["Carol", "Bob", "Alice", "%_Miner"],
|
||||
);
|
||||
assert.equal(all.entries[0].average_score, 22000);
|
||||
assert.equal(all.entries[0].games_played, 3);
|
||||
assert.deepEqual(
|
||||
(await board("easy")).leaders.map((player) => player.display_name),
|
||||
["Bob", "Carol", "Alice"],
|
||||
);
|
||||
assert.equal((await board("normal")).total_players, 0);
|
||||
assert.equal((await board("all", "alice")).entries[0].rank, 3);
|
||||
assert.equal((await board("all", "%_")).matching_players, 1);
|
||||
assert.equal((await board("all", "' OR 1=1--")).matching_players, 0);
|
||||
assert.equal((await board("all", "missing")).pages, 1);
|
||||
const last = await board("all", "", 999, 2);
|
||||
assert.equal(last.page, 2);
|
||||
assert.equal(last.pages, 2);
|
||||
assert.deepEqual(
|
||||
last.entries.map((player) => player.rank),
|
||||
[3, 4],
|
||||
);
|
||||
assert.equal((await board("all", "", -1, 2)).page, 1);
|
||||
assert.equal((await board("all", "", 1, 0)).entries.length, 1);
|
||||
assert.equal((await board(null, null, null, null)).page, 1);
|
||||
assert.equal(JSON.stringify(all).includes("private-fixture-secret"), false);
|
||||
assert.equal(JSON.stringify(all).includes("viewpoint"), false);
|
||||
for (const role of ["anon", "authenticated", "service_role"]) {
|
||||
const result = await db.query(
|
||||
"select has_function_privilege($1,'public.player_leaderboard(text,text,integer,integer)','execute') as allowed",
|
||||
[role],
|
||||
);
|
||||
assert.equal(result.rows[0].allowed, role === "service_role");
|
||||
}
|
||||
console.log(
|
||||
"Leaderboard SQL passed: empty board, completed games, guests, ranking ties, difficulty, literal search, pagination, and server-only permissions.",
|
||||
);
|
||||
} finally {
|
||||
await db.close();
|
||||
}
|
||||
Reference in New Issue
Block a user