From dae4edb7ec68d468a3d8d472d9c447d38830a93f Mon Sep 17 00:00:00 2001 From: garrytan Date: Mon, 2 Nov 2026 05:56:52 +0000 Subject: [PATCH] test(pregate): clear --run-url accepts a failed or running run of the lane's own workflow (red, GSTA-203 PR 8) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- test/pregate-clear-runs.test.ts | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/test/pregate-clear-runs.test.ts b/test/pregate-clear-runs.test.ts index 3107a13b4..5b85c20cc 100644 --- a/test/pregate-clear-runs.test.ts +++ b/test/pregate-clear-runs.test.ts @@ -73,12 +73,12 @@ function check(repo: FixtureRepo, runs: FakeRun[], over: Partial = {}) return { repo: 'fx/fx', deps, profiles: PROFILES, gateRuns: GATE_RUNS, now: NOW, inventory: () => ['test/a.test.ts', 'test/b.test.ts'], ...over }; } -/** A check whose only readable run is 300, a successful run of `workflow` on `head`. */ -function runOfWorkflow(repo: FixtureRepo, head: string, workflow: string): RunCheck { +/** A check whose only readable run is 300, a run of `workflow` on `head` (completed with success unless `state` says otherwise). */ +function runOfWorkflow(repo: FixtureRepo, head: string, workflow: string, state: Pick = { status: 'completed', conclusion: 'success' }): RunCheck { const c = check(repo, []); const run = (id: number): ApiRun => { if (id !== 300) throw new Error('HTTP 404'); - return { id, run_attempt: 1, status: 'completed', conclusion: 'success', event: 'pull_request', head_sha: head, path: `.github/workflows/${workflow}`, repository: { full_name: 'fx/fx' }, head_repository: { full_name: 'fx/fx' } }; + return { id, run_attempt: 1, ...state, event: 'pull_request', head_sha: head, path: `.github/workflows/${workflow}`, repository: { full_name: 'fx/fx' }, head_repository: { full_name: 'fx/fx' } }; }; return { ...c, deps: { ...c.deps, run } }; } @@ -206,6 +206,22 @@ describe.skipIf(process.platform === 'win32')('clear --run: Actions runs as remo expect(clearDecision(file, PLATFORM, { runUrl: url }, repo.work)).toEqual({ receipt: url, attested: false }); }, 120_000); + test('a lane with no profile refuses a --run-url run of its own workflow that failed, was cancelled or is still running, and still clears from a green one (GSTA-203 PR 8)', async () => { + const repo = fixture(); + const head = repo.git('rev-parse', 'HEAD'); + const base = await pregateAt(repo); + const PLATFORM = 'a.yml/job'; + const file: PregateFile = { ...base, requires_remote: [...base.requires_remote!, { lane: PLATFORM, platform: 'windows', workflow: 'a.yml', job: 'job', tests: ['test/a.test.ts'], receipt: null }] }; + const url = 'https://github.com/fx/fx/actions/runs/300'; + const msg = (d: ReturnType) => ('refused' in d ? `${d.refused}: ${d.message}; fix: ${d.fix}` : `cleared ${d.receipt}`); + const rerun = `fix: rerun a.yml (gh run rerun 300, or gh workflow run a.yml --ref ), then gstack-pregate clear --remote ${PLATFORM} --run-url `; + for (const conclusion of ['failure', 'cancelled', 'timed_out']) { + expect(msg(clearDecision(file, PLATFORM, { runUrl: url, evidence: runOfWorkflow(repo, head, 'a.yml', { status: 'completed', conclusion }) }, repo.work))).toBe(`PREGATE_RECEIPT_INVALID: REUSE: refused reason=run_not_success — run 300 attempt 1 concluded ${conclusion}; ${PLATFORM} stays uncleared; ${rerun}`); + } + expect(msg(clearDecision(file, PLATFORM, { runUrl: `${url}/job/9?pr=1`, evidence: runOfWorkflow(repo, head, 'a.yml', { status: 'in_progress', conclusion: null }) }, repo.work))).toBe(`PREGATE_RECEIPT_INVALID: REUSE: refused reason=run_in_progress — run 300 attempt 1 is in_progress; ${PLATFORM} stays uncleared; fix: wait for run 300 to finish green (or rerun a.yml if it fails), then gstack-pregate clear --remote ${PLATFORM} --run-url `); + expect(clearDecision(file, PLATFORM, { runUrl: url, evidence: runOfWorkflow(repo, head, 'a.yml') }, repo.work)).toEqual({ receipt: url, attested: false }); + }, 120_000); + test('on this repository\'s own workflows the Windows obligation is windows-smoke.yml/windows-smoke: a windows-smoke.yml run clears it, a run of any other workflow is refused (GSTA-203)', async () => { const lanes = discoverLanes(path.resolve(import.meta.dir, '..'), 'linux'); const owed = lanes.filter(l => l.tier === 'platform' && l.platform === 'windows' && l.runsTests && !l.manual);