import json import subprocess from pathlib import Path POLLER = Path(__file__).parents[1] / "frontend" / "context-poller.js" DASHBOARD = Path(__file__).parents[1] / "frontend" / "dashboard.js" def run_node(script: str) -> dict: result = subprocess.run( ["node", "-e", script], check=True, capture_output=True, text=True ) return json.loads(result.stdout) def test_context_poller_is_single_flight_and_pauses_until_visible(): script = f""" const createContextPoller = require({json.dumps(str(POLLER))}); let calls = 0; let hidden = false; let resolveRequest; const timers = []; const poller = createContextPoller({{ fetchContext: () => {{ calls += 1; if (calls > 1) return Promise.resolve({{ repos: [] }}); return new Promise((resolve) => {{ resolveRequest = resolve; }}); }}, onSnapshot: () => {{}}, onError: () => {{}}, isHidden: () => hidden, setTimer: (callback) => {{ timers.push(callback); return timers.length; }}, clearTimer: () => {{}}, intervalMs: 8000, }}); (async () => {{ const first = poller.start(); const joined = poller.refresh(); const callsWhilePending = calls; resolveRequest({{ repos: [] }}); await Promise.all([first, joined]); hidden = true; poller.setVisible(false); const scheduled = timers.shift(); if (scheduled) scheduled(); await Promise.resolve(); const callsWhileHidden = calls; hidden = false; await poller.setVisible(true); process.stdout.write(JSON.stringify({{ callsWhilePending, callsWhileHidden, callsAfterResume: calls, }})); }})(); """ assert run_node(script) == { "callsWhilePending": 1, "callsWhileHidden": 1, "callsAfterResume": 2, } def test_context_poller_sends_revisions_and_merges_changed_sections(): script = f""" const createContextPoller = require({json.dumps(str(POLLER))}); const requested = []; const rendered = []; const responses = [ {{ context: {{ user: {{ login: 'timmy' }} }}, events: [{{ id: 1 }}], notifications: [], sections: {{ context: 'fresh', events: 'fresh', notifications: 'fresh' }}, revisions: {{ context: 1, events: 1, notifications: 1 }}, freshness: {{ age_seconds: 0 }} }}, {{ sections: {{ context: 'fresh', events: 'fresh', notifications: 'fresh' }}, revisions: {{ context: 1, events: 2, notifications: 1 }}, events: [{{ id: 2 }}], freshness: {{ age_seconds: 1 }} }}, {{ sections: {{ context: 'fresh', events: 'fresh', notifications: 'fresh' }}, revisions: {{ context: 1, events: 2, notifications: 1 }}, freshness: {{ age_seconds: 2 }} }}, ]; const poller = createContextPoller({{ fetchContext: revisions => {{ requested.push({{ ...revisions }}); return Promise.resolve(responses.shift()); }}, onSnapshot: (snapshot, changed) => rendered.push({{ context: snapshot.context.user.login, event: snapshot.events[0].id, age: snapshot.freshness.age_seconds, changed, }}), onError: error => {{ throw error; }}, setTimer: () => 1, clearTimer: () => {{}}, }}); (async () => {{ await poller.refresh(); await poller.refresh(); await poller.refresh(); process.stdout.write(JSON.stringify({{ requested, rendered }})); }})(); """ assert run_node(script) == { "requested": [ {}, {"context": 1, "events": 1, "notifications": 1}, {"context": 1, "events": 2, "notifications": 1}, ], "rendered": [ {"context": "timmy", "event": 1, "age": 0, "changed": ["context", "events", "notifications"]}, {"context": "timmy", "event": 2, "age": 1, "changed": ["events"]}, {"context": "timmy", "event": 2, "age": 2, "changed": []}, ], } def test_context_poller_adopts_progressive_snapshot_before_revision_conditional_refresh(): script = f""" const createContextPoller = require({json.dumps(str(POLLER))}); const requested = []; const rendered = []; const timers = []; const seed = {{ context:{{user:{{login:'timmy'}}}}, events:[{{id:7}}], notifications:[], revisions:{{context:'0123456789abcdef.1',events:'fedcba9876543210.2',notifications:'0011223344556677.3'}}, freshness:{{fresh_for_seconds:8,sections:{{ context:{{degraded:false,age_seconds:2}},events:{{degraded:false,age_seconds:5}},notifications:{{degraded:false,age_seconds:1}}, }}}}, }}; const poller = createContextPoller({{ fetchContext: revisions => {{ requested.push({{...revisions}}); return Promise.resolve({{revisions:{{...revisions}},freshness:seed.freshness}}); }}, onSnapshot: (snapshot, changed) => rendered.push({{login:snapshot.context.user.login,changed}}), onError: error => {{ throw error; }}, setTimer: (callback, delay) => {{ const timer={{callback,delay}}; timers.push(timer); return timer; }}, clearTimer: () => {{}}, intervalMs:8000, }}); (async()=>{{ const adopted=poller.adopt(seed); const before={{adopted,requests:requested.length,rendered:[...rendered],delay:timers[0].delay}}; timers[0].callback(); await new Promise(resolve=>setImmediate(resolve)); process.stdout.write(JSON.stringify({{before,requested}})); }})(); """ assert run_node(script) == { "before": { "adopted": True, "requests": 0, "rendered": [{ "login": "timmy", "changed": ["context", "events", "notifications"], }], "delay": 3000, }, "requested": [{ "context": "0123456789abcdef.1", "events": "fedcba9876543210.2", "notifications": "0011223344556677.3", }], } def test_context_poller_adopts_inflight_progressive_snapshot_without_fetching(): script = f""" const createContextPoller = require({json.dumps(str(POLLER))}); let calls=0; const rendered=[]; const timers=[]; const snapshot={{context:{{user:{{login:'timmy'}}}},events:[],notifications:[]}}; const poller=createContextPoller({{ fetchContext:()=>{{calls+=1;return Promise.resolve(snapshot);}}, onSnapshot:value=>rendered.push(value.context.user.login), onError:error=>{{throw error;}}, setTimer:(callback,delay)=>{{const timer={{callback,delay}};timers.push(timer);return timer;}}, clearTimer:()=>{{}}, setDeadlineTimer:()=>1, clearDeadlineTimer:()=>{{}}, intervalMs:8000, }}); (async()=>{{ const adopted=await poller.adoptPending(Promise.resolve(snapshot)); process.stdout.write(JSON.stringify({{adopted,calls,rendered,delay:timers[0].delay}})); }})(); """ assert run_node(script) == { "adopted": True, "calls": 0, "rendered": ["timmy"], "delay": 8000, } def test_dashboard_adopts_progressive_snapshot_or_falls_back_to_immediate_load(): source = DASHBOARD.read_text() assert "let adoptedProgressiveSnapshot = contextPoller.adopt(progressiveWorkHandoff?.liveSnapshot);" in source assert "contextPoller.adoptPending(progressiveWorkHandoff.liveSnapshotPromise)" in source assert "if (!adoptedProgressiveSnapshot) await load();" in source def test_context_poller_waits_for_server_cooldown_when_every_section_is_degraded(): script = f""" const createContextPoller = require({json.dumps(str(POLLER))}); const delays = []; const poller = createContextPoller({{ fetchContext: () => Promise.resolve({{ context: {{}}, events: [], notifications: [], freshness: {{ fresh_for_seconds: 8, retry_in_seconds: 60, sections: {{ context: {{ degraded: true, retry_in_seconds: 60, age_seconds: 12 }}, events: {{ degraded: true, retry_in_seconds: 60, age_seconds: 12 }}, notifications: {{ degraded: true, retry_in_seconds: 60, age_seconds: 12 }}, }}, }}, }}), onSnapshot: () => {{}}, onError: error => {{ throw error; }}, setTimer: (callback, delay) => {{ delays.push(delay); return delays.length; }}, clearTimer: () => {{}}, intervalMs: 8000, }}); (async () => {{ await poller.start(); process.stdout.write(JSON.stringify({{ delays }})); }})(); """ assert run_node(script) == {"delays": [60000]} def test_context_poller_uses_earliest_healthy_section_freshness_deadline(): script = f""" const createContextPoller = require({json.dumps(str(POLLER))}); const delays = []; const poller = createContextPoller({{ fetchContext: () => Promise.resolve({{ context: {{}}, events: [], notifications: [], freshness: {{ fresh_for_seconds: 8, retry_in_seconds: 60, sections: {{ context: {{ degraded: false, age_seconds: 2 }}, events: {{ degraded: false, age_seconds: 5 }}, notifications: {{ degraded: true, retry_in_seconds: 60, age_seconds: 12 }}, }}, }}, }}), onSnapshot: () => {{}}, onError: error => {{ throw error; }}, setTimer: (callback, delay) => {{ delays.push(delay); return delays.length; }}, clearTimer: () => {{}}, intervalMs: 8000, }}); (async () => {{ await poller.start(); process.stdout.write(JSON.stringify({{ delays }})); }})(); """ assert run_node(script) == {"delays": [3000]} def test_context_poller_does_not_spin_while_stale_sections_are_revalidating(): script = f""" const createContextPoller = require({json.dumps(str(POLLER))}); const delays = []; const poller = createContextPoller({{ fetchContext: () => Promise.resolve({{ context: {{}}, events: [], notifications: [], freshness: {{ fresh_for_seconds: 8, sections: {{ context: {{ degraded: false, revalidating: true, age_seconds: 12 }}, events: {{ degraded: false, revalidating: true, age_seconds: 12 }}, notifications: {{ degraded: false, revalidating: true, age_seconds: 12 }}, }}, }}, }}), onSnapshot: () => {{}}, onError: error => {{ throw error; }}, setTimer: (callback, delay) => {{ delays.push(delay); return delays.length; }}, clearTimer: () => {{}}, intervalMs: 8000, }}); (async () => {{ await poller.start(); process.stdout.write(JSON.stringify({{ delays }})); }})(); """ assert run_node(script) == {"delays": [8000]} def test_context_poller_honors_http_retry_after_delay(): script = f""" const createContextPoller = require({json.dumps(str(POLLER))}); const delays = []; const errors = []; const unavailable = new Error('HTTP 503'); unavailable.retryAfterMs = 45000; const poller = createContextPoller({{ fetchContext: () => Promise.reject(unavailable), onSnapshot: () => {{}}, onError: error => errors.push(error.message), setTimer: (callback, delay) => {{ delays.push(delay); return delays.length; }}, clearTimer: () => {{}}, intervalMs: 8000, }}); (async () => {{ await poller.start(); process.stdout.write(JSON.stringify({{ delays, errors }})); }})(); """ assert run_node(script) == {"delays": [45000], "errors": ["HTTP 503"]} def test_retry_after_parser_accepts_bounded_delta_seconds(): script = f""" const createContextPoller = require({json.dumps(str(POLLER))}); process.stdout.write(JSON.stringify({{ valid: createContextPoller.retryAfterMs('45'), zero: createContextPoller.retryAfterMs('0'), invalid: createContextPoller.retryAfterMs('later'), excessive: createContextPoller.retryAfterMs('999999'), }})); """ assert run_node(script) == { "valid": 45000, "zero": None, "invalid": None, "excessive": 300000, } def test_context_poller_backs_off_transport_failures_and_resets_after_success(): script = f""" const createContextPoller = require({json.dumps(str(POLLER))}); const delays = []; let calls = 0; const poller = createContextPoller({{ fetchContext: () => {{ calls += 1; if (calls < 6) return Promise.reject(new TypeError('network unavailable')); return Promise.resolve({{ context: {{}}, events: [], notifications: [] }}); }}, onSnapshot: () => {{}}, onError: () => {{}}, setTimer: (callback, delay) => {{ delays.push(delay); return delays.length; }}, clearTimer: () => {{}}, intervalMs: 8000, maxBackoffMs: 60000, }}); (async () => {{ await poller.refresh(); await poller.refresh(); await poller.refresh(); await poller.refresh(); await poller.refresh(); await poller.refresh(); process.stdout.write(JSON.stringify({{ delays }})); }})(); """ assert run_node(script) == { "delays": [8000, 16000, 32000, 60000, 60000, 8000] } def test_live_fetch_passes_retry_after_to_the_poller_error(): source = DASHBOARD.read_text() assert "createContextPoller.retryAfterMs(res.headers.get('Retry-After'))" in source assert "error.retryAfterMs" in source def test_live_revision_query_preserves_bounded_opaque_tokens(): script = f""" const createContextPoller = require({json.dumps(str(POLLER))}); const query = createContextPoller.buildRevisionQuery({{ context: '0123456789abcdef.12', events: 'fedcba9876543210.3', notifications: 'not-a-token', extra: '0123456789abcdef.1', }}); process.stdout.write(JSON.stringify({{ query }})); """ assert run_node(script) == { "query": "context_revision=0123456789abcdef.12&events_revision=fedcba9876543210.3" } def test_context_poller_aborts_a_stalled_request_and_recovers_on_schedule(): script = f""" const createContextPoller = require({json.dumps(str(POLLER))}); const timers = []; const deadlineTimers = []; const errors = []; const snapshots = []; const signals = []; let calls = 0; const poller = createContextPoller({{ fetchContext: (revisions, options) => {{ calls += 1; signals.push(options.signal); if (calls === 1) return new Promise(() => {{}}); return Promise.resolve({{ context: {{ user: {{ login: 'timmy' }} }} }}); }}, onSnapshot: snapshot => snapshots.push(snapshot.context.user.login), onError: error => errors.push({{ name: error.name, message: error.message }}), setTimer: (callback, delay) => {{ const timer = {{ callback, delay, cancelled: false }}; timers.push(timer); return timer; }}, clearTimer: timer => {{ timer.cancelled = true; }}, setDeadlineTimer: (callback, delay) => {{ const timer = {{ callback, delay, cancelled: false }}; deadlineTimers.push(timer); return timer; }}, clearDeadlineTimer: timer => {{ timer.cancelled = true; }}, intervalMs: 8000, timeoutMs: 25, }}); (async () => {{ const first = poller.start(); const deadline = deadlineTimers.find(timer => timer.delay === 25 && !timer.cancelled); deadline.callback(); await first; const retry = timers.find(timer => timer.delay === 8000 && !timer.cancelled); retry.callback(); await new Promise(resolve => setImmediate(resolve)); process.stdout.write(JSON.stringify({{ calls, firstAborted: signals[0].aborted, errors, snapshots, }})); }})(); """ assert run_node(script) == { "calls": 2, "firstAborted": True, "errors": [ {"name": "TimeoutError", "message": "Live update timed out after 25ms."} ], "snapshots": ["timmy"], } def test_forced_refresh_retires_stale_generation_without_late_overwrite(): script = f""" const createContextPoller = require({json.dumps(str(POLLER))}); let resolveFirst; let firstSignal; const rendered = []; const errors = []; let calls = 0; const poller = createContextPoller({{ fetchContext: (revisions, options) => {{ calls += 1; if (calls === 1) {{ firstSignal = options.signal; return new Promise(resolve => {{ resolveFirst = resolve; }}); }} return Promise.resolve({{ context: {{ user: {{ login: 'fresh' }} }} }}); }}, onSnapshot: snapshot => rendered.push(snapshot.context.user.login), onError: error => errors.push(error.name), setTimer: () => 1, clearTimer: () => {{}}, setDeadlineTimer: () => 1, clearDeadlineTimer: () => {{}}, }}); (async () => {{ const stale = poller.start(); const fresh = poller.refresh({{ force: true }}); await fresh; resolveFirst({{ context: {{ user: {{ login: 'stale' }} }} }}); await stale; await new Promise(resolve => setImmediate(resolve)); process.stdout.write(JSON.stringify({{ calls, firstAborted: firstSignal.aborted, rendered, errors, }})); }})(); """ assert run_node(script) == { "calls": 2, "firstAborted": True, "rendered": ["fresh"], "errors": [], }