131 lines
3.8 KiB
TypeScript
131 lines
3.8 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { calcXiaobaoVersionRisk } from './xiaobao-risk';
|
|
import type { DevTask } from './dev-task';
|
|
import type { TestCase } from './test-case';
|
|
import type { Bug } from './bug';
|
|
import type { VersionDailyEvidence } from './xiaobao-risk-evidence';
|
|
|
|
function version(patch: any = {}) {
|
|
return {
|
|
id: 'ver-1',
|
|
name: 'V1.0',
|
|
status: 'developing',
|
|
productName: 'FTB',
|
|
projectName: '项目管理',
|
|
expectedReleaseDate: '2026-07-03T10:00:00.000Z',
|
|
members: [{ name: '张三', role: 'frontend' }],
|
|
...patch,
|
|
};
|
|
}
|
|
|
|
function devTask(patch: Partial<DevTask> = {}): DevTask {
|
|
return {
|
|
id: 'dev-1',
|
|
taskNo: 'DEV-001',
|
|
requirementId: 'req-1',
|
|
title: '实现版本风险页',
|
|
categoryId: 'frontend',
|
|
assigneeId: 'm-1',
|
|
priority: 'P1',
|
|
expectedStartAt: '2026-07-01T09:00:00.000Z',
|
|
expectedEndAt: '2026-07-03T18:00:00.000Z',
|
|
estimateHours: 40,
|
|
status: 'todo',
|
|
isBlocked: false,
|
|
createdBy: 'm-1',
|
|
createdAt: '2026-07-01T09:00:00.000Z',
|
|
updatedAt: '2026-07-01T09:00:00.000Z',
|
|
...patch,
|
|
};
|
|
}
|
|
|
|
function testCase(patch: Partial<TestCase> = {}): TestCase {
|
|
return {
|
|
id: 'tc-1',
|
|
caseNo: 'TC-001',
|
|
versionId: 'ver-1',
|
|
title: '验收版本风险页',
|
|
categoryId: 'ui-interaction',
|
|
priority: 'P1',
|
|
assigneeId: 'm-2',
|
|
status: 'pending',
|
|
estimateHours: 16,
|
|
createdBy: 'm-2',
|
|
createdAt: '2026-07-01T09:00:00.000Z',
|
|
updatedAt: '2026-07-01T09:00:00.000Z',
|
|
...patch,
|
|
};
|
|
}
|
|
|
|
function bug(patch: Partial<Bug> = {}): Bug {
|
|
return {
|
|
id: 'bug-1',
|
|
bugNo: 'BUG-001',
|
|
versionId: 'ver-1',
|
|
testCaseId: 'tc-1',
|
|
title: '发布阻断缺陷',
|
|
description: '关键流程无法通过',
|
|
severity: 'critical',
|
|
priority: 'P1',
|
|
reportedBy: 'm-2',
|
|
assigneeId: 'm-1',
|
|
status: 'open',
|
|
createdAt: '2026-07-01T09:00:00.000Z',
|
|
updatedAt: '2026-07-01T09:00:00.000Z',
|
|
...patch,
|
|
};
|
|
}
|
|
|
|
test('calcXiaobaoVersionRisk predicts delay when remaining work exceeds release date', () => {
|
|
const risk = calcXiaobaoVersionRisk({
|
|
version: version(),
|
|
devTasks: [devTask()],
|
|
testCases: [testCase()],
|
|
bugs: [],
|
|
now: new Date('2026-07-02T01:00:00.000Z'),
|
|
});
|
|
|
|
assert.equal(risk.riskLevel, 'likely_delayed');
|
|
assert.ok(risk.riskScore >= 70);
|
|
assert.ok(risk.delayDays > 0);
|
|
assert.ok(risk.reasons.some((reason: { key: string }) => reason.key === 'remaining_work'));
|
|
});
|
|
|
|
test('calcXiaobaoVersionRisk marks blocked when critical Bug / blocked dev task / failed test exist', () => {
|
|
const risk = calcXiaobaoVersionRisk({
|
|
version: version(),
|
|
devTasks: [devTask({ isBlocked: true, blockReason: '依赖接口未完成' })],
|
|
testCases: [testCase({ status: 'failed', failReason: '核心流程失败' })],
|
|
bugs: [bug()],
|
|
now: new Date('2026-07-02T01:00:00.000Z'),
|
|
});
|
|
|
|
assert.equal(risk.riskLevel, 'blocked');
|
|
assert.ok(risk.reasons.some((reason: { key: string }) => reason.key === 'critical_bug'));
|
|
assert.ok(risk.reasons.some((reason: { key: string }) => reason.key === 'blocked_work'));
|
|
});
|
|
|
|
test('calcXiaobaoVersionRisk preserves daily evidence on the returned risk object', () => {
|
|
const dailyEvidence: VersionDailyEvidence = {
|
|
todayDeliveries: [{ id: 'ev-1', title: '提交提测', summary: '完成核心流程', occurredAt: '2026-07-02T02:00:00.000Z' }],
|
|
todayProgress: [],
|
|
todayRisks: [],
|
|
progressNotes: [],
|
|
needsProgressItems: [],
|
|
recentActivityCount: 1,
|
|
lastActivityAt: '2026-07-02T02:00:00.000Z',
|
|
};
|
|
|
|
const risk = calcXiaobaoVersionRisk({
|
|
version: version(),
|
|
devTasks: [],
|
|
testCases: [],
|
|
bugs: [],
|
|
dailyEvidence,
|
|
now: new Date('2026-07-02T01:00:00.000Z'),
|
|
});
|
|
|
|
assert.equal(risk.dailyEvidence, dailyEvidence);
|
|
});
|