"""Re-run the six regressions the GPT-6 module review found surviving. Each mutation is applied to a copy of the tree, the suite is run, and the mutation must FAIL the suite. A survivor means the tests still cannot tell. """ import io, os, shutil, subprocess, sys, tempfile ROOT = os.path.dirname(os.path.abspath(__file__)) CACHE = os.path.join(ROOT, ".gocache") MUTATIONS = [ ("11: non-2xx returns nothing instead of all-UNKNOWN", "s2.go", "\tif status < 200 || status > 299 {\n\t\treturn allUnknown(len(asked))\n\t}", "\tif status < 200 || status > 299 {\n\t\treturn nil\n\t}"), ("12a: the revision is ignored when matching answers to questions", "s2.go", "\t\tpair := ItemRevision{ItemID: *r.ItemID, Revision: *r.Revision}", "\t\tpair := ItemRevision{ItemID: *r.ItemID}"), ("12b: STALE is dropped from the closed vocabulary", "s2.go", "\tcase VISIBLE, NOT_VISIBLE, STALE, UNKNOWN:", "\tcase VISIBLE, NOT_VISIBLE, UNKNOWN:"), ("13: the batch cap is doubled", "subject.go", "const MaxS2Pairs = 256", "const MaxS2Pairs = 512"), ("14: a 403 loses its ErrContractBreach sentinel", "s3.go", '\t\treturn nil, fmt.Errorf("%w: S3 answered 403 where the contract requires 404", ErrContractBreach)', '\t\treturn nil, fmt.Errorf("S3 answered 403")'), ("10: redirects are followed", "transport.go", '\t\t\treturn fmt.Errorf("suite: refusing to follow a redirect to %q", req.URL.Host)', "\t\t\treturn nil"), ("1: the Host header override is not checked", "transport.go", "\tif req.Host != \"\" {", "\tif false {"), ("2: duplicate JSON keys are not rejected in S2", "s2.go", "\tif err := rejectAmbiguousJSON(body); err != nil {\n\t\treturn allUnknown(len(asked))\n\t}", "\tif false {\n\t\treturn allUnknown(len(asked))\n\t}"), ("9: hostname labels are not validated", "transport.go", "\t\tif err := validLabel(label); err != nil {", "\t\tif false {\n\t\t\t_ = label\n\t\tif err := error(nil); err != nil {"), ] survivors = [] for name, path, before, after in MUTATIONS: work = tempfile.mkdtemp(prefix="mut-") tree = os.path.join(work, "m") # Only the module's own files: the worktree root also holds unreadable # dotfiles that a whole-tree copy trips over. os.makedirs(tree) for entry in os.listdir(ROOT): if entry.endswith(".go") or entry == "go.mod": shutil.copy2(os.path.join(ROOT, entry), os.path.join(tree, entry)) shutil.copytree(os.path.join(ROOT, "fixtures"), os.path.join(tree, "fixtures")) shutil.copytree(os.path.join(ROOT, "conformance"), os.path.join(tree, "conformance")) target = os.path.join(tree, path) src = io.open(target, encoding="utf-8").read() if before not in src: print("SKIP %s — anchor not found in %s" % (name, path)) shutil.rmtree(work) continue io.open(target, "w", encoding="utf-8").write(src.replace(before, after, 1)) env = dict(os.environ, GOCACHE=CACHE) r = subprocess.run(["go", "test", "./..."], cwd=tree, capture_output=True, text=True, env=env) shutil.rmtree(work) if r.returncode == 0: print("SURVIVED %s" % name) survivors.append(name) else: print("caught %s" % name) print() if survivors: print("%d mutation(s) survived:" % len(survivors)) for s in survivors: print(" -", s) sys.exit(1) print("all %d mutations caught" % len(MUTATIONS))