Files
stack/agents/rocko/work/queue-77/r2/mutants.py
T

54 lines
4.3 KiB
Python

# Row 77 mutants (rounds 1 and 2): each edit applied alone to <tree>, the service and pinned-copy
# tests run, the file restored. Usage: python3 mutants.py <tree>
import subprocess, shutil, sys
W=sys.argv[1] # the tree with the candidate applied
S='scripts/webui-service.sh'
M=[
('failed counts as running', S, '&& $3 != "failed" {', '{'),
('reloading not running', S, '$3 != "failed" {', '$3 != "failed" && $3 != "reloading" {'),
('list-units failure ignored', S, '2>/dev/null)" || die "refusing: systemctl --user list-units', '2>/dev/null)" || true; : "refusing: systemctl --user list-units'),
('install skips state read', S, ' state="$(running)" || exit 1\n [ -z "$state" ] || die "refusing: $UNIT_NAME is running', ' state=""\n [ -z "$state" ] || die "refusing: $UNIT_NAME is running'),
('uninstall skips state read with --no-reload', S, '[ -z "$state" ] || die "refusing: $UNIT_NAME is still running', '[ -z "$state" ] || [ "$RELOAD" = 0 ] || die "refusing: $UNIT_NAME is still running'),
('uid header case-sensitive', S, '"${AUTH_HEADER,,}" = x-authentik-uid', '"$AUTH_HEADER" = X-authentik-uid'),
('uid allows upper hex', S, '^[0-9a-f]{64}$ ]]; then', '^[0-9a-fA-F]{64}$ ]]; then'),
('allowed user may start with -', S, 'case "$ALLOW_USER" in [A-Za-z0-9]*) ;;', 'case "$ALLOW_USER" in *) ;;'),
('no --checkout in unit', S, 'flags="--checkout $REPO $flags"', 'flags="$flags"'),
('canonical always passes', S, '[ -d "$REPO/.git" ] || [ "$ALLOW_WORKTREE" = 1 ] ||', 'true ||'),
('install without --copy', S, '[ -n "$COPY_ID" ] || die "install needs --copy ID', 'true || die "install needs --copy ID'),
('missing copy accepted', S, '[ -f "$COPIES/$COPY_ID/packages/webui/src/cli.mjs" ] ||', 'true ||'),
('status drift wrong', S, 'if [ "$head" = "$copy" ]; then', 'if true; then'),
('copy misses seat', S, ' packages/seat/src\n', ''),
('copy misses README', S, ' packages/webui/README.md\n', ''),
('commit may start with a dot', S, 'case "$commit" in [A-Za-z0-9]*) ;;', 'case "$commit" in [.A-Za-z0-9]*) ;;'),
('copy misses tasks src', S, ' packages/tasks/src\n', ''),
('copy misses fields table', S, ' packages/tasks/fields.json\n', ''),
('copy stays writable', S, '-type f -exec chmod a-w', '-type f -exec true'),
('copy dirs stay writable', S, '-type d -exec chmod 0555', '-type d -exec true'),
('copy subdirs stay writable', S, '-type d -exec chmod 0555 {} +', '-maxdepth 0 -type d -exec chmod 0555 {} +'),
('trap leaves read-only temp', S, "trap 'chmod -R u+w \"$COPY_TMP\" 2>/dev/null; rm -rf", "trap 'rm -rf"),
('copy off-branch accepted', S, 'merge-base --is-ancestor "$id" refs/heads/refactor ||', 'true ||'),
('queue reads root', 'packages/webui/src/reads.mjs', 'root: queueRoot', 'root'),
('--checkout relative ok', 'packages/webui/src/cli.mjs', 'isAbsolute(', '!!('),
('install skips bus check', S, ' unit="$(render)" || exit 1\n bus_on_copy', ' unit="$(render)" || exit 1\n : bus_on_copy'),
('bus path prefix only', S, 'in "$copy"/*) ;;', 'in "$copy"*) ;;'),
('bus not-found accepted', S, '[ "$load" = loaded ] ||', 'true ||'),
('bus show failure ignored', S, '-p LoadState -p ExecStart 2>/dev/null)" || die', '-p LoadState -p ExecStart 2>/dev/null)" || true; : die'),
('bus list failure ignored', S, "'mosaic-bus@*.service' 2>/dev/null)\" || die", "'mosaic-bus@*.service' 2>/dev/null)\" || true; : die"),
('no loaded bus accepted', S, '[ -n "$units" ] || die', 'true || die'),
('no ExecStart path accepted', S, '[ -n "$paths" ] || die', 'true || die'),
('only first bus unit checked', S, ' for unit in $units; do', ' for unit in ${units%%[[:space:]]*}; do'),
('--business ignored for bus', S, ' if [ -n "$BUSINESS" ]; then\n units=', ' if false; then\n units='),
]
res=[]
for name,f,a,b in M:
p=f'{W}/{f}'; s=open(p).read()
if s.count(a)<1: res.append(f'NOMATCH {name}'); continue
shutil.copy(p,p+'.bak'); open(p,'w').write(s.replace(a,b))
try:
r=subprocess.run(['node','--test','tests/service.test.mjs','tests/pinned-copy.test.mjs'],cwd=f'{W}/packages/webui',capture_output=True,text=True,timeout=300)
fails=[l for l in r.stdout.splitlines() if l.startswith('✖') and 'failing tests' not in l]
res.append(f"{'KILLED' if r.returncode else 'SURVIVED'} {name}: {len(set(fails))} failing")
finally:
shutil.move(p+'.bak',p)
print('\n'.join(res))