import json,tempfile,sys,unittest from pathlib import Path from unittest.mock import patch sys.path.insert(0,str(Path(__file__).resolve().parents[1]/'scripts')) import report class ReportRelayTests(unittest.TestCase): def test_outage_persists_and_retry_is_idempotent(self): with tempfile.TemporaryDirectory() as d: cfg={'outbox_dir':d,'control':{}} payload={'task_id':'task','session_id':'worker','stage':'execution_completed','summary':'checked','current_progress':'ready','evidence':['build passed']} class Client: calls=0 def __init__(self,*a):pass def call(self,*a): self.__class__.calls+=1 if self.calls==1:raise OSError('offline') return {'accepted':True} with patch.object(report,'Client',Client): first=report.submit(cfg,payload);self.assertEqual(first['status'],'pending') path=next(Path(d).glob('*.json'));r=json.loads(path.read_text());r['next_retry']=0;report.atomic_json(path,r) self.assertEqual(report.flush(cfg)['accepted'],1) self.assertEqual(report.submit(cfg,payload)['status'],'accepted') self.assertEqual(Client.calls,2) def test_accepted_files_do_not_starve_pending_and_completion_supersedes_old_progress(self): with tempfile.TemporaryDirectory() as d: for i in range(105): payload={'task_id':'task','session_id':'worker','stage':'progress'} status='accepted' if i<102 else 'pending' if i==104:payload['stage']='execution_completed' report.atomic_json(Path(d)/(('%064x'%i)+'.json'),{'status':status,'created':i,'payload':payload}) class Client: seen=[] def __init__(self,*a):pass def call(self,path,payload):self.seen.append(payload['stage']);return {'ok':True} with patch.object(report,'Client',Client):counts=report.flush({'outbox_dir':d,'control':{}}) self.assertEqual(Client.seen,['execution_completed']) self.assertEqual(counts['superseded'],2) self.assertEqual(counts['pending'],0) if __name__=='__main__':unittest.main()