test(docgov): cover canonical task key collapse for project documents

锁定「7 份旧键本地克隆加 8 行服务端」收成 7 条 bd_*,以及列表与物化不再留下 intake 兄弟行。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
weichen
2026-09-21 06:37:10 +00:00
co-authored by Cursor
parent 51d4390bd6
commit 50c19aaee9
3 changed files with 131 additions and 0 deletions
@@ -277,4 +277,71 @@ describe('mergeWorkflowDocumentsFromServer', () => {
expect(merged).toHaveLength(1);
expect(merged[0].docId).toBe('doc_proj6001_intake');
});
it('collapses seven legacy local clones and a coexisting intake row into seven bd_ docs', () => {
const legacy = [
['intake', '用户初始需求收集表'],
['research', '调研报告(竞品/技术/专利/市场/对比/法规)'],
['feasibility_reg', '注册路径可行性评估报告(法规细则)'],
['feasibility_dev', '设计开发可行性评估报告(研发细则)'],
['user_requirements', '用户需求规格说明书(URS 长标题)'],
['prelim_design', '初步设计说明书(长标题)'],
['cdo_quote', 'CDO报价核算表(长标题)'],
] as const;
const prev: DeliverableItem[] = legacy.map(([taskKey, title]) => ({
docId: `doc-p6579-${taskKey}`,
projectId: 6579,
taskKey,
title,
stage: 'V0.5',
category: 'workflow',
creatorId: 398,
creatorName: '陈威',
isFixedWorkflow: true,
isTemplateOnly: true,
lifecycleState: 'drafting',
isClaimed: false,
author: '',
reviewer: '',
approver: '',
updatedAt: '2026-09-21',
governanceMode: 'controlled',
}));
prev.push({
...prev[0],
docId: 'doc-p99-intake',
projectId: 99,
title: '未激活项目的唯一模板',
});
const server = legacy.map(([taskKey, title]) => ({
doc_id: `doc_proj6579_bd_${taskKey}`,
project_id: 6579,
task_key: `bd_${taskKey}`,
title: title.split('(')[0],
lifecycle_state: 'drafting' as const,
is_template_only: true,
}));
server.push({
doc_id: 'doc_proj6579_intake',
project_id: 6579,
task_key: 'intake',
title: '用户初始需求收集表',
lifecycle_state: 'drafting',
is_template_only: false,
});
const merged = mergeWorkflowDocumentsFromServer(prev, server, 6579, { id: 398, name: '陈威' });
const projectDocs = merged.filter((item) => item.projectId === 6579);
expect(projectDocs).toHaveLength(7);
expect(projectDocs.map((item) => item.taskKey).sort()).toEqual(
legacy.map(([taskKey]) => `bd_${taskKey}`).sort(),
);
expect(new Set(projectDocs.map((item) => item.docId)).size).toBe(7);
expect(projectDocs.some((item) => item.taskKey === 'intake' || item.taskKey === 'research')).toBe(false);
expect(projectDocs.some((item) => item.title.includes('竞品'))).toBe(false);
const intake = projectDocs.find((item) => item.taskKey === 'bd_intake');
expect(intake?.docId).toBe('doc_proj6579_bd_intake');
expect(merged.find((item) => item.projectId === 99)?.docId).toBe('doc-p99-intake');
});
});