refactor(ui): converge semi operations workspace
This commit is contained in:
@@ -605,6 +605,48 @@ test('sourceReadiness reads ops source readiness plan', async () => {
|
||||
expect(result.sources[0].protocol).toBe('GB32960');
|
||||
});
|
||||
|
||||
test('normalizes nullable operations evidence arrays at the API boundary', async () => {
|
||||
const fetchMock = vi.spyOn(globalThis, 'fetch')
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
data: {
|
||||
linkHealth: null,
|
||||
kafkaLag: 0,
|
||||
activeConnections: 10,
|
||||
capacityFindings: null,
|
||||
redisOnlineKeys: 5,
|
||||
tdengineWritable: true,
|
||||
mysqlWritable: true,
|
||||
runtime: { platformRelease: 'test', dataMode: 'production', requestTimeoutMs: 5000 }
|
||||
}
|
||||
})
|
||||
} as Response)
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
data: {
|
||||
totalVehicles: 1024,
|
||||
boundVehicles: 1024,
|
||||
identityRequiredVehicles: 0,
|
||||
onlineVehicles: 234,
|
||||
kafkaLag: 0,
|
||||
activeConnections: 10,
|
||||
redisOnlineKeys: 5,
|
||||
platformRelease: 'test',
|
||||
sources: null
|
||||
}
|
||||
})
|
||||
} as Response);
|
||||
|
||||
const [health, readiness] = await Promise.all([api.opsHealth(), api.sourceReadiness()]);
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledTimes(2);
|
||||
expect(health.linkHealth).toEqual([]);
|
||||
expect(health.capacityFindings).toEqual([]);
|
||||
expect(readiness.sources).toEqual([]);
|
||||
});
|
||||
|
||||
test('api errors include backend message, detail, and trace id', async () => {
|
||||
vi.spyOn(globalThis, 'fetch').mockResolvedValue({
|
||||
ok: false,
|
||||
|
||||
@@ -179,6 +179,21 @@ function withSignal(init: RequestInit | undefined, signal?: AbortSignal) {
|
||||
return signal ? { ...init, signal } : init;
|
||||
}
|
||||
|
||||
function normalizeOpsHealth(value: OpsHealth): OpsHealth {
|
||||
return {
|
||||
...value,
|
||||
linkHealth: Array.isArray(value.linkHealth) ? value.linkHealth : [],
|
||||
capacityFindings: Array.isArray(value.capacityFindings) ? value.capacityFindings : []
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeSourceReadiness(value: SourceReadinessPlan): SourceReadinessPlan {
|
||||
return {
|
||||
...value,
|
||||
sources: Array.isArray(value.sources) ? value.sources : []
|
||||
};
|
||||
}
|
||||
|
||||
async function responseErrorMessage(response: Response) {
|
||||
try {
|
||||
const envelope = (await response.json()) as ApiErrorEnvelope;
|
||||
@@ -347,6 +362,6 @@ export const api = {
|
||||
alertEvents: (params = new URLSearchParams()) => request<Page<QualityIssueRow>>(`/api/alert-events?${params.toString()}`),
|
||||
alertEventNotificationPlan: (params = new URLSearchParams()) => request<QualityNotificationPlan>(`/api/alert-events/notification-plan?${params.toString()}`),
|
||||
reverseGeocode: (params = new URLSearchParams(), signal?: AbortSignal) => request<MapReverseGeocode>(`/api/map/reverse-geocode?${params.toString()}`, withSignal(undefined, signal)),
|
||||
opsHealth: (signal?: AbortSignal) => request<OpsHealth>('/api/ops/health', withSignal(undefined, signal)),
|
||||
sourceReadiness: (signal?: AbortSignal) => request<SourceReadinessPlan>('/api/ops/source-readiness', withSignal(undefined, signal))
|
||||
opsHealth: (signal?: AbortSignal) => request<OpsHealth>('/api/ops/health', withSignal(undefined, signal)).then(normalizeOpsHealth),
|
||||
sourceReadiness: (signal?: AbortSignal) => request<SourceReadinessPlan>('/api/ops/source-readiness', withSignal(undefined, signal)).then(normalizeSourceReadiness)
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user