"""Offline packager regressions; Go compilation and SDK admission are separate.""" import importlib.util import json import tempfile import unittest import zipfile from pathlib import Path from unittest.mock import patch SPEC = importlib.util.spec_from_file_location( "package_rust3_compat", Path(__file__).resolve().parents[1] / "scripts/package_rust3_compat.py", ) packager = importlib.util.module_from_spec(SPEC) SPEC.loader.exec_module(packager) class Rust3PackagerTests(unittest.TestCase): def setUp(self): self.temporary = tempfile.TemporaryDirectory() self.addCleanup(self.temporary.cleanup) self.root = Path(self.temporary.name) self.plugin = self.root / "device-test" self.plugin.mkdir() self.manifest = { "schema_version": 2, "id": "artemis-device-test", "version": packager.DEVICE_TEST_VERSION, "artemis_version": packager.HOST_RANGE, "license": "Apache-2.0", } (self.plugin / "plugin.json").write_bytes(packager.encoded(self.manifest)) for name in ("LICENSE", "SOURCE.md", "VERIFICATION.md", "server.py"): (self.plugin / name).write_bytes((name + "\n").encode()) self.update_market() def update_market(self): hashes = { path.relative_to(self.plugin).as_posix(): packager.sha256(path.read_bytes()) for path in self.plugin.rglob("*") if path.is_file() and path.name != "MARKET.json" } fingerprint = packager.sha256( json.dumps( hashes, ensure_ascii=False, sort_keys=True, separators=(",", ":") ).encode() ) (self.plugin / "MARKET.json").write_bytes( packager.encoded( { "plugin_id": self.manifest["id"], "plugin_version": self.manifest["version"], "content_sha256": fingerprint, } ) ) def assemble(self, name, tag=packager.DEFAULT_RELEASE_TAG): output = self.root / name catalog = self.root / (name + "-catalog.json") with ( patch.object( packager, "base_runtime", return_value=({}, {"plugin_version": packager.BASE_VERSION}), ), patch.object( packager, "build_adapter", return_value=(b"adapter fixture", b"Go license fixture", "go fixture"), ), patch.object( packager.subprocess, "check_output", side_effect=["f" * 40, b""] ), patch("builtins.print"), ): packager.main( [ "--cua-base-archive", str(self.root / "base.zip"), "--go", str(self.root / "go.exe"), "--output", str(output), "--catalog-output", str(catalog), "--device-test-source", str(self.plugin), "--release-tag", tag, ] ) return output, catalog def test_three_archives_catalog_and_bundle_bind_the_same_final_bytes(self): output, catalog_path = self.assemble("candidate") catalog = json.loads(catalog_path.read_bytes()) mapping = json.loads((output / "bundle-map.json").read_bytes()) self.assertEqual(len(catalog["plugins"]), 3) self.assertEqual(len(mapping["packages"]), 3) self.assertEqual( mapping["catalog_sha256"], packager.sha256(catalog_path.read_bytes()) ) self.assertEqual( (output / mapping["catalog"]).read_bytes(), catalog_path.read_bytes() ) for entry, record in zip(catalog["plugins"], mapping["packages"], strict=True): archive = output / record["file"] self.assertEqual(entry["sha256"], packager.sha256(archive.read_bytes())) self.assertEqual(record["sha256"], entry["sha256"]) self.assertEqual(record["archive_url"], entry["archive_url"]) self.assertIn( "/" + packager.DEFAULT_RELEASE_TAG + "/", entry["archive_url"] ) self.assertTrue(entry["archive_url"].startswith(packager.RELEASES + "/")) with zipfile.ZipFile(archive) as package: self.assertEqual( json.loads(package.read("plugin.json")), entry["manifest"] ) device = mapping["packages"][2] self.assertEqual(device["version"], "0.5.0+artemis.7") with zipfile.ZipFile(output / device["file"]) as package: self.assertEqual( package.read("server.py"), (self.plugin / "server.py").read_bytes() ) self.assertEqual( package.read("MARKET.json"), (self.plugin / "MARKET.json").read_bytes() ) sums = (output / "SHA256SUMS.txt").read_text("ascii").splitlines() self.assertEqual(len(sums), 3) def test_repeated_candidate_is_deterministic_and_custom_tag_is_shared(self): first, _ = self.assemble("first", "rust3-preview-test") second, _ = self.assemble("second", "rust3-preview-test") for name in ("SHA256SUMS.txt", "catalog-v5.json", "bundle-map.json"): self.assertEqual((first / name).read_bytes(), (second / name).read_bytes()) catalog = json.loads((first / "catalog-v5.json").read_bytes()) self.assertTrue( all("/rust3-preview-test/" in e["archive_url"] for e in catalog["plugins"]) ) def test_rejects_stale_market_hash_and_mismatched_version(self): (self.plugin / "server.py").write_bytes(b"changed") with self.assertRaisesRegex(ValueError, "MARKET identity or content hash"): packager.device_test_files(self.plugin) self.manifest["version"] = "0.5.0+artemis.6" (self.plugin / "plugin.json").write_bytes(packager.encoded(self.manifest)) self.update_market() with self.assertRaisesRegex(ValueError, "identity or compatibility"): packager.device_test_files(self.plugin) def test_rejects_missing_distribution_material_and_runtime_files(self): (self.plugin / "LICENSE").unlink() with self.assertRaisesRegex(ValueError, "distribution material"): packager.device_test_files(self.plugin) (self.plugin / "LICENSE").write_bytes(b"license") (self.plugin / ".env").write_bytes(b"private fixture") with self.assertRaisesRegex(ValueError, "Non-distributable"): packager.device_test_files(self.plugin) def test_source_change_during_build_fails_before_any_archive(self): output = self.root / "changing" def change_source(*_): (self.plugin / "server.py").write_bytes(b"changed during build") return b"adapter", b"license", "fixture" with ( patch.object(packager, "base_runtime", return_value=({}, {})), patch.object(packager, "build_adapter", side_effect=change_source), self.assertRaisesRegex(ValueError, "MARKET identity or content hash"), ): packager.main( [ "--cua-base-archive", "base.zip", "--go", "go.exe", "--output", str(output), "--catalog-output", str(self.root / "catalog.json"), "--device-test-source", str(self.plugin), ] ) self.assertEqual(list(output.glob("*.zip")), []) def test_refuses_existing_outputs_and_unsafe_release_tags(self): output, catalog = self.assemble("existing") original = catalog.read_bytes() with self.assertRaisesRegex(ValueError, "new or empty"): packager.main( [ "--cua-base-archive", "base.zip", "--go", "go.exe", "--output", str(output), "--catalog-output", str(catalog), ] ) self.assertEqual(catalog.read_bytes(), original) with self.assertRaisesRegex(ValueError, "Refusing to overwrite an archive"): packager.write_zip(output / "task-review-1.1.1.zip", {"file": b"changed"}) for tag in ("../main", "https://invalid", "", "x#fragment", "x y"): with self.subTest(tag=tag), self.assertRaises(ValueError): packager.release_url(tag, "plugin.zip") if __name__ == "__main__": unittest.main()