83 lines
4.0 KiB
Python
83 lines
4.0 KiB
Python
"""Small archive fixtures; no game build or production data copy required."""
|
|
import hashlib
|
|
from pathlib import Path
|
|
import tempfile
|
|
import unittest
|
|
import zipfile
|
|
|
|
from assemble_runtime import iter_archive_files
|
|
|
|
|
|
class RuntimeArchiveTests(unittest.TestCase):
|
|
def test_manifest_derived_payload_survives_cache_filter_and_relocation(self):
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
package = Path(directory) / "package"
|
|
payload = "data/hd/jobs/reviewed-family/derived/200a2-derived.png"
|
|
required = ["data/INDEX", "data/hd/hd.json", payload]
|
|
fixtures = {
|
|
"data/INDEX": b"catalog",
|
|
"data/hd/hd.json": b"manifest",
|
|
payload: b"accepted derived PNG bytes",
|
|
"data/hd/jobs/reviewed-family/derived/rejected-preview.png": b"not admitted",
|
|
"derived/preview.png": b"private cache",
|
|
"target/build-cache": b"build",
|
|
".reference/private": b"private",
|
|
"mods/demo/art/source/parent.png": b"authoring reference",
|
|
"mods/demo/art/runtime.png": b"mod runtime",
|
|
"bin/raptor-rust": b"Linux only",
|
|
"bin/raptor-rust.exe": b"Windows",
|
|
}
|
|
for name, data in fixtures.items():
|
|
path = package / name
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_bytes(data)
|
|
archive = Path(directory) / "windows.zip"
|
|
skipped = {"target", "dist", ".reference", "derived", "lib"}
|
|
selected = list(iter_archive_files(
|
|
package, archive, skipped, excluded_files=(Path("bin/raptor-rust"),),
|
|
required_files=required))
|
|
names = {path.relative_to(package).as_posix() for path in selected}
|
|
self.assertTrue(set(required) <= names)
|
|
self.assertEqual(names - set(required), {"mods/demo/art/runtime.png", "bin/raptor-rust.exe"})
|
|
with zipfile.ZipFile(archive, "w") as output:
|
|
for path in selected:
|
|
output.write(path, "RustRaptor/" + path.relative_to(package).as_posix())
|
|
relocated = Path(directory) / "Portable Windows Ω folder"
|
|
with zipfile.ZipFile(archive) as packed:
|
|
packed.extractall(relocated)
|
|
for name in required:
|
|
original = package / name
|
|
copied = relocated / "RustRaptor" / name
|
|
self.assertEqual(hashlib.sha256(copied.read_bytes()).digest(),
|
|
hashlib.sha256(original.read_bytes()).digest())
|
|
|
|
def test_missing_manifest_payload_fails_before_yielding_files(self):
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
package = Path(directory)
|
|
with self.assertRaises(FileNotFoundError):
|
|
next(iter_archive_files(package, package / "out.zip", {"derived"},
|
|
required_files=["data/hd/missing.png"]))
|
|
|
|
def test_explicit_exclusion_cannot_silently_drop_manifest_payload(self):
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
package = Path(directory)
|
|
path = package / "data/required"
|
|
path.parent.mkdir()
|
|
path.write_bytes(b"required")
|
|
with self.assertRaisesRegex(ValueError, "missing required files"):
|
|
list(iter_archive_files(package, package / "out.zip", set(),
|
|
excluded_files=(Path("data/required"),),
|
|
required_files=["data/required"]))
|
|
|
|
def test_manifest_path_cannot_escape_package(self):
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
package = Path(directory)
|
|
for name in ("../outside", str(package / "absolute")):
|
|
with self.subTest(name=name), self.assertRaises(ValueError):
|
|
list(iter_archive_files(package, package / "out.zip", set(),
|
|
required_files=[name]))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|