scripts/mutation.ts
Rama main · réplica de lectura del repositorio original.
#!/usr/bin/env -S deno run --allow-all
/**
* In-house mutation tester — "tests for your tests".
*
* Copies the checkout into `.mutation-runs/<id>/work`, mutates
* binary/logical/assignment operators in the copied source file(s), runs the
* mapped test file(s), and reports which mutants SURVIVED (were not caught by
* any assertion). It is the real version of the heuristic in
* `test-quality-audit.ts`: instead of guessing which assertions look weak, it
* proves which code changes your tests fail to notice.
*
* The operator tables and AST walk are derived from Mutasaurus (MIT); the
* execution model is our own. The child process still mutates in place inside
* the copied checkout so import-map aliases bind to the mutant — see
* scripts/mutation/LICENSE.mutasaurus.md.
*
* Usage: deno task mutation <source-glob> <test-glob> [options]
*/
import { DEFAULT_DEADLINE, parseArgs } from "./mutation/args.ts";
import { expand } from "./mutation/expand.ts";
import { runIsolatedMutationCommand } from "./mutation/isolation.ts";
import {
isSnapshotChild,
runSnapshotChild,
} from "./mutation/snapshot-child.ts";
const USAGE = `Usage:
deno task mutation <source-glob> <test-glob> [options]
deno task mutation --source <glob> --test <glob> [--source …] [--test …]
Mutates operators in the source file(s), runs the mapped test file(s), and
reports which mutants survived (were NOT caught by your tests).
Options:
--exhaustive Try every operator replacement, not just one per operator.
--harness Build static assets and start stripe-mock first (needed for
tests that import the app / Stripe; slower).
--jobs <n> Concurrent test-file batches per mutant (default: CPU-aware,
or MUTATION_JOBS when set).
Static gates use separate CPU-aware workers, capped at 4;
set MUTATION_STATIC_JOBS to lower that cap.
--deadline <ms> Fail the whole run if it is still going after this
long (default ${DEFAULT_DEADLINE}). A guard against a mutant
that hangs the tests; it never decides a mutant.
-h, --help Show this help.
Examples:
deno task mutation src/shared/dates.ts test/shared/dates.test.ts
deno task mutation 'src/shared/forms/definition.ts' 'test/shared/forms/definition/*.test.ts' --exhaustive`;
// Ends by returning, never by Deno.exit: as a snapshot child this runs
// inside runSnapshotChild, whose clean-up must go ahead after the work.
const main = async (): Promise<number> => {
const args = parseArgs(Deno.args);
if (args.error !== null) {
console.error(args.error);
return 1;
}
if (args.help || args.sources.length === 0 || args.tests.length === 0) {
console.log(USAGE);
return args.help ? 0 : 1;
}
const sourceFiles = await expand(args.sources);
const testFiles = await expand(args.tests);
if (sourceFiles.length === 0) {
console.error("No source files matched.");
return 1;
}
if (testFiles.length === 0) {
console.error("No test files matched.");
return 1;
}
const { runMutationTesting } = await import("./mutation/runner.ts");
return await runMutationTesting({
...(args.batchJobs === undefined ? {} : { batchJobs: args.batchJobs }),
deadline: args.deadline,
exhaustive: args.exhaustive,
sourceFiles,
testFiles,
useHarness: args.useHarness,
});
};
if (import.meta.main) {
if (isSnapshotChild()) {
Deno.exit(await runSnapshotChild(main));
} else {
Deno.exit(await runIsolatedMutationCommand(Deno.args));
}
}