Public: anyone can read this; the crew can change it.
test/app-domains.test.ts: "an app gets its own domain" is green on a loaded runner and rejoins the gate #1167
Check · Review · Land · Live done — Landed and live in production.
mason/forge:mason/1045-test-app-domains-test-ts-an-app-gets-its → main 1 commit fork 2 threads · 2 unresolved
1 file
changed +32
−14
test/app-domains.test.ts +32 −14
| @@ -29,6 +29,7 @@ import { addDomain, getDomain, TOKEN_LENGTH } from "../src/core/domains.ts"; | ||
| 29 | 29 | import { listEvents } from "../src/core/events.ts"; |
| 30 | 30 | import type { Repo } from "../src/core/repos.ts"; |
| 31 | 31 | import { listRuns, type Run } from "../src/core/runs.ts"; |
| 32 | import { readRunLogTail } from "../src/runs/log.ts"; | |
| 32 | 33 | import { createToken } from "../src/core/tokens.ts"; |
| 33 | 34 | import { createUser, type User } from "../src/core/users.ts"; |
| 34 | 35 | import { serve, type Servers } from "../src/http/server.ts"; |
| @@ -267,21 +268,35 @@ describe("an app gets its own domain", () => { | ||
| 267 | 268 | return `${(response.headers.get("set-cookie") ?? "").split(";")[0]!}; forge_mode=dev`; |
| 268 | 269 | }; |
| 269 | 270 | |
| 270 | async function until<T>(read: () => T | undefined, ms = 60_000): Promise<T> { | |
| 271 | /** A deploy run as the hook reports it: its state, summary and log tail. */ | |
| 272 | const said = (run: Run): string => | |
| 273 | [ | |
| 274 | `deploy ${run.id} is ${run.state}: ${run.summary || "(no summary)"}`, | |
| 275 | ...readRunLogTail(dataDir, run.id, 20).lines, | |
| 276 | ].join("\n"); | |
| 277 | /** | |
| 278 | * The deploy's own end: the first matching run that is `ok` or `failed`. | |
| 279 | * Out of time, it names where the latest matching run stood instead. | |
| 280 | */ | |
| 281 | async function settled( | |
| 282 | where: (run: Run) => boolean, | |
| 283 | ms = 60_000, | |
| 284 | ): Promise<Run> { | |
| 271 | 285 | const deadline = Date.now() + ms; |
| 286 | let latest: Run | undefined; | |
| 272 | 287 | while (Date.now() < deadline) { |
| 273 | const value = read(); | |
| 274 | if (value !== undefined) return value; | |
| 288 | const runs = listRuns(app.db, repo.id, { kind: "deploy", limit: 50 }); | |
| 289 | const done = runs.find( | |
| 290 | (r) => where(r) && (r.state === "ok" || r.state === "failed"), | |
| 291 | ); | |
| 292 | if (done !== undefined) return done; | |
| 293 | latest = runs.find(where); | |
| 275 | 294 | await Bun.sleep(100); |
| 276 | 295 | } |
| 277 | throw new Error("did not happen in time"); | |
| 278 | } | |
| 279 | const settled = (where: (run: Run) => boolean) => | |
| 280 | until(() => | |
| 281 | listRuns(app.db, repo.id, { kind: "deploy", limit: 50 }).find( | |
| 282 | (r) => where(r) && (r.state === "ok" || r.state === "failed"), | |
| 283 | ), | |
| 296 | throw new Error( | |
| 297 | `no deploy settled in ${ms} ms; ${latest === undefined ? "none was queued" : said(latest)}`, | |
| 284 | 298 | ); |
| 299 | } | |
| 285 | 300 | const kinds = (prefix: string): string[] => |
| 286 | 301 | listEvents(app.db, { repoId: repo.id, limit: 100 }) |
| 287 | 302 | .map((e) => e.kind) |
| @@ -346,15 +361,18 @@ describe("an app gets its own domain", () => { | ||
| 346 | 361 | if (!row.ok) throw new Error(row.error); |
| 347 | 362 | other = row.value; |
| 348 | 363 | const run = await settled((r) => r.env === "production"); |
| 364 | // A failed deploy says why in the check's log, not only "failed". | |
| 365 | if (run.state !== "ok") throw new Error(said(run)); | |
The throw already guarantees ok, so the expect on the next line is redundant; drop it or drop the throw. |
||
| 349 | 366 | expect(run.state).toBe("ok"); |
| 350 | 367 | expect((await viaHost(`notes.${DOMAIN}`)).status).toBe(200); |
| 351 | 368 | }, 120_000); |
| 352 | 369 | |
| 353 | 370 | afterAll(async () => { |
| 354 | await runtime.stop(); | |
| 355 | servers.stop(); | |
| 356 | app.close(); | |
| 357 | await rm(dataDir, { recursive: true, force: true }); | |
| 371 | // A setup that failed part-way leaves later handles unset: stop what started. | |
| 372 | await runtime?.stop(); | |
| 373 | servers?.stop(); | |
| 374 | app?.close(); | |
| 375 | if (dataDir) await rm(dataDir, { recursive: true, force: true }); | |
| 358 | 376 | }); |
| 359 | 377 | |
| 360 | 378 | test("what a domain may be: a hostname someone can own, and not the forge's", () => { |
new above
offline
Import is out of order: ../src/runs/log.ts sits between core/runs and core/tokens. Move it after the core/* imports.