fix(小宝预警): 保证建议不为空并显示更新中
This commit is contained in:
127
apps/web/lib/xiaobao-risk-suggestion.test.ts
Normal file
127
apps/web/lib/xiaobao-risk-suggestion.test.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { buildRiskInsightSignature } from './xiaobao-risk-ai';
|
||||
import type { XiaobaoRiskInsightCacheItem } from './xiaobao-risk-cache';
|
||||
import type { XiaobaoVersionRisk } from './xiaobao-risk';
|
||||
import { attachXiaobaoRiskSuggestion } from './xiaobao-risk-suggestion';
|
||||
|
||||
function risk(patch: Partial<XiaobaoVersionRisk> = {}): XiaobaoVersionRisk {
|
||||
return {
|
||||
versionId: 'ver-1',
|
||||
versionName: 'V1.0',
|
||||
productId: 'prod-1',
|
||||
productName: 'Product A',
|
||||
projectId: 'proj-1',
|
||||
projectName: 'Project A',
|
||||
riskScore: 68,
|
||||
riskLevel: 'attention',
|
||||
expectedReleaseDate: '2026-07-05',
|
||||
confidence: 80,
|
||||
confidenceLevel: 'high',
|
||||
forecastReleaseDate: '2026-07-05T10:00:00.000Z',
|
||||
delayDays: 0,
|
||||
remainingWorkHours: 8,
|
||||
reasons: [
|
||||
{
|
||||
key: 'remaining_work',
|
||||
title: '剩余工作量',
|
||||
detail: '预计还剩 8h 工作量。',
|
||||
severity: 'warning',
|
||||
},
|
||||
],
|
||||
silentRisks: [],
|
||||
signals: {
|
||||
unfinishedCount: 1,
|
||||
openBugCount: 0,
|
||||
criticalBugCount: 0,
|
||||
failedTestCount: 0,
|
||||
blockedCount: 0,
|
||||
silentRiskCount: 0,
|
||||
daysToExpectedRelease: 2,
|
||||
},
|
||||
currentSnapshot: {
|
||||
versionId: 'ver-1',
|
||||
date: '2026-06-30',
|
||||
riskScore: 68,
|
||||
riskLevel: 'attention',
|
||||
forecastReleaseDate: '2026-07-05T10:00:00.000Z',
|
||||
openBugCount: 0,
|
||||
criticalBugCount: 0,
|
||||
failedTestCount: 0,
|
||||
blockedCount: 0,
|
||||
silentRiskCount: 0,
|
||||
confidence: 80,
|
||||
createdAt: '2026-06-30T10:00:00.000Z',
|
||||
},
|
||||
trend: { direction: 'flat', delta: 0, summary: '风险保持稳定。', pattern: 'stable' },
|
||||
...patch,
|
||||
};
|
||||
}
|
||||
|
||||
function insight(itemRisk: XiaobaoVersionRisk, patch: Partial<XiaobaoRiskInsightCacheItem> = {}): XiaobaoRiskInsightCacheItem {
|
||||
return {
|
||||
versionId: itemRisk.versionId,
|
||||
riskSignature: buildRiskInsightSignature(itemRisk),
|
||||
insight: {
|
||||
summary: '旧的小宝建议',
|
||||
why: ['仍有工作未完成'],
|
||||
forecast: '当前建议继续观察。',
|
||||
suggestedActions: ['确认剩余任务负责人和完成时间。'],
|
||||
ownerHints: [],
|
||||
generatedAt: '2026-06-30T09:00:00.000Z',
|
||||
},
|
||||
generatedAt: '2026-06-30T09:00:00.000Z',
|
||||
...patch,
|
||||
};
|
||||
}
|
||||
|
||||
test('attachXiaobaoRiskSuggestion provides a rule suggestion when AI cache is empty', () => {
|
||||
const result = attachXiaobaoRiskSuggestion(risk(), {
|
||||
insights: [],
|
||||
pendingInsightKeys: [],
|
||||
});
|
||||
|
||||
assert.equal(result.aiInsightSource, 'rule');
|
||||
assert.equal(result.aiInsightUpdating, false);
|
||||
assert.ok(result.aiInsight?.summary);
|
||||
assert.ok(result.aiInsight?.forecast);
|
||||
assert.ok((result.aiInsight?.suggestedActions.length ?? 0) > 0);
|
||||
});
|
||||
|
||||
test('attachXiaobaoRiskSuggestion keeps previous AI suggestion while a new one is updating', () => {
|
||||
const previous = risk({ riskScore: 52, riskLevel: 'attention' });
|
||||
const current = risk({ riskScore: 82, riskLevel: 'at_risk', delayDays: 1 });
|
||||
const pendingKey = `${current.versionId}:${buildRiskInsightSignature(current)}`;
|
||||
|
||||
const result = attachXiaobaoRiskSuggestion(current, {
|
||||
insights: [insight(previous)],
|
||||
pendingInsightKeys: [pendingKey],
|
||||
});
|
||||
|
||||
assert.equal(result.aiInsightSource, 'previous_ai');
|
||||
assert.equal(result.aiInsightUpdating, true);
|
||||
assert.equal(result.aiInsight?.summary, '旧的小宝建议');
|
||||
});
|
||||
|
||||
test('attachXiaobaoRiskSuggestion uses the current AI suggestion when the signature matches', () => {
|
||||
const current = risk({ riskScore: 82, riskLevel: 'at_risk', delayDays: 1 });
|
||||
|
||||
const result = attachXiaobaoRiskSuggestion(current, {
|
||||
insights: [insight(current, {
|
||||
insight: {
|
||||
summary: '新的小宝建议',
|
||||
why: ['风险已升高'],
|
||||
forecast: '预计延期 1 天。',
|
||||
suggestedActions: ['先处理阻塞和失败用例。'],
|
||||
ownerHints: [],
|
||||
generatedAt: '2026-06-30T10:00:00.000Z',
|
||||
},
|
||||
generatedAt: '2026-06-30T10:00:00.000Z',
|
||||
})],
|
||||
pendingInsightKeys: [],
|
||||
});
|
||||
|
||||
assert.equal(result.aiInsightSource, 'current_ai');
|
||||
assert.equal(result.aiInsightUpdating, false);
|
||||
assert.equal(result.aiInsight?.summary, '新的小宝建议');
|
||||
});
|
||||
Reference in New Issue
Block a user