64 lines
2.7 KiB
JavaScript
64 lines
2.7 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { executeWorkflow } from '../src/workflow-execute.mjs';
|
|
|
|
const args = {version:1,intent:'generate_candidate',outputId:'detail',slotId:'hero',productVariantId:'walnut'};
|
|
function setup() {
|
|
const calls = [];
|
|
const state = {revision:17,conversationActive:true,writeBlocked:false};
|
|
let accepted = 0;
|
|
let attempts = 0;
|
|
const host = {turnEpoch:1,canvas:{run:'run',documentId:7,
|
|
async read() { return {...state}; },
|
|
async workflowExecute(input) {
|
|
calls.push(input.operationId);
|
|
if (attempts++ < 2) return {operationId:input.operationId,phase:'waiting_save',dispatched:false,revision:17};
|
|
accepted++;
|
|
return {operationId:input.operationId,phase:'submitted'};
|
|
},
|
|
async workflowPoll(operationId) { return {operationId,phase:'complete'}; }
|
|
},store:{async appendEvent() {}},async readWorkflow() { return {version:1}; }};
|
|
return {host,calls,state,accepted:()=>accepted};
|
|
}
|
|
|
|
test('normal saves defer one candidate intent until its original save acknowledgement',async()=>{
|
|
const x = setup();
|
|
const result = await executeWorkflow(x.host,args,undefined,{pause:async()=>{}});
|
|
assert.equal(result.operation.phase,'complete');
|
|
assert.equal(new Set(x.calls).size,1);
|
|
assert.equal(x.calls.length,3);
|
|
assert.equal(x.accepted(),1);
|
|
assert.equal(x.host.nativeUnresolved,undefined);
|
|
});
|
|
|
|
test('handover, revision drift and save failure stop a waiting intent before dispatch',async()=>{
|
|
for (const change of [state=>state.writeBlocked=true,state=>state.revision++,state=>state.error='本机保存失败']) {
|
|
const x = setup();
|
|
await assert.rejects(executeWorkflow(x.host,args,undefined,{pause:async()=>change(x.state)}));
|
|
assert.equal(x.calls.length,1);
|
|
assert.equal(x.accepted(),0);
|
|
assert.equal(x.host.nativeUnresolved,undefined);
|
|
}
|
|
});
|
|
|
|
test('cancelling or replacing a turn while saving never creates a candidate',async()=>{
|
|
for (const action of ['cancel','replace']) {
|
|
const x = setup();
|
|
const controller = new AbortController();
|
|
await assert.rejects(executeWorkflow(x.host,args,controller.signal,{pause:async()=>{
|
|
if (action==='cancel') controller.abort(); else x.host.turnEpoch++;
|
|
}}));
|
|
assert.equal(x.calls.length,1);
|
|
assert.equal(x.accepted(),0);
|
|
assert.equal(x.host.nativeUnresolved,undefined);
|
|
}
|
|
});
|
|
|
|
test('a save wait timeout is known not dispatched and cannot become an unknown image request',async()=>{
|
|
const x = setup();
|
|
await assert.rejects(executeWorkflow(x.host,args,undefined,{timeoutMs:0}),/仍在保存/);
|
|
assert.equal(x.calls.length,1);
|
|
assert.equal(x.accepted(),0);
|
|
assert.equal(x.host.nativeUnresolved,undefined);
|
|
});
|