from pathlib import Path
import subprocess,sys,json,os,re
ROOT=Path(__file__).resolve().parents[1]; HERE=Path(__file__).resolve().parent
cp=subprocess.run([sys.executable,str(HERE/'public-contract_q1.test.py')],cwd=ROOT,text=True,capture_output=True,env={**os.environ,'PYTHONDONTWRITEBYTECODE':'1'}); print(cp.stdout,end='')
m=re.findall(r'SUMMARY\s+(\d+)/(\d+)\s+PASS',cp.stdout);bp,bt=(map(int,m[-1]) if m else (0,1));checks=[]
def ck(n,v): checks.append((n,bool(v)))
g=json.loads((ROOT/'fixture/golden-policy.json').read_text());b=json.loads((ROOT/'fixture/baseline-mfa.json').read_text());p=json.loads((ROOT/'fixture/probe-cases.json').read_text());req=json.loads((ROOT/'requirements.json').read_text());tr=json.loads((ROOT/'transfer/changed-context.template.json').read_text());et=json.loads((ROOT/'evidence/MFA_Recovery_Control_Record_v1.template.json').read_text())
ck('policy time step',g.get('time_step')==30);ck('policy digits',g.get('digits')==8);ck('policy window',g.get('verification_window')==1);ck('policy attempts',g.get('max_attempts')==3);ck('policy replay',g.get('totp_replay_protection') is True);ck('policy recovery single use',g.get('recovery_single_use') is True);ck('policy regen revoke',g.get('regeneration_revokes_old_set') is True);ck('baseline aligned',b.get('totp',{})=={'algorithm':'SHA1','digits':8,'time_step':30,'verification_window':1,'max_attempts':3});ck('baseline recovery single use',b.get('recovery',{}).get('single_use') is True);ck('reuse probe rejects',any(x.get('id')=='RECOVERY_REUSE' and x.get('expect')=='RECOVERY_CODE_REUSED_OR_INVALID' for x in p.get('probes',[])));ck('local only',req.get('network')=='forbidden' and req.get('external_targets')=='forbidden' and req.get('real_credentials')=='forbidden');ck('transfer non equivalence',tr.get('factor_equivalence_claimed') is False);ck('learner evidence blank',all(v=='' for v in et.get('rows',[{}])[0].values()))
for n,v in checks: print(('PASS' if v else 'FAIL'),n)
p=bp+sum(v for _,v in checks);t=bt+len(checks);print(f'SUMMARY {p}/{t} PASS');sys.exit(0 if cp.returncode==0 and p==t else 1)
