Files
stack/agents/darkwing/work/queue-91/mutants.sh
T

70 lines
4.9 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# Row 91 (#1585) mutants: one or more per guard in checkout.mjs, then the
# launcher's refusal and record, then the prompt line. Run from the
# candidate worktree's root:
# LOG=/some/dir bash mutants.sh /path/to/mut.mjs > mutants.txt
# Each mutant edits one site with mut.mjs (exactly one match, else exit 3),
# runs the named tests and restores the file. "killed" means the run
# failed; "survived" means it passed.
set -u
MUT=$1
run() {
local name=$1 file=$2 from=$3 to=$4; shift 4
cp -- "$file" "$file.orig"
if ! node "$MUT" "$file" "$from" "$to"; then
echo "$name: NO MATCH"; mv -- "$file.orig" "$file"; return
fi
if node --test "$@" > "${LOG:-.}/mut-$name.log" 2>&1; then
echo "$name: survived"
else
echo "$name: killed ($(grep -m1 -o 'ℹ fail [0-9]*' "${LOG:-.}/mut-$name.log"); $(grep '^✖ ' "${LOG:-.}/mut-$name.log" | grep -v 'failing tests' | sort -u | sed 's/^✖ //; s/ ([0-9.]*ms)$//' | paste -sd '|'))"
fi
mv -- "$file.orig" "$file"
}
C=packages/seat/src/checkout.mjs
L=packages/cli/src/launcher.mjs
B=packages/harness/src/bundle.mjs
CT=(packages/seat/tests/checkout.test.mjs)
LT=(packages/cli/tests/launcher.test.mjs)
BT=(packages/harness/tests/bundle.test.mjs)
# The walk of .git.
run walk-off $C 'if (walk(join(dest, ".git"))) return "not-a-clone";' 'if (false) return "not-a-clone";' "${CT[@]}"
run walk-no-nlink $C '} else if (!st.isFile() || st.nlink !== 1) return true;' '} else if (!st.isFile()) return true;' "${CT[@]}"
run walk-symlinks-pass $C '} else if (!st.isFile() || st.nlink' '} else if ((!st.isFile() && !st.isSymbolicLink()) || st.nlink' "${CT[@]}"
run clone-dir-symlink-ok $C 'return lstatSync(p).isDirectory();' 'return lstatSync(p).isDirectory() || lstatSync(p).isSymbolicLink();' "${CT[@]}"
run no-commondir $C 'for (const f of ["commondir", join("objects", "info", "alternates")])' 'for (const f of [join("objects", "info", "alternates")])' "${CT[@]}"
run no-alternates $C 'for (const f of ["commondir", join("objects", "info", "alternates")])' 'for (const f of ["commondir"])' "${CT[@]}"
# Hooks and the config file.
run hooks-ignored $C 'if (readdirSync(join(dest, ".git", "hooks")).length > 0) return "hooks-present";' 'if (false) return "hooks-present";' "${CT[@]}"
run config-cap-off $C 'st.size > CONFIG_MAX_BYTES' 'st.size > Infinity' "${CT[@]}"
# The allowlist.
run allow-any-key $C 'default: return false;' 'default: return true;' "${CT[@]}"
run origin-any-url $C 'case "remote.origin.url": return value === root;' 'case "remote.origin.url": return true;' "${CT[@]}"
run fetch-any $C 'case "remote.origin.fetch": return value === FETCH;' 'case "remote.origin.fetch": return true;' "${CT[@]}"
run branch-remote-any $C 'return branch[1] === "remote" ? value === "origin" :' 'return branch[1] === "remote" ? true :' "${CT[@]}"
run branch-merge-any $C ': /^refs\/heads\/./.test(value ?? "");' ': true;' "${CT[@]}"
run duplicate-key-ok $C 'if (seen.has(key) || !allowed(key, value, root))' 'if (!allowed(key, value, root))' "${CT[@]}"
run no-origin-ok $C 'if (!seen.has("remote.origin.url")) return "config-changed";' '' "${CT[@]}"
# The root, the update and the clone.
run ceiling-off $C 'const ceiling = { GIT_CEILING_DIRECTORIES: dirname(root) };' 'const ceiling = {};' "${CT[@]}"
run detached-root-clones $C 'if (!at.branch) throw new CheckoutError(project, `the root ${root} has no branch checked out`);' '' "${CT[@]}"
run other-branch-updates $C 'if (!head.ok || head.out.trim() !== at.branch) return' 'if (!head.ok) return' "${CT[@]}"
run dirty-updates $C 'if (status.out !== "") return { outcome: "kept", reason: "dirty" };' '' "${CT[@]}"
run untracked-ignored $C '"--untracked-files=all", ' '"--untracked-files=no", ' "${CT[@]}"
run merge-not-ff-only $C '"merge", "-q", "--ff-only", "--no-edit"' '"merge", "-q", "--no-edit"' "${CT[@]}"
run temp-left $C 'rmSync(tmp, { recursive: true, force: true });' 'void tmp;' "${CT[@]}"
run clone-in-place $C 'const tmp = join(workspace, `.${project}.clone-${randomBytes(4).toString("hex")}`);' 'const tmp = dest;' "${CT[@]}"
run clone-hardlinks $C '"clone", "-q", "--no-hardlinks", ' '"clone", "-q", ' "${CT[@]}"
# Second-line guards: the checks above stop each case first, so these are
# expected to survive.
run safe-no-hookspath $C '"-c", "core.hooksPath=/dev/null",' '' "${CT[@]}"
run safe-no-fsmonitor $C '"-c", "core.fsmonitor=false",' '' "${CT[@]}"
# The launcher.
run refusal-unmapped $L 'if (e instanceof CheckoutError) throw new Refusal(' 'if (false) throw new Refusal(' "${LT[@]}"
run kept-unrecorded $L 'for (const c of checkouts) if (c.outcome === "kept") record(' 'for (const c of checkouts) if (false) record(' "${LT[@]}"
run checkouts-not-in-session $L 'session: { run, launchedBy, workspace, toolSocket, turnMarker, checkouts },' 'session: { run, launchedBy, workspace, toolSocket, turnMarker },' "${LT[@]}"
# The prompt line.
run prompt-no-clones $B '...checkoutLines(session.checkouts ?? []),' '' "${BT[@]}"
run prompt-kept-as-current $B '(c.outcome === "kept" ?' '(c.outcome === "never" ?' "${BT[@]}"