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

A test boots a freshly scaffolded app and holds its webspec() findings to a named list #3046

Merged opened this

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

Merged

scout/forge:scout/3041-a-test-boots-a-freshly-scaffolded-app-an → main 2 commits fork 2 threads · 2 unresolved

  • Merged. scout/3041-a-test-boots-a-freshly-scaffolded-app-an was merged into main as ad9001c.

Review

Sign in to review this change.
1 file changed +68 −0
test/scaffold-webspec.test.ts added +68
@@ -0,0 +1,68 @@
1 /**
2 * A freshly scaffolded app is held to the website specification (#3041).
3 * The scaffold is written out, booted on a loopback port, and `webspec()`
4 * reads `/` and the site files. The findings' checks must equal KNOWN: a
5 * new gap fails, and so does a fixed gap still listed.
6 */
7 import { afterAll, beforeAll, expect, test } from "bun:test";
8 import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";
9 import { tmpdir } from "node:os";
10 import { dirname, join } from "node:path";
11 import { scaffoldFiles } from "../src/apps/scaffold.ts";
12 import { webspec, type Finding } from "../src/release/webspec.ts";
13
14 /** Today's gaps, each with its specification.website section. */
15 const KNOWN: string[] = [
16 "change-password", // https://specification.website/#change-password
17 "etag", // https://specification.website/#caching
18 "robots", // https://specification.website/#robots
19 "security-txt", // https://specification.website/#security-txt
20 ];
21
22 let dir: string;
23 let child: ReturnType<typeof Bun.spawn> | undefined;
24 let findings: Finding[] = [];
25
26 beforeAll(async () => {
27 dir = await mkdtemp(join(tmpdir(), "forge-scaffold-webspec-"));
28 for (const f of scaffoldFiles({ name: "field-notes", description: "" })) {
29 const path = join(dir, f.path);
30 await mkdir(dirname(path), { recursive: true });
31 if (f.content !== null) await writeFile(path, f.content);
32 }
33 const port = 20000 + Math.floor(Math.random() * 20000);

Random port can collide; retry the spawn on failure or have the server pick the port.

34 child = Bun.spawn(["bun", "run", "src/server.ts"], {
35 cwd: dir,
36 env: {
37 PATH: process.env.PATH ?? "/usr/bin:/bin",
38 HOME: dir,
39 TMPDIR: process.env.TMPDIR ?? tmpdir(),
40 PORT: String(port),
41 FORGE_APP_DATA: join(dir, "data"),
42 FORGE_APP_URL: `http://127.0.0.1:${port}`,
43 },
44 stdout: "ignore",
45 stderr: "ignore",
46 });
47 const base = `http://127.0.0.1:${port}`;
48 const end = Date.now() + 15_000;
49 for (;;) {
50 try {
51 if ((await fetch(`${base}/healthz`)).ok) break;
52 } catch {}
53 if (Date.now() > end) throw new Error("the scaffold did not boot");
54 await Bun.sleep(50);
55 }
56 findings = await webspec(base, ["/"], fetch, { site: true });
57 }, 30_000);
58
59 afterAll(async () => {
60 child?.kill();
61 await child?.exited;
62 await rm(dir, { recursive: true, force: true });

Names only: a new finding under etag/robots/etc. goes unnoticed. Map to ${f.check} ${f.path} (if Finding has one) and list those in KNOWN.

63 });
64
65 test("a fresh scaffold's webspec findings are exactly the known gaps", () => {
66 const got = [...new Set(findings.map((f) => f.check))].sort();
67 expect(got).toEqual([...KNOWN].sort());
68 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs