import json import subprocess from pathlib import Path ROOT = Path(__file__).resolve().parents[1] RECONNECT = ROOT / "frontend" / "reconnect-outboxes.js" def run_node(script: str) -> dict: result = subprocess.run( ["node", "-e", script], cwd=ROOT, text=True, capture_output=True, check=True ) return json.loads(result.stdout) def test_successful_reconnect_restores_identity_and_flushes_unchanged_outboxes(): result = run_node( f""" const createReconnectOutboxes = require({json.dumps(str(RECONNECT))}); const calls = []; const reconnect = createReconnectOutboxes({{ refresh: async () => ({{context:{{user:{{login:'timmy'}}}}}}), restoreIdentity: login => calls.push(['identity', login]), flushIssue: () => calls.push(['issue']), flushAuthored: () => calls.push(['authored']), flushNotificationReads: () => calls.push(['notification-read']), }}); reconnect().then(result => process.stdout.write(JSON.stringify({{result, calls}}))); """ ) assert result == { "result": True, "calls": [ ["identity", "timmy"], ["issue"], ["authored"], ["notification-read"], ], } def test_concurrent_reconnect_requests_queue_one_follow_up_flush(): result = run_node( f""" const createReconnectOutboxes = require({json.dumps(str(RECONNECT))}); (async () => {{ let releaseFirst; let issueCalls=0; let active=0; let maxActive=0; const reconnect = createReconnectOutboxes({{ refresh: async () => ({{context:{{user:{{login:'timmy'}}}}}}), restoreIdentity: () => {{}}, flushIssue: async () => {{ issueCalls++; active++; maxActive=Math.max(maxActive,active); if (issueCalls === 1) await new Promise(resolve => {{ releaseFirst=resolve; }}); active--; }}, flushAuthored: async () => {{}}, flushNotificationReads: async () => {{}}, }}); const first=reconnect(); while (!releaseFirst) await new Promise(resolve=>setTimeout(resolve,0)); const second=reconnect(); releaseFirst(); await Promise.all([first,second]); process.stdout.write(JSON.stringify({{issueCalls,maxActive}})); }})().catch(error => {{ console.error(error); process.exit(1); }}); """ ) assert result == {"issueCalls": 2, "maxActive": 1} def test_dashboard_uses_reconnect_flush_after_live_identity_refresh(): index = (ROOT / "frontend" / "index.html").read_text() dashboard = (ROOT / "frontend" / "dashboard.js").read_text() assert '' in index assert "const reconnectOutboxes = createReconnectOutboxes({" in dashboard assert "restoreIdentity: login => { activeFlushLogin = login; confirmedOwnerLogin = login; }" in dashboard assert "window.addEventListener('online', reconnectAfterOnline);" in dashboard assert "setTimeout(reconnectOutboxes, 500)" in dashboard def test_dashboard_registers_reconnect_before_offline_startup_can_wait(): dashboard = (ROOT / "frontend" / "dashboard.js").read_text() listener = dashboard.index("window.addEventListener('online', reconnectAfterOnline);") offline_startup = dashboard.index("await updateOfflineWorkControls();") assert listener < offline_startup