Inject response loss for Created upload receipts as well as OK

This commit is contained in:
2026-11-09 14:42:55 +08:00
parent 3a71c9ac57
commit bceaf2eca6
+2 -2
View File
@@ -14,7 +14,7 @@ assert.ok(url&&project&&accountId&&workspaceId&&rootId&&token,'requires isolated
const registry=createRegistry(builtinComponents()),basePath=`${url}/projects/${project}/`,calls=[];
let loseNextAck=false,loseNextAssetAck=false;
const rawTransport=createFoundryTransport({basePath,accountId,isCurrentIdentity:()=>true,fetch:async(resource,init)=>{const headers=new Headers(init.headers);headers.set('Authorization',`Bearer ${token}`);return fetch(resource,{...init,headers});}});
const transport=async input=>{calls.push({method:input.method,path:input.path,requestId:input.requestId,body:input.body});const result=await rawTransport(input);if(loseNextAssetAck&&input.method==='POST'&&input.path.endsWith('/assets')&&result.status===200){loseNextAssetAck=false;throw Error('injected upload response loss');}if(loseNextAck&&input.method==='POST'&&result.status===200){loseNextAck=false;throw Error('injected response loss after server commit');}return result;};
const transport=async input=>{calls.push({method:input.method,path:input.path,requestId:input.requestId,body:input.body});const result=await rawTransport(input);if(loseNextAssetAck&&input.method==='POST'&&input.path.endsWith('/assets')&&result.status>=200&&result.status<300){loseNextAssetAck=false;throw Error('injected upload response loss');}if(loseNextAck&&input.method==='POST'&&result.status===200){loseNextAck=false;throw Error('injected response loss after server commit');}return result;};
const path=`workspaces/${workspaceId}/materials/${rootId}/canvas`;
const migration=await loadFoundryModel(transport,workspaceId,rootId,registry);assert.equal(migration.status,'migration-required');
const migrationId=randomUUID(),migrationBody=JSON.stringify({protocol_version:2,request_id:migrationId,expected_versions:Object.fromEntries(migration.sourceVersions)});
@@ -60,7 +60,7 @@ try{
// The upload commits on the real server, then its response is deliberately
// lost. Reopening must query that upload before either image document sends.
loseNextAssetAck=true;const imageId=randomUUID();let imageResource;
const imageResult=engine.stageFiles([{name:'live.png',bytes:new Blob([Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAIAAAASFvFNAAAAEElEQVR4nGP4z8AAQQxwFgBB0gX7h/C5SAAAAABJRU5ErkJggg==','base64')],{type:'image/png'})}],([ref],tx)=>{imageResource=ref;tx.dispatch({type:'element.create',element:{...node(imageId,childId),componentId:'image',data:{title:'Live scoped image',text:'',assetId:ref.id,src:ref.src}}});});assert.equal(imageResult.status,'applied');assert.equal(engine.getSnapshot().document.elements.has(imageId),true);await engine.settle();const upload=[...engine.getSnapshot().runtime.ledger.requests.values()].find(request=>request.kind==='asset.upload');assert.ok(upload);assert.equal(upload.phase,'waiting');const uploadCount=calls.filter(call=>call.method==='POST'&&call.path.endsWith('/assets')).length;
const imageResult=engine.stageFiles([{name:'live.png',bytes:new Blob([Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAIAAAASFvFNAAAAEElEQVR4nGP4z8AAQQxwFgBB0gX7h/C5SAAAAABJRU5ErkJggg==','base64')],{type:'image/png'})}],([ref],tx)=>{imageResource=ref;tx.dispatch({type:'element.create',element:{...node(imageId,childId),componentId:'image',data:{title:'Live scoped image',text:'',assetId:ref.id,src:ref.src}}});});assert.equal(imageResult.status,'applied');assert.equal(engine.getSnapshot().document.elements.has(imageId),true);await engine.settle();const upload=[...engine.getSnapshot().runtime.ledger.requests.values()].find(request=>request.kind==='asset.upload');assert.ok(upload);assert.equal(loseNextAssetAck,false,'successful upload response was not intercepted');assert.equal(upload.phase,'waiting');const uploadCount=calls.filter(call=>call.method==='POST'&&call.path.endsWith('/assets')).length;
const imageCopyId=await engine.imports.duplicate([{id:imageId,incarnation:1}],{viewCanvasId:childId});await engine.settle();const imageCopy=engine.imports.getJobs().get(imageCopyId);assert.equal(imageCopy.status,'completed');const copiedImage=imageCopy.stableIds['element:'+imageId];assert.equal(engine.getSnapshot().document.elements.get(copiedImage).data.assetId,imageResource.id);assert.equal(engine.getSnapshot().document.elements.has(tail.at(-1).id),false);
await engine.close();now+=10_000;engine=await open({document:capture.document,versions:sourceVersions,loading});await engine.settle();assert.equal(engine.getSnapshot().runtime.ledger.requests.get(upload.id).phase,'resolved');assert.equal(engine.getSnapshot().runtime.ledger.requests.get(upload.id).body,upload.body);assert.equal(calls.filter(call=>call.method==='POST'&&call.path.endsWith('/assets')).length,uploadCount);assert.ok(calls.some(call=>call.method==='GET'&&call.path.endsWith('/assets/operations/'+upload.id)));
live=await loadFoundryModel(transport,workspaceId,rootId,registry);assert.equal(live.document.elements.get(imageId).data.assetId,imageResource.id);assert.equal(live.document.elements.get(copiedImage).data.assetId,imageResource.id);