import test from 'node:test'; import assert from 'node:assert/strict'; import { buildVersionDataScope, buildVersionDataScopeMap } from './version-data-scope'; import type { Bug } from './bug'; import type { DevTask } from './dev-task'; import type { OvertimeRecord } from './overtime'; import type { Requirement } from './requirement'; import type { TestCase } from './test-case'; import type { VersionPlan } from './version-plan'; function requirement(id: string, versionId: string | undefined): Requirement { return { id, code: id.toUpperCase(), title: `Requirement ${id}`, description: '', productId: 'product-1', projectId: 'project-1', versionId, sourceType: 'internal', sourceTarget: 'product', platforms: ['web'], typeId: 'type-1', status: 'planned', priority: 'P1', effort: 'M', creator: 'PM', createdAt: '2026-07-02T09:00:00.000Z', }; } function plan(id: string, versionId: string, type: VersionPlan['type']): VersionPlan { return { id, versionId, type, title: `Plan ${id}`, owner: 'PM', startTime: '2026-07-02T09:00:00.000Z', endTime: '2026-07-02T18:00:00.000Z', status: 'pending', createdAt: '2026-07-02T09:00:00.000Z', addedBy: 'PM', }; } function devTask(id: string, patch: Partial): DevTask { return { id, taskNo: id.toUpperCase(), versionId: undefined, requirementId: '', title: `Task ${id}`, categoryId: 'frontend', assigneeId: 'Alice', priority: 'P1', expectedStartAt: '', expectedEndAt: '', status: 'todo', isBlocked: false, createdBy: 'PM', createdAt: '2026-07-02T09:00:00.000Z', updatedAt: '2026-07-02T09:00:00.000Z', ...patch, }; } function testCase(id: string, versionId: string): TestCase { return { id, caseNo: id.toUpperCase(), versionId, title: `Case ${id}`, categoryId: 'testing', priority: 'P1', status: 'pending', createdBy: 'QA', createdAt: '2026-07-02T09:00:00.000Z', updatedAt: '2026-07-02T09:00:00.000Z', }; } function bug(id: string, versionId: string): Bug { return { id, bugNo: id.toUpperCase(), versionId, testCaseId: 'tc-1', title: `Bug ${id}`, description: '', severity: 'major', priority: 'P1', reportedBy: 'QA', assigneeId: 'Bob', status: 'open', createdAt: '2026-07-02T09:00:00.000Z', updatedAt: '2026-07-02T09:00:00.000Z', }; } function overtime(id: string, versionId: string): OvertimeRecord { return { id, projectId: 'project-1', versionId, person: 'Alice', startTime: '2026-07-02T19:00:00.000Z', endTime: '2026-07-02T21:00:00.000Z', duration: 2, reasonId: 'reason-4', createdAt: '2026-07-02T21:00:00.000Z', }; } test('buildVersionDataScope scopes version detail data once', () => { const scope = buildVersionDataScope({ versionId: 'version-1', requirements: [requirement('req-1', 'version-1'), requirement('req-2', 'version-2')], plans: [plan('plan-1', 'version-1', 'research'), plan('plan-2', 'version-1', 'product'), plan('plan-3', 'version-2', 'ui')], devTasks: [ devTask('dev-direct', { versionId: 'version-1', requirementId: '' }), devTask('dev-legacy', { requirementId: 'req-1' }), devTask('dev-other', { versionId: 'version-2', requirementId: 'req-2' }), ], testCases: [testCase('tc-1', 'version-1'), testCase('tc-2', 'version-2')], bugs: [bug('bug-1', 'version-1'), bug('bug-2', 'version-2')], overtimeRecords: [overtime('ot-1', 'version-1'), overtime('ot-2', 'version-2')], }); assert.deepEqual(scope.requirementIds, ['req-1']); assert.deepEqual(scope.plans.map((item) => item.id), ['plan-1', 'plan-2']); assert.deepEqual(scope.plansByType.research.map((item) => item.id), ['plan-1']); assert.deepEqual(scope.plansByType.product.map((item) => item.id), ['plan-2']); assert.deepEqual(scope.devTasks.map((item) => item.id), ['dev-direct', 'dev-legacy']); assert.deepEqual(scope.testCases.map((item) => item.id), ['tc-1']); assert.deepEqual(scope.bugs.map((item) => item.id), ['bug-1']); assert.deepEqual(scope.overtimeRecords.map((item) => item.id), ['ot-1']); }); test('buildVersionDataScopeMap indexes many versions in one pass', () => { const scopeMap = buildVersionDataScopeMap({ versionIds: ['version-1', 'version-2', 'version-empty'], requirements: [ requirement('req-1', 'version-1'), requirement('req-2', 'version-2'), requirement('req-outside', 'version-3'), ], plans: [ plan('plan-1', 'version-1', 'research'), plan('plan-2', 'version-2', 'ui'), plan('plan-outside', 'version-3', 'product'), ], devTasks: [ devTask('dev-direct', { versionId: 'version-2', requirementId: 'req-1' }), devTask('dev-legacy', { requirementId: 'req-1' }), devTask('dev-outside', { requirementId: 'req-outside' }), ], testCases: [testCase('tc-1', 'version-1'), testCase('tc-2', 'version-2'), testCase('tc-outside', 'version-3')], bugs: [bug('bug-1', 'version-1'), bug('bug-2', 'version-2'), bug('bug-outside', 'version-3')], overtimeRecords: [overtime('ot-1', 'version-1'), overtime('ot-2', 'version-2'), overtime('ot-outside', 'version-3')], }); assert.deepEqual(scopeMap['version-1'].requirements.map((item) => item.id), ['req-1']); assert.deepEqual(scopeMap['version-1'].devTasks.map((item) => item.id), ['dev-legacy']); assert.deepEqual(scopeMap['version-2'].requirements.map((item) => item.id), ['req-2']); assert.deepEqual(scopeMap['version-2'].devTasks.map((item) => item.id), ['dev-direct']); assert.deepEqual(scopeMap['version-2'].plansByType.ui.map((item) => item.id), ['plan-2']); assert.deepEqual(scopeMap['version-empty'].requirements, []); assert.deepEqual(Object.keys(scopeMap).sort(), ['version-1', 'version-2', 'version-empty']); });