2026-04-04 15:51:54 +09:00
/// <reference types="bun-types" />
type CiTestPlan = {
isolatedTestTargets : string [ ]
isolatedModuleMockFiles : string [ ]
sharedTestFiles : string [ ]
}
2026-04-04 16:12:22 +09:00
const TEST_ROOTS = [ "bin" , "script" , "src" ] as const
2026-04-04 15:51:54 +09:00
const MODULE_MOCK_PATTERN = "mock.module("
async function collectTestFiles ( rootDirectory : string ) : Promise < string [ ] > {
const testFiles : string [ ] = [ ]
for ( const testRoot of TEST_ROOTS ) {
const glob = new Bun . Glob ( "**/*.test.ts" )
for await ( const testFile of glob . scan ( { cwd : ` ${ rootDirectory } / ${ testRoot } ` } ) ) {
testFiles . push ( ` ${ testRoot } / ${ testFile } ` )
}
}
return testFiles . sort ( ( left , right ) = > left . localeCompare ( right ) )
}
async function usesModuleMock ( rootDirectory : string , testFile : string ) : Promise < boolean > {
const testContents = await Bun . file ( ` ${ rootDirectory } / ${ testFile } ` ) . text ( )
return testContents . includes ( MODULE_MOCK_PATTERN )
}
function toIsolatedTarget ( testFile : string ) : string {
const pathSegments = testFile . split ( "/" )
if ( pathSegments . length <= 3 ) {
return testFile
}
return pathSegments . slice ( 0 , - 1 ) . join ( "/" )
}
function isCoveredByTarget ( testFile : string , isolatedTarget : string ) : boolean {
return testFile === isolatedTarget || testFile . startsWith ( ` ${ isolatedTarget } / ` )
}
function collapseNestedTargets ( isolatedTargets : string [ ] ) : string [ ] {
return isolatedTargets . filter ( ( isolatedTarget ) = > {
return ! isolatedTargets . some ( ( otherTarget ) = > {
return otherTarget !== isolatedTarget && isolatedTarget . startsWith ( ` ${ otherTarget } / ` )
} )
} )
}
export async function createCiTestPlan ( rootDirectory : string = process . cwd ( ) ) : Promise < CiTestPlan > {
const allTestFiles = await collectTestFiles ( rootDirectory )
const isolatedModuleMockFiles : string [ ] = [ ]
for ( const testFile of allTestFiles ) {
if ( await usesModuleMock ( rootDirectory , testFile ) ) {
isolatedModuleMockFiles . push ( testFile )
}
}
const isolatedTestTargets = collapseNestedTargets (
Array . from ( new Set ( isolatedModuleMockFiles . map ( ( testFile ) = > toIsolatedTarget ( testFile ) ) ) ) . sort ( ( left , right ) = >
left . localeCompare ( right ) ,
) ,
)
const sharedTestFiles = allTestFiles . filter ( ( testFile ) = > {
return ! isolatedTestTargets . some ( ( isolatedTarget ) = > isCoveredByTarget ( testFile , isolatedTarget ) )
} )
return {
isolatedTestTargets ,
isolatedModuleMockFiles ,
sharedTestFiles ,
}
}
async function runBunTest ( testFiles : string [ ] , label : string ) : Promise < void > {
if ( testFiles . length === 0 ) {
return
}
console . log ( ` ::group:: ${ label } ` )
2026-04-04 17:53:24 +09:00
// For directory paths, exclude _auc* directories which are separate isolated targets
const args = testFiles . map ( tf = > {
if ( tf . includes ( '/' ) && ! tf . endsWith ( '.test.ts' ) ) {
// It's a directory path, add negation glob
return [ tf , '!_auc-*/**/*.test.ts' ]
}
return tf
} ) . flat ( )
const command = [ "bun" , "test" , . . . args ]
2026-04-04 15:51:54 +09:00
const spawnedProcess = Bun . spawn ( command , {
cwd : process.cwd ( ) ,
stdin : "inherit" ,
stdout : "inherit" ,
stderr : "inherit" ,
} )
const exitCode = await spawnedProcess . exited
console . log ( "::endgroup::" )
if ( exitCode !== 0 ) {
throw new Error ( ` Command failed: ${ command . join ( " " ) } ` )
}
}
async function main ( ) : Promise < void > {
const ciTestPlan = await createCiTestPlan ( )
console . log (
` Detected ${ ciTestPlan . isolatedModuleMockFiles . length } mock.module() test files, ${ ciTestPlan . isolatedTestTargets . length } isolated targets, and ${ ciTestPlan . sharedTestFiles . length } shared test files. ` ,
)
for ( const isolatedTestTarget of ciTestPlan . isolatedTestTargets ) {
await runBunTest ( [ isolatedTestTarget ] , ` Isolated ${ isolatedTestTarget } ` )
}
await runBunTest ( ciTestPlan . sharedTestFiles , "Shared Bun test suite" )
}
export const moduleMockPattern = MODULE_MOCK_PATTERN
export const testRoots = TEST_ROOTS
if ( process . argv . includes ( "--print-plan" ) ) {
const ciTestPlan = await createCiTestPlan ( )
console . log ( JSON . stringify ( ciTestPlan , null , 2 ) )
} else if ( import . meta . main ) {
try {
await main ( )
} catch ( error ) {
const message = error instanceof Error ? error.message : String ( error )
console . error ( message )
process . exit ( 1 )
}
}