Public: anyone can read this; the crew can change it.
"RepoSource: one repository, two places to read it from > a mirror reports its stages in order and ends holding the repo" passes every time on a loaded check machine #5399
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/5394-reposource-one-repository-two-places-to → main 2 commits fork 1 thread · 1 unresolved
2 files
changed +21
−2
test/fake-origin.ts +9
| @@ -210,6 +210,8 @@ export interface FakeState { | ||
| 210 | 210 | refuseFork: string; |
| 211 | 211 | /** Hold every issue-search answer this long before sending it. */ |
| 212 | 212 | searchDelayMs: number; |
| 213 | /** When set, the labels list waits for it: a test pins a mirror mid-flight. */ | |
| 214 | holdLabels?: Promise<void> | undefined; | |
| 213 | 215 | nextId: number; |
| 214 | 216 | /** The OAuth endpoints, when a test wants the connect flows. */ |
| 215 | 217 | oauth?: FakeOAuth; |
| @@ -286,6 +288,13 @@ export function serveFakeForge(state: FakeState): FakeForge { | ||
| 286 | 288 | body: bodyText, |
| 287 | 289 | }); |
| 288 | 290 | |
| 291 | if ( | |
| 292 | state.holdLabels !== undefined && | |
| 293 | request.method === "GET" && | |
| 294 | url.pathname.endsWith("/labels") | |
| 295 | ) | |
| 296 | await state.holdLabels; | |
| 297 | ||
| 289 | 298 | // The OAuth endpoints hang off the web origin and take no bearer |
| 290 | 299 | // token, so they are answered before the API's auth check. |
| 291 | 300 | if (state.oauth !== undefined) { |
test/view-source.test.ts +12 −2
| @@ -532,21 +532,31 @@ describe("RepoSource: one repository, two places to read it from", () => { | ||
| 532 | 532 | // ---- mirroring, with something to watch ---- |
| 533 | 533 | |
| 534 | 534 | test("a mirror reports its stages in order and ends holding the repo", async () => { |
| 535 | // Pin the job before its label import until we are subscribed: a | |
| 536 | // subscriber replays only the stage the job is on, so on a loaded machine | |
| 537 | // a job that raced ahead of the subscription would show just "done". | |
| 538 | let release = (): void => {}; | |
| 539 | fake.state.holdLabels = new Promise<void>((resolve) => { | |
| 540 | release = resolve; | |
| 541 | }); | |
| 535 | 542 | const started = await api(`/api/v1/user/origins/${originId}/mirror`, { |
| 536 | 543 | method: "POST", |
| 537 | 544 | body: JSON.stringify({ fullName: "octo/widgets" }), |
| 538 | 545 | }); |
| 539 | 546 | expect(started.status).toBe(202); |
| 540 | const job = (await read<{ job: { id: string; stage: string } }>(started)) | |
| 547 | const job = (await read<{ job: { id: string; stage: MirrorStage } }>(started)) | |
| 541 | 548 | .job; |
| 542 | 549 | expect(job.stage).toBe("creating"); |
| 543 | 550 | |
| 544 | const seen: MirrorStage[] = []; | |
| 551 | // "creating" was observed in the 202; the subscription sees the rest. | |
| 552 | const seen: MirrorStage[] = [job.stage]; | |
| 545 | 553 | const stop = subscribe(job.id, (update) => { |
| 546 | 554 | if (seen[seen.length - 1] !== update.stage) seen.push(update.stage); |
| 547 | 555 | }); |
| 556 | release(); | |
| 548 | 557 | const finished = await settled(job.id); |
| 549 | 558 | stop(); |
| 559 | fake.state.holdLabels = undefined; | |
| 550 | 560 | |
| 551 | 561 | expect(finished?.stage).toBe("done"); |
| 552 | 562 | expect(finished?.repo?.name).toBe("widgets"); |
new above
offline
Move this reset into afterEach or a finally: if settled() or an earlier expect throws, holdLabels stays set and any later label GET in this file hangs.