diff --git a/src/components/latency-chart.test.tsx b/src/components/latency-chart.test.tsx index 3ca51ba..136aec1 100644 --- a/src/components/latency-chart.test.tsx +++ b/src/components/latency-chart.test.tsx @@ -367,6 +367,32 @@ describe('latency chart on sample data', () => { } }) + it('agrees with the p95 tile, as a live platform would', () => { + // On a platform the tile and the chart's newest point are one number. The demo + // used to print 88 ms on the tile beside a curve ending in the 40s — sample + // data, but a false lesson about what the two panels mean. + const restoreEventSource = installFakeEventSource() + vi.useFakeTimers() + try { + const { result } = renderHook(() => useConsoleData({ sampleDelayMs: 500 })) + act(() => { + vi.advanceTimersByTime(600) // no platform answered: the demo takes over + }) + expect(result.current.conn.status).toBe('sample') + const { latency, summary } = result.current.data! + const newest = latency[latency.length - 1] + const windowBack = latency.findLast((p) => p.at <= newest.at - summary!.windowMs)! + expect(summary!.p95LatencyMs).toBe(newest.p95) + expect(summary!.p95DeltaMs).toBe(newest.p95 - windowBack.p95) + + const { getByText } = render() + expect(getByText(String(newest.p95))).toBeTruthy() + } finally { + vi.useRealTimers() + restoreEventSource() + } + }) + it('drops the label once real samples replace it', () => { const { queryByText, rerender } = render() rerender() diff --git a/src/hooks/use-console-data.ts b/src/hooks/use-console-data.ts index 812abf9..0397080 100644 --- a/src/hooks/use-console-data.ts +++ b/src/hooks/use-console-data.ts @@ -166,12 +166,13 @@ function int(v: unknown): number | null { } function mockData(now: number): LiveData { + const latency = mockLatencySeries(now) return { traces: mockTraces(now), metrics: mockMetrics(now), modules: mockModules(), - summary: mockSummary(), - latency: mockLatencySeries(now), + summary: mockSummary(latency), + latency, } } diff --git a/src/lib/mock.ts b/src/lib/mock.ts index c3c4943..6a18f40 100644 --- a/src/lib/mock.ts +++ b/src/lib/mock.ts @@ -123,19 +123,28 @@ export function mockModules(): ModuleStatus[] { // Windowed KPI header aggregate, mirroring the platform `summary` SSE event. // Trends are populated (not null) so the sample demo shows the fully-wired header. -export function mockSummary(): TraceSummary { +// +// The p95 and its trend are read off the sample latency series rather than made up +// beside it. On a real platform the p95 tile and the chart's newest point are the +// same number, and a demo that showed them disagreeing would teach the wrong thing +// about the screen — sample data is still a claim about how the console behaves. +export function mockSummary(latency: readonly LatencyPoint[]): TraceSummary { + const windowMs = 60000 + const newest = latency[latency.length - 1] + // The point one window back is what the platform compares against. + const prev = latency.findLast((p) => p.at <= newest.at - windowMs) ?? latency[0] return { - windowMs: 60000, + windowMs, count: 512, errorCount: 7, errorRate: 0.0137, p50LatencyMs: 12, - p95LatencyMs: 88, + p95LatencyMs: newest.p95, p99LatencyMs: 210, maxLatencyMs: 640, requestsDeltaPct: 0.12, errorRateDeltaPp: 0.31, - p95DeltaMs: -6, + p95DeltaMs: newest.p95 - prev.p95, activeModules: 6, modulesByMode: { worker: 3, container: 1, 'in-process': 2 }, }