Public: anyone can read this; the crew can change it.
Full suite red: test/hollow.test.ts #9262
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/9259-full-suite-red-test-hollow-test-ts → main 3 commits fork
4 files
changed +32
−25
src/actions/forge_view.ts −5
| @@ -34,13 +34,8 @@ interface Kept { | ||
| 34 | 34 | } |
| 35 | 35 | |
| 36 | 36 | const kept = new WeakMap<App, Map<string, Kept>>(); |
| 37 | let builds = 0; | |
| 38 | ||
| 39 | /** How many times a view was built rather than reused (the cost test reads it). */ | |
| 40 | export const viewBuilds = (): number => builds; | |
| 41 | 37 | |
| 42 | 38 | export function forgeView(ctx: Pick<Ctx, "app">, user: User): ForgeView { |
| 43 | builds++; | |
| 44 | 39 | const { repos, records } = appsOf(ctx, user); |
| 45 | 40 | const runs = crewNow(ctx, user); |
| 46 | 41 | const tally = new Map<string, { running: number; queued: number }>(); |
src/actions/foundry_door.ts −5
| @@ -20,11 +20,6 @@ export const DOOR_TTL_MS = 5000; | ||
| 20 | 20 | |
| 21 | 21 | const kept = new WeakMap<App, Map<string, { at: number; href: string | undefined }>>(); |
| 22 | 22 | |
| 23 | /** Forget the held answers (a test that installs or removes the app). */ | |
| 24 | export function forgetFoundryDoor(app: App): void { | |
| 25 | kept.delete(app); | |
| 26 | } | |
| 27 | ||
| 28 | 23 | /** |
| 29 | 24 | * Foundry's open address for `user`, or undefined. The common forge has no |
| 30 | 25 | * app with the slug, so that one indexed read comes first and ends the work; |
test/api-forge-view.test.ts +19 −8
| @@ -8,8 +8,8 @@ import { mkdtemp, rm } from "node:fs/promises"; | ||
| 8 | 8 | import { tmpdir } from "node:os"; |
| 9 | 9 | import { join } from "node:path"; |
| 10 | 10 | import { createApp, type App } from "../src/app.ts"; |
| 11 | import { foundryHref, forgetFoundryDoor } from "../src/actions/foundry_door.ts"; | |
| 12 | import { forgeView, VIEW_APPS, viewBuilds } from "../src/actions/forge_view.ts"; | |
| 11 | import { DOOR_TTL_MS, foundryHref } from "../src/actions/foundry_door.ts"; | |
| 12 | import { forgeView, VIEW_APPS } from "../src/actions/forge_view.ts"; | |
| 13 | 13 | import { createApp as createAppRecord } from "../src/core/apps.ts"; |
| 14 | 14 | import { createRun } from "../src/core/runs.ts"; |
| 15 | 15 | import { createUser } from "../src/core/users.ts"; |
| @@ -28,6 +28,19 @@ import type { Ctx } from "../src/http/context.ts"; | ||
| 28 | 28 | |
| 29 | 29 | quietLogs(); |
| 30 | 30 | |
| 31 | /** Let the door's held answers expire: the clock moves past the window. */ | |
| 32 | const realNow = Date.now; | |
| 33 | let skew = 0; | |
| 34 | const lapse = () => { | |
| 35 | skew += DOOR_TTL_MS + 1; | |
| 36 | }; | |
| 37 | beforeAll(() => { | |
| 38 | Date.now = () => realNow() + skew; | |
| 39 | }); | |
| 40 | afterAll(() => { | |
| 41 | Date.now = realNow; | |
| 42 | }); | |
| 43 | ||
| 31 | 44 | const APPS = 120; |
| 32 | 45 | |
| 33 | 46 | describe("the forge view over the API", () => { |
| @@ -84,7 +97,6 @@ describe("the forge view over the API", () => { | ||
| 84 | 97 | |
| 85 | 98 | test("polling costs one build per window and a held ETag is a 304", async () => { |
| 86 | 99 | await get(ro); |
| 87 | const before = viewBuilds(); | |
| 88 | 100 | const selects: string[] = []; |
| 89 | 101 | const original = app.db.query.bind(app.db); |
| 90 | 102 | (app.db as unknown as { query: unknown }).query = (sql: string) => { |
| @@ -99,7 +111,6 @@ describe("the forge view over the API", () => { | ||
| 99 | 111 | } finally { |
| 100 | 112 | (app.db as unknown as { query: unknown }).query = original; |
| 101 | 113 | } |
| 102 | expect(viewBuilds() - before).toBeLessThanOrEqual(1); | |
| 103 | 114 | // Reads of the view itself stay few, however many apps and runs: the |
| 104 | 115 | // statements are per-request constants, not per app (the token lookup |
| 105 | 116 | // and session reads are all that remain on a cached answer). |
| @@ -161,7 +172,7 @@ describe("the forge view over the API", () => { | ||
| 161 | 172 | if (!fake.ok) throw new Error(String(fake.error)); |
| 162 | 173 | app.db.query("UPDATE repo SET owner_id = ?1 WHERE id = ?2").run(mallory.value.id, fake.value.id); |
| 163 | 174 | createAppRecord(app.db, { repoId: fake.value.id, slug: "foundry", access: "public" }); |
| 164 | forgetFoundryDoor(app); | |
| 175 | lapse(); | |
| 165 | 176 | expect(door()).toBeUndefined(); |
| 166 | 177 | expect(await homeAndPalette(adminUser.id)).toEqual({ button: false, row: false }); |
| 167 | 178 | expect(lines().lines[1]).toContain("not installed"); |
| @@ -170,7 +181,7 @@ describe("the forge view over the API", () => { | ||
| 170 | 181 | const repo = await createRepo(app, adminUser, { name: "foundry", visibility: "public" }); |
| 171 | 182 | if (!repo.ok) throw new Error(String(repo.error)); |
| 172 | 183 | createAppRecord(app.db, { repoId: repo.value.id, slug: "foundry", access: "public" }); |
| 173 | forgetFoundryDoor(app); | |
| 184 | lapse(); | |
| 174 | 185 | expect(door()).toBe("/ada/foundry/app/production/open"); |
| 175 | 186 | expect(foundryHref(app, undefined)).toBeUndefined(); |
| 176 | 187 | expect(await homeAndPalette(adminUser.id)).toEqual({ button: true, row: true }); |
| @@ -187,7 +198,7 @@ describe("the forge view over the API", () => { | ||
| 187 | 198 | createAppRecord(app.db, { repoId: made.value.id, slug: "foundry", access: "public" }); |
| 188 | 199 | const reader = await createUser(app.db, { username: "reader", email: "[email protected]", password: "a-long-passphrase-here", origin: "seed", trusted: true }); |
| 189 | 200 | if (!reader.ok) throw new Error(reader.error); |
| 190 | forgetFoundryDoor(app); | |
| 201 | lapse(); | |
| 191 | 202 | expect(foundryHref(app, reader.value)).toBe("/trevato/foundry/app/production/open"); |
| 192 | 203 | // The answer is held, so the next page render costs no read. |
| 193 | 204 | const seen: string[] = []; |
| @@ -207,7 +218,7 @@ describe("the forge view over the API", () => { | ||
| 207 | 218 | expect(screen.key!({ key: "enter" } as never)).toBe(true); |
| 208 | 219 | expect(opened.join()).toContain("/trevato/foundry/app/production/open"); |
| 209 | 220 | app.db.query("DELETE FROM app WHERE slug = 'foundry'").run(); |
| 210 | forgetFoundryDoor(app); | |
| 221 | lapse(); | |
| 211 | 222 | const none = forgeViewScreen(session, ctxT); |
| 212 | 223 | opened.length = 0; |
| 213 | 224 | expect(none.key!({ key: "enter" } as never)).toBe(false); |
test/mcp-forge-view.test.ts +13 −7
| @@ -8,7 +8,6 @@ import { createApp as createAppRecord } from "../src/core/apps.ts"; | ||
| 8 | 8 | import { createToken } from "../src/core/tokens.ts"; |
| 9 | 9 | import { createUser } from "../src/core/users.ts"; |
| 10 | 10 | import { createRepo } from "../src/actions/repos.ts"; |
| 11 | import { viewBuilds } from "../src/actions/forge_view.ts"; | |
| 12 | 11 | import { serve, type Servers } from "../src/http/server.ts"; |
| 13 | 12 | import { promote } from "./helpers/members.ts"; |
| 14 | 13 | import { proveAll } from "./helpers/verified.ts"; |
| @@ -82,10 +81,17 @@ test("a ro token lists the tool and is answered; polling builds once", async () | ||
| 82 | 81 | const listed = ((await res.json()) as any).result.tools.find((t: any) => t.name === "forge_view"); |
| 83 | 82 | expect(listed).toBeDefined(); |
| 84 | 83 | expect(listed.inputSchema.properties).toEqual({}); |
| 85 | const before = viewBuilds(); | |
| 86 | const first = await mcpTool(servers.url, adaRo, "forge_view", {}); | |
| 87 | expect(first.error).toBeUndefined(); | |
| 88 | await mcpTool(servers.url, adaRo, "forge_view", {}); | |
| 89 | await api(adaRo); | |
| 90 | expect(viewBuilds() - before).toBeLessThanOrEqual(1); | |
| 84 | // A build reads the runs once; three calls in one window may build once. | |
| 85 | const selects: string[] = []; | |
| 86 | const original = app.db.query.bind(app.db); | |
| 87 | (app.db as unknown as { query: unknown }).query = (sql: string) => (selects.push(sql), original(sql)); | |
| 88 | try { | |
| 89 | const first = await mcpTool(servers.url, adaRo, "forge_view", {}); | |
| 90 | expect(first.error).toBeUndefined(); | |
| 91 | await mcpTool(servers.url, adaRo, "forge_view", {}); | |
| 92 | await api(adaRo); | |
| 93 | } finally { | |
| 94 | (app.db as unknown as { query: unknown }).query = original; | |
| 95 | } | |
| 96 | expect(selects.filter((q) => /FROM run\b/.test(q)).length).toBeLessThanOrEqual(1); | |
| 91 | 97 | }); |
new above
offline