from pathlib import Path
import subprocess,sys,json,os,re,importlib.util
R=Path(__file__).resolve().parents[1]
cp=subprocess.run([sys.executable,str(R/'src/static_preflight_q1.py')],cwd=R,text=True,capture_output=True,env={**os.environ,'PYTHONDONTWRITEBYTECODE':'1'}); print(cp.stdout,end='')
m=re.findall(r'(\d+)/(\d+) PASS',cp.stdout); bp,bt=(map(int,m[-1]) if m else (0,1)); checks=[]
def ck(n,v): checks.append((n,bool(v)))
sp=importlib.util.spec_from_file_location('sf',R/'src/session_fixture.py'); sf=importlib.util.module_from_spec(sp); sp.loader.exec_module(sf)
b=json.loads((R/'fixture/baseline-session.json').read_text()); g=json.loads((R/'fixture/golden-policy.json').read_text()); req=json.loads((R/'requirements.json').read_text()); tr=json.loads((R/'transfer/changed-context.template.json').read_text())
ck('model invariants hardened',sf.validate_model_invariants()['pass'])
ck('record schema exact',b['record_fields']==sf.EXPECTED_RECORD_FIELDS and len(set(b['record_fields']))==10)
ck('positive paths exact',g['required_positive_paths']==sf.EXPECTED_POSITIVE_PATHS)
ck('negative paths exact',g['required_negative_paths']==sf.EXPECTED_NEGATIVE_PATHS)
ck('forbidden exact',g['forbidden_conclusions']==sf.EXPECTED_FORBIDDEN_CONCLUSIONS)
ck('transfer exact',g['transfer_context']==sf.EXPECTED_TRANSFER_CONTEXT)
ck('handles distinct',len(set(b['synthetic_handles'].values()))==3)
ck('external target absent',b['external_target'] is None)
ck('local no network',req['external_network_required'] is False)
ck('local no credentials',req['real_credentials_required'] is False)
ck('local no external target',req['external_target_required'] is False)
blank={k:'observed' for k in sf.EXPECTED_RECORD_FIELDS}; blank['Observation']=' '; ck('blank evidence rejected',sf.validate_evidence_record(blank)['pass'] is False)
ck('reauth gate denies before',sf.sensitive_action_gate(reauthenticated=False)['classification']=='REAUTHENTICATION_REQUIRED')
rt=sf.reauthentication_transition(); ck('reauth boundary enforced',rt['classification']=='REAUTHENTICATION_BOUNDARY_ENFORCED'); ck('reauth rotates',rt['rotated']); ck('old auth stale after reauth',rt['old_handle_rejected_after_reauth']); ck('transfer template reauth',tr['reauthentication_required'] is True and tr['risk_event']=='recovery_settings.change')
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'{p}/{t} PASS'); sys.exit(0 if cp.returncode==0 and p==t else 1)
