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

test/scaffold.test.ts passes in the builder's sandbox: the scaffolded app's typecheck and its OpenID sign-in #11058

Merged opened this

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

Merged

scout/forge:scout/11051-test-scaffold-test-ts-passes-in-the-buil → main 1 commit fork

  • Merged. scout/11051-test-scaffold-test-ts-passes-in-the-buil was merged into main as 9315d9a.

Review

Sign in to review this change.
1 file changed +31 −19
test/scaffold.test.ts +31 −19
@@ -28,11 +28,16 @@ import { scaffoldFiles } from "../src/apps/scaffold.ts";
2828 /** How long the written-out app's `bun test` may take, wall clock. */
2929 const SUITE_BUDGET_MS = 3_000;
3030
31 /** A child that inherits nothing it was not handed. */
31 /**
32 * A child that inherits nothing it was not handed. Its TMPDIR is a directory
33 * made inside the test's own temp dir: the builder's sandbox holds anything
34 * else read-only, and a child bun refuses to start without a tempdir it can
35 * write (#11051).
36 */
3237 const run = async (
3338 cwd: string,
3439 argv: string[],
35 temp = process.env.TMPDIR ?? tmpdir(),
40 temp: string,
3641 ): Promise<{ code: number; out: string }> => {
3742 const child = Bun.spawn(argv, {
3843 cwd,
@@ -53,7 +58,9 @@ const run = async (
5358 };
5459
5560 describe("a scaffolded app, written out and run on its own", () => {
61 let scratch: string;
5662 let dir: string;
63 let temp: string;
5764 const files = scaffoldFiles({ name: "field-notes", description: "" });
5865 const content = (path: string): string => {
5966 const file = files.find((f) => f.path === path);
@@ -63,21 +70,24 @@ describe("a scaffolded app, written out and run on its own", () => {
6370 };
6471
6572 beforeAll(async () => {
66 dir = await mkdtemp(join(tmpdir(), "forge-scaffold-"));
73 scratch = await mkdtemp(join(tmpdir(), "forge-scaffold-"));
74 dir = join(scratch, "app");
75 temp = join(scratch, "tmp");
76 await mkdir(temp);
6777 for (const file of files) {
6878 const path = join(dir, file.path);
6979 await mkdir(dirname(path), { recursive: true });
7080 await writeFile(path, file.content as string);
7181 }
7282 });
73 afterAll(() => rm(dir, { recursive: true, force: true }));
83 afterAll(() => rm(scratch, { recursive: true, force: true }));
7484
7585 test("its own suite is green, inside its 3 s budget", async () => {
7686 // Every deploy and every pull on the app waits on this suite, so it is
7787 // held to a budget (docs/APPS.md, "What a new app starts as").
78 const temp = await mkdtemp(join(tmpdir(), "forge-scaffold-tmp-"));
88 const own = await mkdtemp(join(scratch, "suite-tmp-"));
7989 const started = performance.now();
80 const { code, out } = await run(dir, [process.execPath, "test"], temp);
90 const { code, out } = await run(dir, [process.execPath, "test"], own);
8191 const ms = Math.round(performance.now() - started);
8292 console.log(`the scaffold's own suite ran in ${ms} ms (budget ${SUITE_BUDGET_MS} ms)`);
8393 expect([code, out.includes(" 0 fail")]).toEqual([0, true]);
@@ -85,8 +95,7 @@ describe("a scaffolded app, written out and run on its own", () => {
8595 // It leaves its temporary directory as it found it: a check cell's /tmp
8696 // is a small tmpfs every scaffolded suite in the forge's own check shares
8797 // (#5326), and every app's own check cell has one too.
88 expect(await readdir(temp)).toEqual([]);
89 await rm(temp, { recursive: true, force: true });
98 expect(await readdir(own)).toEqual([]);
9099 // Each guarantee has its own file of tests in the suite.
91100 for (const name of [
92101 "design.test.ts",
@@ -101,12 +110,12 @@ describe("a scaffolded app, written out and run on its own", () => {
101110
102111 test("the design lint passes, and refuses a colour spelled out", async () => {
103112 expect(
104 (await run(dir, [process.execPath, "run", "lint:design"])).code,
113 (await run(dir, [process.execPath, "run", "lint:design"], temp)).code,
105114 ).toBe(0);
106115 const css = join(dir, "static", "app.css");
107116 const before = await readFile(css, "utf8");
108117 await writeFile(css, `${before}\n.oops { color: #ff0000; }\n`);
109 const refused = await run(dir, [process.execPath, "run", "lint:design"]);
118 const refused = await run(dir, [process.execPath, "run", "lint:design"], temp);
110119 await writeFile(css, before);
111120 expect(refused.code).toBe(1);
112121 expect(refused.out).toContain("static/app.css");
@@ -115,7 +124,7 @@ describe("a scaffolded app, written out and run on its own", () => {
115124
116125 test("the committed API description is exactly what contract:emit writes", async () => {
117126 const committed = content("contract/openapi.json");
118 const emitted = await run(dir, [process.execPath, "run", "contract:emit"]);
127 const emitted = await run(dir, [process.execPath, "run", "contract:emit"], temp);
119128 expect(emitted.code).toBe(0);
120129 expect(await readFile(join(dir, "contract", "openapi.json"), "utf8")).toBe(
121130 committed,
@@ -130,9 +139,11 @@ describe("a scaffolded app, written out and run on its own", () => {
130139 }, 30_000);
131140
132141 test("it typechecks under strict settings, with the forge's own type definitions", async () => {
133 // The app ships no dependencies, so the checker borrows the forge's
134 // Bun types; the app's code itself must be clean under the same strictness
135 // the forge holds itself to.
142 // The app ships no dependencies, so the checker borrows the forge's own
143 // tsc and Bun types, run straight from node_modules: `bun x` would reach
144 // for a package cache and the registry, neither of which a sandbox
145 // promises (#11051). The app's code itself must be clean under the same
146 // strictness the forge holds itself to.
136147 const root = fileURLToPath(new URL("..", import.meta.url));
137148 const sources = [
138149 ...(await readdir(join(dir, "src"))).map((f) => join("src", f)),
@@ -140,8 +151,7 @@ describe("a scaffolded app, written out and run on its own", () => {
140151 ].filter((f) => f.endsWith(".ts"));
141152 const { code, out } = await run(dir, [
142153 process.execPath,
143 "x",
144 "tsc",
154 join(root, "node_modules", "typescript", "bin", "tsc"),
145155 "--noEmit",
146156 "--strict",
147157 "--target",
@@ -158,9 +168,8 @@ describe("a scaffolded app, written out and run on its own", () => {
158168 "--typeRoots",
159169 `${root}node_modules/@types,${root}node_modules`,
160170 ...sources,
161 ]);
162 // tsc's exit code is the verdict: `bun x` may print its own install
163 // lines on a fresh machine (#664). On a failure, the output is the
171 ], temp);
172 // tsc's exit code is the verdict. On a failure, the output is the
164173 // reason, so the assertion shows it beside the code.
165174 expect({ code, out: code === 0 ? "" : out.trim() }).toEqual({ code: 0, out: "" });
166175 }, 120_000);
@@ -180,6 +189,8 @@ describe("a scaffolded app signs a person in through the forge", () => {
180189 const root = await mkdtemp(join(tmpdir(), "forge-signin-"));
181190 const appDir = join(root, "app");
182191 const dataDir = join(root, "data");
192 const temp = join(root, "tmp");
193 await mkdir(temp);
183194 for (const file of scaffoldFiles({ name: "field-notes", description: "" })) {
184195 const path = join(appDir, file.path);
185196 await mkdir(dirname(path), { recursive: true });
@@ -211,6 +222,7 @@ describe("a scaffolded app signs a person in through the forge", () => {
211222 FORGE_APP_DATA: dataDir,
212223 FORGE_APP_URL: base,
213224 FORGE_URL: servers.url,
225 TMPDIR: temp,
214226 },
215227 stdout: "ignore",
216228 stderr: "ignore",

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs