# SPDX-License-Identifier: MIT
"""Regression closure for the 12 residual findings and 13 accepted cases."""
import copy, json, subprocess, sys, tempfile, unittest
from pathlib import Path
ROOT=Path(__file__).resolve().parents[1]; sys.path.insert(0,str(ROOT/'tools'))
from conform import Failure, build_report, load_json, run_vectors, validate_document

class ResidualClosure(unittest.TestCase):
 @classmethod
 def setUpClass(cls):
  cls.p=load_json(ROOT/'vectors/positive.json');cls.by={x['id']:x for x in cls.p['vectors']};cls.a=load_json(ROOT/'vectors/adversarial.json')
 def reject(self,d,terms=None):
  with self.assertRaises(Failure):validate_document(d,terms)
 def test_C02_empty_extra_and_internal_vectors_fail(self):
  for b in ({'positive':{'vectors':[]},'adversarial':{'vectors':[]}},
            {'positive':self.p,'adversarial':{'vectors':self.a['vectors']+[{'id':'X','base':'P-ACCOUNTABILITY','mutation':{'op':'replace','path':'missing.path','value':0},'expectedError':'VF3-INTERNAL-001'}]}}):
   r=run_vectors(b);self.assertTrue(r);self.assertTrue(any(x[1]=='fail' for x in r));self.assertFalse(build_report(r)['summary']['conformant'])
 def test_H01_zero_r_and_settlement_bindings(self):
  s=copy.deepcopy(self.by['P-SIGNED-TERMS']['document']);s['signatures'][0]['signature']='0x'+'00'*32+'22'*32+'1b';self.reject(s,self.by['P-SIGNED-TERMS']['terms'])
  for field in ('authorizationDigest','termsHash'):
   d=copy.deepcopy(self.by['P-MUTUAL-SETTLEMENT']['document']);d[field]='0x'+'00'*32;self.reject(d)
  d=copy.deepcopy(self.by['P-MUTUAL-SETTLEMENT']['document']);d['consentingParties']=['0x'+'aa'*20,'0x'+'bb'*20];self.reject(d)
 def test_H03_closed_provenance_and_opaque_privacy(self):
  d=copy.deepcopy(self.by['P-ACCOUNTABILITY']['document']);d['provenanceGraph']['nodes'][0]['digest']='0x'+'99'*32;self.reject(d)
  d=copy.deepcopy(self.by['P-ACCOUNTABILITY']['document']);d['extensions']['x-biometric-hash']={'securityCritical':False,'decisionInfluence':'none','value':'sha256:x'};self.reject(d)
 def test_H04_state_exit_credit_continuity_and_time(self):
  base=self.by['P-STATE-PAUSED-EXIT']['document']
  mutations=[lambda d:d.update(exitCallableWhilePaused=False),
             lambda d:d.update(pullCredits=[]),
             lambda d:d['transitionHistory'].__setitem__(1,{**d['transitionHistory'][1],'from':'Accepted'}),
             lambda d:d['transitionHistory'][1].update(at='1')]
  for m in mutations:
   d=copy.deepcopy(base);m(d);self.reject(d)
 def test_H05_L01_freshness_requires_trusted_current_time(self):
  e=copy.deepcopy(self.by['P-EVIDENCE-SOURCE-ONLY']['document'])
  with self.assertRaises(Failure):validate_document(e,production_current_time=True)
  e['validUntil']='2126-08-26T00:00:00Z';self.reject(e)
  r=load_json(ROOT/'reports/conformance-report.json');r['generatedAt']='2000-01-01T00:00:00Z';self.reject(r)
 def test_H07_report_and_commit_are_objectively_blocked(self):
  r=load_json(ROOT/'reports/conformance-report.json');self.assertFalse(r['summary']['conformant']);self.assertFalse(r['truth']['specificationCompliant'])
  with self.assertRaises(Failure):validate_document(r,production_current_time=True)
 def test_H08_all_sources_and_routes_checker(self):
  repo=ROOT.parent
  run=subprocess.run([sys.executable,str(ROOT/'tools/check.py')],cwd=ROOT,text=True,capture_output=True)
  package_dirty=bool(subprocess.run(
   ['git','-C',str(repo),'status','--porcelain','--','LICENSE','v3-open-spec'],
   text=True,capture_output=True,check=True,
  ).stdout.strip())
  if package_dirty:
   self.assertEqual(run.returncode,2,run.stderr);self.assertIn('"status": "blocked"',run.stdout)
  else:
   self.assertEqual(run.returncode,0,run.stderr);self.assertIn('"status": "pass"',run.stdout)
  with tempfile.TemporaryDirectory() as directory:
   worktree=Path(directory)/'mutated-exact-commit'
   subprocess.run(
    ['git','-C',str(repo),'worktree','add','--detach',str(worktree),'HEAD'],
    text=True,capture_output=True,check=True,
   )
   try:
    report=worktree/'v3-open-spec/reports/conformance-report.json'
    report.write_bytes(report.read_bytes()+b'\n')
    mutated=subprocess.run(
     [sys.executable,str(worktree/'v3-open-spec/tools/check.py')],
     cwd=worktree/'v3-open-spec',text=True,capture_output=True,
    )
    self.assertEqual(mutated.returncode,2,mutated.stderr)
    self.assertIn('"status": "blocked"',mutated.stdout)
   finally:
    subprocess.run(
     ['git','-C',str(repo),'worktree','remove','--force',str(worktree)],
     text=True,capture_output=True,check=True,
    )
 def test_M02_source_component_symlinks_are_rejected_by_code(self):
  self.assertIn('candidate.lstat()', (ROOT/'tools/conform.py').read_text())
 def test_M03_parser_internal_never_passes(self):
  b={'positive':self.p,'adversarial':{'vectors':self.a['vectors']+[{'id':'BROKEN','base':'P-ACCOUNTABILITY','mutation':{'op':'replace','path':'missing.path','value':0},'expectedError':'VF3-INTERNAL-001'}]}}
  self.assertTrue(any(x[1]=='fail' for x in run_vectors(b)))
 def test_M04_policy_is_exact_and_fail_closed(self):
  c=load_json(ROOT/'registry/compatibility.v1.json');self.assertIs(c['extensions']['securityCriticalAllowed'],False);self.assertEqual(c['canonicalization']['duplicateKeys'],'reject')
 def test_M05_vector_suite_is_exact_8_38(self):
  self.assertEqual((len(self.p['vectors']),len(self.a['vectors'])),(8,38))
 def test_portability_zero_and_arbitrary_chain_rejected(self):
  d=copy.deepcopy(self.by['P-ACCOUNTABILITY']['document']);d['portability'].update(historyDigest='0x'+'00'*32,authorizationDigest='0x'+'00'*32,destinationChainId='1');self.reject(d)

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