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

ROOT = Path(__file__).resolve().parent
spec = importlib.util.spec_from_file_location("publication", ROOT / "build-publication.py")
publication = importlib.util.module_from_spec(spec)
spec.loader.exec_module(publication)


class PublicationTests(unittest.TestCase):
    def setUp(self):
        self.runs = [json.loads((ROOT / name).read_text()) for name in publication.RUN_FILES]

    def test_all_runs_and_counts(self):
        rows, matrix = publication.derive(self.runs)
        self.assertEqual(len(rows), 132)
        self.assertEqual(sum(r["phase"] == "online" for r in rows), 120)
        self.assertEqual(len(matrix), 11)
        self.assertEqual(sum(r["matches_expected"] is False for r in rows), 3)
        self.assertEqual(sum(r["matches_expected"] is True for r in rows), 6)

    def test_hash_read_failure_has_no_comparison(self):
        rows, _ = publication.derive(self.runs)
        rejected = [r for r in rows if r["probe"] == "whole_sha256" and r["returncode"]]
        self.assertEqual(len(rejected), 3)
        self.assertTrue(all(r["matches_expected"] is None for r in rejected))

    def test_missing_or_extra_probe_rejected(self):
        for extra in (True, False):
            runs = copy.deepcopy(self.runs)
            probes = runs[2]["cases"][0]["clean"]["probes"]
            if extra:
                probes["unexpected"] = probes["row_count"]
            else:
                del probes["row_count"]
            with self.assertRaises((AssertionError, KeyError)):
                publication.derive(runs)

    def test_disabled_checksum_false_success_rejected(self):
        self.runs[2]["cases"][1]["damaged"]["offline_checksums"]["returncode"] = 0
        with self.assertRaises(AssertionError):
            publication.derive(self.runs)

    def test_changed_harness_identity_rejected(self):
        self.runs[2]["harness_sha256"] = "0" * 64
        with self.assertRaises(AssertionError):
            publication.derive(self.runs)

    def test_mutation_contract_rejected(self):
        self.runs[2]["cases"][0]["mutation"]["checksum_recomputed"] = True
        with self.assertRaises(AssertionError):
            publication.derive(self.runs)


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