Accelerate rules-first feedback and integrate native Strongholds
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.
This commit is contained in:
1 parent
af6d27095c
commit
e9830b6ced
27 files changed
+2339
-194
No files matched your search
+167
-20
@@ -1,9 +1,17 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import {
|
||||
cpSync,
|
||||
mkdtempSync,
|
||||
mkdirSync,
|
||||
rmSync,
|
||||
symlinkSync,
|
||||
writeFileSync,
|
||||
} from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { spawn, spawnSync } from 'node:child_process';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import {
|
||||
classifyTest,
|
||||
discoverTests,
|
||||
@@ -67,6 +75,7 @@ void test('an unmarked HTTP test cannot silently enter the offline suite', (t) =
|
||||
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', ['']));
|
||||
assert.throws(
|
||||
() => selectTests(files, 'offline', ['rules', 'typo']),
|
||||
/No offline test files match/,
|
||||
@@ -78,28 +87,166 @@ void test('each filename filter must match within its suite, including combined
|
||||
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-'));
|
||||
function runnerFixture(t: test.TestContext, sources: Record<string, string>) {
|
||||
const root = mkdtempSync(join(tmpdir(), 'dune-test-runner-'));
|
||||
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",
|
||||
mkdirSync(join(root, 'tools'));
|
||||
mkdirSync(join(root, 'tests'));
|
||||
writeFileSync(join(root, 'package.json'), '{"type":"module"}\n');
|
||||
for (const tool of ['test.ts', 'test-discovery.ts', 'test-reporter.ts'])
|
||||
cpSync(new URL(`../tools/${tool}`, import.meta.url), join(root, 'tools', tool));
|
||||
symlinkSync(
|
||||
fileURLToPath(new URL('../node_modules', import.meta.url)),
|
||||
join(root, 'node_modules'),
|
||||
'dir',
|
||||
);
|
||||
for (const [file, source] of Object.entries(sources))
|
||||
writeFileSync(join(root, 'tests', file), source);
|
||||
// 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',
|
||||
},
|
||||
);
|
||||
const args = ['--import', import.meta.resolve('tsx'), 'tools/test.ts'];
|
||||
return {
|
||||
root,
|
||||
env,
|
||||
args,
|
||||
run: (...options: string[]) =>
|
||||
spawnSync(process.execPath, [...args, ...options], {
|
||||
cwd: root,
|
||||
env,
|
||||
timeout: 15_000,
|
||||
encoding: 'utf8',
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
void test('concise CLI preserves nested assertion details, output and failure exit', (t) => {
|
||||
const fixture = runnerFixture(t, {
|
||||
'failure.test.mjs': `
|
||||
import { test, describe } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
describe('parent suite', () => {
|
||||
test('passing sentinel', () => {});
|
||||
test('failure sentinel', t => {
|
||||
console.log('stdout sentinel');
|
||||
console.error('stderr sentinel');
|
||||
t.diagnostic('diagnostic sentinel');
|
||||
assert.deepEqual({ payment: 7 }, { payment: 11 });
|
||||
});
|
||||
});
|
||||
`,
|
||||
});
|
||||
for (const options of [[], ['--verbose']]) {
|
||||
const result = fixture.run(...options);
|
||||
assert.ifError(result.error);
|
||||
assert.equal(result.status, 1);
|
||||
const output = result.stdout + result.stderr;
|
||||
assert.match(output, /failure sentinel/);
|
||||
assert.match(output, /failure\.test\.mjs:\d+:\d+/);
|
||||
assert.match(output, /payment: 7/);
|
||||
assert.match(output, /payment: 11/);
|
||||
for (const channel of ['stdout', 'stderr', 'diagnostic'])
|
||||
assert.match(output, new RegExp(`${channel} sentinel`));
|
||||
if (options.length) assert.match(output, /passing sentinel/);
|
||||
else assert.doesNotMatch(output, /passing sentinel/);
|
||||
}
|
||||
});
|
||||
|
||||
void test('CLI applies union filename selection and name intersection without green empty runs', (t) => {
|
||||
const fixture = runnerFixture(t, {
|
||||
'empty.test.mjs': '',
|
||||
'skipped.test.mjs': `
|
||||
import test from 'node:test';
|
||||
test('pending case', { skip: true }, () => {});
|
||||
`,
|
||||
'alpha.test.mjs': `
|
||||
import test from 'node:test';
|
||||
test('chosen alpha', () => {});
|
||||
test('excluded failure', () => { throw new Error('must not execute'); });
|
||||
`,
|
||||
'beta.test.mjs': `
|
||||
import test from 'node:test';
|
||||
test('chosen beta', () => {});
|
||||
`,
|
||||
'other.test.mjs': `
|
||||
import test from 'node:test';
|
||||
test('chosen outside selection', () => { throw new Error('must not execute'); });
|
||||
`,
|
||||
});
|
||||
const result = fixture.run('alpha', 'beta', '--name', '^chosen', '--verbose');
|
||||
assert.ifError(result.error);
|
||||
assert.equal(result.status, 1);
|
||||
assert.match(result.stdout + result.stderr, /expected failure/);
|
||||
assert.equal(result.status, 0);
|
||||
assert.match(result.stdout, /chosen alpha/);
|
||||
assert.match(result.stdout, /chosen beta/);
|
||||
assert.doesNotMatch(result.stdout + result.stderr, /must not execute/);
|
||||
const listed = fixture.run('alpha', 'beta', '--list');
|
||||
assert.equal(listed.status, 0);
|
||||
assert.match(listed.stdout, /tests\/alpha\.test\.mjs/);
|
||||
assert.match(listed.stdout, /tests\/beta\.test\.mjs/);
|
||||
assert.doesNotMatch(listed.stdout, /tests\/other\.test\.mjs/);
|
||||
for (const options of [
|
||||
['empty'],
|
||||
['skipped'],
|
||||
['alpha', '--name', '^no matching tests$'],
|
||||
['alpha', '--name', '^no matching tests$', '--verbose'],
|
||||
['alpha', '--name', '['],
|
||||
['alpha', '--name', ''],
|
||||
['', '--name', '^chosen'],
|
||||
['alpha', 'missing'],
|
||||
['--suite', 'unknown'],
|
||||
]) {
|
||||
const invalid = fixture.run(...options);
|
||||
assert.ifError(invalid.error);
|
||||
assert.equal(invalid.status, 1, JSON.stringify(options));
|
||||
}
|
||||
});
|
||||
|
||||
void test('CLI cancellation remains failure when its test driver handles the signal with exit zero', { timeout: 30_000 }, async (t) => {
|
||||
for (const [signal, expected] of [
|
||||
['SIGINT', 130],
|
||||
['SIGTERM', 143],
|
||||
] as const) {
|
||||
const fixture = runnerFixture(t, {
|
||||
'waiting.test.mjs': `
|
||||
import test from 'node:test';
|
||||
test('waiting', () => {});
|
||||
`,
|
||||
});
|
||||
// A real Node test driver whose reporter gracefully handles cancellation.
|
||||
writeFileSync(
|
||||
join(fixture.root, 'tools/test-reporter.ts'),
|
||||
`import { MessageChannel } from 'node:worker_threads';
|
||||
export default async function* reporter(source) {
|
||||
// Keep the driver alive until the real OS signal, without timer guesses.
|
||||
const { port1, port2 } = new MessageChannel();
|
||||
port1.on('message', () => {});
|
||||
process.on('${signal}', () => {
|
||||
port1.close();
|
||||
port2.close();
|
||||
process.exit(0);
|
||||
});
|
||||
console.log('cancellation-ready');
|
||||
for await (const event of source) yield '';
|
||||
await Promise.withResolvers().promise;
|
||||
}\n`,
|
||||
);
|
||||
const child = spawn(process.execPath, fixture.args, {
|
||||
cwd: fixture.root,
|
||||
env: fixture.env,
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
});
|
||||
t.after(() => child.kill('SIGKILL'));
|
||||
let output = '';
|
||||
child.stdout.on('data', (chunk: Buffer) => {
|
||||
output += chunk.toString();
|
||||
if (output.includes('cancellation-ready')) {
|
||||
output = '';
|
||||
child.kill(signal);
|
||||
}
|
||||
});
|
||||
const { promise, resolve, reject } = Promise.withResolvers<number | null>();
|
||||
child.once('error', reject);
|
||||
child.once('close', resolve);
|
||||
assert.equal(await promise, expected);
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user