refine Semi UI operations health hierarchy

This commit is contained in:
lingniu
2026-07-18 19:25:40 +08:00
parent 5d4aa92cc1
commit 532524b992
3 changed files with 157 additions and 17 deletions

View File

@@ -53,7 +53,7 @@ function seedSession() {
test('renders reconciliation queue, loads evidence on demand and records review conclusion', async () => {
seedSession();
mocks.opsHealth.mockResolvedValue({
linkHealth: [{ name: 'MySQL', status: 'ok', detail: '主库连接正常' }], kafkaLag: 0, activeConnections: 10, capacityFindings: [], redisOnlineKeys: 5,
linkHealth: [{ name: 'MySQL realtime', status: 'ok', detail: '主库连接正常' }], kafkaLag: 0, activeConnections: 10, capacityFindings: [], redisOnlineKeys: 5,
tdengineWritable: true, mysqlWritable: true,
runtime: { platformRelease: 'test-release', dataMode: 'production', requestTimeoutMs: 5000, amapSecurityProxyEnabled: true, amapSecurityCodeExposed: false }
});
@@ -179,7 +179,7 @@ test('renders only the compact mobile reconciliation surface and defers secondar
test('reconciles service identities with bound and identity-required vehicles', async () => {
seedSession();
mocks.opsHealth.mockResolvedValue({
linkHealth: [{ name: 'MySQL', status: 'ok', detail: '主库连接正常' }], kafkaLag: 0, activeConnections: 10, capacityFindings: [], redisOnlineKeys: 5,
linkHealth: [{ name: 'MySQL realtime', status: 'ok', detail: '主库连接正常' }], kafkaLag: 0, activeConnections: 10, capacityFindings: [], redisOnlineKeys: 5,
tdengineWritable: true, mysqlWritable: true,
runtime: { platformRelease: 'test-release', dataMode: 'production', requestTimeoutMs: 5000, amapSecurityProxyEnabled: true, amapSecurityCodeExposed: false }
});
@@ -205,6 +205,8 @@ test('reconciles service identities with bound and identity-required vehicles',
expect(liveOptions.refetchOnWindowFocus).toBe(true);
}
expect(screen.getByText('车辆总数 / 在线')).toBeInTheDocument();
expect(screen.getByText('协议就绪 / 总数')).toBeInTheDocument();
expect(screen.getByText('全部协议达到当前口径')).toBeInTheDocument();
expect(screen.getByText('已绑定 1024 · 待绑定 11')).toBeInTheDocument();
expect(screen.getByRole('heading', { name: '运行概览', level: 5 })).toBeInTheDocument();
expect(screen.getByText('车辆总数 / 在线').closest('.v2-ops-metric')).toBeInTheDocument();
@@ -215,7 +217,7 @@ test('reconciles service identities with bound and identity-required vehicles',
expect(screen.getByRole('heading', { name: '数据链路', level: 5 })).toBeInTheDocument();
expect(screen.getByRole('heading', { name: '运行时安全', level: 5 })).toBeInTheDocument();
expect(screen.getByRole('heading', { name: '协议来源就绪度', level: 5 })).toBeInTheDocument();
expect(document.querySelector('.v2-ops-link-card')).toHaveTextContent('MySQL主库连接正常');
expect(document.querySelector('.v2-ops-link-card')).toHaveTextContent('MySQL 实时连接MySQL realtime主库连接正常');
expect(document.querySelector('.v2-ops-link-card.semi-card')).not.toBeInTheDocument();
expect(document.querySelector('.v2-ops-runtime-descriptions.semi-descriptions')).toBeInTheDocument();
expect(screen.getByText('容量检查通过').closest('.semi-empty')).toHaveClass('v2-ops-capacity-empty');
@@ -236,6 +238,42 @@ test('reconciles service identities with bound and identity-required vehicles',
expect(screen.queryByText('统一车辆视角')).not.toBeInTheDocument();
});
test('includes protocol readiness in the global health verdict and prioritizes its evidence', async () => {
seedSession();
mocks.opsHealth.mockResolvedValue({
linkHealth: [{ name: 'MySQL realtime', status: 'ok', detail: '主库连接正常' }], kafkaLag: 0, activeConnections: 10, capacityFindings: [], redisOnlineKeys: 5,
tdengineWritable: true, mysqlWritable: true,
runtime: { platformRelease: 'test-release', dataMode: 'production', requestTimeoutMs: 5000, amapSecurityProxyEnabled: true, amapSecurityCodeExposed: false }
});
mocks.sourceReadiness.mockResolvedValue({
totalVehicles: 1024, boundVehicles: 1024, identityRequiredVehicles: 11, onlineVehicles: 234,
sources: [
{
protocol: 'GB32960', role: '仪表盘里程与整车状态', total: 494, online: 123, severity: 'warning',
status: '覆盖不足', evidence: '在线覆盖偏低', action: '核对接入范围', acceptance: '缺失车辆下降'
},
{
protocol: 'YUTONG_MQTT', role: '宇通补充来源', total: 50, online: 12, severity: 'error',
status: '链路异常', evidence: '投影停滞', action: '恢复投影与回补', acceptance: '日里程恢复'
}
]
});
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
renderOperations(client, ['/operations?view=health']);
expect(await screen.findByText('需要处理')).toBeInTheDocument();
expect(screen.getByText('协议覆盖 2 项 · 链路运行 0 项')).toBeInTheDocument();
expect(screen.getByText('0 / 2')).toBeInTheDocument();
expect(screen.getByText('2 个协议需要处置')).toBeInTheDocument();
expect(within(screen.getByRole('navigation', { name: '运维质量视图' })).getByText('2 项关注')).toBeInTheDocument();
const sourcePanel = screen.getByRole('heading', { name: '协议来源就绪度', level: 5 }).closest('.v2-ops-sources');
const infrastructureGrid = document.querySelector('.v2-ops-grid');
expect(sourcePanel).toHaveClass('has-attention');
expect(sourcePanel).not.toBeNull();
expect(infrastructureGrid).not.toBeNull();
expect(Boolean(sourcePanel!.compareDocumentPosition(infrastructureGrid!) & Node.DOCUMENT_POSITION_FOLLOWING)).toBe(true);
});
test('keeps health evidence visible when source readiness fails and retries that section', async () => {
seedSession();
mocks.opsHealth.mockResolvedValue({
@@ -252,6 +290,8 @@ test('keeps health evidence visible when source readiness fails and retries that
fireEvent.click(screen.getByRole('tab', { name: '全局健康' }));
expect(await screen.findByText('来源就绪度暂时不可用')).toBeInTheDocument();
expect(screen.getByText('协议来源证据不可用')).toBeInTheDocument();
expect(screen.getByText('证据缺失')).toBeInTheDocument();
expect(screen.getByLabelText('运维质量操作')).toHaveTextContent('上海时间 · 版本 test-release');
expect(screen.getByRole('heading', { name: '数据链路', level: 5 })).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: /重试/ }));