import test from 'node:test'; import assert from 'node:assert/strict'; import type { Bug } from './bug'; import { calcXiaobaoVersionRisk } from './xiaobao-risk'; import { buildRiskSignature, summarizeRiskTrend } from './xiaobao-risk-trend'; import type { XiaobaoRiskSnapshot } from './xiaobao-risk-trend'; function snapshot(patch: Partial): XiaobaoRiskSnapshot { return { versionId: 'ver-1', date: '2026-06-27', riskScore: 38, riskLevel: 'attention', openBugCount: 0, failedTestCount: 0, blockedCount: 0, silentRiskCount: 0, confidence: 80, createdAt: '2026-06-27T01:00:00.000Z', ...patch, }; } test('summarizeRiskTrend detects continuous rising risk across three snapshots', () => { const trend = summarizeRiskTrend([ snapshot({ riskScore: 71, createdAt: '2026-06-29T01:00:00.000Z', date: '2026-06-29' }), snapshot({ riskScore: 38, createdAt: '2026-06-27T01:00:00.000Z', date: '2026-06-27' }), snapshot({ riskScore: 52, createdAt: '2026-06-28T01:00:00.000Z', date: '2026-06-28' }), ]); assert.equal(trend.direction, 'up'); assert.equal(trend.delta, 33); assert.equal(trend.pattern, 'continuous_rising'); }); test('buildRiskSignature changes when score and bug counts change', () => { const base = buildRiskSignature(snapshot({ riskScore: 52, openBugCount: 1 })); const changed = buildRiskSignature(snapshot({ riskScore: 71, openBugCount: 2 })); assert.notEqual(base, changed); }); test('calcXiaobaoVersionRisk uses all open bugs in the current trend snapshot signature', () => { const now = new Date('2026-07-02T01:00:00.000Z'); const risk = calcXiaobaoVersionRisk({ version: { id: 'ver-1', name: 'V1.0', expectedReleaseDate: '2026-07-03T10:00:00.000Z', members: [{ name: 'Alice' }], }, devTasks: [], testCases: [], bugs: [bug({ severity: 'major', priority: 'P2' })], now, }); const currentSignature = (risk.trend as { currentSignature?: string }).currentSignature; assert.equal(risk.signals.openBugCount, 1); assert.equal(risk.signals.criticalBugCount, 0); assert.equal(currentSignature, buildRiskSignature({ versionId: risk.versionId, date: now.toISOString().slice(0, 10), riskScore: risk.riskScore, riskLevel: risk.riskLevel, forecastReleaseDate: risk.forecastReleaseDate, openBugCount: 1, failedTestCount: 0, blockedCount: 0, silentRiskCount: 0, confidence: risk.confidence, createdAt: now.toISOString(), })); }); function bug(patch: Partial = {}): Bug { return { id: 'bug-1', bugNo: 'BUG-001', versionId: 'ver-1', testCaseId: 'tc-1', title: 'Non-critical open bug', description: 'Open but not critical.', severity: 'major', priority: 'P2', reportedBy: 'qa-1', assigneeId: 'dev-1', status: 'open', createdAt: '2026-07-01T09:00:00.000Z', updatedAt: '2026-07-01T09:00:00.000Z', ...patch, }; }