Files
Agent-System/web/src/lib/postTaskDeliverableSync.test.ts
T
weichenandCursor 7466a15319 fix(docgov): harden UI-03 sync poll, reconnectRunId, and E2E status attrs
Use deliverablesRef + limited poll for post-task server docs; pass
reconnectRunId across auto-retry; GetRun reads idempotency_key;
StartClaimedRun fails closed on UpdateRun; MessageBubble/E2E scope
this-turn assistant; correct audit retry/schema19 wording.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-13 13:54:49 +00:00

156 lines
4.3 KiB
TypeScript

import test from 'node:test';
import assert from 'node:assert/strict';
import {
resolvePostTaskActiveDocId,
shouldSwitchTemplateSlotToServer,
pollUntilPostTaskServerDoc,
} from './postTaskDeliverableSync';
import type { DeliverableItem } from '../types/workbench';
import type { WorkflowDocumentSummary } from '../api/projectDocuments';
const base = (over: Partial<DeliverableItem>): DeliverableItem => ({
docId: 'doc-x',
projectId: 6089,
taskKey: 'intake',
title: '用户初始需求收集表',
stage: 'V0.1',
category: 'workflow',
creatorId: 1,
creatorName: 'A',
isFixedWorkflow: true,
isTemplateOnly: true,
lifecycleState: 'drafting',
isClaimed: false,
author: '',
reviewer: '',
approver: '',
updatedAt: '',
governanceMode: 'controlled',
...over,
});
test('resolvePostTaskActiveDocId prefers server doc_proj for taskKey', () => {
const docs = [
base({ docId: 'doc-p6089-intake', isTemplateOnly: true }),
base({
docId: 'doc_proj6089_intake',
isTemplateOnly: false,
title: '用户初始需求收集表',
}),
];
assert.equal(
resolvePostTaskActiveDocId({ deliverables: docs, projectId: 6089, taskKey: 'intake' }),
'doc_proj6089_intake',
);
});
test('resolvePostTaskActiveDocId honors preferred server doc id', () => {
const docs = [
base({ docId: 'doc_proj6089_intake', isTemplateOnly: false }),
base({
docId: 'doc_proj6089_user_requirements',
taskKey: 'user_requirements',
isTemplateOnly: false,
}),
];
assert.equal(
resolvePostTaskActiveDocId({
deliverables: docs,
projectId: 6089,
taskKey: 'intake',
preferredDocId: 'doc_proj6089_intake',
}),
'doc_proj6089_intake',
);
});
test('shouldSwitchTemplateSlotToServer when active is local template slot', () => {
const docs = [
base({ docId: 'doc-p6089-intake', isTemplateOnly: true }),
base({ docId: 'doc_proj6089_intake', isTemplateOnly: false }),
];
assert.equal(
shouldSwitchTemplateSlotToServer({
activeDocId: 'doc-p6089-intake',
deliverables: docs,
projectId: 6089,
taskKey: 'intake',
serverDocId: 'doc_proj6089_intake',
}),
true,
);
assert.equal(
shouldSwitchTemplateSlotToServer({
activeDocId: 'doc_proj6089_intake',
deliverables: docs,
projectId: 6089,
taskKey: 'intake',
serverDocId: 'doc_proj6089_intake',
}),
false,
);
});
test('pollUntilPostTaskServerDoc retries until same taskKey server doc appears', async () => {
const local = [base({ docId: 'doc-p6089-intake', isTemplateOnly: true })];
let calls = 0;
const snaps: DeliverableItem[][] = [];
const result = await pollUntilPostTaskServerDoc({
listDocs: async () => {
calls += 1;
if (calls < 3) return [];
return [
{
doc_id: 'doc_proj6089_intake',
project_id: 6089,
task_key: 'intake',
title: '用户初始需求收集表',
lifecycle_state: 'drafting',
is_template_only: false,
} satisfies WorkflowDocumentSummary,
];
},
getDeliverables: () => local,
merge: (prev, docs) => {
if (docs.length === 0) return prev;
return [
...prev.filter((d) => d.docId !== 'doc_proj6089_intake'),
base({
docId: docs[0].doc_id,
isTemplateOnly: false,
taskKey: docs[0].task_key,
}),
];
},
projectId: 6089,
taskKey: 'intake',
maxAttempts: 5,
delayMs: 1,
sleep: async () => undefined,
onMerged: (merged) => {
snaps.push(merged);
},
});
assert.equal(result.serverDocId, 'doc_proj6089_intake');
assert.equal(result.attempts, 3);
assert.ok(snaps.length >= 3);
assert.ok(snaps[snaps.length - 1].some((d) => d.docId === 'doc_proj6089_intake'));
});
test('pollUntilPostTaskServerDoc returns null after maxAttempts without relying on setState', async () => {
const local = [base({ docId: 'doc-p6089-intake', isTemplateOnly: true })];
const result = await pollUntilPostTaskServerDoc({
listDocs: async () => [],
getDeliverables: () => local,
merge: (prev) => prev,
projectId: 6089,
taskKey: 'intake',
maxAttempts: 3,
delayMs: 1,
sleep: async () => undefined,
});
assert.equal(result.serverDocId, null);
assert.equal(result.attempts, 3);
assert.equal(result.merged[0]?.docId, 'doc-p6089-intake');
});