Public: anyone can read this; the crew can change it.

forked from trevato/cs

Take updates from trevato/cs #301

Merged opened this in answer to

Check · Review · Land · Live done — Landed and live in production.

Merged

torque/cs-engine:torque/287-take-updates-from-trevato-cs → main 3 commits fork

  • Merged. torque/287-take-updates-from-trevato-cs was merged into main as 23aee7c.

Review

Sign in to review this change.
2 files changed +65 −4
src/db.test.ts +56 −1
@@ -10,10 +10,12 @@ import { mkdtempSync } from "node:fs";
1010 import { tmpdir } from "node:os";
1111 import { join } from "node:path";
1212 import {
13 dayBoard, displayName, IDLE_SECONDS, liveness, openDatabase, type Player, recentMatches, saveMatch, standings,
13 dayBoard, displayName, IDLE_SECONDS, liveness, openDatabase, type Player, recentMatches, recentRuns,
14 runBoard, saveMatch, saveRun, standings,
1415 } from "./db.ts";
1516 import { playKey } from "./matchid.ts";
1617 import type { Result } from "./result.ts";
18 import type { RangeRun } from "./run.ts";
1719
1820 const dir = (): string => mkdtempSync(join(tmpdir(), "db-"));
1921
@@ -313,3 +315,56 @@ test("a post that knows more still moves every number up", () => {
313315 expect([row.score_mine, row.score_theirs]).toEqual([3, 3]);
314316 expect(liveness(row, Date.now())).toBe("live");
315317 });
318
319 const range = (over: Partial<RangeRun> = {}): RangeRun => ({
320 runId: crypto.randomUUID(),
321 name: "ignored — the player says the name",
322 drill: "popup",
323 score: 1400, hits: 14, shots: 20, seed: 7, seconds: 30,
324 ...over,
325 });
326
327 test("a run on the board is the run that was posted: a second post under its id cannot lower it", () => {
328 // The matches' rule (`saveMatch` above) read for the range: a post that
329 // arrives after the one the board is showing must not walk the numbers
330 // backwards. A match accumulates, so a late post may raise it; a run is one
331 // finished clock, so a second post under its id is the same run again and
332 // changes nothing but the name it is shown under.
333 const db = openDatabase(dir());
334 const id = crypto.randomUUID();
335 const kestrel = player("u-1", "kestrel");
336 expect(saveRun(db, kestrel, range({ runId: id, score: 1400, hits: 14, shots: 20 }))).toBe(true);
337 // The same id again, claiming a worse run — a retry that raced, or a page
338 // that posted twice. It is accepted (it is the same owner's id) and ignored.
339 expect(saveRun(db, kestrel, range({ runId: id, score: 10, hits: 1, shots: 99, seed: 9, seconds: 5 }))).toBe(true);
340
341 const row = recentRuns(db)[0]!;
342 expect([row.score, row.hits, row.shots, row.seed, row.seconds]).toEqual([1400, 14, 20, 7, 30]);
343 // One row, not two, and the board still ranks the run that was played.
344 expect(recentRuns(db).length).toBe(1);
345 expect(runBoard(db, "popup").map((r) => [r.score, r.hits, r.runs])).toEqual([[1400, 14, 1]]);
346
347 // A different run is a different row, and the board takes the better of the
348 // two and says there were two.
349 expect(saveRun(db, kestrel, range({ score: 1600, hits: 16, shots: 18 }))).toBe(true);
350 expect(runBoard(db, "popup").map((r) => [r.score, r.runs])).toEqual([[1600, 2]]);
351
352 // A name is not a number: a player who renames themselves is shown renamed.
353 expect(saveRun(db, player("u-1", "kestrel the second"), range({ runId: id }))).toBe(true);
354 expect(recentRuns(db).find((r) => r.id === id)!.name).toBe("kestrel the second");
355 });
356
357 test("a run belongs to whoever posted it: nobody else writes over that id", () => {
358 const db = openDatabase(dir());
359 const id = crypto.randomUUID();
360 expect(saveRun(db, player("u-1", "kestrel"), range({ runId: id, score: 1400 }))).toBe(true);
361 // Another identity, and a guest, both bounce off it.
362 expect(saveRun(db, player("u-2", "wren"), range({ runId: id, score: 9000 }))).toBe(false);
363 expect(saveRun(db, guest("finch", "s9"), range({ runId: id, score: 9000 }))).toBe(false);
364 expect(recentRuns(db)[0]!.score).toBe(1400);
365 // A guest's run is that browser's: another browser cannot take its id either.
366 const mine = crypto.randomUUID();
367 expect(saveRun(db, guest("finch", "s9"), range({ runId: mine, score: 800 }))).toBe(true);
368 expect(saveRun(db, guest("finch", "s10"), range({ runId: mine, score: 9000 }))).toBe(false);
369 expect(recentRuns(db).find((r) => r.id === mine)!.score).toBe(800);
370 });
src/db.ts +9 −3
@@ -343,6 +343,14 @@ export type RunRow = {
343343 * rather than allowed to overwrite it. The page posts a run once, when its
344344 * clock runs out, so in practice this only ever inserts; the update is there so
345345 * that a retried post is the same row and not a second one.
346 *
347 * A run that is on the board stays the run that was posted. A match's numbers
348 * accumulate while it is played, so a late post may raise them (`saveMatch`
349 * above); a run is one finished thirty seconds and has no later state to carry,
350 * so a second post under an id already taken can only be the same run again.
351 * Its numbers are therefore frozen at the first post and only the name is
352 * refreshed — otherwise a re-post of an id is a way to write over a score that
353 * is already ranked, and `runBoard` ranks by score.
346354 */
347355 export function saveRun(db: Database, player: Player, run: RangeRun): boolean {
348356 const existing = db
@@ -356,9 +364,7 @@ export function saveRun(db: Database, player: Player, run: RangeRun): boolean {
356364 `INSERT INTO run (id, session, user_id, name, drill, score, hits, shots, seed, seconds)
357365 VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)
358366 ON CONFLICT(id) DO UPDATE SET
359 name = excluded.name, drill = excluded.drill, score = excluded.score, hits = excluded.hits,
360 shots = excluded.shots, seed = excluded.seed, seconds = excluded.seconds,
361 updated_at = datetime('now')`,
367 name = excluded.name, updated_at = datetime('now')`,
362368 ).run(
363369 run.runId, player.session, player.userId, player.name, run.drill,
364370 run.score, run.hits, run.shots, run.seed, run.seconds,

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs