import test from 'node:test'; import assert from 'node:assert/strict'; import * as versionPlan from './version-plan'; import type { VersionPlan } from './version-plan'; function plan(patch: Partial): VersionPlan { return { id: 'plan-1', versionId: 'version-1', type: 'product', title: '产品方案', owner: 'PM', startTime: '2026-06-25T09:00', endTime: '2026-06-25T18:00', status: 'pending', createdAt: '2026-06-25', addedBy: 'PM', ...patch, }; } test('sortPlansNewestFirst places newly created plans before older plans', () => { const sortPlansNewestFirst = (versionPlan as any).sortPlansNewestFirst as undefined | ((plans: VersionPlan[]) => VersionPlan[]); assert.equal(typeof sortPlansNewestFirst, 'function'); const plans = [ plan({ id: 'plan-100', createdAt: '2026-06-26' }), plan({ id: 'manual-old', createdAt: '2026-06-25' }), plan({ id: 'plan-300', createdAt: '2026-06-26' }), ]; const sorted = (sortPlansNewestFirst as (items: VersionPlan[]) => VersionPlan[])(plans); assert.deepEqual(sorted.map((item) => item.id), ['plan-300', 'plan-100', 'manual-old']); assert.deepEqual(plans.map((item) => item.id), ['plan-100', 'manual-old', 'plan-300']); }); test('derives requirement coverage from new records and legacy completed ids', () => { const getRequirementCoverageStatus = (versionPlan as any).getRequirementCoverageStatus as undefined | ((item: VersionPlan, requirementId: string) => string); const getRequirementCoverageSummary = (versionPlan as any).getRequirementCoverageSummary as undefined | ((item: VersionPlan) => { total: number; completed: number; partial: number; notStarted: number; percent: number; }); assert.equal(typeof getRequirementCoverageStatus, 'function'); assert.equal(typeof getRequirementCoverageSummary, 'function'); const item = plan({ linkedRequirementIds: ['r1', 'r2', 'r3', 'r4'], completedRequirementIds: ['r2'], requirementCoverage: [ { requirementId: 'r1', status: 'partial', completedContent: '完成主流程原型', remainingContent: '补充异常状态', updatedAt: '2026-06-29T09:00:00.000Z', updatedBy: 'PM', }, { requirementId: 'r3', status: 'completed', completedContent: '已覆盖列表和详情', updatedAt: '2026-06-29T10:00:00.000Z', updatedBy: 'PM', }, ], } as Partial); assert.equal(getRequirementCoverageStatus!(item, 'r1'), 'partial'); assert.equal(getRequirementCoverageStatus!(item, 'r2'), 'completed'); assert.equal(getRequirementCoverageStatus!(item, 'r4'), 'not_started'); assert.deepEqual(getRequirementCoverageSummary!(item), { total: 4, completed: 2, partial: 1, notStarted: 1, percent: 50, }); }); test('updates requirement coverage, syncs legacy completed ids, and creates a plan log', () => { const updateRequirementCoverage = (versionPlan as any).updateRequirementCoverage as undefined | ((item: VersionPlan, input: { requirementId: string; status: string; completedContent?: string; remainingContent?: string; updatedBy: string; updatedAt: string; requirementCode?: string; requirementTitle?: string; }) => any); assert.equal(typeof updateRequirementCoverage, 'function'); const next = updateRequirementCoverage!(plan({ linkedRequirementIds: ['r1'], completedRequirementIds: ['r1'], }), { requirementId: 'r1', status: 'partial', completedContent: '完成移动端主流程', remainingContent: 'PC 端筛选规则未完成', updatedBy: 'PM', updatedAt: '2026-06-29T12:00:00.000Z', requirementCode: 'QY0001', requirementTitle: '需求池筛选', }); assert.deepEqual(next.completedRequirementIds, []); assert.equal(next.requirementCoverage?.[0]?.status, 'partial'); assert.equal(next.logs?.length, 1); assert.equal(next.logs?.[0]?.type, 'requirement_progress'); assert.equal(next.logs?.[0]?.actor, 'PM'); assert.equal(next.logs?.[0]?.requirementCode, 'QY0001'); });