From ce7e518c882302276e29bedd3ce87f27ca5fa473 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 1 Oct 2026 19:26:46 -0700 Subject: [PATCH 1/6] ci(cross-version-wire): run the whole directory so no compatibility test is left out Three cross-version tests ran in no CI job because the job named its files by hand. Run the directory instead, ratchet that every file kept out of the unit shards runs in some PR job, and re-run the job when the modules the newly running tests guard change. --- .github/workflows/pr.yml | 17 +---- .../ci-unit-exclusion-coverage.test.mjs | 67 +++++++++++++++++++ config/scripts/ci-unit-files.mjs | 5 +- config/scripts/pr-code-change-scope.mjs | 12 +++- config/scripts/pr-code-change-scope.test.mjs | 4 +- 5 files changed, 88 insertions(+), 17 deletions(-) create mode 100644 config/scripts/ci-unit-exclusion-coverage.test.mjs diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 9edeeece8f3a..278c6ca42d51 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -860,21 +860,10 @@ jobs: DIFF_BASE="$(node config/scripts/git-pull-request-diff-base.mjs "$BASE_SHA")" pnpm run check:runtime-launcher-protocol-ratchet -- --base "$DIFF_BASE" - # A path filter that matches nothing exits 1 ("No test files found"), so this - # lane cannot report success while running zero tests. + # Runs the whole directory so a new file there cannot be left out; an empty match + # exits 1 ("No test files found"), so this lane cannot pass while running zero tests. - name: Old/new client and server compatibility journeys - run: >- - pnpm exec vitest run --config config/vitest.config.ts - tests/e2e/cross-version-wire/release-checkout.unit.test.ts - tests/e2e/cross-version-wire/cross-version-browser-placement.unit.test.ts - tests/e2e/cross-version-wire/cross-version-terminal-wire.unit.test.ts - tests/e2e/cross-version-wire/reported-lossy-initial-snapshot.unit.test.ts - tests/e2e/cross-version-wire/cross-version-agent-session-wire.unit.test.ts - tests/e2e/cross-version-wire/cross-version-worktree-identity-downgrade.unit.test.ts - tests/e2e/cross-version-wire/cross-version-session-tabs-retirement-proof.unit.test.ts - tests/e2e/cross-version-wire/agent-session-resume-marker-downgrade.unit.test.ts - tests/e2e/cross-version-wire/agent-session-stop-event-downgrade.unit.test.ts - tests/e2e/cross-version-wire/cross-version-worktree-ps-verdict.unit.test.ts + run: pnpm exec vitest run --config config/vitest.config.ts tests/e2e/cross-version-wire/ managed_hook_node18: name: managed hooks on Node 18 diff --git a/config/scripts/ci-unit-exclusion-coverage.test.mjs b/config/scripts/ci-unit-exclusion-coverage.test.mjs new file mode 100644 index 000000000000..ad141093cd26 --- /dev/null +++ b/config/scripts/ci-unit-exclusion-coverage.test.mjs @@ -0,0 +1,67 @@ +import { globSync, readFileSync } from 'node:fs' +import { parse } from 'yaml' +import { defaultExclude } from 'vitest/config' +import { describe, expect, it } from 'vitest' +import { CROSS_VERSION_WIRE_DIR, UNIT_INCLUDE, discoverUnitFiles } from './ci-unit-files.mjs' + +function posixPaths(files) { + return files.map((file) => file.replaceAll('\\', '/')) +} + +function workflowJobs(path) { + return Object.values(parse(readFileSync(path, 'utf8')).jobs) +} + +// Jobs of pr.yml plus the reusable workflows it calls, since those run on every PR too. +function prJobs() { + const jobs = workflowJobs('.github/workflows/pr.yml') + const called = jobs + .map((job) => job.uses) + .filter((uses) => typeof uses === 'string' && uses.startsWith('./.github/workflows/')) + return [...jobs, ...called.flatMap((uses) => workflowJobs(uses.slice(2)))] +} + +// Path arguments of every PR step that runs vitest; a trailing slash means a directory. +function vitestPathArguments() { + return prJobs() + .flatMap((job) => job.steps ?? []) + .map((step) => step.run) + .filter((run) => typeof run === 'string' && /\bvitest\b/.test(run)) + .flatMap((run) => run.replaceAll('\\\n', ' ').split(/\s+/)) + .map((token) => token.replace(/^['"]|['"]$/g, '')) + .filter((token) => /^(?:src|config|tests|mobile)\//.test(token)) +} + +function runByPrVitestStep(file, pathArguments) { + return pathArguments.some( + (path) => path === file || (path.endsWith('/') && file.startsWith(path)) + ) +} + +describe('unit files kept out of the sharded test job', () => { + it('each still runs in some PR job', () => { + const sharded = new Set(discoverUnitFiles()) + const excluded = posixPaths(globSync(UNIT_INCLUDE, { exclude: defaultExclude })).filter( + (file) => !sharded.has(file) + ) + const pathArguments = vitestPathArguments() + + expect(excluded.length).toBeGreaterThan(0) + // Why: an excluded file no step names guards nothing, and nothing else reports it. + expect(excluded.filter((file) => !runByPrVitestStep(file, pathArguments))).toEqual([]) + }) + + it('runs the whole cross-version-wire directory, not a list of its files', () => { + expect(vitestPathArguments()).toContain(CROSS_VERSION_WIRE_DIR) + }) + + it('names every cross-version-wire test so the unit include picks it up', () => { + // Why: a directory argument only reaches files the vitest include matches; any other name is silently skipped. + const tests = posixPaths( + globSync(`${CROSS_VERSION_WIRE_DIR}**/*.{test,spec}.{js,cjs,mjs,ts,tsx}`) + ) + const included = new Set(posixPaths(globSync(UNIT_INCLUDE))) + expect(tests.length).toBeGreaterThan(0) + expect(tests.filter((file) => !included.has(file))).toEqual([]) + }) +}) diff --git a/config/scripts/ci-unit-files.mjs b/config/scripts/ci-unit-files.mjs index ab8d5038aed9..80252973e224 100644 --- a/config/scripts/ci-unit-files.mjs +++ b/config/scripts/ci-unit-files.mjs @@ -1,6 +1,9 @@ import { globSync } from 'node:fs' import { defaultExclude } from 'vitest/config' +// Why one constant: the cross-version-wire job runs this directory, so a new file there runs with no list to update. +export const CROSS_VERSION_WIRE_DIR = 'tests/e2e/cross-version-wire/' + export const UNIT_INCLUDE = [ 'src/**/*.test.ts', 'src/**/*.test.tsx', @@ -32,7 +35,7 @@ export const UNIT_EXCLUDE = [ 'src/shared/posix-command-path-lookup.test.ts', 'tests/e2e/relay-region-compatibility.unit.test.ts', 'tests/e2e/relay-region-correction.unit.test.ts', - 'tests/e2e/cross-version-wire/**' + `${CROSS_VERSION_WIRE_DIR}**` ] export function discoverUnitFiles(root = process.cwd()) { diff --git a/config/scripts/pr-code-change-scope.mjs b/config/scripts/pr-code-change-scope.mjs index c72825c9fe1c..dc190a5a23ce 100644 --- a/config/scripts/pr-code-change-scope.mjs +++ b/config/scripts/pr-code-change-scope.mjs @@ -178,7 +178,17 @@ const CROSS_VERSION_WIRE_PREFIXES = [ 'src/main/runtime/runtime-worktree-agent-', 'src/main/runtime/runtime-worktree-pty-agent-sources', 'src/shared/runtime-worktree-contracts', - 'src/renderer/src/runtime/remote-runtime-terminal-multiplexer' + 'src/renderer/src/runtime/remote-runtime-terminal-multiplexer', + // Status an older host publishes, read by current code (cross-version-host-observed-turn-end). + 'src/shared/agent-status-types', + 'src/shared/agent-lead-status-fold', + 'src/shared/agent-session-turn-record', + 'src/shared/structured-agent-session-agent-status', + 'src/shared/structured-agent-session-projection', + 'src/shared/workspace-session-sleeping-agents', + // An older app opening a newer orchestration database (orchestration-delivery-downgrade). + 'src/main/runtime/orchestration/db.ts', + 'src/main/runtime/orchestration/db/' ] const MANAGED_HOOK_PREFIXES = [ diff --git a/config/scripts/pr-code-change-scope.test.mjs b/config/scripts/pr-code-change-scope.test.mjs index 5c1302ca3546..76f2449f889c 100644 --- a/config/scripts/pr-code-change-scope.test.mjs +++ b/config/scripts/pr-code-change-scope.test.mjs @@ -367,7 +367,9 @@ describe('per-job path classification', () => { 'src/main/runtime/runtime-worktree-agent-rows.ts', 'src/main/runtime/runtime-worktree-pty-agent-sources.ts', 'src/shared/runtime-worktree-contracts.ts', - 'src/renderer/src/runtime/remote-runtime-terminal-multiplexer.ts' + 'src/renderer/src/runtime/remote-runtime-terminal-multiplexer.ts', + 'src/shared/structured-agent-session-projection.ts', + 'src/main/runtime/orchestration/db.ts' ]) { expectClassification([file], { 'cross-version-wire': true, From 7bb42714a20e5e1c7a5264e4f140d9076a8e62d5 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 1 Oct 2026 19:49:02 -0700 Subject: [PATCH 2/6] test(cross-version): give the orchestration downgrade test its siblings' 120 s budget --- .../orchestration-delivery-downgrade.unit.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/e2e/cross-version-wire/orchestration-delivery-downgrade.unit.test.ts b/tests/e2e/cross-version-wire/orchestration-delivery-downgrade.unit.test.ts index 8126d33d6010..3abf22326ca5 100644 --- a/tests/e2e/cross-version-wire/orchestration-delivery-downgrade.unit.test.ts +++ b/tests/e2e/cross-version-wire/orchestration-delivery-downgrade.unit.test.ts @@ -9,6 +9,7 @@ import { importReleaseCheckoutModule, materializeReleaseCheckout } from './relea // Pin the last pre-v41 implementation: this contract specifically exercises status-only readers. const PRE_V41 = 'aac38d698ff75ac4c8658addab48ef5a83617619' +// Materializes a release checkout inside the test body, so it needs the same budget as its siblings. test('pre-v41 code opens, acknowledges and writes a current-schema database, then current code reopens it', async () => { const checkout = await materializeReleaseCheckout(PRE_V41) const baseline = await importReleaseCheckoutModule( @@ -67,4 +68,4 @@ test('pre-v41 code opens, acknowledges and writes a current-schema database, the db?.close() rmSync(directory, { recursive: true, force: true }) } -}) +}, 120_000) From 2bf590737f9aaba59df681e081e939fce05ef985 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 1 Oct 2026 20:02:56 -0700 Subject: [PATCH 3/6] ci(unit-exclusion): count only merge-gating jobs, and require each excluded file's job to fire on it The coverage check counted any pr.yml job, including e2e, terminal IME and Windows WSL, which are left out of verify.needs and so cannot block a merge. It now reads verify.needs and the reusable workflows those jobs call. It also only proved that some step names each excluded file, not that the job runs when the file changes. The structured-session zsh login-shell test runs only in shell_contracts, whose path trigger matched neither it, its harness nor its subject, so a PR touching only those ran it nowhere. The check now asserts a change to each excluded file fires a gating job that names it, and the shell trigger gains those three paths. --- .../ci-unit-exclusion-coverage.test.mjs | 67 +++++++++++++------ config/scripts/pr-code-change-scope.mjs | 3 + config/scripts/pr-code-change-scope.test.mjs | 17 +++++ 3 files changed, 66 insertions(+), 21 deletions(-) diff --git a/config/scripts/ci-unit-exclusion-coverage.test.mjs b/config/scripts/ci-unit-exclusion-coverage.test.mjs index ad141093cd26..fdb54cd7ba74 100644 --- a/config/scripts/ci-unit-exclusion-coverage.test.mjs +++ b/config/scripts/ci-unit-exclusion-coverage.test.mjs @@ -3,28 +3,33 @@ import { parse } from 'yaml' import { defaultExclude } from 'vitest/config' import { describe, expect, it } from 'vitest' import { CROSS_VERSION_WIRE_DIR, UNIT_INCLUDE, discoverUnitFiles } from './ci-unit-files.mjs' +import { classifyPrJobs } from './pr-code-change-scope.mjs' function posixPaths(files) { return files.map((file) => file.replaceAll('\\', '/')) } function workflowJobs(path) { - return Object.values(parse(readFileSync(path, 'utf8')).jobs) + return parse(readFileSync(path, 'utf8')).jobs } -// Jobs of pr.yml plus the reusable workflows it calls, since those run on every PR too. -function prJobs() { +// Why verify.needs: a job left out of it (e2e, ime, wsl) can be red or skipped without blocking a merge. +// Each gating job carries the code_paths output that decides whether it runs; a called workflow's jobs inherit the caller's. +function gatingJobs() { const jobs = workflowJobs('.github/workflows/pr.yml') - const called = jobs - .map((job) => job.uses) - .filter((uses) => typeof uses === 'string' && uses.startsWith('./.github/workflows/')) - return [...jobs, ...called.flatMap((uses) => workflowJobs(uses.slice(2)))] + return jobs.verify.needs.flatMap((name) => { + const { if: condition = '', uses } = jobs[name] + const gate = /needs\.code_paths\.outputs\.([\w-]+) == 'true'/.exec(condition)?.[1] + const called = uses?.startsWith('./.github/workflows/') + ? Object.values(workflowJobs(uses.slice(2))) + : [jobs[name]] + return called.map((job) => ({ gate, pathArguments: vitestPathArguments(job) })) + }) } -// Path arguments of every PR step that runs vitest; a trailing slash means a directory. -function vitestPathArguments() { - return prJobs() - .flatMap((job) => job.steps ?? []) +// Path arguments of every step in the job that runs vitest; a trailing slash means a directory. +function vitestPathArguments(job) { + return (job.steps ?? []) .map((step) => step.run) .filter((run) => typeof run === 'string' && /\bvitest\b/.test(run)) .flatMap((run) => run.replaceAll('\\\n', ' ').split(/\s+/)) @@ -32,27 +37,47 @@ function vitestPathArguments() { .filter((token) => /^(?:src|config|tests|mobile)\//.test(token)) } -function runByPrVitestStep(file, pathArguments) { +function runByVitestStep(file, pathArguments) { return pathArguments.some( (path) => path === file || (path.endsWith('/') && file.startsWith(path)) ) } +function excludedUnitFiles() { + const sharded = new Set(discoverUnitFiles()) + return posixPaths(globSync(UNIT_INCLUDE, { exclude: defaultExclude })).filter( + (file) => !sharded.has(file) + ) +} + describe('unit files kept out of the sharded test job', () => { - it('each still runs in some PR job', () => { - const sharded = new Set(discoverUnitFiles()) - const excluded = posixPaths(globSync(UNIT_INCLUDE, { exclude: defaultExclude })).filter( - (file) => !sharded.has(file) - ) - const pathArguments = vitestPathArguments() + it('each still runs in a job that gates the PR', () => { + const excluded = excludedUnitFiles() + const jobs = gatingJobs() expect(excluded.length).toBeGreaterThan(0) - // Why: an excluded file no step names guards nothing, and nothing else reports it. - expect(excluded.filter((file) => !runByPrVitestStep(file, pathArguments))).toEqual([]) + // Why: an excluded file no gating step names guards nothing, and nothing else reports it. + expect( + excluded.filter((file) => !jobs.some((job) => runByVitestStep(file, job.pathArguments))) + ).toEqual([]) + }) + + it('a change to each runs a gating job that names it', () => { + const jobs = gatingJobs() + // Why: otherwise a PR editing only the file skips the one job that runs it. + expect( + excludedUnitFiles().filter( + (file) => + !jobs.some( + ({ gate, pathArguments }) => + runByVitestStep(file, pathArguments) && (!gate || classifyPrJobs([file])[gate]) + ) + ) + ).toEqual([]) }) it('runs the whole cross-version-wire directory, not a list of its files', () => { - expect(vitestPathArguments()).toContain(CROSS_VERSION_WIRE_DIR) + expect(gatingJobs().flatMap((job) => job.pathArguments)).toContain(CROSS_VERSION_WIRE_DIR) }) it('names every cross-version-wire test so the unit include picks it up', () => { diff --git a/config/scripts/pr-code-change-scope.mjs b/config/scripts/pr-code-change-scope.mjs index dc190a5a23ce..5fb29e8c21e3 100644 --- a/config/scripts/pr-code-change-scope.mjs +++ b/config/scripts/pr-code-change-scope.mjs @@ -98,6 +98,9 @@ const SHELL_PREFIXES = [ 'src/main/shell-wrapper-', 'src/main/terminal-history-fish', 'src/main/zsh-', + 'src/main/runtime/structured-session-cli-login-shell', + 'src/main/runtime/structured-session-login-shell-test-harness', + 'src/main/runtime/structured-session-child-identity-env', 'src/renderer/src/components/terminal-pane/fish-color-scheme', 'src/shared/fish-', 'src/shared/pty-reply-echo-shapes', diff --git a/config/scripts/pr-code-change-scope.test.mjs b/config/scripts/pr-code-change-scope.test.mjs index 76f2449f889c..66310f6ed237 100644 --- a/config/scripts/pr-code-change-scope.test.mjs +++ b/config/scripts/pr-code-change-scope.test.mjs @@ -286,6 +286,23 @@ describe('per-job path classification', () => { }) }) + it('runs shell contracts for the structured-session login-shell test, its harness and its subject', () => { + expectClassification( + ['src/main/runtime/structured-session-cli-login-shell.live-shell.test.ts'], + { shell_contracts: true } + ) + for (const file of [ + 'src/main/runtime/structured-session-login-shell-test-harness.ts', + 'src/main/runtime/structured-session-child-identity-env.ts' + ]) { + expectClassification([file], { + shell_contracts: true, + package: true, + package_windows: true + }) + } + }) + it('runs orcad browser when Chrome launch, session, or tab modules change', () => { for (const file of [ 'src/main/orcad/external-chromium-browser-session.ts', From 8d48e8f550e55381ce99e620113e929225bbb895 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 1 Oct 2026 20:03:08 -0700 Subject: [PATCH 4/6] ci(cross-version-wire): trigger on the turn-outcome vocabulary and the schema version-skew resolver A change confined to src/shared/agent-turn-outcome (the arms a newer host publishes) or to orchestration-schema-version-skew (how current code reopens a downgraded database) skipped the job whose tests guard exactly those contracts. Also corrects the publish/read direction in the turn-end comment. --- config/scripts/pr-code-change-scope.mjs | 6 ++++-- config/scripts/pr-code-change-scope.test.mjs | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/config/scripts/pr-code-change-scope.mjs b/config/scripts/pr-code-change-scope.mjs index 5fb29e8c21e3..1b5679344c08 100644 --- a/config/scripts/pr-code-change-scope.mjs +++ b/config/scripts/pr-code-change-scope.mjs @@ -182,7 +182,8 @@ const CROSS_VERSION_WIRE_PREFIXES = [ 'src/main/runtime/runtime-worktree-pty-agent-sources', 'src/shared/runtime-worktree-contracts', 'src/renderer/src/runtime/remote-runtime-terminal-multiplexer', - // Status an older host publishes, read by current code (cross-version-host-observed-turn-end). + // Turn-end status a newer host publishes and an older desktop reads (cross-version-host-observed-turn-end). + 'src/shared/agent-turn-outcome', 'src/shared/agent-status-types', 'src/shared/agent-lead-status-fold', 'src/shared/agent-session-turn-record', @@ -191,7 +192,8 @@ const CROSS_VERSION_WIRE_PREFIXES = [ 'src/shared/workspace-session-sleeping-agents', // An older app opening a newer orchestration database (orchestration-delivery-downgrade). 'src/main/runtime/orchestration/db.ts', - 'src/main/runtime/orchestration/db/' + 'src/main/runtime/orchestration/db/', + 'src/main/runtime/orchestration/orchestration-schema-version-skew' ] const MANAGED_HOOK_PREFIXES = [ diff --git a/config/scripts/pr-code-change-scope.test.mjs b/config/scripts/pr-code-change-scope.test.mjs index 66310f6ed237..da6670845e45 100644 --- a/config/scripts/pr-code-change-scope.test.mjs +++ b/config/scripts/pr-code-change-scope.test.mjs @@ -386,7 +386,9 @@ describe('per-job path classification', () => { 'src/shared/runtime-worktree-contracts.ts', 'src/renderer/src/runtime/remote-runtime-terminal-multiplexer.ts', 'src/shared/structured-agent-session-projection.ts', - 'src/main/runtime/orchestration/db.ts' + 'src/shared/agent-turn-outcome.ts', + 'src/main/runtime/orchestration/db.ts', + 'src/main/runtime/orchestration/orchestration-schema-version-skew.ts' ]) { expectClassification([file], { 'cross-version-wire': true, From 88e0a239f241a80fb945d57c7377fe27cbb1e45e Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 1 Oct 2026 20:03:13 -0700 Subject: [PATCH 5/6] test(cross-version): state why the orchestration downgrade test needs 120 s --- .../orchestration-delivery-downgrade.unit.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/cross-version-wire/orchestration-delivery-downgrade.unit.test.ts b/tests/e2e/cross-version-wire/orchestration-delivery-downgrade.unit.test.ts index 3abf22326ca5..a836372642cd 100644 --- a/tests/e2e/cross-version-wire/orchestration-delivery-downgrade.unit.test.ts +++ b/tests/e2e/cross-version-wire/orchestration-delivery-downgrade.unit.test.ts @@ -9,7 +9,7 @@ import { importReleaseCheckoutModule, materializeReleaseCheckout } from './relea // Pin the last pre-v41 implementation: this contract specifically exercises status-only readers. const PRE_V41 = 'aac38d698ff75ac4c8658addab48ef5a83617619' -// Materializes a release checkout inside the test body, so it needs the same budget as its siblings. +// Why 120 s: a cold release checkout materializes in the test body, and its 45 s extraction deadline alone exceeds the 30 s default. test('pre-v41 code opens, acknowledges and writes a current-schema database, then current code reopens it', async () => { const checkout = await materializeReleaseCheckout(PRE_V41) const baseline = await importReleaseCheckoutModule( From 1e5e9a7e1a50a7a35c59b2944ef652cdafd31211 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 1 Oct 2026 20:15:08 -0700 Subject: [PATCH 6/6] test(ci): glob the unit tree once for the unit-exclusion coverage checks --- .../ci-unit-exclusion-coverage.test.mjs | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/config/scripts/ci-unit-exclusion-coverage.test.mjs b/config/scripts/ci-unit-exclusion-coverage.test.mjs index fdb54cd7ba74..9061b5723a6c 100644 --- a/config/scripts/ci-unit-exclusion-coverage.test.mjs +++ b/config/scripts/ci-unit-exclusion-coverage.test.mjs @@ -43,32 +43,29 @@ function runByVitestStep(file, pathArguments) { ) } -function excludedUnitFiles() { - const sharded = new Set(discoverUnitFiles()) - return posixPaths(globSync(UNIT_INCLUDE, { exclude: defaultExclude })).filter( - (file) => !sharded.has(file) - ) -} +// Why module scope: globbing the unit tree costs seconds, and every test reads the same result. +const UNIT_FILES = posixPaths(globSync(UNIT_INCLUDE, { exclude: defaultExclude })) +const SHARDED_UNIT_FILES = new Set(discoverUnitFiles()) +const EXCLUDED_UNIT_FILES = UNIT_FILES.filter((file) => !SHARDED_UNIT_FILES.has(file)) +const GATING_JOBS = gatingJobs() describe('unit files kept out of the sharded test job', () => { it('each still runs in a job that gates the PR', () => { - const excluded = excludedUnitFiles() - const jobs = gatingJobs() - - expect(excluded.length).toBeGreaterThan(0) + expect(EXCLUDED_UNIT_FILES.length).toBeGreaterThan(0) // Why: an excluded file no gating step names guards nothing, and nothing else reports it. expect( - excluded.filter((file) => !jobs.some((job) => runByVitestStep(file, job.pathArguments))) + EXCLUDED_UNIT_FILES.filter( + (file) => !GATING_JOBS.some((job) => runByVitestStep(file, job.pathArguments)) + ) ).toEqual([]) }) it('a change to each runs a gating job that names it', () => { - const jobs = gatingJobs() // Why: otherwise a PR editing only the file skips the one job that runs it. expect( - excludedUnitFiles().filter( + EXCLUDED_UNIT_FILES.filter( (file) => - !jobs.some( + !GATING_JOBS.some( ({ gate, pathArguments }) => runByVitestStep(file, pathArguments) && (!gate || classifyPrJobs([file])[gate]) ) @@ -77,7 +74,7 @@ describe('unit files kept out of the sharded test job', () => { }) it('runs the whole cross-version-wire directory, not a list of its files', () => { - expect(gatingJobs().flatMap((job) => job.pathArguments)).toContain(CROSS_VERSION_WIRE_DIR) + expect(GATING_JOBS.flatMap((job) => job.pathArguments)).toContain(CROSS_VERSION_WIRE_DIR) }) it('names every cross-version-wire test so the unit include picks it up', () => { @@ -85,7 +82,7 @@ describe('unit files kept out of the sharded test job', () => { const tests = posixPaths( globSync(`${CROSS_VERSION_WIRE_DIR}**/*.{test,spec}.{js,cjs,mjs,ts,tsx}`) ) - const included = new Set(posixPaths(globSync(UNIT_INCLUDE))) + const included = new Set(UNIT_FILES) expect(tests.length).toBeGreaterThan(0) expect(tests.filter((file) => !included.has(file))).toEqual([]) })