| author | Alan Dipert
<alan@tailrecursion.com> 2026-08-09 22:04:39 UTC |
| committer | Alan Dipert
<alan@tailrecursion.com> 2026-08-09 22:04:39 UTC |
| parent | 55bfb26dee49befb2b4a51d36be20bc1dc32c42d |
| tests/test_archive_techworks.py | +29 | -0 |
| tools/archive_techworks.py | +4 | -1 |
diff --git a/tests/test_archive_techworks.py b/tests/test_archive_techworks.py index 3fe4d8f..975a2c8 100644 --- a/tests/test_archive_techworks.py +++ b/tests/test_archive_techworks.py @@ -3,6 +3,7 @@ import hashlib from pathlib import Path import tempfile import unittest +import unittest.mock import archive_techworks @@ -72,6 +73,34 @@ class ArchiveTechWorksTests(unittest.TestCase): (occupied / "file").unlink() occupied.rmdir() + def test_export_does_not_require_optional_format_tools(self): + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + artifact = root / "md" / "TechWorks" / "recording.mp4" + artifact.parent.mkdir(parents=True) + artifact.write_bytes(b"preserved without ffprobe") + row = { + "work_id": "work", + "artifact_id": "work-video", + "title": "Work video", + "kind": "video", + "source_url": "https://example.com/work", + "local_path": "md/TechWorks/recording.mp4", + "retrieved_utc": "2026-08-09T00:00:00Z", + "sha256": hashlib.sha256(b"preserved without ffprobe").hexdigest(), + "bytes": str(len(b"preserved without ffprobe")), + "visibility": "public", + "status": "preserved", + "notes": "", + } + manifest = self.write_manifest(root, [row]) + with unittest.mock.patch.object(archive_techworks, "run") as run: + with self.assertRaisesRegex( + archive_techworks.AuditError, "unsafe export destination" + ): + archive_techworks.export(root, manifest, root) + run.assert_not_called() + if __name__ == "__main__": unittest.main() diff --git a/tools/archive_techworks.py b/tools/archive_techworks.py index 99816d4..903db31 100644 --- a/tools/archive_techworks.py +++ b/tools/archive_techworks.py @@ -169,7 +169,10 @@ def safe_destination(root: Path, destination: Path) -> Path: def export(root: Path, manifest_path: Path, destination: Path) -> None: - rows = audit(root, manifest_path) + # Export must remain usable on a recovery host with only Python and Git. + # The dedicated archive audit performs the optional ffprobe/pdfinfo checks; + # here we still verify every artifact's path, size, and SHA-256 digest. + rows = audit(root, manifest_path, verify_formats=False) destination = safe_destination(root, destination) destination.mkdir(parents=True, exist_ok=True) bundle_dir = destination / "git"