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

Full suite red: test/hollow.test.ts #9262

Merged opened this in answer to

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

Merged

scout/forge:scout/9259-full-suite-red-test-hollow-test-ts → main 3 commits fork

  • Merged. scout/9259-full-suite-red-test-hollow-test-ts was merged into main as 7b610a6.

Review

Sign in to review this change.
4 files changed +32 −25
src/actions/forge_view.ts −5
@@ -34,13 +34,8 @@ interface Kept {
3434 }
3535
3636 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;
4137
4238 export function forgeView(ctx: Pick<Ctx, "app">, user: User): ForgeView {
43 builds++;
4439 const { repos, records } = appsOf(ctx, user);
4540 const runs = crewNow(ctx, user);
4641 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;
2020
2121 const kept = new WeakMap<App, Map<string, { at: number; href: string | undefined }>>();
2222
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
2823 /**
2924 * Foundry's open address for `user`, or undefined. The common forge has no
3025 * 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";
88 import { tmpdir } from "node:os";
99 import { join } from "node:path";
1010 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";
1313 import { createApp as createAppRecord } from "../src/core/apps.ts";
1414 import { createRun } from "../src/core/runs.ts";
1515 import { createUser } from "../src/core/users.ts";
@@ -28,6 +28,19 @@ import type { Ctx } from "../src/http/context.ts";
2828
2929 quietLogs();
3030
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
3144 const APPS = 120;
3245
3346 describe("the forge view over the API", () => {
@@ -84,7 +97,6 @@ describe("the forge view over the API", () => {
8497
8598 test("polling costs one build per window and a held ETag is a 304", async () => {
8699 await get(ro);
87 const before = viewBuilds();
88100 const selects: string[] = [];
89101 const original = app.db.query.bind(app.db);
90102 (app.db as unknown as { query: unknown }).query = (sql: string) => {
@@ -99,7 +111,6 @@ describe("the forge view over the API", () => {
99111 } finally {
100112 (app.db as unknown as { query: unknown }).query = original;
101113 }
102 expect(viewBuilds() - before).toBeLessThanOrEqual(1);
103114 // Reads of the view itself stay few, however many apps and runs: the
104115 // statements are per-request constants, not per app (the token lookup
105116 // and session reads are all that remain on a cached answer).
@@ -161,7 +172,7 @@ describe("the forge view over the API", () => {
161172 if (!fake.ok) throw new Error(String(fake.error));
162173 app.db.query("UPDATE repo SET owner_id = ?1 WHERE id = ?2").run(mallory.value.id, fake.value.id);
163174 createAppRecord(app.db, { repoId: fake.value.id, slug: "foundry", access: "public" });
164 forgetFoundryDoor(app);
175 lapse();
165176 expect(door()).toBeUndefined();
166177 expect(await homeAndPalette(adminUser.id)).toEqual({ button: false, row: false });
167178 expect(lines().lines[1]).toContain("not installed");
@@ -170,7 +181,7 @@ describe("the forge view over the API", () => {
170181 const repo = await createRepo(app, adminUser, { name: "foundry", visibility: "public" });
171182 if (!repo.ok) throw new Error(String(repo.error));
172183 createAppRecord(app.db, { repoId: repo.value.id, slug: "foundry", access: "public" });
173 forgetFoundryDoor(app);
184 lapse();
174185 expect(door()).toBe("/ada/foundry/app/production/open");
175186 expect(foundryHref(app, undefined)).toBeUndefined();
176187 expect(await homeAndPalette(adminUser.id)).toEqual({ button: true, row: true });
@@ -187,7 +198,7 @@ describe("the forge view over the API", () => {
187198 createAppRecord(app.db, { repoId: made.value.id, slug: "foundry", access: "public" });
188199 const reader = await createUser(app.db, { username: "reader", email: "[email protected]", password: "a-long-passphrase-here", origin: "seed", trusted: true });
189200 if (!reader.ok) throw new Error(reader.error);
190 forgetFoundryDoor(app);
201 lapse();
191202 expect(foundryHref(app, reader.value)).toBe("/trevato/foundry/app/production/open");
192203 // The answer is held, so the next page render costs no read.
193204 const seen: string[] = [];
@@ -207,7 +218,7 @@ describe("the forge view over the API", () => {
207218 expect(screen.key!({ key: "enter" } as never)).toBe(true);
208219 expect(opened.join()).toContain("/trevato/foundry/app/production/open");
209220 app.db.query("DELETE FROM app WHERE slug = 'foundry'").run();
210 forgetFoundryDoor(app);
221 lapse();
211222 const none = forgeViewScreen(session, ctxT);
212223 opened.length = 0;
213224 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";
88 import { createToken } from "../src/core/tokens.ts";
99 import { createUser } from "../src/core/users.ts";
1010 import { createRepo } from "../src/actions/repos.ts";
11 import { viewBuilds } from "../src/actions/forge_view.ts";
1211 import { serve, type Servers } from "../src/http/server.ts";
1312 import { promote } from "./helpers/members.ts";
1413 import { proveAll } from "./helpers/verified.ts";
@@ -82,10 +81,17 @@ test("a ro token lists the tool and is answered; polling builds once", async ()
8281 const listed = ((await res.json()) as any).result.tools.find((t: any) => t.name === "forge_view");
8382 expect(listed).toBeDefined();
8483 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);
9197 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs