Certify loaded canvas scopes and publish completeness with runtime queries
This commit is contained in:
@@ -117,3 +117,39 @@ test('Workspace reports a background source failure while preserving the pending
|
||||
const {createWorkspaceRuntime}=await import('../../src/lib/canvas-runtime/workspace.ts'),f=fixture(),reports=[];let current=true;const workspace=createWorkspaceRuntime({accountId:scope.accountId,workspaceId:scope.workspaceId,authEpoch:1,registry,storage:f.storage,isCurrentIdentity:()=>current,onError:error=>reports.push(error),adapters:()=>({persistence:{...f.persistence,async read(){throw Error('offline');}}})});
|
||||
try{const engine=await workspace.openPartial(prefix,versions,state);engine.editor.dispatch({type:'element.data',target,patch:{text:'held while offline'}});await engine.persist();await assert.rejects(workspace.resumeLoading(root),/offline/);assert.equal(reports.length,1);assert.ok(engine.loading.getState());assert.equal(f.calls.length,0);assert.equal(f.storage.image().formatVersion,7);current=false;await assert.rejects(workspace.resumeLoading(root),/identity_changed/);assert.equal(reports.length,1);}finally{await workspace.close();}
|
||||
});
|
||||
|
||||
test('certified scopes distinguish a complete child from an incomplete parent and publish coverage atomically',async()=>{
|
||||
const loading={...state,version:2,canvasElementCounts:{[root]:4,[child]:1}},f=fixture();let engine=await f.open({loading});
|
||||
try{
|
||||
const first=engine.editor.getQueries();assert.equal(first.lookupElement('B').status,'unloaded');assert.equal(first.lookupElement('A').status,'present');assert.equal(first.membersResult(root).complete,false);assert.equal(first.hitNodes(root,{x:50,y:40}).complete,false);
|
||||
const next=createDocument(root,canvases,[a,frame,c]);assert.equal(engine.loading.accept(next,versions,{...loading,receivedElements:3}).status,'applied');
|
||||
const queries=engine.editor.getQueries();assert.equal(queries.membersResult(child).complete,true);assert.deepEqual(queries.membersResult(child).ids,['C']);assert.equal(queries.membersResult(root).complete,false);assert.equal(queries.childCanvasesResult(root).complete,true);assert.equal(queries.incidentResult('C').complete,false);assert.equal(queries.relations.referenceComplete,false);assert.equal(queries.coverage.projectComplete,false);assert.equal(first.lookupElement('C').status,'unloaded');
|
||||
engine.editor.dispatch({type:'element.data',target:{id:'C',incarnation:1},patch:{text:'child edit'}});await engine.persist();assert.equal(f.storage.image().formatVersion,8);assert.equal(f.calls.length,0);decodeRecovery(f.storage.image(),registry);
|
||||
const source=f.storage.image();for(const formatVersion of [1,2,3,4,5,6,7])assert.throws(()=>decodeRecovery({...source,formatVersion},registry),{code:'unsupported_recovery_version'});
|
||||
for(const mutate of [image=>delete image.payload.runtime.loading.canvasElementCounts,image=>image.payload.runtime.loading.version=1,image=>image.payload.runtime.loading.version=99,image=>image.payload.runtime.loading.canvasElementCounts[child]=0]){const image=structuredClone(source);mutate(image);assert.throws(()=>decodeRecovery(image,registry));}
|
||||
await engine.close();engine=await f.open({loading});assert.equal(engine.editor.getQueries().membersResult(child).complete,true);assert.equal(engine.getSnapshot().document.elements.get('C').data.text,'child edit');
|
||||
assert.equal(engine.loading.accept(full,versions,{...loading,receivedElements:5}).status,'applied');const before=engine.getSnapshot(),oldQueries=engine.editor.getQueries();assert.equal(oldQueries.lookupElement('missing').status,'unloaded');assert.equal(oldQueries.membersResult(root).complete,true);
|
||||
assert.equal((await engine.loading.complete(full,versions,{sourceToken:loading.sourceToken})).status,'applied');assert.equal(engine.getSnapshot().revision,before.revision,'coverage-only completion must not invent a document revision');assert.equal(engine.editor.getQueries().lookupElement('missing').status,'absent');assert.equal(engine.editor.getQueries().coverage.projectComplete,true);assert.equal(oldQueries.lookupElement('missing').status,'unloaded');await engine.settle();assert.equal(f.server().elements.get('C').data.text,'child edit');assert.equal(f.storage.image().formatVersion<7,true);
|
||||
}finally{await engine.close();}
|
||||
});
|
||||
test('legacy prefixes do not infer scope completion; a changed count manifest cannot replace the published root',async()=>{
|
||||
const f=fixture(),engine=await f.open();try{
|
||||
assert.equal(engine.loading.accept(full,versions,{...state,receivedElements:5}).status,'applied');assert.equal(engine.editor.getQueries().membersResult(child).complete,false);
|
||||
const before=engine.getSnapshot();assert.equal(engine.loading.accept(full,versions,{...state,version:2,canvasElementCounts:{[root]:4,[child]:1},receivedElements:5}).code,'loading_source_mismatch');assert.strictEqual(engine.getSnapshot(),before);
|
||||
}finally{await engine.close();}
|
||||
});
|
||||
test('zero-member scopes are complete only once their entry dependencies publish',async()=>{
|
||||
const f=fixture(),document=createDocument(root,canvases,[a,frame]),loading={...state,version:2,totalElements:2,receivedElements:2,canvasElementCounts:{[root]:2,[child]:0}},engine=await f.open({document,loading});
|
||||
try{assert.deepEqual(engine.editor.getQueries().membersResult(child),{ids:[],complete:true});assert.equal(engine.editor.getQueries().lookupElement('not-here').status,'unloaded');}finally{await engine.close();}
|
||||
});
|
||||
|
||||
test('v2 admits path edits in a certified connection owner while another scope is incomplete, preserving undo and v8 recovery',async()=>{
|
||||
const f=fixture(),loading={...state,version:2,receivedElements:5,totalElements:6,canvasElementCounts:{[root]:4,[child]:2}},engine=await f.open({document:full,loading}),path={type:'bezier-chain',knots:[{id:'k',mode:'corner',position:{x:50,y:25},in:{x:0,y:0},out:{x:0,y:0}}]};
|
||||
try{
|
||||
assert.equal(engine.editor.getQueries().membersResult(root).complete,true);assert.equal(engine.editor.getQueries().membersResult(child).complete,false);
|
||||
const command={type:'connection.path',target:{id:'E',incarnation:1},path};assert.equal(engine.editor.can(command).enabled,true);assert.equal(engine.editor.dispatch(command).status,'applied');assert.equal(engine.getSnapshot().document.elements.get('E').path.knots[0].position.x,50);
|
||||
assert.equal(engine.editor.history.undo().status,'applied');assert.equal(engine.getSnapshot().document.elements.get('E').path,null);assert.equal(engine.editor.history.redo().status,'applied');await engine.persist();assert.equal(f.calls.length,0);assert.equal(f.storage.image().formatVersion,8);assert.equal(decodeRecovery(f.storage.image(),registry).document.elements.get('E').path.knots[0].position.x,50);
|
||||
const before=engine.getSnapshot();assert.equal(engine.loading.accept(full,versions,{...loading,canvasElementCounts:{[root]:5,[child]:1}}).code,'loading_source_mismatch');assert.strictEqual(engine.getSnapshot(),before);
|
||||
}finally{await engine.close();}
|
||||
const other=fixture(),incomplete=await other.open({document:full,loading:{...loading,canvasElementCounts:{[root]:5,[child]:1}}});try{assert.equal(incomplete.editor.can({type:'connection.path',target:{id:'E',incarnation:1},path}).enabled,false);}finally{await incomplete.close();}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user