import json import subprocess from pathlib import Path import pytest from tests.dashboard_bundle import dashboard OFFLINE_WORK = Path(__file__).parents[1] / "frontend" / "offline-work.js" def run_scenario(scenario: str) -> dict: script = f""" const createOfflineWorkStore = require({json.dumps(str(OFFLINE_WORK))}); const values = new Map(); const storage = {{ getItem: key => values.has(key) ? values.get(key) : null, setItem: (key, value) => values.set(key, value), removeItem: key => values.delete(key), }}; {scenario} """ result = subprocess.run( ["node", "-e", script], check=True, capture_output=True, text=True ) return json.loads(result.stdout) def test_opted_in_snapshot_survives_restart_with_only_queue_card_fields(): result = run_scenario(""" const store = createOfflineWorkStore({storage, now:() => new Date('2026-08-07T12:00:00Z')}); store.setEnabled(true); store.save({ user:{login:'timmy', full_name:'Timmy', token:'secret'}, issues:[{id:1, number:8, title:'Ship offline work', body:'private body', state:'open', repository:'stackchain/dashboard', labels:['P0'], assignees:['timmy'], updated_at:'2026-08-07T11:00:00Z', url:'https://forge.example/issues/8'}], pull_requests:[], notifications:[{id:9, number:8, title:'Updated', unread:true, subject_body:'private comment', repository:'stackchain/dashboard', subject_type:'Issue', updated_at:'2026-08-07T11:30:00Z', url:'https://forge.example/issues/8'}], work_pagination:{issue:{page:1,total:1,has_more:false}}, repos:[{private:true}], events:[{body:'private event'}], }); const restarted = createOfflineWorkStore({storage, now:() => new Date('2026-08-07T12:01:00Z')}); process.stdout.write(JSON.stringify({loaded:restarted.load('timmy'), raw:[...values.values()].join(' ')})); """) assert result["loaded"]["user"] == {"login": "timmy", "full_name": "Timmy"} assert result["loaded"]["issues"][0]["title"] == "Ship offline work" assert result["loaded"]["notifications"][0]["unread"] is True assert result["loaded"]["saved_at"] == "2026-08-07T12:00:00.000Z" assert "private body" not in result["raw"] assert "private comment" not in result["raw"] assert "private event" not in result["raw"] assert "secret" not in result["raw"] def test_expired_snapshot_is_deleted_and_wrong_user_cannot_load_it(): result = run_scenario(""" const writer = createOfflineWorkStore({storage, now:() => new Date('2026-08-01T12:00:00Z'), maxAgeMs:1000}); writer.setEnabled(true); writer.save({user:{login:'timmy'}, issues:[], pull_requests:[], notifications:[]}); const wrongUser = writer.load('alexander'); const expired = createOfflineWorkStore({storage, now:() => new Date('2026-08-01T12:00:02Z'), maxAgeMs:1000}); const expiredValue = expired.load('timmy'); process.stdout.write(JSON.stringify({wrongUser, expiredValue, snapshotStillStored:values.has('stackchain.offline-work.snapshot.v1')})); """) assert result == { "wrongUser": None, "expiredValue": None, "snapshotStillStored": False, } def test_opting_out_deletes_the_persisted_snapshot(): result = run_scenario(""" const store = createOfflineWorkStore({storage}); store.setEnabled(true); store.save({user:{login:'timmy'}, issues:[], pull_requests:[], notifications:[]}); store.setEnabled(false); process.stdout.write(JSON.stringify({enabled:store.enabled(), loaded:store.load()})); """) assert result == {"enabled": False, "loaded": None} def test_opted_in_detail_cache_is_allowlisted_account_bound_and_bounded(): result = run_scenario(""" const store = createOfflineWorkStore({storage, now:() => new Date('2026-08-07T12:00:00Z'), maxDetails:2}); store.setEnabled(true); const issue = {kind:'issue', repository:'stackchain/dashboard', number:8}; store.saveDetail('timmy', issue, { title:'Ship offline work', body:'Safe description', state:'open', labels:['P0'], assignees:['timmy'], url:'https://forge.example/issues/8', token:'secret', files:[{patch:'private diff'}], conversation:{comments:[{id:1, author:'alexander', body:'Newest message', created_at:'2026-08-07T11:00:00Z', token:'comment-secret'}], page:1, older_page:null, total:1}, }); store.saveDetail('timmy', {kind:'pull', repository:'stackchain/dashboard', number:9}, {title:'PR 9', body:'Body 9'}); store.saveDetail('timmy', {kind:'issue', repository:'stackchain/dashboard', number:10}, {title:'Issue 10', body:'Body 10'}); const raw = [...values.values()].join(' '); process.stdout.write(JSON.stringify({ evicted:store.loadDetail('timmy', issue), loaded:store.loadDetail('timmy', {kind:'pull', repository:'stackchain/dashboard', number:9}), wrongUser:store.loadDetail('alexander', {kind:'pull', repository:'stackchain/dashboard', number:9}), raw, })); """) assert result["evicted"] is None assert result["wrongUser"] is None assert result["loaded"]["title"] == "PR 9" assert result["loaded"]["saved_at"] == "2026-08-07T12:00:00.000Z" assert "secret" not in result["raw"] assert "private diff" not in result["raw"] @pytest.mark.anyio async def test_dashboard_offers_private_offline_work_controls_and_read_only_hydration(): html = await dashboard() assert '' in html assert 'id="keep-work-offline"' in html assert 'Keep My Work available offline' in html assert 'id="clear-offline-work"' in html assert 'id="offline-work-status"' in html assert 'offlineWorkStore.save({' in html assert 'offlineWorkStore.load()' in html assert 'Offline · saved ' in html assert "setOfflineWorkMode(true)" in html assert "if (offlineWorkMode)" in html assert '.offline-work-controls button { min-height:44px;' in html @pytest.mark.anyio async def test_initial_http_outage_hydrates_saved_work_and_recovers_on_live_snapshot(): html = await dashboard() assert "if (!hasContextSnapshot && hydrateOfflineWork('outage')) return;" in html assert "Outage · saved " in html assert "Server unavailable · showing private My Work saved " in html assert "Live details and actions will return automatically." in html assert "function renderLiveSnapshot(snapshot, changedSections" in html assert "setOfflineWorkMode(false);\n offlineStatus.hidden = true;" in html @pytest.mark.anyio async def test_saved_today_details_open_offline_without_enabling_server_state_actions(): html = await dashboard() assert "offlineWorkStore.loadDetail(offlineLogin, item)" in html assert "Details not saved—reconnect to open this item." in html assert "openIssueSheet(item, trigger, savedDetail)" in html assert "openPullSheet(item, trigger, savedDetail)" in html assert "offlineWorkStore.saveDetail(confirmedOwnerLogin, item, detail)" in html assert "todayWork.contains(item)" in html assert "Offline copy · saved " in html assert "setOfflineDetailControls('issue')" in html assert "setOfflineDetailControls('pull')" in html assert "qs('#issue-planning').inert = true;" in html assert "qs('#issue-handoff').inert = true;" in html assert "qs('#pull-review').inert = true;" in html assert "qs('#issue-planning').inert = false;" in html assert "qs('#pull-review').inert = false;" in html assert "confirmedOwnerLogin = String(saved.user?.login || '').trim();" in html