fix(ci): retain full coverage for shared unit runtime inputs (#32417)

This commit is contained in:
Neil
2026-10-23 05:09:33 -07:00
committed by GitHub
parent 8f6283b32b
commit 4d026ac042
4 changed files with 74 additions and 8 deletions
+8
View File
@@ -13,6 +13,14 @@ export const UNIT_INCLUDE = [
'tests/e2e/**/*.unit.test.ts'
]
export const UNIT_SETUP_FILES = [
'config/scripts/vitest-real-agent-home-write-guard.ts',
'config/scripts/vitest-bun-node-builtins.ts',
'config/scripts/vitest-dom-setup.ts',
'config/scripts/vitest-host-ports-setup.ts',
'config/scripts/vitest-caller-identity-env-setup.ts'
]
export const UNIT_EXCLUDE = [
...defaultExclude,
'src/main/daemon/repro-13767-shell-ready-marker-lost-to-exec.test.ts',
+17
View File
@@ -1,5 +1,19 @@
import { unitConsumers } from './ci-unit-dependency-graph.mjs'
import { balanceFiles } from './ci-shard-assignment.mjs'
import { UNIT_SETUP_FILES } from './ci-unit-files.mjs'
const RUNTIME_ENTRY_POINTS = new Set([
'config/vitest.config.ts',
'config/scripts/run-vitest.mjs',
...UNIT_SETUP_FILES
])
function isUnitRuntimeEntryPoint(file) {
return (
RUNTIME_ENTRY_POINTS.has(file) ||
(/^config\/scripts\/vitest[^/]*\.(?:ts|mjs)$/.test(file) && !file.includes('.test.'))
)
}
export function selectUnitFiles(files, changed, graphInput) {
const full = (reason) => ({ files, reason, full: true })
@@ -15,6 +29,9 @@ export function selectUnitFiles(files, changed, graphInput) {
}
for (const file of changed) {
const consumers = unitConsumers([file], graph.reverse)
if ([...consumers].some(isUnitRuntimeEntryPoint)) {
return full('Shared unit runtime input')
}
if (!files.some((test) => consumers.has(test))) {
return full('No proven test coverage for changed inputs')
}
+47
View File
@@ -1,5 +1,6 @@
import { describe, expect, it } from 'vitest'
import { buildUnitDependencyGraph } from './ci-unit-dependency-graph.mjs'
import { UNIT_SETUP_FILES } from './ci-unit-files.mjs'
import {
FULL_SHARD_COUNT,
auditUnitSelection,
@@ -26,6 +27,52 @@ const graph = { ...buildUnitDependencyGraph(sources), files: new Set(sources.key
const files = [...sources.keys()].filter((file) => file.endsWith('.test.ts')).sort()
describe('conservative unit selection', () => {
it.each([
'config/vitest.config.ts',
'config/scripts/run-vitest.mjs',
...UNIT_SETUP_FILES,
'config/scripts/vitest-node-runtime-worker.mjs'
])('retains every test for a transitive dependency of %s', (runtime) => {
const sources = new Map([
['src/input.ts', 'export const value = 1'],
['config/scripts/setup-forward.ts', "export * from '../../src/input'"],
[
runtime,
`import '${runtime === 'config/vitest.config.ts' ? './scripts' : '.'}/setup-forward'`
],
['src/direct.test.ts', "import './input'"],
['src/unrelated.test.ts', 'export const value = 1']
])
const files = ['src/direct.test.ts', 'src/unrelated.test.ts']
const graph = { ...buildUnitDependencyGraph(sources), files: new Set(sources.keys()) }
expect(selectUnitFiles(files, ['src/input.ts'], graph)).toMatchObject({ full: true, files })
expect(
planUnitSelection({
files,
changed: ['src/input.ts'],
graph,
timings: {},
mode: 'selected',
event: { pull_request: { draft: true } }
})
).toMatchObject({ mode: 'shadow', executionFiles: files, selectionAvailable: false })
})
it('does not mistake a Vitest harness test for a shared runtime entry point', () => {
const sources = new Map([
['src/input.ts', 'export const value = 1'],
['config/scripts/vitest-harness.test.mjs', "import '../../src/input'"],
['src/direct.test.ts', "import './input'"],
['src/unrelated.test.ts', 'export const value = 1']
])
expect(
selectUnitFiles(['src/direct.test.ts', 'src/unrelated.test.ts'], ['src/input.ts'], {
...buildUnitDependencyGraph(sources),
files: new Set(sources.keys())
})
).toMatchObject({ full: false, files: ['src/direct.test.ts'] })
})
it('follows re-exports, literal dynamic imports and requires, retaining indirect readers', () => {
expect(selectUnitFiles(files, ['src/leaf.ts'], graph).files).toEqual([
'src/consumer.test.ts',
+2 -8
View File
@@ -2,7 +2,7 @@ import { readFileSync } from 'node:fs'
import { createRequire } from 'node:module'
import { resolve } from 'node:path'
import { defaultExclude, defineConfig } from 'vitest/config'
import { UNIT_INCLUDE, UNIT_EXCLUDE } from './scripts/ci-unit-files.mjs'
import { UNIT_INCLUDE, UNIT_EXCLUDE, UNIT_SETUP_FILES } from './scripts/ci-unit-files.mjs'
import TimingSequencer from './scripts/ci-unit-sequencer.mjs'
import RuntimeSequencer from './scripts/vitest-runtime-sequencer.mjs'
import { getNodeRuntimeInclude } from './scripts/vitest-node-runtime-files.mjs'
@@ -64,13 +64,7 @@ const testOptions = {
server: { deps: { inline: ['zod'] } },
// Node's storage globals and V8 retention checks require the existing child flags.
execArgv: ['--no-experimental-webstorage', '--expose-gc'],
setupFiles: [
resolve('config/scripts/vitest-real-agent-home-write-guard.ts'),
resolve('config/scripts/vitest-bun-node-builtins.ts'),
resolve('config/scripts/vitest-dom-setup.ts'),
resolve('config/scripts/vitest-host-ports-setup.ts'),
resolve('config/scripts/vitest-caller-identity-env-setup.ts')
],
setupFiles: UNIT_SETUP_FILES.map((file) => resolve(file)),
include: UNIT_INCLUDE,
exclude: balancedShards ? UNIT_EXCLUDE : defaultExclude,
hookTimeout: 60_000,