Files
ftb-project-management/apps/web/lib/xiaobao-risk-evidence.test.ts
2026-06-29 18:05:58 +08:00

164 lines
5.0 KiB
TypeScript

import test from 'node:test';
import assert from 'node:assert/strict';
import type { Bug } from './bug';
import type { DevTask } from './dev-task';
import type { TestCase } from './test-case';
import type { TaskWorklog } from './task-worklog';
import type { VersionPlan } from './version-plan';
import type { WorkActivity } from './work-activity';
import { buildVersionDailyEvidence, buildXiaobaoWorkItems } from './xiaobao-risk-evidence';
function plan(patch: Partial<VersionPlan> = {}): VersionPlan {
return {
id: 'plan-1',
versionId: 'ver-1',
type: 'product',
title: 'Product plan',
owner: 'pm-1',
startTime: '2026-06-20T01:00:00.000Z',
endTime: '2026-06-25T10:00:00.000Z',
status: 'in_progress',
actualStartAt: '2026-06-20T01:00:00.000Z',
createdAt: '2026-06-20T01:00:00.000Z',
addedBy: 'pm-1',
...patch,
};
}
function devTask(patch: Partial<DevTask> = {}): DevTask {
return {
id: 'dev-1',
taskNo: 'DEV-001',
requirementId: 'req-1',
title: 'Build warning panel',
categoryId: 'frontend',
assigneeId: 'dev-other',
priority: 'P1',
expectedStartAt: '2026-06-20T01:00:00.000Z',
expectedEndAt: '2026-06-26T10:00:00.000Z',
status: 'in_progress',
isBlocked: false,
actualStartAt: '2026-06-20T01:00:00.000Z',
createdBy: 'pm-1',
createdAt: '2026-06-20T01:00:00.000Z',
updatedAt: '2026-06-24T02:00:00.000Z',
...patch,
};
}
function testCase(patch: Partial<TestCase> = {}): TestCase {
return {
id: 'tc-1',
caseNo: 'TC-001',
versionId: 'ver-1',
title: 'Verify warning panel',
categoryId: 'ui-interaction',
priority: 'P2',
assigneeId: 'qa-other',
status: 'running',
startedAt: '2026-06-20T03:00:00.000Z',
createdBy: 'qa-other',
createdAt: '2026-06-20T01:00:00.000Z',
updatedAt: '2026-06-24T02:00:00.000Z',
...patch,
};
}
function bug(patch: Partial<Bug> = {}): Bug {
return {
id: 'bug-1',
bugNo: 'BUG-001',
versionId: 'ver-1',
testCaseId: 'tc-1',
title: 'Warning count mismatch',
description: 'The count is wrong.',
severity: 'major',
priority: 'P1',
reportedBy: 'qa-other',
assigneeId: 'dev-other',
status: 'open',
createdAt: '2026-06-20T01:00:00.000Z',
updatedAt: '2026-06-24T02:00:00.000Z',
...patch,
};
}
test('buildVersionDailyEvidence groups version activity and detects no activity/no update', () => {
const workItems = buildXiaobaoWorkItems({
plans: [plan()],
devTasks: [devTask()],
testCases: [testCase()],
bugs: [bug()],
versions: [{ id: 'ver-1', name: 'V1.0', productName: 'FTB', projectName: 'PM' }],
requirementVersionMap: new Map([['req-1', 'ver-1']]),
});
const activities: WorkActivity[] = [
{
id: 'act-delivery',
actorId: 'dev-other',
date: '2026-06-29',
occurredAt: '2026-06-29T02:00:00.000Z',
sourceType: 'dev_task',
sourceId: 'dev-1',
action: 'dev_task_submitted',
category: 'delivery',
title: 'Build warning panel',
summary: 'Submitted development task',
},
{
id: 'act-risk',
actorId: 'qa-other',
date: '2026-06-29',
occurredAt: '2026-06-29T03:00:00.000Z',
sourceType: 'bug',
sourceId: 'bug-1',
action: 'bug_blocked',
category: 'risk',
title: 'Warning count mismatch',
summary: 'Blocked by missing data',
},
];
const worklogs: TaskWorklog[] = [
{
id: 'wl-note',
taskId: 'tc-1',
userId: 'qa-other',
date: '2026-06-29',
hours: 1,
workContent: 'Continued regression testing.',
createdAt: '2026-06-29T04:00:00.000Z',
},
];
const evidence = buildVersionDailyEvidence({
versionId: 'ver-1',
workItems,
activities,
worklogs,
now: new Date('2026-06-29T08:00:00.000Z'),
});
assert.deepEqual(evidence.todayDeliveries.map((item) => item.id), ['act-delivery']);
assert.deepEqual(evidence.todayRisks.map((item) => item.id), ['act-risk']);
assert.deepEqual(evidence.progressNotes.map((item) => item.id), ['wl-note']);
assert.equal(evidence.recentActivityCount, 3);
assert.equal(evidence.lastActivityAt, '2026-06-29T04:00:00.000Z');
assert.ok(evidence.silentRisks?.some((risk) => risk.key === 'no_update' && risk.itemId === 'plan-1'));
assert.ok(evidence.silentRisks?.some((risk) => risk.key === 'no_activity' && risk.itemId === 'plan-1'));
});
test('buildXiaobaoWorkItems keeps all version work without current-user filtering', () => {
const items = buildXiaobaoWorkItems({
plans: [plan({ owner: 'pm-other' })],
devTasks: [devTask({ assigneeId: 'dev-other' })],
testCases: [testCase({ assigneeId: 'qa-other' })],
bugs: [bug({ assigneeId: 'dev-other' })],
versions: [{ id: 'ver-1', name: 'V1.0', productName: 'FTB', projectName: 'PM' }],
requirementVersionMap: new Map([['req-1', 'ver-1']]),
});
assert.deepEqual(items.map((item) => item.id), ['plan-1', 'dev-1', 'tc-1', 'bug-1']);
assert.deepEqual(items.map((item) => item.versionId), ['ver-1', 'ver-1', 'ver-1', 'ver-1']);
});