Files
core2026/tools/integration-tests/run-test-lane.mjs
T

90 lines
3.3 KiB
JavaScript

import { spawnSync } from 'node:child_process';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const packageRoot = path.dirname(fileURLToPath(import.meta.url));
const registryPath = path.join(packageRoot, 'test-lanes.tsv');
const testRoot = path.join(packageRoot, 'test');
const supportedLanes = new Set(['core', 'reference', 'lifecycle', 'conditional']);
const supportedCommands = new Set(['core', 'reference', 'lifecycle', 'check']);
const usage = () => {
process.stderr.write('usage: node run-test-lane.mjs <core|reference|lifecycle> [vitest arguments...]\n');
process.exit(64);
};
const [lane, ...rawVitestArguments] = process.argv.slice(2);
const vitestArguments = rawVitestArguments[0] === '--' ? rawVitestArguments.slice(1) : rawVitestArguments;
if (!lane || !supportedCommands.has(lane)) {
usage();
}
const rows = fs
.readFileSync(registryPath, 'utf8')
.split(/\r?\n/u)
.map((line) => line.trim())
.filter((line) => line !== '' && !line.startsWith('#'))
.map((line, index) => {
const fields = line.split('\t');
if (fields.length !== 2) {
throw new Error(`test-lanes.tsv row ${index + 1} must have exactly two tab-separated fields`);
}
const [file, registeredLane] = fields;
if (!file || !registeredLane || !supportedLanes.has(registeredLane)) {
throw new Error(`test-lanes.tsv row ${index + 1} is invalid`);
}
return { file, lane: registeredLane };
});
const duplicateFiles = rows.map(({ file }) => file).filter((file, index, files) => files.indexOf(file) !== index);
if (duplicateFiles.length > 0) {
throw new Error(`duplicate integration test lane entries: ${[...new Set(duplicateFiles)].join(', ')}`);
}
const discoveredFiles = fs
.readdirSync(testRoot, { withFileTypes: true })
.filter((entry) => entry.isFile() && entry.name.endsWith('.test.ts'))
.map((entry) => `test/${entry.name}`)
.sort();
const registeredFiles = rows.map(({ file }) => file).sort();
if (JSON.stringify(discoveredFiles) !== JSON.stringify(registeredFiles)) {
const registered = new Set(registeredFiles);
const discovered = new Set(discoveredFiles);
const missing = discoveredFiles.filter((file) => !registered.has(file));
const stale = registeredFiles.filter((file) => !discovered.has(file));
throw new Error(
`integration test lane registry mismatch; missing=[${missing.join(', ')}] stale=[${stale.join(', ')}]`
);
}
if (lane === 'check') {
process.stdout.write(`integration test lane registry is valid (${registeredFiles.length} files)\n`);
process.exit(0);
}
if (lane === 'lifecycle' && process.env.INTEGRATION_LIFECYCLE_DISPOSABLE !== '1') {
throw new Error(
'lifecycle tests truncate public/che schemas and Redis; set INTEGRATION_LIFECYCLE_DISPOSABLE=1 only for a dedicated stack'
);
}
const files = rows.filter((row) => row.lane === lane).map(({ file }) => file);
const environment = {
...process.env,
...(lane === 'reference' ? { TURN_DIFFERENTIAL_REFERENCE: '1' } : {}),
};
const result = spawnSync(
'pnpm',
['exec', 'vitest', 'run', '--config', 'vitest.config.ts', ...files, ...vitestArguments],
{
cwd: packageRoot,
env: environment,
stdio: 'inherit',
}
);
if (result.error) {
throw result.error;
}
process.exit(result.status ?? 1);