M8-T18: add source and multi-commodity meter UI

This commit is contained in:
2026-08-23 21:22:06 +02:00
parent 39c11ae606
commit 963e43e3e4
15 changed files with 319 additions and 18 deletions
+19
View File
@@ -81,6 +81,7 @@ function makeWrapper() {
// ---------------------------------------------------------------------------
describe('useDevices', () => {
// Source hooks use the same typed client and QueryClient invalidation boundary.
beforeEach(() => vi.clearAllMocks())
it('calls GET /api/modbus/devices and returns device list', async () => {
@@ -98,6 +99,24 @@ describe('useDevices', () => {
})
})
describe('useDeclareMeter source binding invalidation', () => {
beforeEach(() => vi.clearAllMocks())
it('invalidates every cache affected by atomic meter and binding creation', async () => {
mockPost.mockResolvedValue({ data: { id: 9 } })
const { qc, Wrapper } = makeWrapper()
const affectedKeys = [
['energy-meters'], ['energy-source-channels'], ['energy-meter-bindings', 9],
['energy-sources'], ['expose-catalog'], ['energy-costs'], ['energy-costs-summary'],
]
for (const queryKey of affectedKeys) qc.setQueryData(queryKey, { cached: true })
const { useDeclareMeter } = await import('./hooks')
const { result } = renderHook(() => useDeclareMeter(), { wrapper: Wrapper })
await act(async () => { await result.current.mutateAsync({ label: 'Heat', commodity: 'heating', started_at: '2026-08-01T00:00:00Z', reason: 'initial', source_channel_uuid: 'channel-1' } as never) })
expect(mockPost).toHaveBeenCalledWith('/api/energy/meters', expect.objectContaining({ body: expect.objectContaining({ source_channel_uuid: 'channel-1' }) }))
for (const queryKey of affectedKeys) expect(qc.getQueryState(queryKey)?.isInvalidated).toBe(true)
})
})
describe('useProfiles', () => {
beforeEach(() => vi.clearAllMocks())