Validator refuses a messaging-limited arbiter; decision.raise refuses a route
that can't message the raiser (decision-route-refused). Candidate on e6e5b41bf,
manifest sha256 edb43aed. Targeted gate green, 5/5 mutants killed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
46 lines
3.1 KiB
Python
Executable File
46 lines
3.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Row 133 (#1627): one fault per check. Sequential process groups; whole-group timeout cleanup.
|
|
--dry checks every mutation site matches and changes source, and runs nothing.
|
|
A control run first runs each distinct test set on the unmutated source; any failure stops the run."""
|
|
import json,os,pathlib,signal,subprocess,sys
|
|
args=[a for a in sys.argv[1:] if a!='--dry'];DRY='--dry' in sys.argv
|
|
ROOT=pathlib.Path(args[0] if args else os.path.expanduser('~/dewey-scratch/r133/wt'))
|
|
OUT=pathlib.Path(__file__).resolve().parent/'mutants'
|
|
AT='packages/business/tests/assistant.test.mjs';RT='packages/bus/tests/decision-route.test.mjs'
|
|
cases=[]
|
|
def add(id,path,old,new,test):cases.append(dict(id=id,path=path,old=old,new=new,test=test if isinstance(test,list) else [test]))
|
|
add('01-validator-check','packages/business/src/business.mjs','if (definitions[definition].messaging) {','if (false) {',AT)
|
|
p='packages/bus/src/broker.mjs'
|
|
RAISE="if (cls !== 'within-role') this.#checkMessaging(s.business, route, s.role, 'decision-route-refused');"
|
|
add('02-raise-check',p,RAISE,'/* no route check */',RT)
|
|
add('03-raise-direction',p,RAISE,"if (cls !== 'within-role') this.#checkMessaging(s.business, s.role, route, 'decision-route-refused');",RT)
|
|
add('04-within-role-exempt',p,RAISE,"this.#checkMessaging(s.business, route, s.role, 'decision-route-refused');",RT)
|
|
add('05-raise-own-code',p,RAISE,"if (cls !== 'within-role') this.#checkMessaging(s.business, route, s.role);",RT)
|
|
def run(name,test):
|
|
env={**os.environ,'DOCKER_HOST':'unix:///nonexistent.sock'}
|
|
with (OUT/(name+'.log')).open('w') as log:
|
|
p=subprocess.Popen(['node','--test',*test],cwd=ROOT,env=env,stdout=log,stderr=subprocess.STDOUT,start_new_session=True)
|
|
try:return p.wait(timeout=300)
|
|
except subprocess.TimeoutExpired:os.killpg(p.pid,signal.SIGKILL);p.wait();return 'timeout'
|
|
results=[];control=[]
|
|
if not DRY:
|
|
OUT.mkdir(exist_ok=True)
|
|
sets=[];[sets.append(c['test']) for c in cases if c['test'] not in sets]
|
|
for i,t in enumerate(sets):
|
|
rc=run('control-%02d'%(i+1),t);control.append(dict(id='control-%02d'%(i+1),test=t,exit=rc));print('control-%02d'%(i+1),' '.join(t),'PASS' if rc==0 else 'FAIL',rc,flush=True)
|
|
if rc!=0:(OUT/'control.json').write_text(json.dumps(control,indent=2)+'\n');sys.exit('control failed: unmutated source fails '+' '.join(t))
|
|
(OUT/'control.json').write_text(json.dumps(control,indent=2)+'\n')
|
|
for c in cases:
|
|
file=ROOT/c['path'];before=file.read_text();matches=before.count(c['old'])
|
|
if not matches:raise RuntimeError('missing mutation site '+c['id'])
|
|
after=before.replace(c['old'],c['new'])
|
|
if after==before:raise RuntimeError('no change '+c['id'])
|
|
if DRY:print(c['id'],'sites',matches,flush=True);continue
|
|
file.write_text(after)
|
|
try:
|
|
rc=run(c['id'],c['test'])
|
|
verdict='KILLED' if isinstance(rc,int) and rc!=0 else 'SURVIVED' if rc==0 else 'TIMEOUT'
|
|
row={k:c[k] for k in ['id','path','test']};row.update(result=verdict,exit=rc,sites=matches);results.append(row);print(c['id'],verdict,rc,flush=True)
|
|
finally:file.write_text(before)
|
|
if not DRY:(OUT/'results.json').write_text(json.dumps(results,indent=2)+'\n')
|