feat(platform): add notification rules workspace
This commit is contained in:
@@ -3183,6 +3183,82 @@ test('uses backend quality notification plan on quality page', async () => {
|
||||
expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/quality/notification-plan?limit=20'), undefined);
|
||||
});
|
||||
|
||||
test('renders notification rules as a standalone operations page', async () => {
|
||||
window.history.replaceState(null, '', '/#/notification-rules');
|
||||
const writeText = vi.fn(() => Promise.resolve());
|
||||
Object.defineProperty(navigator, 'clipboard', {
|
||||
configurable: true,
|
||||
value: { writeText }
|
||||
});
|
||||
const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
|
||||
const path = String(input);
|
||||
if (path.includes('/api/ops/health')) {
|
||||
return {
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
data: {
|
||||
linkHealth: [],
|
||||
kafkaLag: 0,
|
||||
activeConnections: 1000,
|
||||
capacityFindings: [],
|
||||
redisOnlineKeys: 800,
|
||||
tdengineWritable: true,
|
||||
mysqlWritable: true,
|
||||
runtime: { requestTimeoutMs: 5000, platformRelease: 'platform-rules-test' }
|
||||
},
|
||||
traceId: 'trace-test',
|
||||
timestamp: 1783094400000
|
||||
})
|
||||
} as Response;
|
||||
}
|
||||
if (path.includes('/api/quality/notification-plan')) {
|
||||
return {
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
data: {
|
||||
summary: { issueVehicleCount: 5, issueRecordCount: 8, errorCount: 2, warningCount: 6, protocols: [], issueTypes: [{ name: 'NO_SOURCE', count: 3 }] },
|
||||
rules: [
|
||||
{ issueType: 'NO_SOURCE', title: '无来源规则', level: 'P0', owner: '平台接入', trigger: '车辆无任何来源证据', notify: '立即通知接入运维', sla: '30 分钟确认', count: 3 },
|
||||
{ issueType: 'FIELD_MISSING', title: '字段缺失规则', level: 'P1', owner: '协议解析', trigger: '核心字段缺失', notify: '进入每日治理清单', sla: '当日闭环', count: 1 }
|
||||
],
|
||||
policies: [
|
||||
{ name: 'P0 实时中断', target: '接入运维 + 业务责任人', channel: '邮件 / 企业微信', condition: '无来源、VIN 缺失、存储不可写', escalationMinutes: 30, acceptanceCriteria: '来源恢复并持续 10 分钟' },
|
||||
{ name: 'P1 数据质量', target: '协议解析 + 数据治理', channel: '每日汇总邮件', condition: '字段缺失、链路间断', escalationMinutes: 120, acceptanceCriteria: '核心字段恢复解析' }
|
||||
],
|
||||
priorityIssues: [],
|
||||
activeRuleCount: 2,
|
||||
p0RuleCount: 1
|
||||
},
|
||||
traceId: 'trace-test',
|
||||
timestamp: 1783094400000
|
||||
})
|
||||
} as Response;
|
||||
}
|
||||
return {
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
data: { items: [], total: 0, limit: 20, offset: 0 },
|
||||
traceId: 'trace-test',
|
||||
timestamp: 1783094400000
|
||||
})
|
||||
} as Response;
|
||||
});
|
||||
|
||||
render(<App />);
|
||||
|
||||
expect(await screen.findByRole('heading', { name: '通知规则' })).toBeInTheDocument();
|
||||
expect(screen.getByText('无来源规则')).toBeInTheDocument();
|
||||
expect(screen.getByText('车辆无任何来源证据')).toBeInTheDocument();
|
||||
expect(screen.getByText('P0 实时中断')).toBeInTheDocument();
|
||||
expect(screen.getByText('接入运维 + 业务责任人')).toBeInTheDocument();
|
||||
expect(screen.getByText('platform-rules-test')).toBeInTheDocument();
|
||||
expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/quality/notification-plan?limit=50'), undefined);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '复制通知规则Runbook' }));
|
||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【通知规则Runbook】'));
|
||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('无来源规则 / P0 / 平台接入'));
|
||||
});
|
||||
|
||||
test('copies notification text from quality priority queue', async () => {
|
||||
window.history.replaceState(null, '', '/#/quality');
|
||||
const writeText = vi.fn(() => Promise.resolve());
|
||||
|
||||
Reference in New Issue
Block a user