import { afterEach, describe, expect, it, vi } from 'vitest' import { cleanup, fireEvent, render, screen, waitFor, within } from '@testing-library/react' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { TestDataProvider } from '../../test/providerCompat.jsx' import { MemoryRouter } from '../../test/routerCompat.jsx' import { AuditPage, auditCSV } from './AuditPage' afterEach(() => { cleanup(); vi.restoreAllMocks() }) const httpQuery = () => new QueryClient({ defaultOptions: { queries: { retry: false } } }) function renderAudit(dataProvider, initialEntry = '/audit?task_id=task-a') { return render( , ) } describe('AuditPage', () => { it('deep-links safe correlation fields and exposes no mutation or secret details', async () => { const dataProvider = { getList: vi.fn().mockResolvedValue({ data: [{ id: 7, event_type: 'task_verified', account_id: 'account-a', task_id: 'task-a', attempt_id: 'attempt-a', browser_env_alias: 'env-a', network_exit_id: 'exit-a', binding_version: 4, reason_code: 'manual_verification_recorded', details: { token: 'must-not-render' }, created_at: '2026-08-30T00:00:00Z', }], total: 1 }), getOne: vi.fn(), getMany: vi.fn(), getManyReference: vi.fn(), create: vi.fn(), update: vi.fn(), updateMany: vi.fn(), delete: vi.fn(), deleteMany: vi.fn(), } renderAudit(dataProvider) expect((await screen.findAllByRole('link', { name: 'task-a' })).length).toBeGreaterThan(0) expect(screen.getAllByRole('link', { name: 'account-a' })[0].getAttribute('href')).toBe('/accounts/account-a') expect(screen.getAllByRole('link', { name: 'attempt-a' })[0].getAttribute('href')).toBe('/attempts/attempt-a') expect(screen.getAllByRole('link', { name: 'env-a' })[0].getAttribute('href')).toBe('/browsers/env-a') expect(screen.getAllByRole('link', { name: 'exit-a' })[0].getAttribute('href')).toBe('/network-exits/exit-a') expect(screen.queryByText('must-not-render')).toBeNull() expect(screen.queryByRole('button', { name: /编辑|删除/ })).toBeNull() }) it('exports only allowlisted correlation columns and requests the next page', async () => { const event = { id: 7, event_type: 'task_verified', task_id: 'task-a', attempt_id: 'attempt-a', browser_env_alias: 'env-a', network_exit_id: 'exit-a', details: { api_key: 'api-secret', private_key: 'private-secret', credential_key: 'credential-secret', authorization_header: 'auth-secret', proxy_url: 'proxy-secret' }, created_at: '2026-08-30T00:00:00Z', } const csv = auditCSV([event]) expect(csv).toContain('attempt-a') for (const secret of ['api-secret', 'private-secret', 'credential-secret', 'auth-secret', 'proxy-secret']) expect(csv).not.toContain(secret) const getList = vi.fn().mockImplementation(() => Promise.resolve({ data: [{ ...event, id: 7 }], total: 26 })) const dataProvider = { getList, getOne: vi.fn(), getMany: vi.fn(), getManyReference: vi.fn(), create: vi.fn(), update: vi.fn(), updateMany: vi.fn(), delete: vi.fn(), deleteMany: vi.fn(), } renderAudit(dataProvider, '/audit') fireEvent.click(await screen.findByRole('button', { name: '下一页' })) await waitFor(() => expect(getList.mock.calls.some(([{ pagination }]) => (pagination?.currentPage || pagination?.page) === 2)).toBe(true)) }) })