Files

54 lines
3.9 KiB
Python

#!/usr/bin/env python3
# Row 292 mutants. M1-M5 are Gander's (agents/gander/work/config-checks-any-key/changes.json), with
# the same edits on the resolved config_checks; M1 restores the console-only skip (the brief's
# required mutant). M6-M9 are the resolution's: row 203's console and federation text. `apply <id>`
# edits the worktree and prints the touched path; each edit asserts its anchor appears exactly once.
# `check` prints each anchor's count and kill. K is read from the tests, not run. A restore is `git checkout -- <path>`, then sha256 against the blob in BUILD.md.
import sys
WT = "/mnt/storage/scratch/gizmo-292/"
S = "scripts/stack-release.sh"
T = "packages/cli/tests/stack-release.test.mjs"
M = {
"M1": (S, ' [ "$key" != 1 ] || return 0\n if [ "$key" != 0 ]; then',
' [ "$key" != 1 ] || return 0\n [ "$extra" = \'"console"\' ] || return 0\n if [ "$key" != 0 ]; then'),
"M2": (S, 'elif out="$(MOSAIC_CONFIG="$cfg" node "$loader" validate 2>&1 >/dev/null)"; then return 0',
'elif out="$(MOSAIC_CONFIG="$cfg" node "$loader" validate 2>&1 >/dev/null)" || true; then return 0'),
"M3": (S, ' [ -n "$extra" ] || return 0', ' return 0 # drop missing-loader later-key refusal'),
"M4": (S, ' if [ "$key" != 0 ]; then\n check_refuse config-console-key',
' if [ "$key" = 99 ]; then\n check_refuse config-console-key'),
"M5": (S, ' if [ ! -f "$loader" ]; then', ' [ -n "$extra" ] || return 0\n if [ ! -f "$loader" ]; then'),
"M6": (S, 'Object.hasOwn(d,"federation")?"federation":', ''),
"M7": (S, 'const k=Object.hasOwn(d,"console")?"console":', 'const k='),
"M8": (S, ' if [ "$extra" = \'"console"\' ] || [ "$extra" = \'"federation"\' ]; then\n check_refuse',
' if [ "$extra" = \'"console"\' ]; then\n check_refuse'),
"M9": (S, '[ "$extra" = \'"federation"\' ]; then out="it has no scripts/mosaic-config.mjs"',
'[ "$extra" = \'"federation-off"\' ]; then out="it has no scripts/mosaic-config.mjs"'),
}
# K: (--test-name-pattern, the test.mjs lines the failing stack must show, the text the failure
# must show). Kill rule (sage-3152): the named test fails, its first error's stack names every
# listed line, and the log holds the text (no quote characters, which node's inspect may escape). A fail anywhere else, or a crash first, is a STOP to Sage.
# A survivor of its targeted run gets one whole-file run before it counts as a survivor.
K = {
"M1": ('^config-any-key: a non-console target refusal stops activate and rollback before any stop$', [517, 2619], "actual: 0"),
"M2": ('^config-any-key: a non-console target refusal stops activate and rollback before any stop$', [517, 2619], "actual: 0"),
"M3": ('^config-any-key: missing-loader copies accept only the four oldest keys$', [2670], "actual: 0"),
"M4": ('^config-any-key: missing config passes but malformed JSON refuses without calling the loader$', [2684], "actual: 0"),
"M5": ('^config-any-key: accepting loaders run validate with the selected config on activate and rollback$', [2654], "actual: []"),
"M6": ('^config-any-key: missing-loader copies accept only the four oldest keys$', [2677], "to validate configuration key: "),
"M7": ('^config-any-key: missing-loader copies accept only the four oldest keys$', [2677], "to validate configuration key: "),
"M8": ('^config-any-key: missing-loader copies accept only the four oldest keys$', [2677], "is refused by old"),
"M9": ('^config-any-key: missing-loader copies accept only the four oldest keys$', [2677], "to validate configuration key: "),
}
cmd = sys.argv[1]
if cmd == "list":
print(" ".join(M)); sys.exit(0)
if cmd == "check":
for k, (f, a, b) in M.items():
print(k, f, open(WT + f).read().count(a), "kill:", *K[k])
sys.exit(0)
f, a, b = M[sys.argv[2]]
s = open(WT + f).read()
assert s.count(a) == 1, (sys.argv[2], s.count(a))
open(WT + f, "w").write(s.replace(a, b))
print(f)