import { cleanup, fireEvent, render, screen, within } from '@testing-library/react'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { InventoryPage } from '../../src/InventoryPage'; afterEach(() => { cleanup(); vi.unstubAllGlobals(); }); const json = (value: unknown) => new Response(JSON.stringify(value), { status: 200, headers: { 'Content-Type': 'application/json' } }); describe('InventoryPage', () => { it('renders effective status and sends bounded filters and pagination', async () => { const fetch = vi.fn(async (input: RequestInfo | URL) => { const url = String(input); if (url.includes('after=')) return json({ items: [{ id: 'b', entityType: 'probe', canonicalName: 'probe.b', displayName: 'Probe B', status: 'unknown', factCount: 0, overrideCount: 0, relationCount: 0, sourceCount: 0, staleFactCount: 0 }], hasMore: false, nextCursor: '' }); return json({ items: [{ id: 'a', entityType: 'container', canonicalName: 'container.a', displayName: 'Handmatige API', status: 'operational', factCount: 2, overrideCount: 1, relationCount: 1, sourceCount: 2, staleFactCount: 0 }], hasMore: true, nextCursor: 'next' }); }); vi.stubGlobal('fetch', fetch); render(); expect(await screen.findByText('Handmatige API')).toBeVisible(); expect(screen.getByText('2 bronnen · 2 feiten · 1 relaties · 1 correcties')).toBeVisible(); fireEvent.change(screen.getByLabelText('Zoeken'), { target: { value: 'api' } }); expect(await screen.findByText('Handmatige API')).toBeVisible(); expect(fetch.mock.calls.some(([url]) => String(url).includes('q=api'))).toBe(true); expect(window.location.search).toContain('q=api'); fireEvent.click(screen.getByRole('button', { name: 'Volgende pagina' })); expect(await screen.findByText('Probe B')).toBeVisible(); expect(window.location.search).toContain('after=next'); }); it('shows override priority while preserving discovered facts and stale relations', async () => { vi.stubGlobal('fetch', vi.fn(async () => json({ entity: { id: 'a', entityType: 'container', canonicalName: 'container.a', displayName: 'Handmatige API', status: 'operational', firstSeenAt: '2026-08-12T00:00:00Z', factCount: 2, overrideCount: 1, relationCount: 1, sourceCount: 2, staleFactCount: 1 }, aliases: [{ sourceName: 'Unraid', externalType: 'container', externalId: 'api' }], facts: [{ fieldName: 'image', sourceId: 'unraid', sourceName: 'Unraid', value: 'discovered:latest', observedAt: '2026-08-12T00:00:00Z', confidence: 1, stale: false }, { fieldName: 'runtimeState', sourceId: 'unraid', sourceName: 'Unraid', value: 'running', observedAt: '2026-08-12T00:00:00Z', confidence: 1, stale: false }], overrides: [{ fieldName: 'image', value: 'manual:pinned', updatedAt: '2026-08-12T01:00:00Z' }], effectiveValues: [{ fieldName: 'image', value: 'manual:pinned', origin: 'override', stale: false, overriddenAt: '2026-08-12T01:00:00Z' }, { fieldName: 'runtimeState', value: 'running', origin: 'discovered', sourceName: 'Unraid', observedAt: '2026-08-12T00:00:00Z', confidence: 1, stale: false }], relations: [{ id: 'r', direction: 'outgoing', relationType: 'depends_on', peerId: 'b', peerType: 'service', peerName: 'Database', peerStatus: 'Ontbrekende entiteit', peerTombstonedAt: '2026-08-11T00:00:00Z', sourceName: 'Agent', confidence: .8, confirmed: false }], }))); render(); const effective = await screen.findByRole('heading', { name: 'Wat Pulse momenteel gebruikt' }); expect(within(effective.closest('section')!).getByText('manual:pinned')).toBeVisible(); expect(screen.getByText('Handmatige correctie')).toBeVisible(); expect(screen.getByText('Runtime-status')).toBeVisible(); expect(screen.getByText('Actief')).toBeVisible(); fireEvent.click(screen.getByText('Alle bronfeiten en correcties')); expect(screen.getByText(/discovered:latest/)).toBeVisible(); expect(screen.getByText(/afgeleid/)).toBeVisible(); expect(screen.getByText('Ontbrekende entiteit')).toBeVisible(); }); });