#!/usr/bin/env python3 """Slot-only runner. Every mutation restores and verifies the complete broker bytes.""" from pathlib import Path import hashlib, os, re, subprocess, sys ROOT = Path(__file__).resolve().parents[4] SOURCE = ROOT / 'packages/bus/src/broker.mjs' OUT = Path(__file__).resolve().parent / 'logs' MUTANTS = [ ('M01-human', 'human-required', 'room verbs require the human', ' this.#human(s);\n', ''), ('M02-tester', 'tester-refused', 'room verbs require the human', None, None), ('M03-project', 'project-not-found', 'room verbs refuse missing projects', " if (!this.#store.get('SELECT 1 FROM projects WHERE id=? AND business=?', project, s.business)) fail('project-not-found');\n", ''), ('M04-members', 'thread-instance-missing', 'room verbs refuse missing projects', " if (!members.length) fail('thread-instance-missing');\n", ''), ('M05-collision', 'room-thread-name-taken', 'room.open preflights instance collisions', " if (t && (t.instance !== instance || t.owner !== owner))\n fail(opening ? 'room-thread-name-taken' : 'room-not-open');\n", ''), ('M06-open', 'room-not-open', 'send/read refuse a missing canonical member', " if (!t && !opening) fail('room-not-open');\n", ''), ('M07-body', 'invalid-request', 'unknown or non-member addressees and tags-only bodies', " if (!addressed.body.trim()) fail('invalid-request');\n", ''), ('M08-unknown', 'unknown-addressee', 'unknown or non-member addressees and tags-only bodies', " if (!candidates.size) fail('unknown-addressee');\n", ''), ('M09-ambiguous', 'addressee-ambiguous', 'a legacy name colliding with another member key', " if (candidates.size > 1) fail('addressee-ambiguous');\n", ''), ('M10-messaging', 'message-sender-refused', 'messaging denial on the last target', " this.#checkMessaging(s.business, 'human', t.instance);\n", ''), ('M11-cursor', 'message-not-found', 'room.read groups one send, pages', " if (after === undefined) fail('message-not-found');\n", ' if (after === undefined) after = 0;\n'), ('M12-fence', 'session-clearing', 'a clearing last member fences the whole send', ' this.#sessionFence(session);\n', ''), ] def sha(data): return hashlib.sha256(data).hexdigest() def main(): OUT.mkdir(exist_ok=True) original = SOURCE.read_bytes() text = original.decode() start = text.index(" case 'room.open':") end = text.index(" // A thread belongs", start) block = text[start:end] for name, refusal, expected, old, new in MUTANTS: if old is None: needle = 'export const TESTER_VERBS = Object.freeze([' assert text.count(needle) == 1 changed = text.replace(needle, needle + "\n 'room.open', 'room.send', 'room.read',", 1) else: assert block.count(old) == 1, (name, block.count(old)) changed = text[:start] + block.replace(old, new, 1) + text[end:] # Save conventional original→mutant diff without relying on shell interpolation. import difflib (OUT / f'{name}.diff').write_text(''.join(difflib.unified_diff(text.splitlines(True), changed.splitlines(True), fromfile='broker.original', tofile=name))) tmp = subprocess.check_output(['mktemp', '-d', '/mnt/storage/scratch/tmp/XXXXXX'], text=True).strip() env = {**os.environ, 'TMPDIR': tmp} log = OUT / f'{name}.log' try: SOURCE.write_text(changed) with log.open('w') as stream: for command in [['date', '-u'], ['uptime'], ['bash', '-c', 'bash /mnt/storage/scratch/sage-busy-now.sh 2>/dev/null | tail -1']]: subprocess.run(command, cwd=ROOT, env=env, stdout=stream, stderr=subprocess.STDOUT, check=True) stream.write(f'TMPDIR={tmp}\nmutant={name} refusal={refusal} original_sha256={sha(original)}\n') stream.flush() result = subprocess.run(['timeout', '-k', '10', '120', 'node', '--test', '--test-reporter=tap', 'packages/bus/tests/rooms.test.mjs'], cwd=ROOT, env=env, stdout=stream, stderr=subprocess.STDOUT) output = log.read_text() killed = result.returncode == 1 and re.search(r'(?m)^not ok \d+ - ' + re.escape(expected), output) print(f'{name}: exit={result.returncode} target_killed={bool(killed)} log={log}', flush=True) if not killed: return 1 finally: SOURCE.write_bytes(original) assert SOURCE.read_bytes() == original assert sha(SOURCE.read_bytes()) == sha(original) with log.open('a') as stream: stream.write(f'RESTORED sha256={sha(original)}\n') return 0 if __name__ == '__main__': sys.exit(main())