docs(tests): clarify import loading and package validation boundaries
This commit is contained in:
@@ -123,7 +123,8 @@ test('permitted command cannot hide a reference write inside a feature handler',
|
||||
const customRegistry=createRegistry([...builtinComponents(),custom]),record={...a,componentId:custom.id,data:{title:'A',link:'old'}},document=createDocument(root,[canvases[0]],[record]),f=fixture(),engine=await f.open({registry:customRegistry,document});
|
||||
try{const before=engine.getSnapshot();assert.equal(engine.editor.dispatch({type:'element.data',target,patch:{title:'allowed title'}}).code,'model_loading');assert.strictEqual(engine.getSnapshot(),before);assert.equal(f.calls.length,0);}finally{await engine.close();}
|
||||
});
|
||||
// 未来 loading 解释器、错误 received 数、空声明范围或伪造 frozen 请求均在恢复校验拒绝。
|
||||
// 把 v1 标记改为 v2 却不补计数证书、错误 received 数、空声明范围或伪造 frozen 请求,均在恢复校验拒绝。
|
||||
// v2 本身是支持的解释器;第一种损坏验证升级后的必需字段,不能把它解释为“不支持未来版本”。
|
||||
test('future loading interpreters, changed capture counts and forged frozen requests fail closed on recovery',async()=>{
|
||||
const f=fixture(),engine=await f.open();try{await engine.persist();const source=f.storage.image();
|
||||
for(const mutate of [image=>image.payload.runtime.loading.version=2,image=>image.payload.runtime.loading.receivedElements=99,image=>image.payload.runtime.loading.declaredCanvasIds=[],image=>image.payload.runtime.ledger.requests.push(['fake',{}])]){const image=structuredClone(source);mutate(image);assert.throws(()=>decodeRecovery(image,registry));}
|
||||
@@ -209,6 +210,7 @@ test('v2 admits path edits in a certified connection owner while another scope i
|
||||
});
|
||||
|
||||
// v3 完整 child 的修改可先同步,root 编辑继续等待;原流捕获版本和新 ACK 分开,旧后缀/最终完成不覆盖已确认 child 编辑。
|
||||
// loadingRead 保留捕获时的 child=1,用来核对后续源片段;runtime.versions 的 child=2 用于下一次写入,二者不能相互覆盖。
|
||||
test('v3 sends a certified child before the parent tail, retains its ACK through old chunks and completes the original source',async()=>{
|
||||
const f=fixture(),document=createDocument(root,canvases,[a,frame,c]),loading={...state,version:3,receivedElements:3,canvasElementCounts:{[root]:4,[child]:1}},engine=await f.open({document,loading});
|
||||
try{
|
||||
@@ -338,7 +340,8 @@ test('v5 copy rejects an incomplete ancestor or a missing declared child before
|
||||
// v5 无关第三 canvas 可继续加载,只要被复制子树及上级入边范围闭合;暂停/重开保留去重映射与 C 的单副本,不吸入未加载 V。
|
||||
test('v5 deep copy includes upper incoming edges and a closed subtree while unrelated canvas contents are still loading',async()=>{
|
||||
const other='10000000-0000-4000-8000-000000000029',g={...node('G'),componentId:'frame'},incoming={...edge,source:{elementId:'A'},target:{elementId:'F'}},topology=[...canvases,{id:other,parent:{canvasId:root,entryElementId:'G'}}],server=createDocument(root,topology,[a,frame,g,incoming,c,node('U',other),node('V',other)]),document=createDocument(root,topology,[a,frame,g,incoming,c,node('U',other)]),f=fixture(server),serverVersions=new Map([[root,1],[child,1],[other,1]]),loading={version:5,sourceToken:'closed-copy',declaredCanvasIds:[root,child,other],sourceCanvases:topology,receivedElements:6,totalElements:7,canvasElementCounts:{[root]:4,[child]:1,[other]:2},elementOwners:Object.fromEntries([...server.elements.values()].map(element=>[element.id,element.ownerCanvasId]))};
|
||||
// The fixture's initial version vector follows the server's active scopes.
|
||||
// 此场景多一个 other canvas,打开时显式提供三范围版本向量,不能沿用默认仅 root/child 的 versions。
|
||||
// 同选 F 和其后代 C 时复用同一映射;上级 A→F 保留并新增 A→F′,未加载的无关 V 不进入复制范围。
|
||||
let engine=await f.open({document,versions:serverVersions,loading});
|
||||
try{const selection=[{id:'F',incarnation:1},{id:'C',incarnation:1}];assert.equal(engine.imports.canDuplicate(selection,{viewCanvasId:root}).enabled,true);const id=await engine.imports.duplicate(selection,{viewCanvasId:root});await engine.imports.pause(id);await engine.settle();assert.equal(engine.imports.getJobs().get(id).status,'paused');decodeRecovery(f.storage.image(),registry);await engine.close();engine=await f.open({document,versions:serverVersions,loading});await engine.imports.resume(id);await engine.settle();const job=engine.imports.getJobs().get(id);assert.equal(job.status,'completed',JSON.stringify({job,errors:f.errors}));const copied=job.stableIds['element:F'],copiedChild=job.stableIds['element:C'];assert.ok(copied&&copiedChild);assert.equal([...engine.getSnapshot().document.elements.values()].filter(element=>element.id===copiedChild).length,1);assert.ok([...engine.getSnapshot().document.elements.values()].some(element=>element.structure==='connection'&&element.source.elementId==='A'&&element.target.elementId===copied));assert.ok([...engine.getSnapshot().document.elements.values()].some(element=>element.id==='E'&&element.target.elementId==='F'));assert.equal(engine.getSnapshot().document.elements.has('V'),false);assert.equal(engine.loading.getState().version,5);assert.deepEqual(job.source.loadingAdmission.requiredCanvasIds,[root,child].sort());assert.equal(f.errors.length,0);decodeRecovery(f.storage.image(),registry);
|
||||
}finally{await engine.close();}
|
||||
@@ -382,7 +385,8 @@ function assetPort(f){const uploads=[],receipts=new Map();let lose=false;const a
|
||||
lose(){lose=true;}};}
|
||||
// 在 stageFiles 的同步事务内创建图片,返回资源句柄与提交结果,owner 可切换以验证范围拒绝。
|
||||
function stageLoadingImage(engine,owner=child,id='partial-image'){let ref;const result=engine.stageFiles([{name:'picture.png',bytes:new Blob(['image bytes'],{type:'image/png'})}],([resource],tx)=>{ref=resource;tx.dispatch({type:'element.create',element:{...node(id,owner),componentId:'image',data:{title:'Picture',text:'',assetId:ref.id,src:ref.src}}});});return{result,ref};}
|
||||
// v6 图片立即显示,完整 child 允许持久化后上传并同步,未完成 root 拒绝;恢复校验准入证据,undo/redo 不重复上传,旧源完成保留资源。
|
||||
// v6 图片立即进入工作文档,完整 child 允许持久化后上传并同步,未完成 root 拒绝;恢复校验准入证据,undo/redo 不重复上传,旧源完成保留资源。
|
||||
// 此例检查模型和上传顺序,不挂载图片 renderer;后面的配额失败场景另用 displayResource 读取本地 Blob。
|
||||
test('v6 displays, persists and uploads a scoped image before another owner tail; undo/redo and old completion preserve its resource',async()=>{
|
||||
const f=fixture(),port=assetPort(f),document=createDocument(root,canvases,[a,frame,c]),engine=await f.open({document,loading:assetLoading,assets:port.assets});try{assert.equal(engine.canStageFiles(child).enabled,true);assert.equal(engine.canStageFiles(root).enabled,false);const before=engine.getSnapshot();assert.equal(stageLoadingImage(engine,root,'denied-image').result.status,'rejected');assert.strictEqual(engine.getSnapshot(),before);assert.equal(f.storage.files.size,0);
|
||||
const {result,ref}=stageLoadingImage(engine);assert.equal(result.status,'applied');assert.equal(engine.getSnapshot().document.elements.get('partial-image').data.assetId,ref.id);assert.equal(port.uploads.length,0);await engine.settle();assert.equal(port.uploads.length,1);assert.equal(f.server().elements.get('partial-image').data.assetId,ref.id);assert.equal(f.storage.image().formatVersion,12);decodeRecovery(f.storage.image(),registry);for(const mutate of [image=>image.payload.runtime.resources[0][1].loadingAdmission.sourceToken='other-source',image=>image.payload.runtime.resources[0][1].loadingAdmission.canvasIds=[root],image=>image.payload.runtime.resources[0][1].loadingAdmission.sourceURL='https://wrong.example/',image=>image.payload.runtime.resources[0][1].loadingAdmission.version=2,image=>{const row=image.payload.runtime.ledger.requests.find(([,row])=>row.kind==='asset.upload')[1];const body=JSON.parse(row.body);body.name='changed';row.body=JSON.stringify(body);},image=>image.formatVersion=11]){const image=f.storage.image();mutate(image);assert.throws(()=>decodeRecovery(image,registry));}assert.equal(engine.loading.getState().version,6);assert.equal(engine.editor.history.undo().status,'applied');await engine.settle();assert.equal(f.server().elements.has('partial-image'),false);assert.equal(engine.editor.history.redo().status,'applied');await engine.settle();assert.equal(f.server().elements.get('partial-image').incarnation,2);assert.equal(port.uploads.length,1);assert.equal((await engine.loading.complete(full,versions,{sourceToken:state.sourceToken})).status,'applied');await engine.settle();assert.equal(engine.getSnapshot().runtime.resources.get(ref.id).stored.id,ref.id);assert.equal(engine.getSnapshot().document.elements.get('partial-image').incarnation,2);assert.equal(f.errors.length,0);
|
||||
|
||||
Reference in New Issue
Block a user