diff --git a/apps/waterfall-wiki/tests/canvas-integration.ts b/apps/waterfall-wiki/tests/canvas-integration.ts index bd4325a..55e1ddf 100644 --- a/apps/waterfall-wiki/tests/canvas-integration.ts +++ b/apps/waterfall-wiki/tests/canvas-integration.ts @@ -12,15 +12,20 @@ export async function verifyCanvasWorkspace(base:string){ const root='/'+canvas.id; assert.equal((await call(root,'GET',undefined,identities[1])).status,404); assert.equal((await call(root,'PUT',{revision:1,document:doc},{...identities[0],origin:'https://evil.example'})).status,403); + assert.equal((await call(root,'PUT',{revision:1,document:doc},{...identities[0],authorization:'Bearer invalid',origin:'https://evil.example'})).status,404,'A bearer request cannot fall back to an owner cookie'); + assert.equal((await call(root,'PUT',{revision:1,document:{...doc,links:[['note','script'],['script','note']]}},identities[0])).status,400,'Imported cyclic graphs are rejected'); const linked=await call(root+'/link','POST',{},identities[0]);assert.equal(linked.status,201);const credentials=await linked.json();const agent={authorization:'Bearer '+credentials.token}; assert.equal((await call('','GET',undefined,agent)).status,401); const other=await (await call('','POST',{document:doc},identities[0])).json();assert.equal((await call('/'+other.id,'GET',undefined,agent)).status,404,'A link cannot read another canvas'); assert.equal((await call(root,'PUT',{revision:1,document:doc},identities[0])).status,200); assert.equal((await call(root,'PUT',{revision:1,document:doc},identities[0])).status,409); async function queue(rev:number){const r=await call(root+'/tasks','POST',{revision:rev,targetId:'script'},identities[0]);assert.equal(r.status,201,await r.clone().text());return (await r.json()).id as string} + const concurrent=await Promise.all([0,1].map(()=>call('/'+other.id,'PUT',{revision:1,document:doc},identities[0]))); + assert.deepEqual(concurrent.map(r=>r.status).sort(),[200,409],'Concurrent saves cannot lose an update'); const first=await queue(2); assert.equal((await call(root+'/tasks','POST',{revision:2,targetId:'script'},identities[0])).status,409,'Only one live task per target'); const recipe=await (await call(root+'/tasks/'+first,'GET',undefined,agent)).json();assert.equal(recipe.request.references[0].text,doc.cards[0].text);assert.equal(recipe.request.instruction,'写六个分镜'); + assert.equal((await call(root+'/tasks/'+first,'GET',undefined,identities[1])).status,404); const result={status:'succeeded',result:{text:'第一镜:城市醒来。'}}; assert.equal((await call(root+'/tasks/'+first,'PATCH',result,agent)).status,409,'Result cannot arrive before claim'); assert.equal((await call(root+'/tasks/'+first,'PATCH',{status:'running'},agent)).status,200); @@ -28,6 +33,8 @@ export async function verifyCanvasWorkspace(base:string){ assert.equal((await call(root+'/tasks/'+first,'PATCH',result,agent)).status,409,'Stale result must not overwrite newer user edits'); assert.equal((await call(root+'/tasks/'+first,'PATCH',{status:'failed',error:'依据新版本重新派发'},identities[0])).status,200); const second=await queue(3);assert.equal((await call(root+'/tasks/'+second,'PATCH',{status:'running'},agent)).status,200); + assert.equal((await call(root+'/tasks/'+second,'PATCH',{status:'succeeded',result:{text:{invalid:true}}},agent)).status,400,'Malformed results do not mutate the canvas'); + assert.equal((await call(root+'/tasks/'+second,'PATCH',{status:'succeeded',result:{image:'data:image/png;base64,AAAA'}},agent)).status,400,'A script node cannot accept an image result'); assert.equal((await call(root+'/tasks/'+second,'PATCH',result,agent)).status,200); assert.equal((await call(root+'/tasks/'+second,'PATCH',result,agent)).status,200,'Identical completion is idempotent'); assert.equal((await call(root+'/tasks/'+second,'PATCH',{status:'succeeded',result:{text:'覆盖'}},agent)).status,409);