Spaces:
Running
Running
File size: 8,464 Bytes
57b63b8 35243f1 57b63b8 35243f1 57b63b8 35243f1 57b63b8 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | import assert from "node:assert/strict";
import { spawn } from "node:child_process";
import { once } from "node:events";
import { createServer } from "node:http";
import type { AddressInfo } from "node:net";
import { test } from "node:test";
test("optimized workspace reads preserve authorization, scores and fresh permission checks", { timeout: 30_000 }, async () => {
const owner = "11111111-1111-4111-8111-111111111111";
const other = "22222222-2222-4222-8222-222222222222";
const playerId = "33333333-3333-4333-8333-333333333333";
const otherPlayerId = "44444444-4444-4444-8444-444444444444";
const calls: { method: string; url: URL }[] = [];
let role = "user";
let revoked = false;
let authUnavailable = false;
let failActions = false;
const user = { id: owner, email: "test@example.com", email_confirmed_at: new Date().toISOString(), user_metadata: {} };
const plan = { subscription_id: "plan", plan: "Pro", price: 10, player_limit: 5, session_limit: 10 };
const player = { player_id: playerId, user_id: owner, fname: "Own", lname: "Player", bdate: "2000-01-01", gender: "male", image_path: null };
const sessions = [
{ session_id: "s1", player_id: playerId, user_id: owner, created_at: "2026-01-02T00:00:00Z", status: "completed", name: "Own session", player },
{ session_id: "s2", player_id: otherPlayerId, user_id: other, created_at: "2026-01-01T00:00:00Z", status: "completed", name: "Other session", player: { fname: "Other" } },
];
const actions = [
{ action_id: "a1", session_id: "s1", type: "Pass", annotation: { score: 7 } },
{ action_id: "a2", session_id: "s1", type: "Pass", annotation: null },
{ action_id: "a3", session_id: "s2", type: "Pass", annotation: { score: 10 } },
];
const upstream = createServer((req, res) => {
const url = new URL(req.url!, "http://stub");
calls.push({ method: req.method!, url });
res.setHeader("Content-Type", "application/json");
if (url.pathname === "/auth/v1/user") {
if (authUnavailable) { res.statusCode = 503; return res.end(JSON.stringify({ message: "Temporarily unavailable" })); }
res.statusCode = revoked ? 401 : 200;
return res.end(JSON.stringify(revoked ? { message: "Revoked" } : user));
}
if (url.pathname === "/auth/v1/token") {
return res.end(JSON.stringify({ user, access_token: "access", refresh_token: "refresh", expires_in: 3600, token_type: "bearer" }));
}
const table = url.pathname.split("/").at(-1);
if (table === "actions" && failActions) {
res.statusCode = 400;
return res.end(JSON.stringify({ message: "Unavailable" }));
}
let rows: Record<string, unknown>[] = table === "users" ? [{ user_id: owner, email: user.email, account_role: role }]
: table === "users_subscriptions" ? [{ user_id: owner, subscription_id: "plan", startdate: "2000-01-01", enddate: "2099-01-01", num_players: 1, num_sessions: 1, plan }]
: table === "players" ? [player, { player_id: otherPlayerId, user_id: other, fname: "Other" }]
: table === "sessions" ? sessions : table === "actions" ? actions : [];
for (const [key, filter] of url.searchParams) {
if (filter.startsWith("eq.")) rows = rows.filter((row) => String(row[key]) === filter.slice(3));
if (filter.startsWith("in.(")) {
const values = filter.slice(4, -1).split(",");
rows = rows.filter((row) => values.includes(String(row[key])));
}
}
const from = Number(url.searchParams.get("offset") ?? 0);
const limit = Number(url.searchParams.get("limit") ?? rows.length);
res.end(JSON.stringify(rows.slice(from, from + limit)));
});
upstream.listen(0, "127.0.0.1");
await once(upstream, "listening");
const portProbe = createServer();
portProbe.listen(0, "127.0.0.1");
await once(portProbe, "listening");
const port = (portProbe.address() as AddressInfo).port;
await new Promise<void>((resolve) => portProbe.close(() => resolve()));
const origin = `http://127.0.0.1:${port}`;
const child = spawn(process.execPath, ["--import", "tsx", "src/server.ts"], {
env: { ...process.env, NODE_ENV: "test", PORT: String(port), FRONTEND_URL: origin, BACKEND_URL: origin,
SUPABASE_URL: `http://127.0.0.1:${(upstream.address() as AddressInfo).port}`,
SUPABASE_ANON_KEY: "test-anon", SUPABASE_SERVICE_ROLE_KEY: "test-service", ANALYSIS_PROXY_SECRET: "x".repeat(32) },
stdio: ["ignore", "pipe", "pipe"],
});
try {
await new Promise<void>((resolve, reject) => {
child.stdout.on("data", (data) => { if (String(data).includes("API listening")) resolve(); });
child.once("exit", (code) => reject(new Error(`API exited: ${code}`)));
child.once("error", reject);
});
const request = (path: string, init: RequestInit = {}) => fetch(`${origin}${path}`, {
...init, signal: AbortSignal.timeout(10000), headers: { Cookie: "sp_access=access", Origin: origin, ...init.headers },
}).catch((error) => { throw new Error(`Request failed: ${path}`, { cause: error }); });
assert.equal((await fetch(`${origin}/api/players-overview`)).status, 401);
authUnavailable = true;
const outage = await request("/api/auth/me", { headers: { Cookie: "sp_access=access; sp_refresh=refresh" } });
assert.equal(outage.status, 500);
assert.equal(outage.headers.get("set-cookie"), null, "an outage must preserve session cookies");
assert.equal(calls.some((call) => call.url.pathname === "/auth/v1/token"), false, "an outage must not rotate refresh tokens");
authUnavailable = false;
const meStart = calls.length;
const me = await (await request("/api/auth/me")).json();
assert.equal(me.access.active, true);
assert.equal(me.access.subscription.plan, "Pro");
assert.equal(calls.length - meStart, 3); // auth + role + joined assignment/plan
const rosterResponse = await request("/api/players-overview");
assert.equal(rosterResponse.status, 200);
const roster = (await rosterResponse.json()).players;
assert.equal(roster.length, 1);
assert.equal(roster[0].scores.overall, 70);
assert.equal(roster[0].actionCount, 2);
assert.equal(roster[0].scores.scoredActions, 1);
const list = (await (await request("/api/sessions-overview")).json()).sessions;
assert.equal(list.length, 1);
assert.equal(list[0].playerName, "Own Player");
assert.equal(list[0].actionBreakdown.Pass, 2);
const detail = await (await request(`/api/players-overview/${playerId}`)).json();
assert.equal(detail.scores.overall, 70);
assert.equal(detail.playerCount, 1);
assert.equal((await request(`/api/players-overview/${otherPlayerId}`)).status, 404);
const billing = await (await request("/api/plan-billing")).json();
assert.equal(billing.subscription.name, "Pro");
assert.equal(billing.usage.sessions, 1);
assert.ok(!calls.some(({ url }) => url.pathname.endsWith("/annotations")));
const login = await request("/api/auth/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: user.email, password: "password123" }) });
assert.equal(login.status, 200);
assert.ok(!calls.some(({ method }) => method === "PATCH"));
role = "admin";
const adminPlayers = await (await request("/api/admin/players")).json();
assert.equal(adminPlayers.players.find((row: { player_id: string }) => row.player_id === playerId).sessionCount, 1);
const adminSessions = await (await request("/api/admin/sessions")).json();
assert.equal(adminSessions.sessions.find((row: { session_id: string }) => row.session_id === "s1").actionCount, 2);
const adminUsers = await (await request("/api/admin/users")).json();
assert.equal(adminUsers.users[0].dataCounts.sessions, 1);
const adminRows = (await (await request("/api/players-overview")).json()).players;
assert.equal(adminRows.length, 2);
role = "user";
assert.equal((await (await request("/api/players-overview")).json()).players.length, 1);
failActions = true;
assert.equal((await request("/api/sessions-overview")).status, 500);
revoked = true;
assert.equal((await request("/api/sessions-overview")).status, 401);
} finally {
if (child.exitCode === null && child.signalCode === null) {
const exited = once(child, "exit");
child.kill();
await exited;
}
upstream.closeAllConnections();
await new Promise<void>((resolve) => upstream.close(() => resolve()));
}
});
|