Apply user priority: save/recovery/privacy/custody assurance after all rules. Add quick types/lint, concise failure-preserving reporter, verbose mode, actual empty-execution guard, signal handling and focused verify name selection; compact canonical context/handoffs without mass moves. Tooling15, six negative CLI, named/empty source-bound smokes,206 doc links; same15-case278B vs2455B,2.35s vs2.36s no compute speedclaim. Native Advanced Ix/CHOAM47-card Stronghold profile: actual firstMentat/HMS/sixeffects,18native/four legalpolicies,five genuine2..6games3129actions/no rejection, human390px LAUDR2WNv14 bank2/personal1 plan and real battle. Earlier162/6513 checks before assurance amendment; finalquick25/build/guide pass. No new preservation/privacy audit or public/deployed/full-mode claim.
59 lines
2.2 KiB
TypeScript
59 lines
2.2 KiB
TypeScript
import { readdirSync, readFileSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
|
|
export type TestKind = 'unit' | 'recovery' | 'integration';
|
|
export type TestSuite = TestKind | 'offline' | 'multiplayer' | 'all';
|
|
export type TestFile = { path: string; kind: TestKind };
|
|
|
|
// HTTP tests opt in explicitly. SQLite recovery tests use disposable memory DBs.
|
|
export function classifyTest(source: string): TestKind {
|
|
if (/^\/\/ @dune-suite integration\s*$/m.test(source)) return 'integration';
|
|
if (/^import\b[^\n]*from ['"]node:sqlite['"]/m.test(source))
|
|
return 'recovery';
|
|
return 'unit';
|
|
}
|
|
|
|
export function discoverTests(root: string, directory = 'tests'): TestFile[] {
|
|
return readdirSync(join(root, directory), { withFileTypes: true })
|
|
.sort((a, b) => a.name.localeCompare(b.name))
|
|
.flatMap((entry): TestFile[] => {
|
|
const path = `${directory}/${entry.name}`;
|
|
if (entry.isDirectory()) return discoverTests(root, path);
|
|
if (!entry.isFile() || !/\.test\.(?:ts|tsx|mts|js|mjs)$/.test(entry.name))
|
|
return [];
|
|
const source = readFileSync(join(root, path), 'utf8');
|
|
const kind = classifyTest(source);
|
|
if (
|
|
/process\.env\.DUNE_TEST_URL\b/.test(source) &&
|
|
kind !== 'integration'
|
|
)
|
|
throw new Error(`${path}: HTTP tests need // @dune-suite integration`);
|
|
return [{ path, kind }];
|
|
});
|
|
}
|
|
|
|
export function selectTests(
|
|
files: TestFile[],
|
|
suite: TestSuite,
|
|
filters: string[] = [],
|
|
): TestFile[] {
|
|
if (filters.some((filter) => !filter))
|
|
throw new Error('Filename filters must not be empty.');
|
|
const selected = files.filter(({ path, kind }) => {
|
|
const included =
|
|
suite === 'all' ||
|
|
(suite === 'offline' && kind !== 'integration') ||
|
|
(suite === 'multiplayer' && kind !== 'unit') ||
|
|
suite === kind;
|
|
return (
|
|
included && (!filters.length || filters.some((f) => path.includes(f)))
|
|
);
|
|
});
|
|
// A typo should never produce a green run that executed no tests.
|
|
for (const filter of filters)
|
|
if (!selected.some(({ path }) => path.includes(filter)))
|
|
throw new Error(`No ${suite} test files match "${filter}".`);
|
|
if (!selected.length) throw new Error(`No test files in suite "${suite}".`);
|
|
return selected;
|
|
}
|