"""Tests for preserving exact firmware bytes under pre-build provenance folders.""" import json from pathlib import Path import sys import tempfile import unittest sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "tools")) from identity import archive_build, build_folder_name, export_build, verify class ArchiveTests(unittest.TestCase): def metadata(self, stamp="2026-10-08T12:30:00+00:00"): return {"source": {"commit": "a1b2c3d4" + "0" * 32, "dirty": False}, "templates": {"commit": None, "dirty": None}, "build_id": "NOGIT000", "built_at_utc": stamp, "version": [1, 2, 3]} def test_snapshot_preserves_image_and_records_exact_sha(self): with tempfile.TemporaryDirectory() as root: root = Path(root) image = root / "firmware.bin" payload = bytes(range(256)) image.write_bytes(payload) metadata = root / "metadata.json" metadata.write_text(json.dumps(self.metadata()), encoding="utf-8") result = archive_build(image, metadata, root / "out") folder = Path(result["directory"]) self.assertEqual(folder.name, "a1b2c3d4_2026-10-08_15-30-00_UTC+3") self.assertEqual((folder / image.name).read_bytes(), payload) self.assertEqual(image.read_bytes(), payload) self.assertEqual(json.loads((folder / "firmware_identity.json").read_text()), self.metadata()) manifest = folder / (image.name + ".manifest.json") self.assertEqual(verify(folder / image.name, manifest)["size_bytes"], 256) (folder / image.name).write_bytes(b"corrupted") with self.assertRaises(ValueError): verify(folder / image.name, manifest) def test_prebuild_times_keep_two_builds_separate(self): with tempfile.TemporaryDirectory() as root: root = Path(root) image, metadata = root / "firmware.hex", root / "metadata.json" image.write_bytes(b":00000001FF\r\n") folders = [] for stamp in ("2026-10-08T12:30:00+00:00", "2026-10-08T12:31:00+00:00"): metadata.write_text(json.dumps(self.metadata(stamp)), encoding="utf-8") folders.append(Path(archive_build(image, metadata, root / "out")["directory"])) self.assertNotEqual(folders[0], folders[1]) self.assertTrue(all((folder / image.name).is_file() for folder in folders)) def test_export_copies_current_folder_only(self): with tempfile.TemporaryDirectory() as root: root = Path(root) image, metadata = root / "firmware.bin", root / "metadata.json" image.write_bytes(b"exact current build") # A folder with a later timestamp must not be selected for this build. metadata.write_text(json.dumps(self.metadata("2026-10-08T12:40:00+00:00")), encoding="utf-8") archive_build(image, metadata, root / "local") metadata.write_text(json.dumps(self.metadata()), encoding="utf-8") result = archive_build(image, metadata, root / "local", export=root / "exported") copied = Path(result["exported_directory"]) self.assertEqual([p.name for p in (root / "exported").iterdir()], ["a1b2c3d4_2026-10-08_15-30-00_UTC+3"]) self.assertEqual((copied / image.name).read_bytes(), image.read_bytes()) self.assertTrue((copied / "firmware_identity.json").is_file()) self.assertTrue((copied / (image.name + ".sha256")).is_file()) verify(copied / image.name, copied / (image.name + ".manifest.json")) def test_complete_folder_export_keeps_nested_files_and_old_destination(self): with tempfile.TemporaryDirectory() as root: root = Path(root) image, metadata = root / "firmware.bin", root / "metadata.json" image.write_bytes(b"image") metadata.write_text(json.dumps(self.metadata()), encoding="utf-8") folder = Path(archive_build(image, metadata, root / "local")["directory"]) (folder / "notes").mkdir() (folder / "notes" / "release.txt").write_text("release notes", encoding="utf-8") old = root / "exported" / "old_build" old.mkdir(parents=True) (old / "keep.txt").write_text("keep", encoding="utf-8") copied = export_build(folder, root / "exported", image.name) self.assertEqual((copied / "notes" / "release.txt").read_text(), "release notes") self.assertEqual((old / "keep.txt").read_text(), "keep") with self.assertRaises(ValueError): export_build(folder, folder / "recursive", image.name) def test_offset_normalizes_to_utc_plus_three_and_dirty_is_visible(self): metadata = self.metadata("2026-10-08T15:30:00+03:00") metadata["source"]["dirty"] = True self.assertEqual(build_folder_name(metadata), "a1b2c3d4+_2026-10-08_15-30-00_UTC+3") def test_folder_date_rolls_over_at_utc_plus_three_midnight(self): metadata = self.metadata("2026-10-08T22:30:00+00:00") self.assertEqual(build_folder_name(metadata), "a1b2c3d4_2026-10-09_01-30-00_UTC+3") self.assertEqual(metadata["built_at_utc"], "2026-10-08T22:30:00+00:00") def test_archive_without_git_has_explicit_placeholder(self): metadata = self.metadata() metadata["source"]["commit"] = None self.assertEqual(build_folder_name(metadata), "NOGIT000_2026-10-08_15-30-00_UTC+3") def test_invalid_provenance_cannot_be_a_folder_name(self): metadata = self.metadata() metadata["source"]["commit"] = "../outside" with self.assertRaises(ValueError): build_folder_name(metadata) metadata = self.metadata("2026-10-08T12:30:00") with self.assertRaises(ValueError): build_folder_name(metadata) if __name__ == "__main__": unittest.main()