import json import subprocess from pathlib import Path import pytest from tests.dashboard_bundle import dashboard ROOT = Path(__file__).resolve().parents[1] RECOVERY = ROOT / "frontend" / "mobile-delivery-recovery.js" def run_node(script: str) -> dict: result = subprocess.run( ["node", "-e", script], cwd=ROOT, text=True, capture_output=True, check=False, ) assert result.returncode == 0, result.stderr return json.loads(result.stdout) def test_recovery_orders_deliveries_by_safest_required_intervention(): script = f""" const createRecovery = require({json.dumps(str(RECOVERY))}); const recovery = createRecovery({{ getItems: () => [ {{outbox_id:'waiting', delivery_state:'waiting', title:'Waiting'}}, {{outbox_id:'uncertain', delivery_state:'uncertain', title:'Uncertain'}}, {{outbox_id:'attention', status:'attention', title:'Attention'}}, {{outbox_id:'authorization', status:'authorization', title:'Authorization'}}, ], }}); process.stdout.write(JSON.stringify(recovery.snapshot())); """ assert run_node(script) == { "count": 4, "current": { "id": "authorization", "state": "authorization", "primary": "Authorize & send", "position": 1, "total": 4, }, } def test_recovery_advances_only_after_the_current_delivery_is_resolved(): script = f""" const createRecovery = require({json.dumps(str(RECOVERY))}); (async () => {{ let items = [ {{outbox_id:'authorize', status:'authorization', title:'Approve review'}}, {{outbox_id:'retry', delivery_state:'waiting', title:'Post comment'}}, ]; let resolves = false; const recovery = createRecovery({{ getItems: () => items, activate: async item => {{ if (resolves) items = items.filter(candidate => candidate.outbox_id !== item.outbox_id); return resolves; }}, }}); recovery.open(); const retained = await recovery.activate(); resolves = true; const advanced = await recovery.activate(); process.stdout.write(JSON.stringify({{retained, advanced}})); }})(); """ assert run_node(script) == { "retained": { "count": 2, "current": { "id": "authorize", "state": "authorization", "primary": "Authorize & send", "position": 1, "total": 2, }, }, "advanced": { "count": 1, "current": { "id": "retry", "state": "waiting", "primary": "Retry now", "position": 1, "total": 1, }, }, } def test_recovery_renders_delivery_context_and_hands_off_when_cleared(): script = f""" const createRecovery = require({json.dumps(str(RECOVERY))}); (async () => {{ let items = [{{ outbox_id:'close-7', status:'authorization', outbox_kind:'issue-close', title:'Close resolved incident', repository:'stackchain/ops', details:'#7', last_attempt_error:'Fresh authorization expired', }}]; let completed = 0; const element = () => ({{textContent:'', hidden:false}}); const elements = {{ dialog: {{open:false, showModal(){{this.open=true}}, close(){{this.open=false}}}}, title: element(), destination: element(), reason: element(), progress: element(), action: element(), status: element(), }}; const recovery = createRecovery({{ getItems: () => items, activate: async () => {{items = []; return true}}, onComplete: () => {{completed += 1}}, elements, }}); const opened = recovery.open(); const rendered = {{ opened, dialogOpen:elements.dialog.open, title:elements.title.textContent, destination:elements.destination.textContent, reason:elements.reason.textContent, progress:elements.progress.textContent, action:elements.action.textContent, }}; const cleared = await recovery.activate(); process.stdout.write(JSON.stringify({{rendered, cleared, completed, dialogOpen:elements.dialog.open}})); }})(); """ assert run_node(script) == { "rendered": { "opened": "opened", "dialogOpen": True, "title": "Close resolved incident", "destination": "stackchain/ops · #7", "reason": "Fresh authorization expired", "progress": "Delivery 1 of 1 · Authorization required", "action": "Authorize & close", }, "cleared": {"count": 0, "current": None}, "completed": 1, "dialogOpen": False, } @pytest.mark.anyio async def test_dashboard_packages_phone_safe_guided_delivery_recovery(): html = await dashboard() service_worker = (ROOT / "frontend" / "service-worker.js").read_text() bundle = (ROOT / "src" / "frontend_bundle.py").read_text() assert '' in html assert "const mobileDeliveryRecovery = createMobileDeliveryRecovery({" in html assert "openDelivery: () => mobileDeliveryRecovery.open()" in html assert "mobileStartDay.completePhase('delivery')" in html assert "BASE + 'static/mobile-delivery-recovery.js'" in service_worker assert '"static/mobile-delivery-recovery.js"' in bundle assert ".mobile-delivery-recovery-panel" in html assert "min-height:48px" in html assert "env(safe-area-inset-bottom)" in html