import json import subprocess from pathlib import Path import pytest from tests.dashboard_bundle import dashboard OFFLINE_TODAY = Path(__file__).parents[1] / "frontend" / "offline-today.js" def run_scenario(scenario: str) -> dict: script = f""" const createOfflineToday = require({json.dumps(str(OFFLINE_TODAY))}); (async () => {{ {scenario} }})().catch(error => {{ console.error(error); process.exit(1); }}); """ result = subprocess.run( ["node", "-e", script], check=True, capture_output=True, text=True ) return json.loads(result.stdout) def test_warms_bounded_today_queue_with_two_concurrent_requests(): result = run_scenario(""" let active = 0; let peak = 0; const loaded = []; const saved = []; const items = Array.from({length:7}, (_, index) => ({ kind:index % 2 ? 'pull' : 'issue', repository:'stackchain/dashboard', number:index + 1, updated_at:'2026-08-08T12:0' + index + ':00Z', })); const warmer = createOfflineToday({ loadDetail: async item => { active += 1; peak = Math.max(peak, active); loaded.push(item.number); await new Promise(resolve => setTimeout(resolve, 5)); active -= 1; return {title:'Work ' + item.number, body:'Context ' + item.number}; }, loadSavedDetail: () => null, saveDetail: (login, item, detail) => saved.push({login, number:item.number, detail}), }); const status = await warmer.warm('timmy', items); process.stdout.write(JSON.stringify({peak, loaded, saved, status})); """) assert result["peak"] == 2 assert result["loaded"] == [1, 2, 3, 4, 5] assert [entry["number"] for entry in result["saved"]] == [1, 2, 3, 4, 5] assert result["saved"][0]["detail"]["source_updated_at"] == "2026-08-08T12:00:00Z" assert result["status"] == {"total": 5, "ready": 5, "failed": 0, "pending": 0} def test_readiness_waits_for_durable_async_admission_and_reports_rejection(): result = run_scenario(""" const events = []; const warmer = createOfflineToday({ loadDetail: async () => ({title:'Fetched detail'}), loadSavedDetail: async () => null, saveDetail: async () => { events.push('admission-started'); await new Promise(resolve => setTimeout(resolve, 5)); events.push('admission-rejected'); return false; }, onStatus: status => events.push('ready:' + status.ready + '/failed:' + status.failed), }); const status = await warmer.warm('timmy', [{ kind:'issue', repository:'stackchain/dashboard', number:1, updated_at:'v1', }]); process.stdout.write(JSON.stringify({events, status, failed:warmer.failedKeys()})); """) assert result["status"] == {"total": 1, "ready": 0, "failed": 1, "pending": 0} assert result["failed"] == ["issue:stackchain/dashboard:1"] assert result["events"].index("admission-rejected") < result["events"].index( "ready:0/failed:1" ) def test_refreshes_only_changed_revisions_and_keeps_prior_copy_on_failure(): result = run_scenario(""" const items = [ {kind:'issue', repository:'stackchain/dashboard', number:1, updated_at:'new'}, {kind:'pull', repository:'stackchain/dashboard', number:2, updated_at:'same'}, ]; const old = new Map([ ['issue:stackchain/dashboard:1', {title:'Prior 1', source_updated_at:'old'}], ['pull:stackchain/dashboard:2', {title:'Prior 2', source_updated_at:'same'}], ]); const loaded = []; const warmer = createOfflineToday({ loadDetail: async item => { loaded.push(item.number); throw new Error('temporary outage'); }, loadSavedDetail: (_login, item) => old.get(item.kind + ':' + item.repository + ':' + item.number), saveDetail: () => { throw new Error('must not replace a prior copy after failure'); }, }); const status = await warmer.warm('timmy', items); process.stdout.write(JSON.stringify({loaded, status, failed:warmer.failedKeys()})); """) assert result == { "loaded": [1], "status": {"total": 2, "ready": 2, "failed": 1, "pending": 0}, "failed": ["issue:stackchain/dashboard:1"], } def test_retry_fetches_only_failed_items(): result = run_scenario(""" const items = [ {kind:'issue', repository:'stackchain/dashboard', number:1, updated_at:'v2'}, {kind:'issue', repository:'stackchain/dashboard', number:2, updated_at:'v1'}, ]; let fail = true; const calls = []; const saved = new Map([['issue:stackchain/dashboard:2', {source_updated_at:'v1'}]]); const warmer = createOfflineToday({ loadDetail: async item => { calls.push(item.number); if (fail) throw new Error('offline'); return {title:'Fresh'}; }, loadSavedDetail: (_login, item) => saved.get(item.kind + ':' + item.repository + ':' + item.number), saveDetail: (_login, item, detail) => saved.set(item.kind + ':' + item.repository + ':' + item.number, detail), }); await warmer.warm('timmy', items); fail = false; const status = await warmer.retry('timmy', items); process.stdout.write(JSON.stringify({calls, status})); """) assert result["calls"] == [1, 1] assert result["status"] == {"total": 2, "ready": 2, "failed": 0, "pending": 0} def test_requested_reviews_are_warmed_as_distinct_today_items(): result = run_scenario(""" const loaded = []; const saved = []; const review = { kind:'pull', is_review:true, repository:'stackchain/dashboard', number:12, updated_at:'2026-08-08T12:00:00Z', }; const warmer = createOfflineToday({ loadDetail: async item => { loaded.push(item.is_review ? 'review' : item.kind); return {head_sha:'abc123', files:[{filename:'src/app.js'}]}; }, loadSavedDetail: () => null, saveDetail: (_login, item, detail) => saved.push({item, detail}), }); const status = await warmer.warm('timmy', [review]); process.stdout.write(JSON.stringify({loaded, saved, status})); """) assert result["loaded"] == ["review"] assert result["saved"][0]["item"]["is_review"] is True assert result["saved"][0]["detail"]["head_sha"] == "abc123" assert result["status"] == {"total": 1, "ready": 1, "failed": 0, "pending": 0} @pytest.mark.anyio async def test_dashboard_warms_today_after_fresh_snapshot_and_exposes_mobile_retry(): html = await dashboard() assert '' in html assert 'id="offline-today-status"' in html assert 'id="retry-offline-today"' in html assert "offlineToday.warm(confirmedOwnerLogin, todayMyWork)" in html assert "offlineToday.retry(confirmedOwnerLogin, todayMyWork)" in html assert "Today offline: " in html assert ".offline-today-readiness button { min-height:44px;" in html