Files
stack/agents/dewey/work/queue-63-review/r2/mutants.py
T

40 lines
3.5 KiB
Python

# Row 63 round 2 review mutants (Dewey), round 1's ten unchanged, now with a process group per run: each swaps one exact string in the scratch copy, runs the tasks
# verbs and tree tests, restores the file, and reports killed (tests fail) or SURVIVED.
import subprocess, sys, os, signal
M = os.path.expanduser('~/dewey-scratch/r63mut2')
env = dict(os.environ, TMPDIR=os.path.expanduser('~/dewey-scratch/tmp'))
V = 'packages/tasks/src/verbs.mjs'
T = ['packages/tasks/tests/verbs.test.mjs', 'packages/tasks/tests/tree.test.mjs']
ALL = ['packages/tasks/tests/*.test.mjs']
muts = [
('located: first relation only', V, " for (const r of list) {", " for (const r of list.slice(0, 1)) {", ALL),
('located: a missing task passes', V, "const x = await ctx.sync('GET', `/tasks/${r.other}`);\n if (outcome(x) !== 'ok') fail(refusal(x));", "const x = await ctx.sync('GET', `/tasks/${r.other}`);\n if (outcome(x) !== 'ok') continue;", ALL),
('relation: keeps the root project', V, "return { kind: x.kind, other: other.task, project: other.project };", "return { kind: x.kind, other: other.task, project: ctx.project };", ALL),
('step: re-read on the root board', V, " after = await read(at);\n } catch {\n // the re-read failed too", " after = await read({ ...at, project: ctx.project, view: ctx.view });\n } catch {\n // the re-read failed too", ALL),
('handle: final read on the root board', V, " after = await read(at);\n } catch {\n // handled below: a failed", " after = await read({ ...at, project: ctx.project, view: ctx.view });\n } catch {\n // handled below: a failed", ALL),
('create: final read on the root board', V, " after = await read(at);\n } catch {\n // handled below, as in handle()", " after = await read({ ...at, project: ctx.project, view: ctx.view });\n } catch {\n // handled below, as in handle()", ALL),
('create: fallback todo of the root', V, "taskFields(r.json, at.view.ids.todo)", "taskFields(r.json, ctx.view.ids.todo)", ALL),
('create: authorize before located', V, " await located(plan.others);\n ctx.broker.authorize(cap, 'task.create', { decision: plan.decision, target: null });", " ctx.broker.authorize(cap, 'task.create', { decision: plan.decision, target: null });\n await located(plan.others);", ALL),
('installed: outside is not-installed', V, " if (!ctx.tree.has(project)) fail('task-project');\n", "", ALL),
('create: ref under the root', V, "const taskRef = ref(at.project, id);", "const taskRef = ref(ctx.project, id);", ALL),
]
only = sys.argv[1:]
for name, f, old, new, tests in muts:
if only and not any(o in name for o in only): continue
p = os.path.join(M, f); src = open(p).read()
n = src.count(old)
if n != 1: print(f'BAD-ANCHOR ({n}) {name}', flush=True); continue
open(p, 'w').write(src.replace(old, new))
proc = subprocess.Popen(['node', '--test', *tests], cwd=M, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, start_new_session=True)
try:
out, _ = proc.communicate(timeout=300)
except subprocess.TimeoutExpired:
os.killpg(proc.pid, signal.SIGKILL); proc.communicate()
out = None
finally:
open(p, 'w').write(src)
if out is None:
print(f'killed {name} (hung: killed after 300 s)', flush=True); continue
fails = [l for l in out.splitlines() if l.startswith('not ok')]
print(('killed ' if proc.returncode else 'SURVIVED') + f' {name}' + (f' ({len(fails)} failing)' if fails else ''), flush=True)