#!/usr/bin/env python3

import importlib.util
import json
import tempfile
import unittest
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
SPEC = importlib.util.spec_from_file_location("supply_chain", ROOT / "scripts" / "supply-chain.py")
assert SPEC and SPEC.loader
SUPPLY_CHAIN = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(SUPPLY_CHAIN)


class SecretScanTests(unittest.TestCase):
    def test_detects_canaries_without_returning_secret_values(self):
        canaries = [
            "-----BEGIN " + "PRIVATE KEY-----",
            "eyJ" + "a" * 12 + "." + "b" * 12 + "." + "c" * 12,
            "AKIA" + "A" * 16,
            "ghp_" + "a" * 36,
            "AIza" + "a" * 35,
            "xoxb-" + "a" * 24,
            "sk_" + "live_" + "a" * 20,
        ]
        with tempfile.TemporaryDirectory() as directory:
            root = Path(directory)
            (root / "fixture.txt").write_text("\n".join(canaries), encoding="utf-8")
            findings = SUPPLY_CHAIN.scan_files(root, ["fixture.txt"])

        self.assertEqual(len(SUPPLY_CHAIN.SECRET_PATTERNS), len(findings))
        rendered = "\n".join(f"{kind} {path}:{line}" for kind, path, line in findings)
        for canary in canaries:
            self.assertNotIn(canary, rendered)

    def test_ignores_binary_and_oversized_files(self):
        with tempfile.TemporaryDirectory() as directory:
            root = Path(directory)
            binary_canary = b"\0-----BEGIN " + b"PRIVATE KEY-----"
            (root / "binary.dat").write_bytes(binary_canary)
            (root / "large.txt").write_bytes(b"a" * (SUPPLY_CHAIN.MAX_SCANNED_FILE_BYTES + 1))
            self.assertEqual([], SUPPLY_CHAIN.scan_files(root, ["binary.dat", "large.txt"]))


class SbomTests(unittest.TestCase):
    def test_generates_deterministic_multi_ecosystem_cyclonedx(self):
        first = SUPPLY_CHAIN.generate_sbom(ROOT)
        second = SUPPLY_CHAIN.generate_sbom(ROOT)
        self.assertEqual(first, second)
        self.assertEqual("CycloneDX", first["bomFormat"])
        self.assertEqual("1.5", first["specVersion"])

        components = first["components"]
        purls = {component["purl"] for component in components}
        self.assertTrue(any(purl.startswith("pkg:npm/react@") for purl in purls))
        self.assertTrue(any(purl.startswith("pkg:nuget/Microsoft.EntityFrameworkCore@") for purl in purls))
        self.assertTrue(any(purl.startswith("pkg:pypi/fastapi@") for purl in purls))
        self.assertEqual(len(purls), len(components))
        json.dumps(first)


if __name__ == "__main__":
    unittest.main()
