Files
callstack__agent-device/scripts/mutation/modules.test.ts
Michał Pierzchała 443bbd0cb8 fix(mutation): move the repo size ratchet where the lane cannot reach it (#1977)
Stryker runs the suite from a sandbox copy under `.tmp/stryker/`, so a test that
asserts about the repository checkout itself — its files on disk, or its git
history — reads a repository that does not exist.

`test-file-size-ratchet.test.ts` is such a gate, and it fails there for two
independent reasons: `disableTypeChecks` (Stryker's default) prepends
`// @ts-nocheck` to every copied file, so all 26 pinned files read one line
longer than they are; and the sandbox has no `origin/main`, so the gate's
history-backed half cannot resolve its merge-base. Fixing either leaves the
other. Its own `.tmp` skip entry cannot help: that is matched relative to
`REPO_ROOT`, which inside the sandbox *is* the sandbox.

Move it to `scripts/__tests__/` and include it explicitly in `unit-core`, the
address the repo already uses for maintained gates that are not `src` tests.
`KERNEL_TEST_FILE_RE` admits only root/package `src` tests, and its comment
already names `scripts/__tests__` as unreachable by construction — so the gate
leaves every mutation lane by virtue of where it lives, with no classifier to
recognise it and nothing to keep in sync.

This replaces the source-text scanner of the previous revision, which was the
wrong boundary: it sniffed for a single-quoted `walk-files` import or the string
`origin/main`, so a behavioral test could match and be silently excluded while an
equivalent repo gate using double quotes, another walker, or another base ref
would be missed. The scanner, its test, and its justifying comment are all gone.

`REPO_ROOT` and the walked roots are unchanged — both addresses are two levels
below the repo root, and `TEST_ROOTS` already included `scripts`, so the gate
measures exactly what it did before.

The one new assertion pins the invariant this now depends on: `isKernelTestFile`
accepts root/package `src` tests and rejects `scripts/__tests__`. Widening that
pattern would silently pull the gate back into every lane.

Verified with `pnpm mutation:run --modules kernel-errors`: scope 804 -> 803 test
files, dry run clean, lane `pass` at stage complete, score 74.8%
(187 killed / 63 survived / 250) — unchanged. `pnpm mutation:test` 39/39,
`pnpm check:layering` 181/181, `typecheck`, `lint`, `format` clean.
`stryker.config.json` is untouched, so scores stay comparable.

Unblocks #1964, whose two mutation checks fail on main's tip without its code.
2026-08-23 07:39:49 +02:00

92 lines
3.8 KiB
TypeScript

import assert from 'node:assert/strict';
import fs from 'node:fs';
import path from 'node:path';
import { test } from 'node:test';
import { fileURLToPath } from 'node:url';
import {
affectedModules,
ALL_MODULE_IDS,
isKernelTestFile,
KERNEL_MODULES,
moduleForFile,
mutateGlobs,
shardMatrix,
} from './modules.ts';
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
test('every enumerated kernel path exists', () => {
for (const module of KERNEL_MODULES) {
for (const owned of module.owns) {
assert.ok(
fs.existsSync(path.join(repoRoot, owned)),
`${module.id} owns a path that no longer exists: ${owned}`,
);
}
}
});
test('changed sources map onto the module that owns them', () => {
assert.equal(moduleForFile('packages/kernel/src/errors.ts'), 'kernel-errors');
assert.equal(moduleForFile('./src/daemon/ref-frame.ts'), 'daemon-ref-frame');
assert.equal(moduleForFile('packages/selectors/src/internal/parse.ts'), 'selectors');
// Selector tests live under the owned prefix; every other kernel's tests are
// attributed by ownership.ts, not by this path match.
assert.equal(moduleForFile('packages/selectors/src/internal/resolve.test.ts'), 'selectors');
assert.equal(moduleForFile('packages/kernel/src/rect.ts'), undefined);
assert.equal(moduleForFile('README.md'), undefined);
});
test('affected selection is deduplicated and registry-ordered', () => {
assert.deepEqual(
affectedModules([
'packages/selectors/src/internal/parse.ts',
'packages/selectors/src/internal/match.ts',
'packages/kernel/src/errors.ts',
'docs/agents/testing.md',
]),
['kernel-errors', 'selectors'],
);
assert.deepEqual(affectedModules(['docs/agents/testing.md']), []);
});
test('mutate globs default to every module and narrow on request', () => {
assert.deepEqual(mutateGlobs(), mutateGlobs(ALL_MODULE_IDS));
assert.deepEqual(mutateGlobs(['kernel-errors']), ['packages/kernel/src/errors.ts']);
});
// One job per module is only affordable while a module fits the lane's budget;
// the shard count is registry data so the workflows and the runner agree on it.
test('the shard matrix slices only the modules that declare shards', () => {
assert.deepEqual(shardMatrix(['kernel-errors']), [
{ name: 'kernel-errors', module: 'kernel-errors' },
]);
assert.deepEqual(shardMatrix(['selectors']), [
{ name: 'selectors-1', module: 'selectors', shard: '1/4' },
{ name: 'selectors-2', module: 'selectors', shard: '2/4' },
{ name: 'selectors-3', module: 'selectors', shard: '3/4' },
{ name: 'selectors-4', module: 'selectors', shard: '4/4' },
]);
assert.equal(shardMatrix().length, ALL_MODULE_IDS.length + 3);
// Every registry module reaches the matrix: an unsharded sweep is not a sweep.
for (const id of ALL_MODULE_IDS) {
assert.ok(
shardMatrix().some((spec) => spec.module === id),
`${id} has no shard`,
);
}
});
test('a kernel test is reachable only under root or package src', () => {
// Load-bearing for where `test-file-size-ratchet.test.ts` lives. It measures the
// repository's own files and git history, neither of which Stryker's sandbox copy can
// answer, so it sits in `scripts/__tests__/` (explicitly included by `unit-core`) and
// this predicate is what keeps it out of every mutation lane. Widening the pattern to
// scripts/ would silently pull it back in and fail every dry run.
assert.ok(isKernelTestFile('src/daemon/__tests__/ref-frame.test.ts'));
assert.ok(isKernelTestFile('packages/selectors/src/parse.test.ts'));
assert.ok(!isKernelTestFile('scripts/__tests__/test-file-size-ratchet.test.ts'));
assert.ok(!isKernelTestFile('test/integration/daemon.test.ts'));
assert.ok(!isKernelTestFile('src/daemon/ref-frame.ts'));
});