"""Layout regressions for the packaged first-run example. The shipped first-run example carries a verified prebuilt snapshot. Its ``snapshot.json`` records every packaged file at its **true** path: the approved immutable prebuilt inputs live under ``outputs/build/``, and the example helper ``tools/materialize_demo.py`` copies them into the example's inner ``build/`` working tree at run time. The release packaging must therefore ship the ``outputs/`` tree verbatim and must **never** ship the ``build/`` tree: the ``build/`` tree is a local product of the helper, and an archive that shipped it *instead of* ``outputs/`` makes the packaged example fail at run time (the materializer reads ``outputs/build/...`` and finds nothing). These tests build a real archive from a synthetic example laid out exactly like the shipped one, extract it into a fresh directory with no existing ``build/``, and run the packaged materializer twice. They also pin the refusal behaviour: an undeclared file under ``outputs/``, a FIFO, a hash that disagrees with the snapshot, a private hidden file beside the example, and the materialized ``build/`` tree are all refused without needing an archive at all. Everything here is standard-library ``unittest``. The artifacts are synthetic, so a passing run proves packaging layout and materialization behaviour, not that a compiler ran or that a C64 instruction executed. """ from __future__ import annotations import json import os import subprocess import sys import tarfile import tempfile import unittest from pathlib import Path from types import SimpleNamespace from unittest import mock ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(ROOT / "tools")) sys.path.insert(0, str(Path(__file__).resolve().parent)) import package_release as packaging # noqa: E402 from test_demo_snapshot import SnapshotTestCase # noqa: E402 EXAMPLE = "examples/first-run" #: The example-relative directory that holds the approved prebuilt inputs. OUTPUTS_ROOT = "outputs" #: The example-relative directory that holds the approved corresponding sources. SOURCES_ROOT = "sources" #: The example-relative directory the helper materializes into. MATERIALIZED_ROOT = "build" #: The tool files the packaged example needs to materialize itself. TOOL_FILES = ("materialize_demo.py", "prepare_demo_snapshot.py") def build_manifest(snapshot: dict, *, outputs=None, example: str = EXAMPLE) -> dict: """A release source manifest fragment for one packaged example. ``outputs`` overrides the explicit payload the manifest declares, so a test can prove the packager refuses a payload that disagrees with the snapshot. Hidden per-crate cargo configuration named by the snapshot is approved explicitly, exactly as the shipped manifest does. """ declared_outputs = [ {"path": entry["path"], "sha256": entry["sha256"], "size": entry["size"]} for entry in snapshot["file_entries"] if entry["output"] ] if outputs is not None: declared_outputs = outputs(declared_outputs) include = [{"path": example + "/", "kind": "example-inputs"}] for entry in snapshot["file_entries"]: if entry["output"]: continue parts = entry["path"].split("/") if not any(part.startswith(".") for part in parts): continue if not entry["path"].endswith(".toml"): continue include.append( {"path": example + "/" + entry["path"], "kind": "crate-config", "hidden": True} ) return { "source_paths": { "rules": ["explicit paths only"], "include": include, "deny": [ { "path": example + "/" + MATERIALIZED_ROOT + "/", "reason": "the example helper's local materialization destination", } ], }, "example_snapshot_layout": { "examples": [ { "example": example, "snapshot": "snapshot.json", "outputs_root": OUTPUTS_ROOT, "sources_root": SOURCES_ROOT, "materialized_root": MATERIALIZED_ROOT, "outputs": declared_outputs, } ] }, "inventory": [], "external_prerequisites": [], } class PackagedExampleCase(SnapshotTestCase): """Build a repository shaped like the shipped first-run example.""" def setUp(self) -> None: super().setUp() self.repo = self.tmp / "release-repo" self.example = self.repo / EXAMPLE self.example.mkdir(parents=True) # Lay out the package with the real producer so snapshot.json records the # true paths the packager must ship. code, _out, err = self.run_prepare(package=self.example) self.assertEqual(code, 0, err) self.snapshot = json.loads((self.example / "snapshot.json").read_text()) self.manifest = build_manifest(self.snapshot) # The packaged example ships the helper it runs. tool_dir = self.example / "tools" tool_dir.mkdir() for name in TOOL_FILES: (tool_dir / name).write_bytes((ROOT / EXAMPLE / "tools" / name).read_bytes()) def declared_outputs(self) -> list[dict]: return [entry for entry in self.snapshot["file_entries"] if entry["output"]] def declared_inputs(self) -> list[dict]: return [entry for entry in self.snapshot["file_entries"] if not entry["output"]] def selected_example_files(self) -> list[str]: return packaging.select_shipped_files(self.repo, EXAMPLE, self.manifest) def both_selections(self) -> dict[str, list[str]]: """The member sets both archives are gated on: source and binary.""" files, _report = packaging.collect_source_files(self.repo, self.manifest) return { "source-archive": sorted(entry["path"] for entry in files), "binary-archive": self.selected_example_files(), } def rewrite_snapshot(self, mutate) -> None: path = self.example / "snapshot.json" document = json.loads(path.read_text()) mutate(document) path.write_text(json.dumps(document, indent=2, sort_keys=True) + "\n") def add_stale_local_build(self) -> Path: """Write a local ``build/`` whose bytes differ from every snapshot input.""" stale = self.example / MATERIALIZED_ROOT / "ultimate-tool-smoke.prg" stale.parent.mkdir(parents=True, exist_ok=True) stale.write_bytes(b"\x01\x08" + b"\xEE" * 32) return stale class RealArchiveMaterializationTests(PackagedExampleCase): def test_selected_example_files_are_outputs_not_the_local_build(self) -> None: stale = self.add_stale_local_build() selected = self.selected_example_files() for entry in self.declared_outputs(): self.assertIn(f"{EXAMPLE}/{entry['path']}", selected) for entry in self.declared_inputs(): self.assertIn(f"{EXAMPLE}/{entry['path']}", selected) self.assertNotIn(f"{EXAMPLE}/{MATERIALIZED_ROOT}/ultimate-tool-smoke.prg", selected) self.assertFalse(any(path.startswith(f"{EXAMPLE}/{MATERIALIZED_ROOT}/") for path in selected)) self.assertTrue(stale.exists(), "the local build is left on disk, only excluded from the archive") def test_real_archive_materializes_twice_from_a_clean_extraction(self) -> None: # A local build with different bytes exists in the working tree; proving # the archive does not carry it is the point of the slice. self.add_stale_local_build() selected = self.selected_example_files() record = packaging.check_example_snapshot_selection(self.repo, self.manifest, selected) self.assertIsNotNone(record) declared = record["examples"][0] self.assertTrue(declared["verified"]) self.assertEqual( {item["path"] for item in declared["outputs"]}, {f"{EXAMPLE}/{entry['path']}" for entry in self.declared_outputs()}, ) self.assertEqual( {item["path"] for item in declared["inputs"]}, {f"{EXAMPLE}/{entry['path']}" for entry in self.declared_inputs()}, ) prefix = "pkg" members = [] expected = {} for relative in selected: path = self.repo / relative info = path.stat() members.append((f"{prefix}/{relative}", path, packaging.ARCHIVE_MODE_FILE)) expected[f"{prefix}/{relative}"] = (info.st_size, packaging.sha256_file(path)) archive = self.tmp / "example.tar.gz" packaging.write_archive(archive, members, expected=expected) extract = self.tmp / "extract" extract.mkdir() with tarfile.open(archive, "r:gz") as tar: for member in tar.getmembers(): self.assertIsNone(packaging.unsafe_member_name(member.name), member.name) target = extract / member.name self.assertTrue(str(target.resolve()).startswith(str(extract.resolve()) + os.sep)) target.parent.mkdir(parents=True, exist_ok=True) payload = tar.extractfile(member) self.assertIsNotNone(payload, member.name) target.write_bytes(payload.read()) packaged = extract / prefix / EXAMPLE for entry in self.declared_outputs(): self.assertTrue((packaged / entry["path"]).is_file(), entry["path"]) self.assertFalse((packaged / MATERIALIZED_ROOT).exists(), "the archive must not carry a build/ tree") materializer = packaged / "tools" / "materialize_demo.py" first = self.run_packaged_materializer(materializer) self.assertEqual(first.returncode, 0, first.stderr) summary = json.loads(first.stdout) installed = {item["path"] for item in summary["installed"]} self.assertEqual(installed, {f"{MATERIALIZED_ROOT}/{Path(e['path']).name}" for e in self.declared_outputs()}) for entry in self.declared_outputs(): destination = packaged / MATERIALIZED_ROOT / Path(entry["path"]).name self.assertEqual(packaging.sha256_file(destination), entry["sha256"]) second = self.run_packaged_materializer(materializer) self.assertEqual(second.returncode, 0, second.stderr) summary = json.loads(second.stdout) self.assertEqual({item["path"] for item in summary["reused"]}, installed) self.assertEqual(summary["installed"], []) def run_packaged_materializer(self, materializer: Path) -> subprocess.CompletedProcess: env = dict(os.environ) env["PYTHONDONTWRITEBYTECODE"] = "1" return subprocess.run( [sys.executable, str(materializer), "--json"], capture_output=True, text=True, env=env, ) class SourceArchiveSelectionTests(PackagedExampleCase): def test_source_selection_carries_every_snapshot_input_at_its_true_path(self) -> None: files, _report = packaging.collect_source_files(self.repo, self.manifest) paths = {entry["path"] for entry in files} for entry in self.declared_outputs() + self.declared_inputs(): self.assertIn(f"{EXAMPLE}/{entry['path']}", paths) self.assertFalse(any(path.startswith(f"{EXAMPLE}/{MATERIALIZED_ROOT}/") for path in paths)) record = packaging.check_example_snapshot_selection(self.repo, self.manifest, sorted(paths)) self.assertTrue(record["examples"][0]["verified"]) class RefusalTests(PackagedExampleCase): """Every refusal works against the working tree alone; no archive is needed.""" def test_undeclared_file_under_outputs_is_refused(self) -> None: stray = self.example / OUTPUTS_ROOT / "build" / "stray.bin" stray.write_bytes(b"not part of the reviewed snapshot") selected = self.selected_example_files() self.assertIn(f"{EXAMPLE}/{OUTPUTS_ROOT}/build/stray.bin", selected) with self.assertRaises(packaging.CheckFailure) as caught: packaging.check_example_snapshot_selection(self.repo, self.manifest, selected) self.assertIn("undeclared file", str(caught.exception)) def test_materialized_destination_is_refused(self) -> None: self.add_stale_local_build() selected = self.selected_example_files() selected.append(f"{EXAMPLE}/{MATERIALIZED_ROOT}/ultimate-tool-smoke.prg") with self.assertRaises(packaging.CheckFailure) as caught: packaging.check_example_snapshot_selection(self.repo, self.manifest, selected) self.assertIn("local materialized destination", str(caught.exception)) def test_hash_mismatch_is_refused(self) -> None: target = self.example / self.declared_outputs()[0]["path"] target.write_bytes(b"\x00" * target.stat().st_size) selected = self.selected_example_files() with self.assertRaises(packaging.CheckFailure) as caught: packaging.check_example_snapshot_selection(self.repo, self.manifest, selected) self.assertIn("sha256", str(caught.exception)) def test_missing_declared_input_is_refused(self) -> None: (self.example / self.declared_inputs()[0]["path"]).unlink() selected = [ path for path in self.selected_example_files() if not path.endswith(self.declared_inputs()[0]["path"]) ] with self.assertRaises(packaging.CheckFailure) as caught: packaging.check_example_snapshot_selection(self.repo, self.manifest, selected) self.assertIn("not in the archive", str(caught.exception)) def test_fifo_at_a_declared_output_is_refused(self) -> None: target = self.example / self.declared_outputs()[0]["path"] target.unlink() os.mkfifo(target) with self.assertRaises(packaging.CheckFailure) as caught: self.selected_example_files() self.assertIn("not a regular file", str(caught.exception)) def test_private_hidden_file_beside_the_example_is_refused(self) -> None: (self.example / ".env").write_text("SECRET=value\n") with self.assertRaises(packaging.CheckFailure) as caught: self.selected_example_files() self.assertIn("hidden entry", str(caught.exception)) class ManifestPayloadTests(PackagedExampleCase): def test_manifest_payload_must_match_the_snapshot(self) -> None: def corrupt(outputs: list[dict]) -> list[dict]: broken = [dict(item) for item in outputs] broken[0]["sha256"] = "0" * 64 return broken manifest = build_manifest(self.snapshot, outputs=corrupt) with self.assertRaises(packaging.CheckFailure) as caught: packaging.resolve_example_snapshot(manifest, self.repo) self.assertIn("records size/sha256", str(caught.exception)) def test_manifest_payload_cannot_add_an_unreviewed_output(self) -> None: def extra(outputs: list[dict]) -> list[dict]: return [dict(item) for item in outputs] + [ {"path": f"{OUTPUTS_ROOT}/build/extra.prg", "sha256": "1" * 64, "size": 4} ] manifest = build_manifest(self.snapshot, outputs=extra) with self.assertRaises(packaging.CheckFailure) as caught: packaging.resolve_example_snapshot(manifest, self.repo) self.assertIn("absent from the snapshot", str(caught.exception)) def test_snapshot_cross_check_catches_a_shifted_output_root(self) -> None: manifest = build_manifest(self.snapshot) manifest["example_snapshot_layout"]["examples"][0]["outputs_root"] = "artifacts" with self.assertRaises(packaging.CheckFailure) as caught: packaging.resolve_example_snapshot(manifest, self.repo) self.assertIn("canonical snapshot layout", str(caught.exception)) def test_outputs_and_materialized_roots_must_differ(self) -> None: manifest = build_manifest(self.snapshot) manifest["example_snapshot_layout"]["examples"][0]["materialized_root"] = OUTPUTS_ROOT with self.assertRaises(packaging.CheckFailure) as caught: packaging.resolve_example_snapshot(manifest, self.repo) self.assertIn("both its outputs root", str(caught.exception)) class CanonicalContractTests(PackagedExampleCase): """The packager gate uses the canonical validator, on both archives. A snapshot the materializer would refuse must also fail the packager gate *before* any archive is written, so the release gate and the run-time helper cannot disagree about the schema. """ def gate_case(self, mutate, expected: str) -> None: self.rewrite_snapshot(mutate) for name, selection in self.both_selections().items(): with self.subTest(archive=name): with self.assertRaises(packaging.CheckFailure) as caught: packaging.check_example_snapshot_selection(self.repo, self.manifest, selection) self.assertIn(expected, str(caught.exception)) def test_future_schema_is_refused(self) -> None: self.gate_case(lambda doc: doc.__setitem__("schema", 2), "unsupported snapshot schema") def test_missing_schema_is_refused(self) -> None: self.gate_case(lambda doc: doc.pop("schema"), "missing 'schema'") def test_unknown_metadata_key_is_refused(self) -> None: self.gate_case( lambda doc: doc.__setitem__("future_metadata", {"note": "from a later schema"}), "unknown top-level keys", ) def test_missing_required_metadata_is_refused(self) -> None: self.gate_case(lambda doc: doc.pop("producer"), "missing 'producer'") def test_undeclared_source_file_is_refused_by_both_gates(self) -> None: stray = self.example / SOURCES_ROOT / "notes.txt" stray.write_text("harmless note\n") for name, selection in self.both_selections().items(): with self.subTest(archive=name): self.assertIn(f"{EXAMPLE}/{SOURCES_ROOT}/notes.txt", selection) with self.assertRaises(packaging.CheckFailure) as caught: packaging.check_example_snapshot_selection(self.repo, self.manifest, selection) self.assertIn("undeclared file under", str(caught.exception)) def test_helpers_outside_the_payload_trees_are_allowed(self) -> None: selection = self.selected_example_files() for helper in ( f"{EXAMPLE}/tools/materialize_demo.py", f"{EXAMPLE}/snapshot.json", ): self.assertIn(helper, selection) record = packaging.check_example_snapshot_selection(self.repo, self.manifest, selection) self.assertTrue(record["examples"][0]["verified"]) class GateOrderingTests(PackagedExampleCase): """The gate runs before either archive is written.""" def prepare_source_context(self) -> tuple[SimpleNamespace, dict, Path]: manifest_path = self.repo / "tools" / "release_source_manifest.json" manifest_path.parent.mkdir(parents=True, exist_ok=True) manifest_path.write_text(json.dumps(self.manifest, indent=2), encoding="utf-8") output = self.tmp / "out" output.mkdir() context = { "manifest": self.manifest, "manifest_path": manifest_path, "source_root": self.repo, "output": output, "names": {"source_prefix": "pkg-src", "source_archive": "pkg-src.tar.gz"}, "limits": packaging.DEFAULT_LIMITS, "policy": packaging.sentinel_policy(self.manifest), "exemptions": {}, "vendor_fixture_exemptions": {}, "runtime_bundle": None, "version": "0.2.0-preview.1", "source": {"revision": "0" * 40, "dirty": True, "tree_state": "dirty"}, "development": True, "development_label": "test", "created_at": "2026-01-01T00:00:00Z", "generator": {"tool": "tools/package_release.py", "sha256": "0" * 64}, } args = SimpleNamespace( no_vendor=True, vendor_dir=None, dry_run=False, rebuild_status="not_verified", rebuild_evidence=None, ) return args, context, output def test_invalid_snapshot_fails_the_source_gate_before_the_archive(self) -> None: self.rewrite_snapshot(lambda doc: doc.pop("producer")) args, context, output = self.prepare_source_context() with mock.patch.object(packaging, "write_archive") as write: with self.assertRaises(packaging.CheckFailure): packaging.prepare_source(args, context) write.assert_not_called() self.assertEqual(list(output.iterdir()), [], "no archive or manifest may be left behind") if __name__ == "__main__": unittest.main()