Files
Dune/tests/test-workflow.test.ts
T
Quintine 28ba1618ba Implement Homeworld mechanics through Caladan reinforcement checkpoint
Connect typed Homeworld custody, transport, battle, income, revival and Caladan reinforcement flows with private multiplayer recovery and controls. Preserve the optimized test workflow and keep unfinished rules modes gated. Verified with 3682 offline tests, typecheck, lint, production build and 40 HTTP tests; existing saved games remain unchanged.
2026-09-10 08:12:26 +10:00

106 lines
3.7 KiB
TypeScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { spawnSync } from 'node:child_process';
import {
classifyTest,
discoverTests,
selectTests,
} from '../tools/test-discovery';
void test('discovery includes new and nested tests, excluding fixture helpers', (t) => {
const root = mkdtempSync(join(tmpdir(), 'dune-discovery-'));
t.after(() => rmSync(root, { recursive: true, force: true }));
mkdirSync(join(root, 'tests/nested'), { recursive: true });
writeFileSync(join(root, 'tests/rules.test.ts'), '');
writeFileSync(join(root, 'tests/fixture.ts'), '');
writeFileSync(
join(root, 'tests/nested/restart.test.ts'),
"import { DatabaseSync } from 'node:sqlite';",
);
writeFileSync(
join(root, 'tests/http.test.ts'),
'// @dune-suite integration\n',
);
const files = discoverTests(root);
assert.equal(files.length, 3);
assert.deepEqual(
selectTests(files, 'offline')
.map((f) => f.kind)
.sort(),
['recovery', 'unit'],
);
assert.deepEqual(
selectTests(files, 'integration').map((f) => f.path),
['tests/http.test.ts'],
);
assert.equal(selectTests(files, 'multiplayer').length, 2);
assert.equal(selectTests(files, 'recovery').length, 1);
assert.equal(selectTests(files, 'unit').length, 1);
assert.equal(selectTests(files, 'all').length, 3);
});
void test('HTTP annotation takes precedence over in-memory SQLite imports', () => {
assert.equal(
classifyTest(
"// @dune-suite integration\nimport { DatabaseSync } from 'node:sqlite';",
),
'integration',
);
assert.equal(classifyTest('// Uses node:sqlite in a mocked example'), 'unit');
});
void test('an unmarked HTTP test cannot silently enter the offline suite', (t) => {
const root = mkdtempSync(join(tmpdir(), 'dune-http-discovery-'));
t.after(() => rmSync(root, { recursive: true, force: true }));
mkdirSync(join(root, 'tests'));
// Assemble the fixture expression so this offline regression isn't an HTTP test.
writeFileSync(
join(root, 'tests/http.test.ts'),
['process', 'env', 'DUNE_TEST_URL'].join('.'),
);
assert.throws(() => discoverTests(root), /HTTP tests need/);
});
void test('each filename filter must match within its suite, including combined filters', () => {
const files = [{ path: 'tests/rules.test.ts', kind: 'unit' as const }];
assert.equal(selectTests(files, 'offline', ['rules']).length, 1);
assert.throws(
() => selectTests(files, 'offline', ['rules', 'typo']),
/No offline test files match/,
);
assert.throws(
() => selectTests(files, 'integration', ['rules']),
/No integration test files match/,
);
assert.throws(() => selectTests([], 'all'), /No test files/);
});
void test('the TypeScript test subprocess returns failure for a failing assertion', (t) => {
const root = mkdtempSync(join(tmpdir(), 'dune-test-failure-'));
t.after(() => rmSync(root, { recursive: true, force: true }));
const file = join(root, 'failure.test.mts');
writeFileSync(
file,
"import test from 'node:test';\ntest('failure sentinel', () => { throw new Error('expected failure'); });\n",
);
// A nested runner must not inherit the parent test worker's IPC context.
const env = { ...process.env };
delete env.NODE_TEST_CONTEXT;
const result = spawnSync(
process.execPath,
['--import', 'tsx', '--test', file],
{
cwd: new URL('../', import.meta.url),
env,
timeout: 15_000,
encoding: 'utf8',
},
);
assert.ifError(result.error);
assert.equal(result.status, 1);
assert.match(result.stdout + result.stderr, /expected failure/);
});