"""Real HTTP -> coordinator -> portable C++ StageRunner -> durable delivery test.""" import http.client import json import sys import tempfile import threading import time from pathlib import Path ROOT=Path(__file__).resolve().parents[1] sys.path.insert(0,str(ROOT/'coordinator')) from robot_bt_coordinator.backends import DemoBackend from robot_bt_coordinator.http_api import make_server from robot_bt_coordinator.replay import replay_events from robot_bt_coordinator.service import Coordinator,demo_site def main(): with tempfile.TemporaryDirectory() as tmp: backend=DemoBackend(ROOT/'build/bt_demo',Path(tmp)/'sim') coordinator=Coordinator(str(Path(tmp)/'tasks.db'),backend,{'robot_01'},demo_site()) server=make_server(coordinator,'127.0.0.1',0,'smoke-local-only') thread=threading.Thread(target=server.serve_forever,daemon=True);thread.start() def request(method,path,body=None): c=http.client.HTTPConnection('127.0.0.1',server.server_port,timeout=5) c.request(method,path,json.dumps(body) if body is not None else None,{'Authorization':'Bearer smoke-local-only','Content-Type':'application/json'}) r=c.getresponse();data=json.loads(r.read());c.close() assert r.status<300,(r.status,data) return data try: payload=json.loads((ROOT/'config/demo_request.json').read_text()) start=time.monotonic();receipt=request('POST','/v1/tasks',payload);ack_ms=(time.monotonic()-start)*1000 tid=receipt['task_id'] for _ in range(200): coordinator.tick();view=request('GET','/v1/tasks/'+tid) if view['status'] in ('SUCCEEDED','FAILED','INTERVENTION_REQUIRED'):break time.sleep(.01) assert view['status']=='SUCCEEDED',view assert view['completed_quantity']==1,view duplicate=request('POST','/v1/tasks',payload) assert duplicate['task_id']==tid and duplicate['deduplicated'] is True events=request('GET',f'/v1/tasks/{tid}/events?limit=500')['events'] replay=replay_events(events) assert replay['status']=='SUCCEEDED' and replay['completed_quantity']==1 print(json.dumps({'http_e2e':'passed','task_id':tid,'completed_quantity':1,'deduplicated':True,'offline_replay':'passed','event_count':len(events),'single_receipt_ms':round(ack_ms,2),'robot_motion':'simulated only'},indent=2)) finally: server.shutdown();server.server_close();thread.join();coordinator.close() if __name__=='__main__':main()