volt: beam: Q79 -- test_manifest.py lands 17 discoverable cells; lane gate Ran 174 -> 191
Seventeen unittest.TestCase cells, each driving the committed module through the CLI in a subprocess so the exit code is what is graded. MOD resolves from this file's own directory, not a host path. Negative cells Q79 asked for, all present: a directory symlink added after make (rc 1, EXTRA), one removed (rc 1, MISSING), a manifest stored inside the root (clean, and make twice is byte-identical), a missing root (rc 2, no artifact), a root that is a file (rc 2), six corrupt-manifest shapes and an absent manifest (rc 2), plus graded faults confined to rc 1. Bite measured, not asserted: the same suite against edac5cf's manifest.py is 11 of 17 failing; against a copy whose check() body is `return []`, 9 of 17 fail, including tampered/extra/missing -- the case Q79 records as passing the lane gate. `bash wizard-work/beam/CHECK` -> Ran 191 tests, OK, rc 0. CHECK: python3 wizard-work/beam/test_manifest.py
This commit is contained in:
1 parent
25e88f8426
commit
36ce415842
1 file changed
+243
@@ -0,0 +1,243 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Cells for manifest.py: the 0/1/2 contract, dir symlinks, and self-skip.
|
||||
|
||||
Every cell runs the committed module through the CLI in a subprocess, so the
|
||||
exit code is the thing under test, not an in-process return value. The module
|
||||
path resolves from this file's own directory, never a host path.
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
MOD = os.path.join(os.path.dirname(os.path.abspath(__file__)), "manifest.py")
|
||||
|
||||
FILES = {
|
||||
"a.txt": b"hello",
|
||||
"sub/b.bin": b"\x00\x01\x02",
|
||||
"c.txt": b"world",
|
||||
}
|
||||
|
||||
|
||||
class ManifestTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp = []
|
||||
|
||||
def tearDown(self):
|
||||
for d in self.tmp:
|
||||
shutil.rmtree(d, ignore_errors=True)
|
||||
|
||||
def _scratch(self, prefix="manifest-test-"):
|
||||
d = tempfile.mkdtemp(prefix=prefix)
|
||||
self.tmp.append(d)
|
||||
return d
|
||||
|
||||
def _tree(self):
|
||||
d = self._scratch("manifest-tree-")
|
||||
for rel, data in FILES.items():
|
||||
full = os.path.join(d, rel)
|
||||
os.makedirs(os.path.dirname(full), exist_ok=True)
|
||||
with open(full, "wb") as f:
|
||||
f.write(data)
|
||||
return d
|
||||
|
||||
def _run(self, *args):
|
||||
return subprocess.run([sys.executable, MOD, *args],
|
||||
capture_output=True, text=True)
|
||||
|
||||
def _manifest(self, root):
|
||||
out = os.path.join(self._scratch("manifest-out-"), "m.json")
|
||||
r = self._run("make", root, out)
|
||||
self.assertEqual(r.returncode, 0, r.stderr + r.stdout)
|
||||
return out
|
||||
|
||||
# -- 0: clean ---------------------------------------------------------
|
||||
|
||||
def test_roundtrip_is_clean(self):
|
||||
root = self._tree()
|
||||
man = self._manifest(root)
|
||||
r = self._run("check", root, man)
|
||||
self.assertEqual(r.returncode, 0, r.stderr + r.stdout)
|
||||
self.assertIn("OK: manifest matches", r.stdout)
|
||||
|
||||
def test_make_counts_every_file(self):
|
||||
root = self._tree()
|
||||
man = self._manifest(root)
|
||||
with open(man) as f:
|
||||
doc = json.load(f)
|
||||
self.assertEqual(sorted(e["path"] for e in doc["entries"]), sorted(FILES))
|
||||
self.assertTrue(all(e["type"] == "file" for e in doc["entries"]))
|
||||
|
||||
# -- 1: graded fault --------------------------------------------------
|
||||
|
||||
def test_tampered_content_exits_1(self):
|
||||
root = self._tree()
|
||||
man = self._manifest(root)
|
||||
with open(os.path.join(root, "a.txt"), "wb") as f:
|
||||
f.write(b"tampered")
|
||||
r = self._run("check", root, man)
|
||||
self.assertEqual(r.returncode, 1, r.stderr)
|
||||
self.assertIn("HASH_MISMATCH: a.txt", r.stdout)
|
||||
|
||||
def test_extra_file_exits_1(self):
|
||||
root = self._tree()
|
||||
man = self._manifest(root)
|
||||
with open(os.path.join(root, "extra.txt"), "w") as f:
|
||||
f.write("x")
|
||||
r = self._run("check", root, man)
|
||||
self.assertEqual(r.returncode, 1, r.stderr)
|
||||
self.assertIn("EXTRA: extra.txt", r.stdout)
|
||||
|
||||
def test_missing_file_exits_1(self):
|
||||
root = self._tree()
|
||||
man = self._manifest(root)
|
||||
os.remove(os.path.join(root, "sub", "b.bin"))
|
||||
r = self._run("check", root, man)
|
||||
self.assertEqual(r.returncode, 1, r.stderr)
|
||||
self.assertIn("MISSING: sub/b.bin", r.stdout)
|
||||
|
||||
# -- 1: directory symlinks are entries, not invisible ------------------
|
||||
|
||||
def test_dir_symlink_added_after_make_exits_1(self):
|
||||
root = self._tree()
|
||||
man = self._manifest(root)
|
||||
outside = self._scratch("manifest-outside-")
|
||||
with open(os.path.join(outside, "secret.txt"), "w") as f:
|
||||
f.write("not in the manifest")
|
||||
os.symlink(outside, os.path.join(root, "linked_dir"))
|
||||
r = self._run("check", root, man)
|
||||
self.assertEqual(r.returncode, 1, r.stderr)
|
||||
self.assertIn("EXTRA: linked_dir", r.stdout)
|
||||
self.assertNotIn("secret.txt", r.stdout)
|
||||
|
||||
def test_dir_symlink_removed_after_make_exits_1(self):
|
||||
root = self._tree()
|
||||
os.symlink(self._scratch("manifest-outside-"), os.path.join(root, "linked_dir"))
|
||||
man = self._manifest(root)
|
||||
os.remove(os.path.join(root, "linked_dir"))
|
||||
r = self._run("check", root, man)
|
||||
self.assertEqual(r.returncode, 1, r.stderr)
|
||||
self.assertIn("MISSING: linked_dir", r.stdout)
|
||||
|
||||
def test_dir_symlink_recorded_with_target(self):
|
||||
root = self._tree()
|
||||
outside = self._scratch("manifest-outside-")
|
||||
os.symlink(outside, os.path.join(root, "linked_dir"))
|
||||
man = self._manifest(root)
|
||||
with open(man) as f:
|
||||
doc = json.load(f)
|
||||
row = [e for e in doc["entries"] if e["path"] == "linked_dir"]
|
||||
self.assertEqual(len(row), 1)
|
||||
self.assertEqual(row[0]["type"], "symlink")
|
||||
self.assertEqual(row[0]["target"], outside)
|
||||
r = self._run("check", root, man)
|
||||
self.assertEqual(r.returncode, 0, r.stderr + r.stdout)
|
||||
|
||||
def test_dir_symlink_is_not_descended(self):
|
||||
root = self._tree()
|
||||
os.symlink(os.path.join(root, "sub"), os.path.join(root, "looplink"))
|
||||
man = self._manifest(root)
|
||||
with open(man) as f:
|
||||
doc = json.load(f)
|
||||
paths = [e["path"] for e in doc["entries"]]
|
||||
self.assertIn("looplink", paths)
|
||||
self.assertFalse([p for p in paths if p.startswith("looplink" + os.sep)])
|
||||
r = self._run("check", root, man)
|
||||
self.assertEqual(r.returncode, 0, r.stderr + r.stdout)
|
||||
|
||||
# -- 0: a manifest stored inside its own root -------------------------
|
||||
|
||||
def test_manifest_inside_root_is_not_an_entry(self):
|
||||
root = self._tree()
|
||||
man = os.path.join(root, "MANIFEST.json")
|
||||
r = self._run("make", root, man)
|
||||
self.assertEqual(r.returncode, 0, r.stderr)
|
||||
with open(man) as f:
|
||||
first = json.load(f)
|
||||
self.assertNotIn("MANIFEST.json", [e["path"] for e in first["entries"]])
|
||||
self.assertEqual(len(first["entries"]), len(FILES))
|
||||
# A second make must not grow the entry set, nor change a byte.
|
||||
with open(man, "rb") as f:
|
||||
before = f.read()
|
||||
r = self._run("make", root, man)
|
||||
self.assertEqual(r.returncode, 0, r.stderr)
|
||||
with open(man, "rb") as f:
|
||||
self.assertEqual(f.read(), before)
|
||||
with open(man) as f:
|
||||
self.assertEqual(len(json.load(f)["entries"]), len(FILES))
|
||||
|
||||
def test_manifest_inside_root_checks_clean(self):
|
||||
root = self._tree()
|
||||
man = os.path.join(root, "MANIFEST.json")
|
||||
self._run("make", root, man)
|
||||
r = self._run("check", root, man)
|
||||
self.assertEqual(r.returncode, 0, r.stderr + r.stdout)
|
||||
self.assertIn("OK: manifest matches", r.stdout)
|
||||
|
||||
# -- 2: ungradable ----------------------------------------------------
|
||||
|
||||
def test_missing_root_make_exits_2_and_writes_nothing(self):
|
||||
out = os.path.join(self._scratch("manifest-out-"), "m.json")
|
||||
r = self._run("make", "/nonexistent-root-manifest-test", out)
|
||||
self.assertEqual(r.returncode, 2, r.stderr + r.stdout)
|
||||
self.assertIn("ungradable:", r.stderr)
|
||||
self.assertFalse(os.path.exists(out))
|
||||
|
||||
def test_missing_root_check_exits_2(self):
|
||||
root = self._tree()
|
||||
man = self._manifest(root)
|
||||
r = self._run("check", "/nonexistent-root-manifest-test", man)
|
||||
self.assertEqual(r.returncode, 2, r.stderr + r.stdout)
|
||||
self.assertIn("ungradable:", r.stderr)
|
||||
|
||||
def test_root_that_is_a_file_exits_2(self):
|
||||
file_root = os.path.join(self._scratch("manifest-out-"), "not-a-dir")
|
||||
with open(file_root, "w") as f:
|
||||
f.write("x")
|
||||
out = os.path.join(self._scratch("manifest-out-"), "m.json")
|
||||
r = self._run("make", file_root, out)
|
||||
self.assertEqual(r.returncode, 2, r.stderr + r.stdout)
|
||||
self.assertFalse(os.path.exists(out))
|
||||
root = self._tree()
|
||||
man = self._manifest(root)
|
||||
r = self._run("check", file_root, man)
|
||||
self.assertEqual(r.returncode, 2, r.stderr + r.stdout)
|
||||
|
||||
def test_corrupt_manifest_exits_2(self):
|
||||
root = self._tree()
|
||||
bad = self._scratch("manifest-bad-")
|
||||
cases = {
|
||||
"not-json.json": "{not json",
|
||||
"empty-object.json": "{}",
|
||||
"entries-not-list.json": '{"entries": "nope"}',
|
||||
"unknown-type.json": '{"entries": [{"path": "a.txt", "type": "weird"}]}',
|
||||
"file-without-hash.json": '{"entries": [{"path": "a.txt", "type": "file"}]}',
|
||||
"entry-not-object.json": '{"entries": ["a.txt"]}',
|
||||
}
|
||||
for name, body in cases.items():
|
||||
path = os.path.join(bad, name)
|
||||
with open(path, "w") as f:
|
||||
f.write(body)
|
||||
r = self._run("check", root, path)
|
||||
self.assertEqual(r.returncode, 2, "%s -> %d: %s" % (name, r.returncode, r.stdout))
|
||||
|
||||
def test_unreadable_manifest_exits_2(self):
|
||||
root = self._tree()
|
||||
r = self._run("check", root, os.path.join(self._scratch(), "no-such.json"))
|
||||
self.assertEqual(r.returncode, 2, r.stderr + r.stdout)
|
||||
self.assertIn("ungradable:", r.stderr)
|
||||
|
||||
def test_bad_usage_exits_2(self):
|
||||
r = self._run()
|
||||
self.assertEqual(r.returncode, 2)
|
||||
self.assertIn("usage:", r.stderr)
|
||||
r = self._run("frobnicate", self._tree(), os.path.join(self._scratch(), "m.json"))
|
||||
self.assertEqual(r.returncode, 2)
|
||||
self.assertIn("unknown command", r.stderr)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
Reference in new issue
Block a user