import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'; import { Inventory } from './Inventory'; const VM_INSTANCE = { id: 'vm-1', runtime: 'vm', provider: 'cloud-hypervisor', capabilities: [ { id: 'instance.snapshot', label: 'Snapshot' }, { id: 'instance.restore', label: 'Restore' }, { id: 'instance.fork', label: 'Fork' }, { id: 'warm_pool.manage', label: 'Warm pools' }, ], loadout: 'security-audit', state: 'running', tenant: 'default', card_url: '', runtime_posture: { kind: 'vm', isolation: 'strong', label: 'VM' }, host_daemon: { status: 'available' }, transport: { mode: 'mtls', trust: 'secure', label: 'mTLS', source: 'test' }, launch_context: { name: 'vm-one', loadout: 'security-audit' }, session_backends: [{ mode: 'managed', backend: 'zellij', available: true, drive: true }], }; beforeEach(() => {}); afterEach(() => { cleanup(); vi.restoreAllMocks(); }); describe('Inventory provider-aware fast-start controls', () => { it('distinguishes secure managed UDS, compatibility fallback, and legacy recreation posture', async () => { const postures = [ { ...VM_INSTANCE, id: 'docker-secure', runtime: 'docker', runtime_posture: { kind: 'docker', isolation: 'shared-kernel', label: 'Docker' }, managed_docker_posture: { transport_mode: 'uds', control_identity_present: true, control_identity_range_valid: true, workload_uid: 10001, workload_identity_separated: true, boundary: 'separated', secure_default: true, compatibility: false, requires_recreation: false, source: 'agentic-sandbox' } }, { ...VM_INSTANCE, id: 'docker-desktop', runtime: 'docker', runtime_posture: { kind: 'docker', isolation: 'shared-kernel', label: 'Docker' }, managed_docker_posture: { transport_mode: 'mtls-bootstrap', control_identity_present: true, control_identity_range_valid: true, workload_uid: 10001, workload_identity_separated: true, boundary: 'separated', secure_default: false, compatibility: true, fallback_reason: 'Docker Desktop peer UID unavailable', requires_recreation: false, source: 'agentic-sandbox' } }, { ...VM_INSTANCE, id: 'docker-old', runtime: 'docker', runtime_posture: { kind: 'docker', isolation: 'shared-kernel', label: 'Docker' }, managed_docker_posture: { transport_mode: 'unknown', control_identity_present: false, control_identity_range_valid: false, workload_identity_separated: false, boundary: 'unknown', secure_default: false, compatibility: true, requires_recreation: true, source: 'agentic-sandbox' } }, ]; globalThis.fetch = vi.fn(async () => new Response(JSON.stringify({ count: 3, fetched_at: '2026-08-04T00:00:00Z', instances: postures }), { status: 200, headers: { 'content-type': 'application/json' } })) as typeof fetch; render(); expect(await screen.findByText('Managed UDS · split identity')).toBeTruthy(); expect(screen.getByText('Compatibility transport')).toBeTruthy(); expect(screen.getByText('Recreate required')).toBeTruthy(); }); it('runs a capability-gated snapshot action and records terminal audit evidence', async () => { const fetchMock = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => { const url = String(input); const ok = (body: unknown) => new Response(JSON.stringify(body), { status: 200, headers: { 'content-type': 'application/json' } }); if (url.includes('/api/inventory')) return ok({ count: 1, fetched_at: '2026-07-29T00:00:00Z', instances: [VM_INSTANCE] }); if (url.includes('/api/instances/vm-1/snapshot') && init?.method === 'POST') return ok({ id: 'op-1', state: 'running' }); if (url.includes('/api/operations/op-1')) return ok({ id: 'op-1', state: 'succeeded', result: { provider: 'cloud-hypervisor', snapshot_id: 'vm-one-snap' } }); if (url.includes('/api/audit/intent') && init?.method === 'POST') return ok({ id: 'audit-1' }); return new Response('{}', { status: 404 }); }) as unknown as typeof fetch; globalThis.fetch = fetchMock; vi.spyOn(window, 'prompt').mockReturnValue('vm-one-snap'); render(); fireEvent.click(await screen.findByRole('button', { name: /snapshot vm-1/i })); await waitFor(() => expect(screen.getByRole('status').textContent).toContain('Snapshot succeeded: op-1')); const actionCall = (fetchMock as unknown as ReturnType).mock.calls .find((call) => String(call[0]).includes('/api/instances/vm-1/snapshot')); expect(JSON.parse(String(actionCall?.[1]?.body))).toMatchObject({ asset_ref: 'vm-one-snap' }); const auditCall = (fetchMock as unknown as ReturnType).mock.calls .find((call) => String(call[0]).includes('/api/audit/intent')); expect(JSON.parse(String(auditCall?.[1]?.body))).toMatchObject({ event: 'instance.fast_start.terminal', detail: { instance_id: 'vm-1', provider: 'cloud-hypervisor', action: 'snapshot', operation_id: 'op-1', state: 'succeeded', }, }); }); it('disables unsafe fast-start controls when VFIO constraints exclude them', async () => { const vfioVm = { ...VM_INSTANCE, capability_constraints: [{ capability: 'device.vfio', excludes: ['instance.snapshot', 'instance.restore', 'instance.fork', 'warm_pool.manage'], reason: 'VFIO-backed VMs cannot safely reuse memory state.', }], }; globalThis.fetch = vi.fn(async (input: RequestInfo | URL) => { if (String(input).includes('/api/inventory')) { return new Response(JSON.stringify({ count: 1, fetched_at: '2026-07-29T00:00:00Z', instances: [vfioVm] }), { status: 200, headers: { 'content-type': 'application/json' }, }); } return new Response('{}', { status: 404 }); }) as unknown as typeof fetch; render(); const snapshot = await screen.findByRole('button', { name: /snapshot vm-1/i }) as HTMLButtonElement; expect(snapshot.disabled).toBe(true); expect(snapshot.getAttribute('title')).toBe('VFIO-backed VMs cannot safely reuse memory state.'); expect((screen.getByRole('button', { name: /restore vm-1/i }) as HTMLButtonElement).disabled).toBe(true); expect((screen.getByRole('button', { name: /fork vm-1/i }) as HTMLButtonElement).disabled).toBe(true); expect((screen.getByRole('button', { name: /warm pool vm-1/i }) as HTMLButtonElement).disabled).toBe(true); }); it('renders libvirt checkpoint controls without unsupported fork', async () => { const libvirtVm = { ...VM_INSTANCE, provider: 'libvirt', capabilities: [ { id: 'instance.checkpoint', label: 'Checkpoint' }, { id: 'instance.restore', label: 'Checkpoint restore' }, { id: 'warm_pool.manage', label: 'Warm pools' }, ], }; globalThis.fetch = vi.fn(async (input: RequestInfo | URL) => { if (String(input).includes('/api/inventory')) { return new Response(JSON.stringify({ count: 1, fetched_at: '2026-07-29T00:00:00Z', instances: [libvirtVm] }), { status: 200, headers: { 'content-type': 'application/json' }, }); } return new Response('{}', { status: 404 }); }) as unknown as typeof fetch; render(); expect(await screen.findByRole('button', { name: /checkpoint vm-1/i })).toBeTruthy(); expect(screen.getByRole('button', { name: /restore vm-1/i })).toBeTruthy(); expect(screen.getByRole('button', { name: /warm pool vm-1/i })).toBeTruthy(); expect(screen.queryByRole('button', { name: /fork vm-1/i })).toBeNull(); }); it('renders degraded sandbox trust recovery without secret material', async () => { globalThis.fetch = vi.fn(async (input: RequestInfo | URL) => { if (String(input).includes('/api/inventory')) { return new Response(JSON.stringify({ count: 1, fetched_at: '2026-07-29T00:00:00Z', bootstrap_trust: { status: 'degraded', mode: 'mtls', label: 'Sandbox trust degraded', source: '/api/v2/admin/bootstrap/readiness', ca_provider_ref: 'vault://sandbox-ca/current', trust_bundle_ref: 'trust-bundle://sandbox/current', client_identity_ref: 'spiffe://sandbox.agentic.local/cockpit/bridge', rotation_state: 'reload-required', trust_bundle_fresh: false, token_store_configured: true, missing_required_material: ['fresh_trust_bundle'], recovery: 'Refresh sandbox CA/bootstrap readiness, rotate stale trust material, then reload Cockpit.', }, instances: [VM_INSTANCE], }), { status: 200, headers: { 'content-type': 'application/json' }, }); } return new Response('{}', { status: 404 }); }) as unknown as typeof fetch; render(); const banner = await screen.findByRole('alert'); expect(banner.textContent).toContain('Sandbox trust degraded'); expect(banner.textContent).toContain('rotate stale trust material'); expect(banner.textContent).toContain('vault://sandbox-ca/current'); expect(banner.textContent).not.toMatch(/BEGIN CERTIFICATE|PRIVATE KEY|secret-token/i); }); }); describe('Inventory Open Desktop entry (#2547)', () => { const gatewayId = '11111111-1111-4111-8111-111111111111'; const policy = { observe: false, control: true, sharing: false, clipboard_copy: false, clipboard_paste: false, file_transfer: false, audio: false, recording: false, isolation_tier: 'cooperative', generation: 1 }; const capability = { schema_version: 'rdp-cockpit.v1', instance_id: gatewayId, incarnation: 'boot-1', policy, supported: true, readiness: 'ready', reason_codes: [] }; const ok = (body: unknown) => new Response(JSON.stringify(body), { status: 200, headers: { 'content-type': 'application/json' } }); function stub(capabilityBody: unknown, instances = [{ ...VM_INSTANCE, id: gatewayId }]) { const fetchMock = vi.fn(async (input: RequestInfo | URL) => { const url = String(input); if (url.includes('/api/inventory')) return ok({ count: instances.length, fetched_at: '2026-09-13T00:00:00Z', instances }); if (url.endsWith('/capability')) return ok(capabilityBody); return new Response('{}', { status: 404 }); }) as unknown as typeof fetch; globalThis.fetch = fetchMock; return fetchMock; } it('renders no Open Desktop button without an onOpenDesktop handler and never probes capability', async () => { const fetchMock = stub(capability); render(); await screen.findByRole('button', { name: /stop instance/i }); expect(screen.queryByRole('button', { name: /open desktop/i })).toBeNull(); expect((fetchMock as unknown as ReturnType).mock.calls.some((call) => String(call[0]).endsWith('/capability'))).toBe(false); }); it('enables Open Desktop for a supported, ready instance and opens it', async () => { stub(capability); const onOpenDesktop = vi.fn(); render(); const button = await screen.findByRole('button', { name: `Open desktop for ${gatewayId.slice(0, 8)}…` }) as HTMLButtonElement; await waitFor(() => expect(button.disabled).toBe(false)); fireEvent.click(button); expect(onOpenDesktop).toHaveBeenCalledWith(gatewayId); }); it('disables Open Desktop with the backend reason when unsupported or not ready', async () => { stub({ state: 'unsupported', reason: 'desktop_backend_not_configured' }); render( {}} />); let button = await screen.findByRole('button', { name: /open desktop/i }) as HTMLButtonElement; await waitFor(() => expect(button.title).toBe('Desktop backend is not configured')); expect(button.disabled).toBe(true); cleanup(); stub({ ...capability, readiness: 'not_ready', reason_codes: ['desktop_not_ready'] }); render( {}} />); button = await screen.findByRole('button', { name: /open desktop/i }) as HTMLButtonElement; await waitFor(() => expect(button.title).toBe('Desktop is still getting ready: desktop_not_ready')); expect(button.disabled).toBe(true); }); it('disables Open Desktop for a non-gateway instance id without calling the desktop API', async () => { const fetchMock = stub(capability, [VM_INSTANCE]); render( {}} />); const button = await screen.findByRole('button', { name: /open desktop for vm-1/i }) as HTMLButtonElement; await waitFor(() => expect(button.title).toBe('Desktop requires a gateway instance id')); expect(button.disabled).toBe(true); expect((fetchMock as unknown as ReturnType).mock.calls.some((call) => String(call[0]).endsWith('/capability'))).toBe(false); }); });