fix(ci): root explicit Bun test filters

Avoid Bun filter-mode repository scans that exhaust macOS file descriptors and corrupt subprocess test evidence. Apply rooted filters across server, contract, coverage, persistence, policy, desktop native, and adapter test entrypoints.

Confidence: high

Scope-risk: narrow

Tested: bun run check:policy; bun run check:server; bun run check:chat-contract
This commit is contained in:
程序员阿江(Relakkes)
2026-07-10 23:49:46 +08:00
parent 46dca4e961
commit 08dd64596a
12 changed files with 81 additions and 15 deletions
+26
View File
@@ -0,0 +1,26 @@
import { describe, expect, test } from 'bun:test'
import { rootBunTestFilter } from './bun-test-filter'
describe('rootBunTestFilter', () => {
test('roots repository-relative files and directories', () => {
expect(rootBunTestFilter('src/server/example.test.ts')).toBe(
'./src/server/example.test.ts',
)
expect(rootBunTestFilter('electron')).toBe('./electron')
})
test('preserves already rooted and absolute filters', () => {
expect(rootBunTestFilter('./src/example.test.ts')).toBe(
'./src/example.test.ts',
)
expect(rootBunTestFilter('../shared/example.test.ts')).toBe(
'../shared/example.test.ts',
)
expect(rootBunTestFilter('/tmp/example.test.ts')).toBe(
'/tmp/example.test.ts',
)
expect(rootBunTestFilter('C:\\repo\\example.test.ts')).toBe(
'C:\\repo\\example.test.ts',
)
})
})
+20
View File
@@ -0,0 +1,20 @@
import { isAbsolute } from 'node:path'
const WINDOWS_ABSOLUTE_PATH = /^(?:[a-zA-Z]:[\\/]|\\\\)/
/**
* Root explicit Bun test filters so Bun does not scan the whole repository.
* Unrooted filters can retain enough directory descriptors on macOS to break
* subprocess output capture: https://github.com/oven-sh/bun/issues/32067
*/
export function rootBunTestFilter(filter: string): string {
if (
filter.startsWith('./') ||
filter.startsWith('../') ||
isAbsolute(filter) ||
WINDOWS_ABSOLUTE_PATH.test(filter)
) {
return filter
}
return `./${filter}`
}
+4
View File
@@ -90,6 +90,7 @@ describe('feature quality contract', () => {
expect(packageJson.scripts?.['check:persistence-upgrade']).toBe('bun run scripts/quality-gate/persistence-upgrade.ts')
expect(packageJson.scripts?.['check:provider-contract']).toBe('bun run scripts/pr/run-provider-contract-tests.ts')
expect(packageJson.scripts?.['check:chat-contract']).toBe('bun run scripts/pr/run-chat-contract-tests.ts')
expect(packageJson.scripts?.['check:policy']).toContain('bun test ./scripts/')
expect(packageJson.scripts?.['check:native']).toContain('electron:package:dir')
expect(packageJson.scripts?.['check:native']).toContain('test:package-smoke:current')
expect(packageJson.scripts?.['test:package-smoke:current']).toBe('bun run scripts/quality-gate/package-smoke/current.ts')
@@ -137,6 +138,7 @@ describe('feature quality contract', () => {
expect(serverRunner).toContain('TEST_FILE_PATTERN')
expect(serverRunner).toContain("'--no-env-file'")
expect(serverRunner).toContain('createSandboxedTestEnvironment')
expect(serverRunner).toContain('rootBunTestFilter(file)')
expect(serverRunner).toContain('evidenceComplete')
expect(serverRunner).toContain('reportedFiles === 1')
expect(serverRunner).toContain('passedTests + failedTests > 0')
@@ -145,6 +147,7 @@ describe('feature quality contract', () => {
expect(coverageRunner).toContain('TEST_FILE_PATTERN')
expect(coverageRunner).toContain("'--no-env-file'")
expect(coverageRunner).toContain('createSandboxedTestEnvironment')
expect(coverageRunner).toContain('serverFiles.map(rootBunTestFilter)')
expect(coverageRunner).toContain("correctness is enforced by check:server's per-file sandboxed test processes")
expect(coverageRunner).toContain('rootCoverageAvailable')
expect(coverageRunner).toContain('rootTestDiscoveryComplete')
@@ -152,6 +155,7 @@ describe('feature quality contract', () => {
for (const contractRunner of [providerRunner, chatRunner]) {
expect(contractRunner).toContain("'--no-env-file'")
expect(contractRunner).toContain('createSandboxedTestEnvironment')
expect(contractRunner).toContain('rootBunTestFilter')
}
})
+3 -2
View File
@@ -3,6 +3,7 @@
import { mkdtempSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { resolve } from 'node:path'
import { rootBunTestFilter } from './bun-test-filter'
import { createSandboxedTestEnvironment } from './test-environment'
interface ContractSuite {
@@ -20,7 +21,7 @@ const suites: ContractSuite[] = [
'bun',
'--no-env-file',
'test',
'src/server/__tests__/websocket-handler.test.ts',
rootBunTestFilter('src/server/__tests__/websocket-handler.test.ts'),
],
},
{
@@ -30,7 +31,7 @@ const suites: ContractSuite[] = [
'bun',
'--no-env-file',
'test',
'src/server/__tests__/conversations.test.ts',
rootBunTestFilter('src/server/__tests__/conversations.test.ts'),
],
},
{
+7 -1
View File
@@ -3,6 +3,7 @@
import { mkdtempSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { rootBunTestFilter } from './bun-test-filter'
import { createSandboxedTestEnvironment } from './test-environment'
const root = join(import.meta.dir, '..', '..')
@@ -34,7 +35,12 @@ for (const testFile of testFiles) {
const sandboxHome = mkdtempSync(join(tmpdir(), 'cc-haha-provider-contract-'))
let exitCode = 1
try {
const proc = Bun.spawn(['bun', '--no-env-file', 'test', testFile], {
const proc = Bun.spawn([
'bun',
'--no-env-file',
'test',
rootBunTestFilter(testFile),
], {
cwd: root,
env: createSandboxedTestEnvironment(sandboxHome),
stdout: 'inherit',
+9 -1
View File
@@ -4,6 +4,7 @@ import { mkdtempSync, readdirSync, rmSync, statSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join, relative, sep } from 'node:path'
import { loadQuarantineManifest, quarantinedPathSet } from '../quality-gate/quarantine'
import { rootBunTestFilter } from './bun-test-filter'
import { createSandboxedTestEnvironment } from './test-environment'
const root = process.cwd()
@@ -69,7 +70,14 @@ async function runTestFile(file: string): Promise<TestFileResult> {
const sandboxHome = mkdtempSync(join(tmpdir(), 'cc-haha-server-test-'))
try {
const proc = Bun.spawn(
['bun', '--no-env-file', 'test', '--max-concurrency=1', '--timeout=20000', file],
[
'bun',
'--no-env-file',
'test',
'--max-concurrency=1',
'--timeout=20000',
rootBunTestFilter(file),
],
{
cwd: root,
env: createSandboxedTestEnvironment(sandboxHome),
+2 -1
View File
@@ -3,6 +3,7 @@
import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { dirname, join, relative, sep, win32 } from 'node:path'
import { rootBunTestFilter } from '../pr/bun-test-filter'
import { createSandboxedTestEnvironment } from '../pr/test-environment'
import { loadQuarantineManifest, quarantinedPathSet } from './quarantine'
@@ -805,7 +806,7 @@ export async function runCoverageGate(options: {
const coverageByFile = new Map<string, FileLineCoverage>()
mkdirSync(join(outputDir, 'root-server'), { recursive: true })
const rootCommand = ['bun', '--no-env-file', 'test', '--timeout=20000', '--coverage', '--coverage-reporter=lcov', '--coverage-reporter=text', '--coverage-dir', join(outputDir, 'root-server'), ...serverFiles]
const rootCommand = ['bun', '--no-env-file', 'test', '--timeout=20000', '--coverage', '--coverage-reporter=lcov', '--coverage-reporter=text', '--coverage-dir', join(outputDir, 'root-server'), ...serverFiles.map(rootBunTestFilter)]
const rootLogPath = join(outputDir, 'root-server', 'coverage.log')
const rootResult = await runCommand(rootCommand, rootDir, rootLogPath)
const rootLcovPath = join(outputDir, 'root-server', 'lcov.info')
+1 -1
View File
@@ -150,7 +150,7 @@ export function lanesForMode(mode: QualityGateMode, baselineTargets: BaselineTar
title: 'Baseline case catalog validation',
description: 'Validate real Coding Agent baseline case definitions and fixture metadata.',
kind: 'command',
command: ['bun', 'test', 'scripts/quality-gate/baseline/cases.test.ts'],
command: ['bun', 'test', './scripts/quality-gate/baseline/cases.test.ts'],
requiredForModes: ['baseline', 'release'],
category: 'unit',
},
+1 -1
View File
@@ -10,7 +10,7 @@ const rootDir = process.cwd()
const checks: Check[] = [
{
title: 'Server persistent JSON migrations',
command: ['bun', 'test', 'src/server/__tests__/persistence-upgrade.test.ts'],
command: ['bun', 'test', './src/server/__tests__/persistence-upgrade.test.ts'],
},
{
title: 'Desktop localStorage migrations',