feat: queue offline Today-to-Week moves (Closes #1233)
This commit is contained in:
1 parent
c87d56c939
commit
d63765a9b1
3 files changed
+130
-33
No files matched your search
@@ -122,8 +122,9 @@ console.log(JSON.stringify({opened,confirmed,events,keys:[...values.keys()],reco
|
||||
assert result["events"][2][0] == "week"
|
||||
assert result["events"][2][1]["days"][0]["ids"] == ["active"]
|
||||
assert result["keys"] == ["stackchain.today-week-reschedule.v1.timmy"]
|
||||
assert result["record"]["body"]["operation_id"] == "move-active"
|
||||
assert result["record"]["queued_at"] == 123
|
||||
assert result["record"]["version"] == 2
|
||||
assert result["record"]["records"][0]["body"]["operation_id"] == "move-active"
|
||||
assert result["record"]["records"][0]["queued_at"] == 123
|
||||
|
||||
|
||||
def test_reschedule_controller_restores_and_delivers_same_operation_after_reload():
|
||||
@@ -175,6 +176,65 @@ console.log(JSON.stringify({result,events,calls,pending:controller.pending()}));
|
||||
assert result["pending"]["body"]["operation_id"] == "stable-operation"
|
||||
|
||||
|
||||
def test_reschedule_controller_queues_two_offline_moves_without_replacing_the_first():
|
||||
result = run_controller("""
|
||||
const values=new Map();
|
||||
const storage={getItem:key=>values.get(key)||null,setItem:(key,value)=>values.set(key,value),removeItem:key=>values.delete(key)};
|
||||
let today={revision:4,ids:['active','other'],capacity_minutes:120,estimates:{active:45,other:20}};
|
||||
let weekState={revision:7,timezone:'UTC',offline_snapshot:true,days:[
|
||||
{plan_date:'2026-08-25',ids:[],capacity_minutes:90,estimates:{}}
|
||||
]};
|
||||
const confirmedWeek=JSON.parse(JSON.stringify(weekState));let loads=0;
|
||||
const week={load:async()=>{loads++;weekState=JSON.parse(JSON.stringify(confirmedWeek));return weekState;},state:()=>weekState,review:()=>({days:[
|
||||
{...weekState.days[0],label:'Tue',planned_minutes:Object.values(weekState.days[0].estimates).reduce((a,b)=>a+b,0)}
|
||||
]}),adopt:value=>{weekState={...value,offline_snapshot:true};}};
|
||||
const api=async()=>{const error=new Error('offline');error.status=0;throw error;};
|
||||
let n=0;
|
||||
const controller=createReschedule({week,api,getToday:()=>today,adoptToday:value=>{today=value;},
|
||||
storage,getLogin:()=> 'timmy',operationId:()=> `move-${++n}`,now:()=>100+n});
|
||||
await controller.open('active');
|
||||
await controller.confirm('2026-08-25',45);
|
||||
await controller.open('other');
|
||||
const confirmed=await controller.confirm('2026-08-25',20);
|
||||
const stored=JSON.parse([...values.values()][0]);
|
||||
console.log(JSON.stringify({confirmed,stored,today,weekState,loads}));
|
||||
""")
|
||||
|
||||
assert [entry["body"]["operation_id"] for entry in result["stored"]["records"]] == [
|
||||
"move-1",
|
||||
"move-2",
|
||||
]
|
||||
assert result["confirmed"]["pending_count"] == 2
|
||||
assert result["today"]["ids"] == []
|
||||
assert result["weekState"]["days"][0]["ids"] == ["active", "other"]
|
||||
assert result["loads"] == 1
|
||||
|
||||
|
||||
def test_reschedule_controller_drains_fifo_and_rebases_each_next_move():
|
||||
result = run_controller("""
|
||||
const key='stackchain.today-week-reschedule.v1.timmy';
|
||||
const make=(id,identity,today,week)=>({queued_at:1,body:{operation_id:id,identity,estimate_minutes:20,
|
||||
plan_date:'2026-08-25',today_revision:today,week_revision:week,allow_over_capacity:false},
|
||||
today:{revision:today,ids:[]},week:{revision:week,days:[]}});
|
||||
const values=new Map([[key,JSON.stringify({version:2,records:[make('move-1','one',4,7),make('move-2','two',4,7)]})]]);
|
||||
const storage={getItem:key=>values.get(key)||null,setItem:(key,value)=>values.set(key,value),removeItem:key=>values.delete(key)};
|
||||
const requests=[];let active=0,maxActive=0;
|
||||
const api=async(_url,options)=>{active++;maxActive=Math.max(maxActive,active);
|
||||
const body=JSON.parse(options.body);requests.push(body);await new Promise(resolve=>setTimeout(resolve,1));active--;
|
||||
const step=requests.length;return {today:{revision:4+step,ids:[]},week:{revision:7+step,days:[]}};};
|
||||
const controller=createReschedule({week:{adopt:()=>{}},api,adoptToday:()=>{},storage,getLogin:()=> 'timmy'});
|
||||
const delivered=await controller.flush();
|
||||
console.log(JSON.stringify({requests,maxActive,delivered,remaining:values.size}));
|
||||
""")
|
||||
|
||||
assert [request["operation_id"] for request in result["requests"]] == ["move-1", "move-2"]
|
||||
assert result["requests"][1]["today_revision"] == 5
|
||||
assert result["requests"][1]["week_revision"] == 8
|
||||
assert result["maxActive"] == 1
|
||||
assert result["delivered"]["pending_count"] == 0
|
||||
assert result["remaining"] == 0
|
||||
|
||||
|
||||
def test_reschedule_controller_restores_server_truth_and_keeps_intent_on_conflict():
|
||||
result = run_controller("""
|
||||
const key='stackchain.today-week-reschedule.v1.timmy';
|
||||
|
||||
Reference in new issue
Block a user