Public: anyone can read this; the crew can change it.
test/studio.test.ts: a pull's preview case waits for the deploy it reads, and the file rejoins the gate #1165
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/1044-test-studio-test-ts-a-pull-s-preview-cas → main 1 commit fork 1 thread · 1 unresolved
1 file
changed +42
−8
test/studio.test.ts +42 −8
| @@ -51,6 +51,7 @@ import { | ||
| 51 | 51 | createRun, |
| 52 | 52 | finishRun, |
| 53 | 53 | getRun, |
| 54 | isTerminal, | |
| 54 | 55 | listRuns, |
| 55 | 56 | markServing, |
| 56 | 57 | noteRunProgress, |
| @@ -2540,6 +2541,41 @@ describe("every open pull's preview on the studio page", () => { | ||
| 2540 | 2541 | redirect: "manual", |
| 2541 | 2542 | }); |
| 2542 | 2543 | |
| 2544 | const WAIT_MS = 60_000; | |
| 2545 | ||
| 2546 | /** The deploys this repo has not finished, as "env:state" words. */ | |
| 2547 | const unfinished = () => | |
| 2548 | listRuns(app.db, repo.id, { kind: "deploy", limit: 50 }) | |
| 2549 | .filter((r) => !isTerminal(r.state)) | |
| 2550 | .map((r) => `${r.env}:${r.state}`); | |
| 2551 | ||
| 2552 | /** Wait until the runtime has no deploy open or queued, so the next case | |
| 2553 | * does not queue behind this one's (concurrency is 1). */ | |
| 2554 | async function deploysIdle(): Promise<void> { | |
| 2555 | const deadline = Date.now() + WAIT_MS; | |
| 2556 | while (unfinished().length > 0) { | |
| 2557 | if (Date.now() > deadline) | |
| 2558 | throw new Error(`deploys still open: ${unfinished().join(", ")}`); | |
| 2559 | await Bun.sleep(50); | |
| 2560 | } | |
| 2561 | } | |
| 2562 | ||
| 2563 | /** Wait for `env` to serve; fail naming the state it reached. */ | |
| 2564 | async function previewServes(record: AppRecord, env: EnvName): Promise<void> { | |
| 2565 | const deadline = Date.now() + WAIT_MS; | |
| 2566 | while (runtime.envStatus(record, env)?.state !== "running") { | |
| 2567 | if (Date.now() > deadline) { | |
| 2568 | const status = runtime.envStatus(record, env); | |
| 2569 | throw new Error( | |
| 2570 | `${env} did not serve within ${WAIT_MS} ms: state ${status?.state ?? "absent"}` + | |
| 2571 | (status?.error ? ` (${status.error})` : "") + | |
| 2572 | `; open deploys: ${unfinished().join(", ") || "none"}`, | |
| 2573 | ); | |
| 2574 | } | |
| 2575 | await Bun.sleep(50); | |
| 2576 | } | |
| 2577 | } | |
| 2578 | ||
| 2543 | 2579 | beforeAll(async () => { |
| 2544 | 2580 | dataDir = await mkdtemp(join(tmpdir(), "forge-studio-previews-")); |
| 2545 | 2581 | app = createApp({ dataDir, port: 0 }); |
| @@ -2637,7 +2673,10 @@ describe("every open pull's preview on the studio page", () => { | ||
| 2637 | 2673 | const after = await (await get("/alice/widget")).text(); |
| 2638 | 2674 | expect(after).not.toContain("<h2>Previews</h2>"); |
| 2639 | 2675 | expect(after).not.toContain(`pull-${pull.number}/open`); |
| 2640 | }, 30_000); | |
| 2676 | // The merge queues production's deploy and the pull's was cut short; | |
| 2677 | // leave the single deploy slot free for the next case. | |
| 2678 | await deploysIdle(); | |
| 2679 | }, 120_000); | |
| 2641 | 2680 | |
| 2642 | 2681 | test("a change's page names its preview's data as its own copy and gives the moment it was taken; production's card does not", async () => { |
| 2643 | 2682 | const alice = getUserByName(app.db, "alice")!; |
| @@ -2659,12 +2698,7 @@ describe("every open pull's preview on the studio page", () => { | ||
| 2659 | 2698 | const record = getAppByRepo(app.db, repo.id)!; |
| 2660 | 2699 | const env = `pull-${pull.number}` as const; |
| 2661 | 2700 | |
| 2662 | const deadline = Date.now() + 30_000; | |
| 2663 | while ( | |
| 2664 | Date.now() < deadline && | |
| 2665 | runtime.envStatus(record, env)?.state !== "running" | |
| 2666 | ) | |
| 2667 | await Bun.sleep(100); | |
| 2701 | await previewServes(record, env); | |
| 2668 | 2702 | expect(runtime.envStatus(record, env)?.state).toBe("running"); |
| 2669 | 2703 | |
| 2670 | 2704 | const note = "its own copy of production's data, taken"; |
| @@ -2680,7 +2714,7 @@ describe("every open pull's preview on the studio page", () => { | ||
| 2680 | 2714 | ).text(); |
| 2681 | 2715 | expect(changePage).toContain("<h2>Preview</h2>"); |
| 2682 | 2716 | expect(changePage).toContain(note); |
| 2683 | }, 30_000); | |
| 2717 | }, 120_000); | |
| 2684 | 2718 | }); |
| 2685 | 2719 | |
| 2686 | 2720 | /** |
new above
offline
WAIT_MS is 60 s while the case timeouts are 120 s. That leaves room for both waits plus the assertions, so it is fine. Say so in a short comment so nobody lowers the timeout below 2x.