mirror of
https://github.com/callstack/agent-device.git
synced 2026-09-14 20:06:34 +08:00
423927fdd8
* chore(mutation): shrink the lane to report-only (#1457, #1781 wave 2) The mutation harness's two real catches (#1474, #1475) both came from humans reading the weekly score report. The ratchet half never operated: the baseline was committed exactly twice (8cce0ef6b,60400d04b), both times with `stableRuns: 0, gating: false`, and was never updated after the very fixes it triggered — the weekly job computed a new baseline and then `git checkout --`d it, uploading a proposal nobody applied in 3+ weeks. A gate nobody arms is harness weight; the report is the part that paid. Deletes ratchet.ts + ratchet.test.ts, mutation-baselines/, and every baseline/graduation/gating path in run.ts (`--update`, `mutation:baseline`). run.ts now exits non-zero only on a harness failure, never on a score. The report renders the per-kernel table (kernel, score, killed, survived, total, timeouts) plus the surviving mutants a strengthening PR works from. Kernel scoping stays: stryker.config.json and KERNEL_MODULES are untouched. * fix(mutation): restore denominator coverage and publish the table before judging the shard set Review of #1828: - `report.test.ts` re-asserts that Ignored/CompileError/RuntimeError leave the denominator — the one behaviour `ratchet.test.ts` covered and nothing replaced. A `tally()` edit that counted tool noise would have deflated every published score with a green `mutation:test`. - `assertShardsCoverModules` now runs after `emit()`, so an incomplete shard set still publishes the kernels that completed instead of only an error string. This makes the workflow comments' claim about the job summary true rather than re-wording them down. * chore(mutation): trigger the affected lane on exactly the paths that can select mutants The PR lane returns an empty matrix unless the diff touches the harness, so the kernel-source and `**/*.test.ts` triggers only bought a 1-4 min no-op job on ~96% of PRs. `on.pull_request.paths` is now exactly `LANE_TOOLING` plus the workflow file, asserted in both directions by workflow.test.ts against the exported constant — a missing path would let a harness change merge unproven, an extra one starts a job that can only answer `[]`. Also drops the workflow header's contradictory scope paragraph: it claimed the lane selects on kernel sources and any test reaching one, which has not been true since the ratchet went. * fix(mutation): score and publish a short shard set before failing on the count The expected-count check ran inside readShardedReports, before anything was summarized, so on the weekly's real `--expect-shards 10` one dead shard threw away the nine that had reported — the earlier reorder only moved the zero-mutants check. The merge now returns the shard count, and both verdicts run after emit() with the same exit code and `score` stage. Regression uses the weekly argument shape (`--expect-shards 10`, one shard present) and asserts the reporting kernel's row reaches stdout while the run still fails.
81 lines
2.9 KiB
TypeScript
81 lines
2.9 KiB
TypeScript
// The report is the lane's whole product (#1457): it never gates, so a table
|
|
// that stops carrying the numbers is the lane failing silently rather than
|
|
// loudly. Both #1474 and #1475 were written from this table.
|
|
//
|
|
// Only the renderer is exercised here; every test that runs the CLI lives in
|
|
// envelope.test.ts, because a second file spawning `run.ts` would race it over
|
|
// the repo-relative lane envelope.
|
|
|
|
import assert from 'node:assert/strict';
|
|
import { test } from 'node:test';
|
|
import { renderReport } from './report.ts';
|
|
import { summarizeReport } from './score.ts';
|
|
|
|
const PROVENANCE = { strykerVersion: '9.6.1', configHash: 'sha256:abcdef123456' };
|
|
|
|
test('the table carries score, killed, survived, total and timeouts per kernel', () => {
|
|
const scores = summarizeReport(
|
|
{
|
|
files: {
|
|
'packages/kernel/src/errors.ts': {
|
|
mutants: [
|
|
{ status: 'Killed' },
|
|
{ status: 'Timeout' },
|
|
{
|
|
status: 'Survived',
|
|
mutatorName: 'ConditionalExpression',
|
|
location: { start: { line: 7 } },
|
|
},
|
|
{
|
|
status: 'NoCoverage',
|
|
mutatorName: 'BooleanLiteral',
|
|
location: { start: { line: 9 } },
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
['kernel-errors'],
|
|
);
|
|
const markdown = renderReport(scores, PROVENANCE);
|
|
// Timeouts count as killed, so a score propped up by slow mutants rather than
|
|
// assertions is only visible if the column is rendered.
|
|
assert.match(markdown, /\| `kernel-errors` — [^|]+\| 50% \| 2 \| 2 \| 4 \| 1 \|/);
|
|
// The surviving mutants are what a test-strengthening PR works from.
|
|
assert.match(markdown, /- `packages\/kernel\/src\/errors\.ts:7` ConditionalExpression/);
|
|
assert.match(markdown, /- `packages\/kernel\/src\/errors\.ts:9` BooleanLiteral/);
|
|
});
|
|
|
|
// The denominator is the report's arithmetic: counting tool-side noise would
|
|
// silently deflate every score the lane publishes. Ported from the deleted
|
|
// ratchet.test.ts, which was where this lived.
|
|
test('statuses outside the score (Ignored, CompileError, RuntimeError) leave the denominator', () => {
|
|
const [score] = summarizeReport(
|
|
{
|
|
files: {
|
|
'packages/kernel/src/errors.ts': {
|
|
mutants: [
|
|
{ status: 'Killed' },
|
|
{ status: 'Ignored' },
|
|
{ status: 'CompileError' },
|
|
{ status: 'RuntimeError' },
|
|
],
|
|
},
|
|
},
|
|
},
|
|
['kernel-errors'],
|
|
);
|
|
assert.equal(score?.total, 1);
|
|
assert.equal(score?.score, 100);
|
|
});
|
|
|
|
test('a kernel with no surviving mutants lists no detail section', () => {
|
|
const scores = summarizeReport(
|
|
{ files: { 'packages/kernel/src/errors.ts': { mutants: [{ status: 'Killed' }] } } },
|
|
['kernel-errors'],
|
|
);
|
|
const markdown = renderReport(scores, PROVENANCE);
|
|
assert.match(markdown, /\| `kernel-errors` — [^|]+\| 100% \| 1 \| 0 \| 1 \| 0 \|/);
|
|
assert.doesNotMatch(markdown, /^### /m);
|
|
});
|