test(canvas): verify crash cuts and pending image replacement lifetimes

This commit is contained in:
2026-11-11 19:05:33 +08:00
parent dcbbdad25d
commit d3eef78431
4 changed files with 126 additions and 6 deletions
+15
View File
@@ -86,6 +86,21 @@ test('closing a view keeps jobs alive; closing/reopening the engine restores his
await recovered.engine.close();
});
test('create applied with a lost ACK followed by delete recovers the original request and leaves no server element',async()=>{
const storage=storageFixture(),port=persistenceFixture(),submit=port.submit;let lost=true,now=1000,next=0;
port.submit=async request=>{const status=await submit(request);if(lost){lost=false;return{outcome:'unresolved'};}return status;};
const options={clock:{now:()=>now,setTimer:()=>0,clearTimer(){}},createId:()=>`lifetime-${++next}`};let{engine}=await open(storage,port,options);
try{
const element={...original.elements.get('a'),id:'temporary'},created=engine.editor.dispatch({type:'element.create',element});await engine.settle();
const request=[...engine.getSnapshot().runtime.ledger.requests.values()][0];assert.equal(request.outcome,'unresolved');assert.equal(port.receipts.get(request.id).document.elements.has(element.id),true);
const removed=engine.editor.dispatch({type:'element.remove',target:{id:element.id,incarnation:1}});await engine.settle();assert.equal(port.calls.length,1);assert.equal(engine.getSnapshot().runtime.ledger.operations.get(removed.operationIds[0]).phase,'pending');assert.ok(engine.getSnapshot().runtime.ledger.operations.get(removed.operationIds[0]).intent.dependencies.includes(created.operationIds[0]));
await engine.close();now=10000;({engine}=await open(storage,port,options));await engine.settle();
assert.deepEqual(port.calls.map(([kind])=>kind),['submit','query','submit']);assert.equal(port.calls[1][1].id,request.id);assert.equal(port.calls[1][1].body,request.body);assert.notEqual(port.calls[2][1].id,request.id);
const final=port.receipts.get(port.calls[2][1].id).document;assert.equal(final.elements.has(element.id),false);assert.equal(final.tombstones.get(element.id),1);assert.equal(engine.getSnapshot().document.elements.has(element.id),false);assert.equal(engine.getSnapshot().history.undo.length,2);
for(const id of[...created.operationIds,...removed.operationIds])assert.equal(engine.getSnapshot().runtime.ledger.operations.get(id).phase,'resolved');
}finally{await engine.close();}
});
test('failed local durability preserves editing, prevents sends, and failed close retains the recovery leader', async () => {
const storage = storageFixture(), port = persistenceFixture(), { engine, errors } = await open(storage, port);
await engine.persist(); storage.broken = true;