import pathlib, sys
HERE=pathlib.Path(__file__).resolve().parents[1]
sys.path.insert(0,str(HERE/'src'))
from totp_fixture import TOTPVerifier, RecoveryCodeStore, rfc_vectors_sha1
checks=[]
def c(name,cond): checks.append((name,bool(cond)))
secret=b'12345678901234567890'; v=TOTPVerifier(secret)
for t,e in rfc_vectors_sha1(): c(f'rfc_{t}',v.code_at(t)==e)
v=TOTPVerifier(secret,window=0,max_attempts=3)
now=1234567890; code=v.code_at(now)
ok,s=v.verify(code,now); c('valid_totp',ok and s=='TOTP_ACCEPTED')
ok,s=v.verify(code,now); c('replay_rejected',(not ok) and s=='TOTP_REPLAY_REJECTED')
v2=TOTPVerifier(secret,window=0,max_attempts=2)
c('bad1',v2.verify('00000000',now)[1]=='TOTP_REJECTED')
c('bad2',v2.verify('00000000',now)[1]=='TOTP_REJECTED')
c('limit',v2.verify(code,now)[1]=='ATTEMPT_LIMIT_REACHED')
store=RecoveryCodeStore(); codes=['ALPHA-7K4P','BRAVO-2M8Q']; store.issue(codes)
c('digest_only',not store.state_contains_plaintext(codes))
c('recovery_first',store.consume(codes[0])==(True,'RECOVERY_ACCEPTED'))
c('recovery_reuse',store.consume(codes[0])==(False,'RECOVERY_CODE_REUSED_OR_INVALID'))
store.regenerate(['DELTA-4H6R'])
c('old_after_regen',store.consume(codes[1])==(False,'RECOVERY_CODE_REUSED_OR_INVALID'))
c('new_after_regen',store.consume('DELTA-4H6R')==(True,'RECOVERY_ACCEPTED'))
c('regen_event',any(x['event']=='RECOVERY_SET_REGENERATED' for x in store.events))
for n,ok in checks: print(('PASS' if ok else 'FAIL'),n)
print(f'SUMMARY {sum(x[1] for x in checks)}/{len(checks)} PASS')
raise SystemExit(0 if all(x[1] for x in checks) else 1)
