# SPDX-License-Identifier: MIT
import copy, hashlib, json, sys, unittest
from pathlib import Path
ROOT=Path(__file__).resolve().parents[1]; sys.path.insert(0,str(ROOT/'tools'))
from conform import Failure, canonical_bytes, digest, load_json, mutate, run_vectors, validate_document
class ConformanceTests(unittest.TestCase):
 @classmethod
 def setUpClass(cls):
  cls.p=load_json(ROOT/'vectors/positive.json'); cls.a=load_json(ROOT/'vectors/adversarial.json'); cls.by={v['id']:v for v in cls.p['vectors']}
 def test_all_positive_vectors(self):
  for v in self.p['vectors']:
   with self.subTest(v['id']): self.assertTrue(validate_document(v['document'],v.get('terms'),evaluation_time='2026-08-25T12:00:00Z'))
 def test_all_adversarial_vectors_fail_exactly(self):
  for v in self.a['vectors']:
   with self.subTest(v['id']):
    base=self.by[v['base']]
    with self.assertRaises(Failure) as got: validate_document(mutate(base['document'],v['mutation']),base.get('terms'),evaluation_time='2026-08-25T12:00:00Z')
    self.assertEqual(got.exception.code,v['expectedError'])
 def test_canonicalization_is_order_independent(self):
  d=self.by['P-ACCOUNTABILITY']['document']; reverse=dict(reversed(list(d.items())))
  self.assertEqual(canonical_bytes(d),canonical_bytes(reverse)); self.assertEqual(digest(d),digest(reverse))
 def test_signed_terms_digest_binds_exact_terms(self):
  v=self.by['P-SIGNED-TERMS']; self.assertEqual(v['document']['termsDigest'],digest(v['terms']))
  altered=copy.deepcopy(v['terms']); altered['companyStake']='1000001'
  with self.assertRaises(Failure) as got: validate_document(v['document'],altered)
  self.assertEqual(got.exception.code,'VF3-CANON-001')
 def test_duplicate_json_keys_rejected(self):
  p=ROOT/'reports'/'duplicate-key-negative.tmp.json'; p.write_text('{"schema":"x","schema":"y"}')
  try:
   with self.assertRaises(Failure) as got: load_json(p)
   self.assertEqual(got.exception.code,'VF3-CANON-001')
  finally: p.unlink()
 def test_truth_labels_remain_separate(self):
  for key in ('P-RECEIPT','P-EVIDENCE-SOURCE-ONLY'):
   t=self.by[key]['document']['truth']; self.assertTrue(t['specificationCompliant'])
   self.assertFalse(any(t[x] for x in ('audited','deployed','authorized','endorsed')))
 def test_vector_run_is_deterministic(self):
  b={'positive':self.p,'adversarial':self.a}; self.assertEqual(run_vectors(b),run_vectors(b))
 def test_stable_error_registry_covers_adversarial_vectors(self):
  codes={x['code'] for x in load_json(ROOT/'registry/error-codes.v1.json')['errors']}
  self.assertTrue({x['expectedError'] for x in self.a['vectors']}<=codes)
if __name__=='__main__': unittest.main()
