feat(operations): add source diagnosis workspace

This commit is contained in:
lingniu
2026-07-16 17:35:10 +08:00
parent df7d9799b3
commit 96cad7eef7
18 changed files with 881 additions and 10 deletions

View File

@@ -1,14 +1,22 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { cleanup, fireEvent, render, screen } from '@testing-library/react';
import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
import { afterEach, expect, test, vi } from 'vitest';
import OperationsPage from './OperationsPage';
const mocks = vi.hoisted(() => ({ opsHealth: vi.fn(), sourceReadiness: vi.fn() }));
const mocks = vi.hoisted(() => ({
opsHealth: vi.fn(), sourceReadiness: vi.fn(), session: vi.fn(), vehicleCoverage: vi.fn(),
vehicleSourceDiagnostic: vi.fn(), updateVehicleSourcePolicy: vi.fn()
}));
vi.mock('../../api/client', () => ({ api: mocks }));
afterEach(() => { cleanup(); mocks.opsHealth.mockReset(); mocks.sourceReadiness.mockReset(); });
afterEach(() => { cleanup(); Object.values(mocks).forEach((mock) => mock.mockReset()); });
function seedSession() {
mocks.session.mockResolvedValue({ name: '平台管理员', role: 'admin', userType: 'admin', authMode: 'enforce', menuKeys: ['operations'] });
}
test('reconciles service identities with bound and identity-required vehicles', async () => {
seedSession();
mocks.opsHealth.mockResolvedValue({
linkHealth: [], kafkaLag: 0, activeConnections: 10, capacityFindings: [], redisOnlineKeys: 5,
tdengineWritable: true, mysqlWritable: true,
@@ -34,6 +42,7 @@ test('reconciles service identities with bound and identity-required vehicles',
});
test('keeps health evidence visible when source readiness fails and retries that section', async () => {
seedSession();
mocks.opsHealth.mockResolvedValue({
linkHealth: [], kafkaLag: 0, activeConnections: 10, capacityFindings: [], redisOnlineKeys: 5,
tdengineWritable: true, mysqlWritable: true,
@@ -54,3 +63,39 @@ test('keeps health evidence visible when source readiness fails and retries that
expect(screen.queryByText('来源就绪度暂时不可用')).not.toBeInTheDocument();
expect(mocks.sourceReadiness).toHaveBeenCalledTimes(2);
});
test('fuzzy searches a vehicle and renders all source diagnosis evidence', async () => {
seedSession();
mocks.opsHealth.mockResolvedValue({
linkHealth: [], 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: 1, boundVehicles: 1, identityRequiredVehicles: 0, onlineVehicles: 1, sources: [] });
mocks.vehicleCoverage.mockResolvedValue({ items: [{ vin: 'VIN001', plate: '粤A00001', protocols: ['JT808'], missingProtocols: [], sourceStatus: [], sourceCount: 2, onlineSourceCount: 2, online: true, lastSeen: '', bindingStatus: 'bound' }], total: 1, limit: 20, offset: 0 });
mocks.vehicleSourceDiagnostic.mockResolvedValue({
evidence: {
vin: 'VIN001', plate: '粤A00001', mileageDate: '2026-07-16', recommendedLocationProtocol: 'JT808',
recommendedLocationLabel: 'G7', locationConflict: true, conflictDistanceM: 233,
locationSources: [{
protocol: 'JT808', sourceLabel: 'G7', terminalLabel: '终端 133****0001', sourceKind: 'PLATFORM', sourceRef: 'a'.repeat(64),
selectedWithinProtocol: true, recommended: true, enabled: true, priority: 20, online: true, qualityStatus: 'OK', qualityReason: '',
longitude: 113.1, latitude: 23.1, speedKmh: 30, totalMileageKm: 1000, eventTime: '2026-07-16 10:00:00',
receivedAt: '2026-07-16 10:00:01', firstSeenAt: '2026-03-01 08:00:00', reportIntervalSec: 10, reportSampleCount: 1000,
selectionReason: '当前融合推荐'
}],
mileageSources: [], comparison: { locationMaxDistanceM: 233, totalMileageDeltaKm: 0, dailyMileageDeltaKm: 0, reportTimeDeltaSeconds: 0 }, asOf: ''
},
policy: { vin: 'VIN001', version: 1, updatedBy: 'system', updatedAt: '', audit: [] },
recommendationReason: '当前推荐 G7', refreshHint: '下一次有效上报后重新选举'
});
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
render(<QueryClientProvider client={client}><OperationsPage /></QueryClientProvider>);
fireEvent.change(screen.getByLabelText('按车牌或 VIN 搜索诊断车辆'), { target: { value: '粤A' } });
expect(await screen.findByText('粤A00001')).toBeInTheDocument();
fireEvent.click(screen.getByText('粤A00001'));
expect(await screen.findByText('当前推荐 G7')).toBeInTheDocument();
expect(screen.getByText('终端 133****0001')).toBeInTheDocument();
expect(screen.getByText('10s')).toBeInTheDocument();
await waitFor(() => expect(mocks.vehicleSourceDiagnostic).toHaveBeenCalledWith('VIN001', expect.any(AbortSignal)));
});