import { act, type ReactNode } from 'react' import { createRoot } from 'react-dom/client' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import type { PolicyRollout } from '@evofw/shared' import { WebhooksSheet } from '@/components/webhooks-sheet' import { AgentLabelsSheet } from '@/components/agents/agent-labels-sheet' import { PolicyRolloutsSheet } from '@/components/rules/policy-rollouts-sheet' import { AgentCredentialsSheet } from '@/components/agents/agent-credentials-sheet' import { ensureAuthConfig, setToken, getToken } from '@/lib/auth' import '@evofw/ui/globals.css' if (!import.meta.env.DEV) throw new Error('Development checks only') ;( globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean } ).IS_REACT_ACT_ENVIRONMENT = true const results = document.querySelector('#results')! const metrics = document.querySelector('#metrics')! const root = createRoot(document.querySelector('#fixture')!) const qc = new QueryClient({ defaultOptions: { queries: { retry: false }, mutations: { retry: false } }, }) const originalFetch = window.fetch const originalToken = getToken() const checks: string[] = [] const calls: { method: string; path: string; body: any }[] = [] const flush = () => new Promise((resolve) => setTimeout(resolve, 75)) function check(value: unknown, label: string) { if (!value) throw new Error(label) checks.push(`PASS ${label}`) results.textContent = checks.join('\n') } async function render(node: ReactNode) { await act(async () => { root.render({node}) await flush() }) await act(async () => { await flush() }) } async function click(text: string) { await act(async () => { const button = [ ...document.querySelectorAll('button'), ].find((item) => item.textContent?.trim() === text && !item.disabled) if (!button) throw new Error(`Button missing or disabled: ${text}`) button.click() await flush() }) } function button(text: string) { return [...document.querySelectorAll('button')].find( (item) => item.textContent?.trim() === text, ) } async function input(id: string, value: string) { await act(async () => { const field = document.getElementById(id) as HTMLInputElement Object.getOwnPropertyDescriptor( HTMLInputElement.prototype, 'value', )!.set!.call(field, value) field.dispatchEvent(new Event('input', { bubbles: true })) await flush() }) } async function submit() { await act(async () => { document .querySelector('[data-slot="sheet-content"] form')! .requestSubmit() await flush() }) } async function choose(id: string, label: string) { await act(async () => { const field = document.getElementById(id)! field.focus() field.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true })) await flush() await flush() }) await act(async () => { const option = [ ...document.querySelectorAll('[role="option"]'), ].find((item) => item.textContent?.trim() === label) if (!option) throw new Error(`Option missing: ${label}`) option.click() await flush() }) await act(async () => { document .getElementById(id) ?.dispatchEvent( new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }), ) await flush() }) } async function chooseSelect(index: number, label: string) { await act(async () => { document .querySelectorAll('[data-slot="select-trigger"]') [index].click() await flush() }) await act(async () => { const option = [ ...document.querySelectorAll('[role="option"]'), ].find((item) => item.textContent?.trim() === label) if (!option) throw new Error(`Select option missing: ${label}`) option.click() await flush() }) } const writes = (path: string) => calls.filter((call) => call.method !== 'GET' && call.path === path) const fixtureToken = (permissions: string[]) => `fixture.${btoa(JSON.stringify({ sub: 'fixture', permissions, apps: ['fw'] }))}.unsigned` let configured = true, failHook = true, failLabels = true, pendingRotation = false const endpoint = { id: 'hook', name: 'Existing endpoint', url: 'https://fixture.invalid/hook', events: ['agent.offline'], enabled: false, secret_configured: true, created_at: '', updated_at: '', } let rollout: PolicyRollout = { id: 'rollout', snapshot_id: 'snapshot', state: 'canary', version: 9, created_at: '2026-10-24', updated_at: '', targets: [ { agent_id: 'target', is_canary: true, phase: 'candidate', desired_revision: 8, desired_hash: 'new', applied_revision: null, applied_hash: null, acknowledged: false, previous_desired_snapshot_id: 'previous', }, ], } window.fetch = async (request, init) => { const url = String(request), path = new URL(url, location.origin).pathname, method = init?.method ?? 'GET' const body = init?.body ? JSON.parse(String(init.body)) : null calls.push({ method, path, body }) metrics.textContent = JSON.stringify( calls.map(({ method, path }) => ({ method, path })), null, 2, ) if (path === '/api/auth/config') return Response.json({ required: true }) if (path === '/api/v1/agents') return Response.json({ items: [ { id: 'target', name: 'Fixture target', hostname: 'fixture.invalid' }, ], }) if (path === '/api/v1/webhooks') { if (method === 'POST') return failHook ? Response.json( { error: { message: 'Fixture webhook failure' } }, { status: 503 }, ) : Response.json(endpoint) return Response.json({ items: [endpoint], configured, limits: { endpoints: 10, pending: 100 }, queue: { pending: 1, dead: 2, dropped: 3 }, }) } if (path === '/api/v1/webhooks/hook') return Response.json(endpoint) if (path.endsWith('/deliveries')) return Response.json({ items: [], total: 0 }) if (path === '/api/v1/agent-labels') return Response.json({ items: [ { id: 'label-a', name: 'Fixture label A', color: null, created_at: '' }, { id: 'label-b', name: 'Fixture label B', color: null, created_at: '' }, ], total: 2, }) if (path.endsWith('/labels')) { if (method === 'PUT' && failLabels) return Response.json( { error: { message: 'Fixture label write rejected' } }, { status: 409 }, ) return Response.json({ agent_id: 'target', label_ids: ['label-a'] }) } if (path === '/api/v1/policy-sets') return Response.json({ items: [{ id: 'set', name: 'Fixture set' }] }) if (path.endsWith('/preview-assignment')) return Response.json({ mode: body.mode, items: [ { agent_id: 'target', generation: 7, previous_set_ids: [], set_ids: ['set'], changed: true, }, ], total: 1, }) if (path.endsWith('/assign-policy-sets')) return Response.json({ items: [], affected_agent_ids: ['target'] }) if (path === '/api/v1/policies/snapshots') return Response.json({ items: [{ id: 'snapshot', name: 'Fixture snapshot' }], }) if (path.endsWith('/diff')) return Response.json({ snapshot_id: 'snapshot', items: [ { agent_id: 'target', expected_generation: 7, before_hash: 'old', candidate_hash: 'new', added: 1, removed: 0, examples: [], truncated: false, default_action_changed: false, port_rules_changed: false, conflict: null, }, ], }) if (path === '/api/v1/policies/rollouts') return method === 'POST' ? Response.json(rollout) : Response.json({ items: [rollout] }) if (path === '/api/v1/policies/rollouts/rollout') return Response.json(rollout) if ( path.endsWith('/confirm') || path.endsWith('/rollback') || path.endsWith('/release') ) return Response.json({ ok: true }) if (path.endsWith('/token-rotation')) { if (method === 'POST') { pendingRotation = true return Response.json( { request_id: '00000000-0000-4000-8000-000000000001', expires_at: '2099-01-01T00:00:00Z', }, { status: 202 }, ) } return Response.json({ agent_id: 'target', supported: true, client_version: '1.0.0', agent_status: 'approved', rotation: pendingRotation ? { request_id: '00000000-0000-4000-8000-000000000001', expires_at: '2099-01-01T00:00:00Z', } : null, completed_rotation: null, }) } if (path === '/api/v1/install-links') return Response.json({ items: [ { id: 'install', slug: 'not-rendered', client_name: 'Fixture target', platform: 'linux', agent_id: 'target', created_at: '2026-10-24', expires_at: '2026-10-25', consumed_at: '2026-10-24', revoked_at: null, use_count: 1, urls: { by_id: 'not-rendered', by_slug: 'not-rendered' }, curl: { by_id: 'credential-must-not-render', by_slug: 'credential-must-not-render', }, }, ], total: 1, }) throw new Error(`Unexpected fixture request ${method} ${path}`) } try { await ensureAuthConfig() setToken( fixtureToken(['fw:agents:write', 'fw:policies:write', 'fw:settings:admin']), ) await render( {}} />) check( document.body.textContent?.includes('1 ожидают') && document.body.textContent.includes('2 исчерпали'), 'webhook health keeps pending/dead/dropped distinct', ) await input('webhook-name', 'Draft hook') await input('webhook-url', 'https://fixture.invalid/new') await input('webhook-secret', 'short') await submit() check( writes('/api/v1/webhooks').length === 0 && document .getElementById('webhook-secret') ?.getAttribute('aria-invalid') === 'true', 'short webhook secret blocked inline before POST', ) await input('webhook-secret', 'fixture-only-never-delivered-0000000000') await submit() check( writes('/api/v1/webhooks').length === 1 && (document.getElementById('webhook-name') as HTMLInputElement).value === 'Draft hook', 'failed webhook write preserves dirty draft', ) failHook = false await submit() check( (document.getElementById('webhook-secret') as HTMLInputElement).value === '' && writes('/api/v1/webhooks')[1].body.enabled === false, 'successful create clears write-only secret and defaults disabled', ) await click('Изменить') await input('webhook-name', 'Renamed hook') await submit() check( writes('/api/v1/webhooks/hook')[0]?.body.name === 'Renamed hook' && !('secret' in writes('/api/v1/webhooks/hook')[0].body), 'edit omits blank secret instead of clearing existing secret', ) configured = false await act(async () => { await qc.invalidateQueries({ queryKey: ['webhooks'] }) await flush() }) check( button('Добавить webhook')?.disabled && document.body.textContent?.includes('Шифрование webhook не настроено'), 'missing encryption key disables writes with explanation', ) await render(null) qc.clear() setToken(fixtureToken(['fw:agents:read'])) const previousCalls = calls.length await render( {}} />) check( calls.length === previousCalls && document.body.textContent?.includes('Требуются права администратора'), 'non-admin webhook panel fetches and writes nothing', ) await render(null) qc.clear() setToken(fixtureToken(['fw:agents:write', 'fw:policies:write'])) await render( {}} />, ) await choose('agent-label-ids', 'Fixture label B') await act(async () => { await qc.invalidateQueries({ queryKey: ['agents', 'target', 'labels'] }) await flush() }) check( document.body.textContent?.includes('Fixture label B'), 'agent labels retain dirty selection across refetch', ) await click('Сохранить метки агента') check( writes('/api/v1/agents/target/labels')[0]?.body.label_ids.includes( 'label-b', ) && !button('Сохранить метки агента')?.disabled, 'rejected labels write preserves editable selection', ) await render(null) qc.clear() await render( {}} />) await choose('group-label-selector', 'Fixture label A') await choose('group-sets', 'Fixture set') await click('Предпросмотр назначения') check( writes('/api/v1/agent-labels/assign-policy-sets').length === 0 && document.body.textContent?.includes('Fixture target'), 'group preview resolves named agents without auto-assignment', ) await click('Применить показанное назначение') check( writes('/api/v1/agent-labels/assign-policy-sets').length === 0, 'group write requires explicit confirmation', ) await click('Подтвердить') check( JSON.stringify( writes('/api/v1/agent-labels/assign-policy-sets')[0]?.body.expected, ) === JSON.stringify([{ agent_id: 'target', generation: 7 }]), 'group assignment sends exact preview CAS generations', ) await render(null) qc.clear() await render( {}} />) await chooseSelect(0, 'Fixture snapshot') await choose('rollout-targets', 'Fixture targetfixture.invalid') check( button('Запустить canary')?.disabled, 'canary start gated on prior diff', ) await click('Предпросмотр diff') await choose('rollout-canaries', 'Fixture targetfixture.invalid') await click('Запустить canary') check( writes('/api/v1/policies/rollouts').length === 0, 'canary publication requires separate confirmation', ) await click('Подтвердить') check( writes('/api/v1/policies/rollouts')[0]?.body.targets[0] .expected_generation === 7 && button('Подтвердить после ACK')?.disabled && document.body.textContent?.includes('Ожидается ACK'), 'canary uses diff generation and pending ACK never enables confirmation', ) rollout = { ...rollout, targets: [ { ...rollout.targets[0], acknowledged: true, applied_revision: 8, applied_hash: 'new', }, ], } await act(async () => { await qc.invalidateQueries({ queryKey: ['policy-rollouts'] }) await flush() }) check( !button('Подтвердить после ACK')?.disabled && writes('/api/v1/policies/rollouts/rollout/confirm').length === 0, 'fresh canary ACK enables but does not auto-confirm', ) await click('Подтвердить после ACK') await click('Подтвердить') check( writes('/api/v1/policies/rollouts/rollout/confirm')[0]?.body .expected_version === 9, 'rollout transition sends exact displayed version', ) await click('Снять pin') check( document.body.textContent?.includes('живая политика будет проверена') && writes('/api/v1/agents/target/policy-pin/release').length === 0, 'pin release warns about validated live-policy resume before write', ) await click('Отмена') await render(null) qc.clear() await render( {}} />, ) check( document.body.textContent?.includes('Использована') && !document.body.textContent.includes('credential-must-not-render'), 'credential panel displays link lifecycle without installation credentials', ) await click('Запросить ротацию') check( writes('/api/v1/agents/target/token-rotation').length === 0, 'rotation requires explicit request confirmation', ) await click('Запросить') check( pendingRotation && document.body.textContent?.includes('Ожидается обмен ключа агентом') && button('Запросить ротацию')?.disabled, 'rotation request remains pending until agent exchange', ) results.dataset.status = 'passed' } catch (error) { results.textContent = `${checks.join('\n')}\nFAIL ${error instanceof Error ? error.stack : String(error)}` results.dataset.status = 'failed' } finally { if (results.dataset.status === 'passed') { await act(async () => root.unmount()) qc.clear() window.fetch = originalFetch } if (originalToken) setToken(originalToken) else localStorage.removeItem('fw_auth_token') }