Require distinct panorama locations for rounds and captures
Some checks failed
Deploy MineGeo to Dokploy / deploy (push) Failing after 6m30s
Some checks failed
Deploy MineGeo to Dokploy / deploy (push) Failing after 6m30s
This commit is contained in:
199
scripts/test-round-panoramas-sql.mjs
Normal file
199
scripts/test-round-panoramas-sql.mjs
Normal file
@@ -0,0 +1,199 @@
|
||||
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";
|
||||
|
||||
// Real game-creation functions, isolated from production sessions and scores.
|
||||
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,allowed_mime_types text[]);
|
||||
create table storage.objects(id uuid primary key,bucket_id text,name text,owner_id text);
|
||||
create function storage.foldername(text) returns text[] language sql as $$ select string_to_array($1, '/') $$;`);
|
||||
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 = (
|
||||
await db.query(
|
||||
"insert into auth.users values(gen_random_uuid()) returning id",
|
||||
)
|
||||
).rows[0].id;
|
||||
const map = (
|
||||
await db.query(
|
||||
`insert into public.datasets(owner_id,name,source,bounds,state,panorama_count)
|
||||
values($1,'Repeated capture fixture','fabric','{"minX":-64,"minZ":-64,"maxX":64,"maxZ":64}','published',17) returning id`,
|
||||
[owner],
|
||||
)
|
||||
).rows[0].id;
|
||||
const points = Array.from({ length: 10 }, (_, i) => ({
|
||||
id: `p${i}`,
|
||||
x: i - 5,
|
||||
y: 65,
|
||||
z: i - 5,
|
||||
}));
|
||||
// Reproduce the live map: unique IDs, but seven captures repeat a position.
|
||||
const manifest = {
|
||||
viewpoints: [
|
||||
...points,
|
||||
...points.slice(0, 7).map((p, i) => ({ ...p, id: `repeat${i}` })),
|
||||
],
|
||||
};
|
||||
const revision = (
|
||||
await db.query(
|
||||
"insert into public.revisions(dataset_id,asset_root,manifest) values($1,'fixture',$2) returning id",
|
||||
[map, JSON.stringify(manifest)],
|
||||
)
|
||||
).rows[0].id;
|
||||
await db.query("update public.datasets set revision_id=$1 where id=$2", [
|
||||
revision,
|
||||
map,
|
||||
]);
|
||||
const pool = async (viewpoints) =>
|
||||
(
|
||||
await db.query("select public.game_viewpoint_pool($1,'fixture') pool", [
|
||||
JSON.stringify({ viewpoints }),
|
||||
])
|
||||
).rows[0].pool;
|
||||
assert.equal((await pool(manifest.viewpoints)).length, 10);
|
||||
assert.equal(
|
||||
(
|
||||
await pool([
|
||||
{ id: "a", x: -0.1, y: 65, z: 0 },
|
||||
{ id: "b", x: -0.9, y: 65.8, z: 0.8 },
|
||||
])
|
||||
).length,
|
||||
1,
|
||||
);
|
||||
assert.equal(
|
||||
(
|
||||
await pool([
|
||||
{ id: "a", x: 0, y: 65, z: 0 },
|
||||
{ id: "b", x: 4, y: 65, z: 4, assetId: "a" },
|
||||
])
|
||||
).length,
|
||||
1,
|
||||
);
|
||||
assert.equal(
|
||||
(
|
||||
await pool([
|
||||
{ id: "a", x: 0, y: 65, z: 0 },
|
||||
{ id: "b", x: 4, y: 65, z: 4, assetId: "a", assetRoot: owner },
|
||||
])
|
||||
).length,
|
||||
2,
|
||||
);
|
||||
|
||||
for (let i = 0; i < 30; i++) {
|
||||
const solo = (
|
||||
await db.query("select public.create_game($1,'normal',$2,$3) id", [
|
||||
map,
|
||||
owner,
|
||||
String(i).padStart(64, "0"),
|
||||
])
|
||||
).rows[0].id;
|
||||
const multi = (
|
||||
await db.query(
|
||||
"select public.create_map_room($1,'hard','Host',2,$2,$3) id",
|
||||
[map, String(i).padStart(64, "0"), owner],
|
||||
)
|
||||
).rows[0].id;
|
||||
for (const [table, column, id] of [
|
||||
["game_rounds", "session_id", solo],
|
||||
["multiplayer_rounds", "room_id", multi],
|
||||
]) {
|
||||
const rounds = (
|
||||
await db.query(
|
||||
`select round_number,viewpoint from public.${table} where ${column}=$1 order by round_number`,
|
||||
[id],
|
||||
)
|
||||
).rows;
|
||||
assert.deepEqual(
|
||||
rounds.map((r) => r.round_number),
|
||||
[0, 1, 2, 3, 4],
|
||||
);
|
||||
assert.equal(
|
||||
new Set(
|
||||
rounds.map((r) =>
|
||||
[r.viewpoint.x, r.viewpoint.y, r.viewpoint.z]
|
||||
.map(Math.floor)
|
||||
.join(","),
|
||||
),
|
||||
).size,
|
||||
5,
|
||||
);
|
||||
assert.equal(
|
||||
new Set(
|
||||
rounds.map(
|
||||
(r) =>
|
||||
`${r.viewpoint.assetRoot ?? "fixture"}/${r.viewpoint.assetId ?? r.viewpoint.id}`,
|
||||
),
|
||||
).size,
|
||||
5,
|
||||
);
|
||||
}
|
||||
}
|
||||
const countBefore = (
|
||||
await db.query("select count(*)::int n from public.game_sessions")
|
||||
).rows[0].n;
|
||||
const insufficient = (
|
||||
await db.query(
|
||||
"insert into public.revisions(dataset_id,asset_root,manifest) values($1,'fixture',$2) returning id",
|
||||
[
|
||||
map,
|
||||
JSON.stringify({
|
||||
viewpoints: [...points.slice(0, 4), { ...points[0], id: "repeat" }],
|
||||
}),
|
||||
],
|
||||
)
|
||||
).rows[0].id;
|
||||
await db.query("update public.datasets set revision_id=$1 where id=$2", [
|
||||
insufficient,
|
||||
map,
|
||||
]);
|
||||
await assert.rejects(
|
||||
db.query("select public.create_game($1,'normal',$2,$3)", [
|
||||
map,
|
||||
owner,
|
||||
"0".repeat(64),
|
||||
]),
|
||||
/five distinct/,
|
||||
);
|
||||
await assert.rejects(
|
||||
db.query("select public.create_map_room($1,'normal','Host',2,$2,$3)", [
|
||||
map,
|
||||
"0".repeat(64),
|
||||
owner,
|
||||
]),
|
||||
/five distinct/,
|
||||
);
|
||||
assert.equal(
|
||||
(await db.query("select count(*)::int n from public.game_sessions")).rows[0]
|
||||
.n,
|
||||
countBefore,
|
||||
);
|
||||
assert.equal(
|
||||
(
|
||||
await db.query(
|
||||
"select has_function_privilege('anon','public.game_viewpoint_pool(jsonb,text)','EXECUTE') allowed",
|
||||
)
|
||||
).rows[0].allowed,
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
(
|
||||
await db.query(
|
||||
"select has_function_privilege('service_role','public.game_viewpoint_pool(jsonb,text)','EXECUTE') allowed",
|
||||
)
|
||||
).rows[0].allowed,
|
||||
true,
|
||||
);
|
||||
console.log(
|
||||
"Round panorama SQL regressions passed: 30 solo games, 30 multiplayer rooms, duplicate positions/media, insufficient locations and permissions.",
|
||||
);
|
||||
} finally {
|
||||
await db.close();
|
||||
}
|
||||
Reference in New Issue
Block a user