gongxiangp2/apps/detail-agent-pi/test/workflow-save-wait.test.mjs

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);
});