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();
});
});