fix(#4012): make the artifact say whether the reporter ever loaded
Down to 3 remote failures, all one chain. The explicit no-events reporting is working — the diagnostic now states the events file does not exist, instead of silently printing the generic message. But it then ASSERTED a cause: "the child was killed before the reporter wrote even one event (process/spawn startup stall, not a test hang)". That was a guess dressed as a finding, and the fixture contradicts it: it starts a real test, so test:start should fire in milliseconds against a 2000ms budget. Two hypotheses remained and I could not separate them locally, because the local test runner is hook-blocked here: either the custom reporter never LOADS in the child, or it loads and no event reaches it before the SIGKILL. Rather than guess a third time, the artifact now answers it. The reporter appends a reporter:init line as its first action, before consuming anything, so the file's contents discriminate: absent means the reporter never loaded; init-only means it loaded and saw no test events; init plus events means it works. The diagnostic has a branch for each and, where the cause is genuinely unresolved, names both possibilities instead of picking one. Also passes the reporter as a file:// URL via pathToFileURL. Node documents the --test-reporter value as an import()-style specifier, and a bare absolute path is not a portable one — notably on Windows. That is a correctness fix whichever hypothesis holds, and it is a live candidate for the first. FIXED_OVERHEAD is derived by reducing over the actual argv strings, so the longer URL is accounted automatically. Verified by execution, not assumption: composing the reporter against an EMPTY event stream writes exactly one line, the init marker. That is the whole point of the marker, so it is pinned by a test rather than left to inspection. T1 stays red and untouched. Verification runs on the remote runner. Refs #4012
This commit is contained in:
@@ -934,8 +934,9 @@ test('hangs forever', () => new Promise(() => {}));
|
||||
);
|
||||
const rawContent = fs.readFileSync(eventsFile, 'utf8');
|
||||
const lines = splitLines(rawContent.trim()).filter((l) => l.length > 0);
|
||||
assert.strictEqual(lines.length, 2, `expected exactly 2 NDJSON lines; got:\n${lines.join('\n')}`);
|
||||
const [start, pass] = lines.map((l) => JSON.parse(l));
|
||||
assert.strictEqual(lines.length, 3, `expected exactly 3 NDJSON lines (init marker + 2 handled events); got:\n${lines.join('\n')}`);
|
||||
const [init, start, pass] = lines.map((l) => JSON.parse(l));
|
||||
assert.strictEqual(init.type, 'reporter:init');
|
||||
assert.strictEqual(start.type, 'test:start');
|
||||
assert.strictEqual(start.file, 'a.test.cjs');
|
||||
assert.strictEqual(pass.type, 'test:pass');
|
||||
@@ -950,6 +951,45 @@ test('hangs forever', () => new Promise(() => {}));
|
||||
}
|
||||
});
|
||||
|
||||
// #3889: the init marker is the reporter's FIRST action, written before
|
||||
// the `for await` loop even begins — so it must land even when the
|
||||
// source event stream yields ZERO events (e.g. the child is killed
|
||||
// before node:test emits anything). This pins the marker's whole
|
||||
// purpose: its presence alone proves the reporter module loaded and was
|
||||
// invoked, independent of whether any test ever started.
|
||||
test('the reporter writes only the init marker when the source yields zero events', async () => {
|
||||
const reporter = require('../scripts/lib/ndjson-reporter.cjs');
|
||||
const eventsFile = path.join(tmpDir, 'ndjson-reporter-init-only.ndjson');
|
||||
const savedEventsFile = process.env.GSD_RUN_TESTS_EVENTS_FILE;
|
||||
process.env.GSD_RUN_TESTS_EVENTS_FILE = eventsFile;
|
||||
try {
|
||||
async function* emptyEvents() {}
|
||||
const result = await reporter(emptyEvents());
|
||||
assert.strictEqual(
|
||||
result ?? null,
|
||||
null,
|
||||
`expected the reporter to return nully even with zero source events; got ${JSON.stringify(result)}`,
|
||||
);
|
||||
const rawContent = fs.readFileSync(eventsFile, 'utf8');
|
||||
const lines = splitLines(rawContent.trim()).filter((l) => l.length > 0);
|
||||
assert.strictEqual(
|
||||
lines.length,
|
||||
1,
|
||||
`expected exactly 1 NDJSON line (the init marker only); got:\n${lines.join('\n')}`,
|
||||
);
|
||||
const [init] = lines.map((l) => JSON.parse(l));
|
||||
assert.strictEqual(init.type, 'reporter:init');
|
||||
assert.strictEqual(typeof init.ts, 'number');
|
||||
} finally {
|
||||
if (savedEventsFile === undefined) {
|
||||
delete process.env.GSD_RUN_TESTS_EVENTS_FILE;
|
||||
} else {
|
||||
process.env.GSD_RUN_TESTS_EVENTS_FILE = savedEventsFile;
|
||||
}
|
||||
cleanup(eventsFile);
|
||||
}
|
||||
});
|
||||
|
||||
// T4: the pre-existing timeout / abort / force-exit behavior (#1051)
|
||||
// still holds with the reporter instrumentation wired in — the new
|
||||
// --test-reporter flags must not change detection, the abort-on-timeout
|
||||
|
||||
Reference in New Issue
Block a user