Public: anyone can read this; the crew can change it.
A test flips pulls, user standing and the front page's weights and finds the explorer's rank-inputs hash follows each #6709
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/6698-a-test-flips-pulls-user-standing-and-the → main 1 commit fork 1 thread · 1 unresolved
2 files
changed +88
−2
src/core/rank_inputs.ts +2 −2
| @@ -15,8 +15,8 @@ | ||
| 15 | 15 | * |
| 16 | 16 | * An input added to the ranking belongs here. `test/explore-speed.test.ts` |
| 17 | 17 | * flips the forks, visibility, access, crew seats, app members and team |
| 18 | * grants and watches the answer follow; pulls, user standing and the | |
| 19 | * weights are hashed but not flipped there. | |
| 18 | * grants and watches the answer follow; `test/rank-inputs.test.ts` flips | |
| 19 | * pulls, user standing and the weights and watches the hash follow. | |
| 20 | 20 | */ |
| 21 | 21 | |
| 22 | 22 | import type { Database } from "bun:sqlite"; |
test/rank-inputs.test.ts added +86
| @@ -0,0 +1,86 @@ | ||
| 1 | /** The explorer's rank-inputs hash follows pulls, user standing and the front page's weights (#6698). */ | |
| 2 | import { afterAll, beforeAll, expect, test } from "bun:test"; | |
| 3 | import { mkdtempSync, rmSync } from "node:fs"; | |
| 4 | import { tmpdir } from "node:os"; | |
| 5 | import { join } from "node:path"; | |
| 6 | import { createApp, type App } from "../src/app.ts"; | |
| 7 | import { setSuspended } from "../src/core/accounts.ts"; | |
| 8 | import { DEFAULT_WEIGHTS, setFrontWeights } from "../src/core/front.ts"; | |
| 9 | import { rankInputs } from "../src/core/rank_inputs.ts"; | |
| 10 | import { createRepo } from "../src/core/repos.ts"; | |
| 11 | import { createSession } from "../src/core/sessions.ts"; | |
| 12 | import { createUser } from "../src/core/users.ts"; | |
| 13 | import { id } from "../src/kernel/id.ts"; | |
| 14 | ||
| 15 | let root: string; | |
| 16 | let app: App; | |
| 17 | let ownerId: string; | |
| 18 | let repoId: string; | |
| 19 | let n = 0; | |
| 20 | ||
| 21 | beforeAll(async () => { | |
| 22 | root = mkdtempSync(join(tmpdir(), "forge-rank-inputs-")); | |
| 23 | app = createApp({ dataDir: root, port: 0 }); | |
| 24 | const made = await createUser(app.db, { username: "trevato", email: "[email protected]", password: "a-good-passphrase", origin: "seed", trusted: true, adminIfFirst: true }); | |
| 25 | if (!made.ok) throw new Error(made.error); | |
| 26 | ownerId = made.value.id; | |
| 27 | const repo = await createRepo(app.db, app.reposRoot, { ownerId, name: "ranked", visibility: "public", description: "r" }); | |
| 28 | if (!repo.ok) throw new Error(repo.error); | |
| 29 | repoId = repo.value.id; | |
| 30 | }); | |
| 31 | afterAll(() => { | |
| 32 | app.db.close(); | |
| 33 | rmSync(root, { recursive: true, force: true }); | |
| 34 | }); | |
| 35 | ||
| 36 | function openPull(): string { | |
| 37 | const issue = id(); | |
| 38 | app.db | |
| 39 | .query(`INSERT INTO issue (id, repo_id, number, kind, title, body, author_id, state) VALUES (?1, ?2, ?3, 'pull', 't', '', ?4, 'open')`) | |
| 40 | .run(issue, repoId, ++n, ownerId); | |
| 41 | app.db | |
| 42 | .query(`INSERT INTO pull (issue_id, base_branch, head_branch, base_oid, head_oid, head_repo_id) VALUES (?1, 'main', 'b', '', '', ?2)`) | |
| 43 | .run(issue, repoId); | |
| 44 | return issue; | |
| 45 | } | |
| 46 | ||
| 47 | test("opening and merging a pull each change the hash", () => { | |
| 48 | const before = rankInputs(app.db); | |
| 49 | const pull = openPull(); | |
| 50 | const opened = rankInputs(app.db); | |
| 51 | expect(opened).not.toBe(before); | |
| 52 | app.db.query(`UPDATE pull SET merged_at = '2026-10-26T00:00:00.000Z' WHERE issue_id = ?1`).run(pull); | |
| 53 | app.db.query(`UPDATE issue SET state = 'closed' WHERE id = ?1`).run(pull); | |
| 54 | expect(rankInputs(app.db)).not.toBe(opened); | |
| 55 | }); | |
| 56 | ||
| 57 | test("closing a pull without merging changes nothing the ranking reads, so the hash is equal", () => { | |
| 58 | const pull = openPull(); | |
| 59 | const open = rankInputs(app.db); | |
| 60 | app.db.query(`UPDATE issue SET state = 'closed' WHERE id = ?1`).run(pull); | |
| 61 | expect(rankInputs(app.db)).toBe(open); | |
| 62 | }); | |
| 63 | ||
| 64 | test("suspending the owner of a ranked repository changes the hash, and restoring them restores it", () => { | |
| 65 | const before = rankInputs(app.db); | |
| 66 | setSuspended(app.db, ownerId, "2026-10-26T00:00:00.000Z"); | |
| 67 | expect(rankInputs(app.db)).not.toBe(before); | |
| 68 | setSuspended(app.db, ownerId, null); | |
| 69 | expect(rankInputs(app.db)).toBe(before); | |
| 70 | }); | |
| 71 | ||
| 72 | test("one front-page weight changes the hash, and setting it back restores it", () => { | |
| 73 | setFrontWeights(app.db, DEFAULT_WEIGHTS, ownerId); | |
| 74 | const first = rankInputs(app.db); | |
| 75 | setFrontWeights(app.db, { ...DEFAULT_WEIGHTS, forks: DEFAULT_WEIGHTS.forks + 1 }, ownerId); | |
| 76 | expect(rankInputs(app.db)).not.toBe(first); | |
| 77 | setFrontWeights(app.db, DEFAULT_WEIGHTS, ownerId); | |
| 78 | expect(rankInputs(app.db)).toBe(first); | |
| 79 | }); | |
| 80 | ||
| 81 | test("a write the ranking does not read leaves the hash equal", () => { | |
| 82 | const before = rankInputs(app.db); | |
| 83 | createSession(app.db, ownerId); // moves the write clock; ranks nothing | |
| 84 | createSession(app.db, ownerId); | |
| 85 | expect(rankInputs(app.db)).toBe(before); | |
| 86 | }); |
new above
offline
This asserts equality, so it passes if the hash ignores the pull table entirely. The merge in the first test covers that, so it is fine. A one-line comment saying so would help the next reader.