M5-T07: add latest-reading cards and Recharts trend charts; readings return most-recent rows
This commit is contained in:
@@ -212,3 +212,136 @@ describe('useTestReadDevice', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('useLatestReading', () => {
|
||||
beforeEach(() => vi.clearAllMocks())
|
||||
|
||||
it('calls GET /api/modbus/devices/{uuid}/latest with path param', async () => {
|
||||
mockGet.mockResolvedValue({
|
||||
data: { found: true, recorded_at: '2026-06-22T10:00:00Z', payload: { voltage: 230.2 } },
|
||||
})
|
||||
|
||||
const { Wrapper } = makeWrapper()
|
||||
const { useLatestReading } = await import('./hooks')
|
||||
const { result } = renderHook(() => useLatestReading('test-uuid-1'), { wrapper: Wrapper })
|
||||
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/api/modbus/devices/{uuid}/latest', {
|
||||
params: { path: { uuid: 'test-uuid-1' } },
|
||||
})
|
||||
expect(result.current.data?.found).toBe(true)
|
||||
expect(result.current.data?.payload).toEqual({ voltage: 230.2 })
|
||||
})
|
||||
|
||||
it('returns found=false when device has no readings', async () => {
|
||||
mockGet.mockResolvedValue({
|
||||
data: { found: false, recorded_at: null, payload: null },
|
||||
})
|
||||
|
||||
const { Wrapper } = makeWrapper()
|
||||
const { useLatestReading } = await import('./hooks')
|
||||
const { result } = renderHook(() => useLatestReading('test-uuid-2'), { wrapper: Wrapper })
|
||||
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
expect(result.current.data?.found).toBe(false)
|
||||
expect(result.current.data?.payload).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('useMetrics', () => {
|
||||
beforeEach(() => vi.clearAllMocks())
|
||||
|
||||
it('calls GET /api/modbus/devices/{uuid}/metrics with path param', async () => {
|
||||
mockGet.mockResolvedValue({
|
||||
data: {
|
||||
profile: 'sdm120',
|
||||
metrics: [{ key: 'voltage', label: 'Voltage', unit: 'V', device_class: 'voltage' }],
|
||||
},
|
||||
})
|
||||
|
||||
const { Wrapper } = makeWrapper()
|
||||
const { useMetrics } = await import('./hooks')
|
||||
const { result } = renderHook(() => useMetrics('test-uuid-1'), { wrapper: Wrapper })
|
||||
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/api/modbus/devices/{uuid}/metrics', {
|
||||
params: { path: { uuid: 'test-uuid-1' } },
|
||||
})
|
||||
expect(result.current.data?.metrics).toHaveLength(1)
|
||||
expect(result.current.data?.metrics[0].key).toBe('voltage')
|
||||
})
|
||||
})
|
||||
|
||||
describe('useReadings', () => {
|
||||
beforeEach(() => vi.clearAllMocks())
|
||||
|
||||
it('calls GET /api/modbus/devices/{uuid}/readings with window and limit', async () => {
|
||||
mockGet.mockResolvedValue({
|
||||
data: { items: [{ recorded_at: '2026-06-22T10:00:00Z', payload: { voltage: 230.2 } }] },
|
||||
})
|
||||
|
||||
const { Wrapper } = makeWrapper()
|
||||
const { useReadings } = await import('./hooks')
|
||||
const params = { start: '2026-06-22T09:00:00Z', end: '2026-06-22T10:00:00Z', limit: 500 }
|
||||
const { result } = renderHook(() => useReadings('test-uuid-1', params), { wrapper: Wrapper })
|
||||
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/api/modbus/devices/{uuid}/readings', {
|
||||
params: {
|
||||
path: { uuid: 'test-uuid-1' },
|
||||
query: {
|
||||
start: '2026-06-22T09:00:00Z',
|
||||
end: '2026-06-22T10:00:00Z',
|
||||
limit: 500,
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(result.current.data?.items).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('caps limit at READINGS_MAX_LIMIT (1000) even if caller requests more', async () => {
|
||||
mockGet.mockResolvedValue({ data: { items: [] } })
|
||||
|
||||
const { Wrapper } = makeWrapper()
|
||||
const { useReadings } = await import('./hooks')
|
||||
const { result } = renderHook(
|
||||
() => useReadings('test-uuid-1', { limit: 99999 }),
|
||||
{ wrapper: Wrapper },
|
||||
)
|
||||
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
// The query param limit should be capped at 1000.
|
||||
expect(mockGet).toHaveBeenCalledWith('/api/modbus/devices/{uuid}/readings',
|
||||
expect.objectContaining({
|
||||
params: expect.objectContaining({
|
||||
query: expect.objectContaining({ limit: 1000 }),
|
||||
}),
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('omits start/end from query params when not provided', async () => {
|
||||
mockGet.mockResolvedValue({ data: { items: [] } })
|
||||
|
||||
const { Wrapper } = makeWrapper()
|
||||
const { useReadings } = await import('./hooks')
|
||||
const { result } = renderHook(
|
||||
() => useReadings('test-uuid-1', {}),
|
||||
{ wrapper: Wrapper },
|
||||
)
|
||||
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/api/modbus/devices/{uuid}/readings',
|
||||
expect.objectContaining({
|
||||
params: expect.objectContaining({
|
||||
query: expect.not.objectContaining({ start: expect.anything() }),
|
||||
}),
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user