import json import subprocess from pathlib import Path FRONTEND = Path(__file__).parents[1] / "frontend" MODULE = FRONTEND / "search-week-plan.js" def run_planner(scenario: str) -> dict: harness = f""" const createSearchWeekPlan = require({json.dumps(str(MODULE))}); (async()=>{{ {scenario} }})().catch(error=>{{console.error(error);process.exit(1);}}); """ completed = subprocess.run(["node", "-e", harness], check=True, capture_output=True, text=True) return json.loads(completed.stdout) def test_search_week_plan_previews_seven_days_and_requires_explicit_overload_confirmation(): result = run_planner(""" let claims=0,stages=0; const days=Array.from({length:7},(_,index)=>({date:`2026-08-${21+index}`,label:`Day ${index+1}`})); const week={load:async()=>({}),dates:()=>days,day:date=>date==='2026-08-21' ? {plan_date:date,ids:['existing'],capacity_minutes:60,estimates:{existing:50}} : {plan_date:date,ids:[],capacity_minutes:90,estimates:{}}, placement:()=>null,place:()=>{stages+=1;return true;},flush:async()=>({revision:2})}; const planner=createSearchWeekPlan({week,claim:async item=>{claims+=1;return {...item,assigned_to_me:true,claimable:false};}, accept:item=>item,identity:item=>`issue:${item.repository}:${item.number}:`}); const detail={kind:'issue',state:'open',repository:'stackchain/dashboard',number:42,claimable:true}; const preview=await planner.preview(detail); const invalid=await planner.plan(detail,{date:'2026-08-21',estimate:0}); const overload=await planner.plan(detail,{date:'2026-08-21',estimate:30}); console.log(JSON.stringify({preview,invalid,overload,claims,stages})); """) assert len(result["preview"]["days"]) == 7 assert result["preview"]["days"][0]["planned_minutes"] == 50 assert result["preview"]["days"][0]["capacity_minutes"] == 60 assert result["invalid"] == {"status": "estimate-required"} assert result["overload"]["status"] == "overload-confirmation-required" assert result["overload"]["planned_minutes"] == 80 assert result["claims"] == 0 assert result["stages"] == 0 def test_search_week_plan_assigns_once_then_durably_places_canonical_issue(): result = run_planner(""" let release,claims=0,places=0,flushes=0; const week={load:async()=>({}),dates:()=>[{date:'2026-08-22',label:'Sat, Aug 22'}], day:date=>({plan_date:date,ids:[],capacity_minutes:120,estimates:{}}),placement:()=>null, place:(id,date,estimate)=>{places+=1;return {id,date,estimate};},flush:async()=>{flushes+=1;return {revision:3};}}; const planner=createSearchWeekPlan({week, claim:async item=>{claims+=1;await new Promise(resolve=>{release=resolve});return {...item,assigned_to_me:true,claimable:false,title:'Canonical'};}, accept:item=>({...item,accepted:true}),identity:item=>`issue:${item.repository}:${item.number}:`}); const detail={kind:'issue',state:'open',repository:'stackchain/dashboard',number:42,claimable:true}; const first=planner.plan(detail,{date:'2026-08-22',estimate:45}); const second=planner.plan(detail,{date:'2026-08-22',estimate:45}); await Promise.resolve();release(); const outcomes=await Promise.all([first,second]); console.log(JSON.stringify({outcomes,claims,places,flushes})); """) assert result["claims"] == 1 assert result["places"] == 1 assert result["flushes"] == 1 assert result["outcomes"][0] == result["outcomes"][1] assert result["outcomes"][0]["status"] == "planned" assert result["outcomes"][0]["item"]["accepted"] is True def test_search_week_plan_reports_existing_placement_and_partial_assignment_truthfully(): result = run_planner(""" const base={load:async()=>({}),dates:()=>[{date:'2026-08-22',label:'Sat'}], day:date=>({plan_date:date,ids:[],capacity_minutes:120,estimates:{}}),flush:async()=>({})}; const detail={kind:'issue',state:'open',repository:'stackchain/dashboard',number:42,claimable:true}; const existing=createSearchWeekPlan({week:{...base,placement:()=>({date:'2026-08-23',estimate:30}),place:()=>true}, claim:async item=>item,accept:item=>item,identity:()=> 'work'}); const blocked=await existing.plan(detail,{date:'2026-08-22',estimate:45}); const partial=createSearchWeekPlan({week:{...base,placement:()=>null,place:()=>false}, claim:async item=>({...item,assigned_to_me:true,claimable:false}),accept:item=>item,identity:()=> 'work'}); const failed=await partial.plan(detail,{date:'2026-08-22',estimate:45}); console.log(JSON.stringify({blocked,failed})); """) assert result["blocked"] == {"status": "already-planned", "date": "2026-08-23", "estimate": 30} assert result["failed"]["status"] == "assigned-not-planned" def test_search_week_plan_moves_without_duplicates_and_keeps_pending_sync_on_network_failure(): result = run_planner(""" const week={load:async()=>({}),dates:()=>[{date:'2026-08-22',label:'Sat'}], day:date=>({plan_date:date,ids:[],capacity_minutes:120,estimates:{}}), placement:()=>({date:'2026-08-21',estimate:30}),place:()=>true, flush:async()=>{throw new Error('offline');}}; const planner=createSearchWeekPlan({week,claim:async item=>item,accept:item=>item,identity:()=> 'work'}); const outcome=await planner.plan({kind:'issue',state:'open',assigned_to_me:true}, {date:'2026-08-22',estimate:45,move:true}); console.log(JSON.stringify(outcome)); """) assert result == {"status": "planned-pending", "date": "2026-08-22", "estimate": 45}