feat(小宝预警): 增加版本风险预测引擎

This commit is contained in:
Script Generator
2026-06-29 17:35:45 +08:00
parent ea360c74d6
commit 7b3638c915
4 changed files with 453 additions and 0 deletions

View File

@@ -0,0 +1,106 @@
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';
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'));
});