149 lines
5.1 KiB
Python
149 lines
5.1 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import shutil
|
|
import unittest
|
|
from pathlib import Path
|
|
from uuid import uuid4
|
|
|
|
from scripts.generate_component_inventory import (
|
|
canonical_name,
|
|
component,
|
|
file_component,
|
|
ocr_runtime_components,
|
|
parse_lockfile,
|
|
requirement_names,
|
|
)
|
|
|
|
|
|
class ComponentInventoryTests(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
self.fixture_root = (
|
|
Path(__file__).resolve().parent
|
|
/ f".component-inventory-test-{uuid4().hex}"
|
|
)
|
|
self.fixture_root.mkdir()
|
|
|
|
def tearDown(self) -> None:
|
|
shutil.rmtree(self.fixture_root, ignore_errors=True)
|
|
|
|
def test_python_package_names_are_canonicalized_for_purls(self) -> None:
|
|
self.assertEqual(canonical_name("python_multipart"), "python-multipart")
|
|
self.assertEqual(canonical_name("Pillow"), "pillow")
|
|
|
|
def test_requirements_parser_ignores_comments_and_extras(self) -> None:
|
|
requirements = self.fixture_root / "requirements.txt"
|
|
requirements.write_text(
|
|
"fastapi>=0.1\nuvicorn[standard]>=0.2 # comment\n# skip\n",
|
|
encoding="utf-8",
|
|
)
|
|
self.assertEqual(requirement_names(requirements), {"fastapi", "uvicorn"})
|
|
|
|
def test_pub_lock_parser_keeps_scope_source_and_version(self) -> None:
|
|
lockfile = self.fixture_root / "pubspec.lock"
|
|
lockfile.write_text(
|
|
"""# Generated by pub
|
|
packages:
|
|
demo_package:
|
|
dependency: direct main
|
|
source: hosted
|
|
version: \"1.2.3\"
|
|
flutter:
|
|
dependency: direct main
|
|
source: sdk
|
|
version: \"0.0.0\"
|
|
sdks:
|
|
dart: \"^3.0.0\"
|
|
""",
|
|
encoding="utf-8",
|
|
)
|
|
self.assertEqual(
|
|
parse_lockfile(lockfile),
|
|
[
|
|
{
|
|
"name": "demo_package",
|
|
"dependency": "direct main",
|
|
"source": "hosted",
|
|
"version": "1.2.3",
|
|
},
|
|
{
|
|
"name": "flutter",
|
|
"dependency": "direct main",
|
|
"source": "sdk",
|
|
"version": "0.0.0",
|
|
},
|
|
],
|
|
)
|
|
|
|
def test_component_records_license_file_hash_as_evidence(self) -> None:
|
|
license_file = self.fixture_root / "LICENSE"
|
|
license_file.write_text("sample notice\n", encoding="utf-8")
|
|
result = component(
|
|
ecosystem="pub",
|
|
name="demo_package",
|
|
version="1.2.3",
|
|
scope="direct main",
|
|
raw_license="See included LICENSE",
|
|
license_source="test fixture",
|
|
license_file=license_file,
|
|
)
|
|
self.assertEqual(result["purl"], "pkg:pub/demo_package@1.2.3")
|
|
self.assertTrue(
|
|
any(
|
|
property_item["name"] == "inventory.license_file_sha256"
|
|
for property_item in result["properties"]
|
|
)
|
|
)
|
|
self.assertEqual(json.loads(json.dumps(result)), result)
|
|
|
|
def test_runtime_file_component_records_sha256_and_review_status(self) -> None:
|
|
artifact = self.fixture_root / "model.pth"
|
|
artifact.write_bytes(b"model fixture")
|
|
result = file_component(
|
|
artifact,
|
|
name="EasyOCR model model.pth",
|
|
component_type="machine-learning-model",
|
|
scope="local-runtime-artifact",
|
|
license_status="human review required",
|
|
)
|
|
self.assertEqual(result["type"], "machine-learning-model")
|
|
self.assertEqual(
|
|
result["hashes"],
|
|
[{"alg": "SHA-256", "content": "21249a290a4255a0f3ee6685ff7933bffa241c3e35bb13a52dc0c7a679bed3b2"}],
|
|
)
|
|
self.assertIn(
|
|
{"name": "inventory.license_status", "value": "human review required"},
|
|
result["properties"],
|
|
)
|
|
|
|
def test_ocr_inventory_includes_present_weights_and_reports_missing_english(self) -> None:
|
|
model_dir = self.fixture_root / "models"
|
|
model_dir.mkdir()
|
|
(model_dir / "arabic.pth").write_bytes(b"arabic weights")
|
|
(model_dir / "craft_mlt_25k.pth").write_bytes(b"detection weights")
|
|
(model_dir / "arabic.pth.backup").write_bytes(b"backup")
|
|
(model_dir / "temp.zip").write_bytes(b"download fragment")
|
|
|
|
artifacts, missing = ocr_runtime_components(model_dir)
|
|
|
|
self.assertEqual(
|
|
{artifact["name"] for artifact in artifacts},
|
|
{"EasyOCR model arabic.pth", "EasyOCR model craft_mlt_25k.pth"},
|
|
)
|
|
self.assertEqual(missing, ["english_g2.pth"])
|
|
for artifact in artifacts:
|
|
properties = {item["name"]: item["value"] for item in artifact["properties"]}
|
|
self.assertEqual(
|
|
properties["inventory.license_status"],
|
|
"model redistribution terms not established; human review required",
|
|
)
|
|
self.assertEqual(
|
|
properties["inventory.evidence.upstream_manifest"],
|
|
"easyocr==1.7.2 config.py",
|
|
)
|
|
self.assertEqual(properties["inventory.evidence.upstream_md5_match"], "false")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|