87 lines
3.7 KiB
Python
87 lines
3.7 KiB
Python
import copy
|
|
import hashlib
|
|
import json
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
from scripts.check_upstream_contracts import ROOT, inspect_tree
|
|
|
|
|
|
class UpstreamContractTests(unittest.TestCase):
|
|
def _tree(self, root: Path) -> tuple[dict, str]:
|
|
license_path = root / "LICENSE"
|
|
api_path = root / "package" / "api.py"
|
|
api_path.parent.mkdir(parents=True)
|
|
license_path.write_text("synthetic MIT text\n", encoding="utf-8")
|
|
api_path.write_text(
|
|
"VERSION = 3\nclass Registry:\n def unregister(self):\n pass\n",
|
|
encoding="utf-8",
|
|
)
|
|
commit = "a" * 40
|
|
manifest = {
|
|
"name": "Synthetic upstream",
|
|
"repository": "https://example.invalid/upstream.git",
|
|
"commit": commit,
|
|
"license": "MIT",
|
|
"license_file": {
|
|
"path": "LICENSE",
|
|
"sha256_lf": hashlib.sha256(b"synthetic MIT text\n").hexdigest(),
|
|
},
|
|
"contract_files": [{
|
|
"path": "package/api.py",
|
|
"sha256": hashlib.sha256(api_path.read_bytes()).hexdigest(),
|
|
"stability": "documented-api",
|
|
"required_symbols": ["VERSION", "Registry", "Registry.unregister"],
|
|
}],
|
|
}
|
|
return manifest, commit
|
|
|
|
def test_matching_tree_passes(self):
|
|
with tempfile.TemporaryDirectory(dir=ROOT / ".build") as directory:
|
|
root = Path(directory)
|
|
manifest, commit = self._tree(root)
|
|
result = inspect_tree(manifest, root, commit)
|
|
self.assertEqual(result["status"], "passed")
|
|
self.assertEqual(result["failures"], [])
|
|
|
|
def test_commit_hash_and_symbol_drift_fail(self):
|
|
with tempfile.TemporaryDirectory(dir=ROOT / ".build") as directory:
|
|
root = Path(directory)
|
|
manifest, _ = self._tree(root)
|
|
changed = copy.deepcopy(manifest)
|
|
changed["contract_files"][0]["required_symbols"].append("Registry.missing")
|
|
result = inspect_tree(changed, root, "b" * 40)
|
|
self.assertEqual(result["status"], "failed")
|
|
self.assertTrue(any("commit mismatch" in item for item in result["failures"]))
|
|
self.assertTrue(any("Registry.missing" in item for item in result["failures"]))
|
|
|
|
def test_license_drift_fails(self):
|
|
with tempfile.TemporaryDirectory(dir=ROOT / ".build") as directory:
|
|
root = Path(directory)
|
|
manifest, commit = self._tree(root)
|
|
(root / "LICENSE").write_text("changed\n", encoding="utf-8")
|
|
result = inspect_tree(manifest, root, commit)
|
|
self.assertEqual(result["status"], "failed")
|
|
self.assertTrue(any("license hash mismatch" in item for item in result["failures"]))
|
|
|
|
def test_real_manifests_pin_mit_license_and_stability(self):
|
|
for name in ("rayforge", "meerk40t"):
|
|
manifest = json.loads(
|
|
(ROOT / "upstreams" / f"{name}.json").read_text(encoding="utf-8")
|
|
)
|
|
self.assertEqual(manifest["license"], "MIT")
|
|
self.assertEqual(len(manifest["commit"]), 40)
|
|
self.assertEqual(len(manifest["license_file"]["sha256_lf"]), 64)
|
|
self.assertTrue(manifest["contract_files"])
|
|
self.assertTrue(all(item["stability"] for item in manifest["contract_files"]))
|
|
notice = ROOT / "third_party" / "licenses" / f"{manifest['name']}-LICENSE.txt"
|
|
normalized = notice.read_text(encoding="utf-8").replace("\r\n", "\n").replace("\r", "\n")
|
|
self.assertEqual(
|
|
hashlib.sha256(normalized.encode("utf-8")).hexdigest(),
|
|
manifest["license_file"]["sha256_lf"],
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|