import importlib.util from pathlib import Path import unittest spec=importlib.util.spec_from_file_location('reuse_recovery',Path(__file__).parents[1]/'scripts/revoke-reuse-surface-forgejo-source.py') r=importlib.util.module_from_spec(spec); spec.loader.exec_module(r) class Journal: def __init__(self): self.rows=[] def put(self,event,**kw): self.rows.append((event,kw)) class Fake: def __init__(self,ambiguous=False,deleted=True): self.journal=Journal(); self.mutations=[]; self.token={'id':123,'name':r.p.PREFIX+'attempt','scopes':r.p.SCOPES}; self.ambiguous=ambiguous; self.deleted=deleted; self.basic=None def pages(self,path): return [] if self.token is None else [self.token] def request(self,method,path,payload=None,**kw): if method=='GET': return 200,{'login':r.p.USER,'restricted':True,'is_admin':False,'visibility':'private'} self.mutations.append((method,path)) if method=='PATCH': assert set(payload)=={'password','must_change_password'} return 200,{} if method=='DELETE': self.basic=kw.get('basic'); assert self.basic.startswith('Basic ') if self.deleted: self.token=None if self.ambiguous: raise r.p.Refused('request_observation_failed') return 204,None raise AssertionError(method) class Tests(unittest.TestCase): def target(self): return {'id':123,'name':r.p.PREFIX+'attempt'} def test_basic_exact_token_revocation(self): c=Fake(); r.recover(c,self.target()); self.assertEqual(c.mutations[-1],('DELETE','/users/'+r.p.USER+'/tokens/123')); self.assertEqual(c.journal.rows[-1][1]['outcome'],'revoked_verified_kv_preserved') def test_unexpected_token_refuses_without_password_change(self): c=Fake(); c.token['id']=124 with self.assertRaisesRegex(r.p.Refused,'inventory_conflict'): r.recover(c,self.target()) self.assertEqual(c.mutations,[]) def test_ambiguous_delete_reconciles_absence_without_retry(self): c=Fake(ambiguous=True); r.recover(c,self.target()); self.assertEqual(sum(m=='DELETE' for m,p in c.mutations),1) def test_ambiguous_delete_still_present_stops(self): c=Fake(ambiguous=True,deleted=False) with self.assertRaisesRegex(r.p.Refused,'revoke_unverified'): r.recover(c,self.target()) self.assertEqual(sum(m=='DELETE' for m,p in c.mutations),1) def test_absent_token_verified_noop_has_no_mutations(self): c=Fake(); c.token=None; r.recover(c,self.target()); self.assertEqual(c.mutations,[]) if __name__=='__main__': unittest.main()