Files
Charon/frontend/src/hooks/__tests__/useImport.test.tsx
GitHub Actions 7ca5a11572 Add ImportSuccessModal tests, enhance AuthContext for token management, and improve useImport hook
- Implement tests for ImportSuccessModal to verify rendering and functionality.
- Update AuthContext to store authentication token in localStorage and manage token state.
- Modify useImport hook to capture and expose commit results, preventing unnecessary refetches.
- Enhance useCertificates hook to support optional refetch intervals.
- Update Dashboard to conditionally poll certificates based on pending status.
- Integrate ImportSuccessModal into ImportCaddy for user feedback on import completion.
- Adjust Login component to utilize returned token for authentication.
- Refactor CrowdSecConfig tests for improved readability and reliability.
- Add debug_db.py script for inspecting the SQLite database.
- Update integration and test scripts for better configuration and error handling.
- Introduce Trivy scan script for vulnerability assessment of Docker images.
2025-12-12 00:05:15 +00:00

349 lines
9.6 KiB
TypeScript

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { renderHook, waitFor, act } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import React from 'react'
import { useImport } from '../useImport'
import * as api from '../../api/import'
// Mock the API
vi.mock('../../api/import', () => ({
uploadCaddyfile: vi.fn(),
getImportPreview: vi.fn(),
commitImport: vi.fn(),
cancelImport: vi.fn(),
getImportStatus: vi.fn(),
}))
const createWrapper = () => {
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
})
return ({ children }: { children: React.ReactNode }) => (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
)
}
describe('useImport', () => {
beforeEach(() => {
vi.clearAllMocks()
vi.mocked(api.getImportStatus).mockResolvedValue({ has_pending: false })
})
afterEach(() => {
vi.clearAllMocks()
})
it('starts with no active session', async () => {
const { result } = renderHook(() => useImport(), { wrapper: createWrapper() })
await waitFor(() => {
expect(result.current.loading).toBe(false)
expect(result.current.session).toBeNull()
})
expect(result.current.error).toBeNull()
})
it('uploads content and creates session', async () => {
const mockSession = {
id: 'session-1',
state: 'reviewing' as const,
created_at: '2025-01-18T10:00:00Z',
updated_at: '2025-01-18T10:00:00Z',
}
const mockPreviewData = {
hosts: [{ domain_names: 'test.com' }],
conflicts: [],
errors: [],
}
const mockResponse = {
session: mockSession,
preview: mockPreviewData,
}
vi.mocked(api.uploadCaddyfile).mockResolvedValue(mockResponse)
vi.mocked(api.getImportStatus).mockResolvedValue({ has_pending: true, session: mockSession })
vi.mocked(api.getImportPreview).mockResolvedValue(mockResponse)
const { result } = renderHook(() => useImport(), { wrapper: createWrapper() })
await act(async () => {
await result.current.upload('example.com { reverse_proxy localhost:8080 }')
})
await waitFor(() => {
expect(result.current.session).toEqual(mockSession)
})
expect(api.uploadCaddyfile).toHaveBeenCalledWith('example.com { reverse_proxy localhost:8080 }')
expect(result.current.loading).toBe(false)
})
it('handles upload errors', async () => {
const mockError = new Error('Upload failed')
vi.mocked(api.uploadCaddyfile).mockRejectedValue(mockError)
const { result } = renderHook(() => useImport(), { wrapper: createWrapper() })
let threw = false
await act(async () => {
try {
await result.current.upload('invalid')
} catch {
threw = true
}
})
expect(threw).toBe(true)
await waitFor(() => {
expect(result.current.error).toBe('Upload failed')
})
})
it('commits import with resolutions', async () => {
const mockSession = {
id: 'session-2',
state: 'reviewing' as const,
created_at: '2025-01-18T10:00:00Z',
updated_at: '2025-01-18T10:00:00Z',
}
const mockResponse = {
session: mockSession,
preview: { hosts: [], conflicts: [], errors: [] },
}
let isCommitted = false
vi.mocked(api.uploadCaddyfile).mockResolvedValue(mockResponse)
vi.mocked(api.getImportStatus).mockImplementation(async () => {
if (isCommitted) return { has_pending: false }
return { has_pending: true, session: mockSession }
})
vi.mocked(api.getImportPreview).mockResolvedValue(mockResponse)
vi.mocked(api.commitImport).mockImplementation(async () => {
isCommitted = true
return { created: 0, updated: 0, skipped: 0, errors: [] }
})
const { result } = renderHook(() => useImport(), { wrapper: createWrapper() })
await act(async () => {
await result.current.upload('test')
})
await waitFor(() => {
expect(result.current.session).toEqual(mockSession)
})
await act(async () => {
await result.current.commit({ 'test.com': 'skip' }, { 'test.com': 'Test' })
})
expect(api.commitImport).toHaveBeenCalledWith('session-2', { 'test.com': 'skip' }, { 'test.com': 'Test' })
await waitFor(() => {
expect(result.current.session).toBeNull()
})
})
it('cancels active import session', async () => {
const mockSession = {
id: 'session-3',
state: 'reviewing' as const,
created_at: '2025-01-18T10:00:00Z',
updated_at: '2025-01-18T10:00:00Z',
}
const mockResponse = {
session: mockSession,
preview: { hosts: [], conflicts: [], errors: [] },
}
let isCancelled = false
vi.mocked(api.uploadCaddyfile).mockResolvedValue(mockResponse)
vi.mocked(api.getImportStatus).mockImplementation(async () => {
if (isCancelled) return { has_pending: false }
return { has_pending: true, session: mockSession }
})
vi.mocked(api.getImportPreview).mockResolvedValue(mockResponse)
vi.mocked(api.cancelImport).mockImplementation(async () => {
isCancelled = true
})
const { result } = renderHook(() => useImport(), { wrapper: createWrapper() })
await act(async () => {
await result.current.upload('test')
})
await waitFor(() => {
expect(result.current.session).toEqual(mockSession)
})
await act(async () => {
await result.current.cancel()
})
expect(api.cancelImport).toHaveBeenCalled()
await waitFor(() => {
expect(result.current.session).toBeNull()
})
})
it('handles commit errors', async () => {
const mockSession = {
id: 'session-4',
state: 'reviewing' as const,
created_at: '2025-01-18T10:00:00Z',
updated_at: '2025-01-18T10:00:00Z',
}
const mockResponse = {
session: mockSession,
preview: { hosts: [], conflicts: [], errors: [] },
}
vi.mocked(api.uploadCaddyfile).mockResolvedValue(mockResponse)
vi.mocked(api.getImportStatus).mockResolvedValue({ has_pending: true, session: mockSession })
vi.mocked(api.getImportPreview).mockResolvedValue(mockResponse)
const mockError = new Error('Commit failed')
vi.mocked(api.commitImport).mockRejectedValue(mockError)
const { result } = renderHook(() => useImport(), { wrapper: createWrapper() })
await act(async () => {
await result.current.upload('test')
})
await waitFor(() => {
expect(result.current.session).toEqual(mockSession)
})
let threw = false
await act(async () => {
try {
await result.current.commit({}, {})
} catch {
threw = true
}
})
expect(threw).toBe(true)
await waitFor(() => {
expect(result.current.error).toBe('Commit failed')
})
})
it('captures and exposes commit result on success', async () => {
const mockSession = {
id: 'session-5',
state: 'reviewing' as const,
created_at: '2025-01-18T10:00:00Z',
updated_at: '2025-01-18T10:00:00Z',
}
const mockResponse = {
session: mockSession,
preview: { hosts: [], conflicts: [], errors: [] },
}
const mockCommitResult = {
created: 3,
updated: 1,
skipped: 2,
errors: [],
}
let isCommitted = false
vi.mocked(api.uploadCaddyfile).mockResolvedValue(mockResponse)
vi.mocked(api.getImportStatus).mockImplementation(async () => {
if (isCommitted) return { has_pending: false }
return { has_pending: true, session: mockSession }
})
vi.mocked(api.getImportPreview).mockResolvedValue(mockResponse)
vi.mocked(api.commitImport).mockImplementation(async () => {
isCommitted = true
return mockCommitResult
})
const { result } = renderHook(() => useImport(), { wrapper: createWrapper() })
await act(async () => {
await result.current.upload('test')
})
await waitFor(() => {
expect(result.current.session).toEqual(mockSession)
})
await act(async () => {
await result.current.commit({}, {})
})
expect(result.current.commitResult).toEqual(mockCommitResult)
expect(result.current.commitSuccess).toBe(true)
})
it('clears commit result when clearCommitResult is called', async () => {
const mockSession = {
id: 'session-6',
state: 'reviewing' as const,
created_at: '2025-01-18T10:00:00Z',
updated_at: '2025-01-18T10:00:00Z',
}
const mockResponse = {
session: mockSession,
preview: { hosts: [], conflicts: [], errors: [] },
}
const mockCommitResult = {
created: 2,
updated: 0,
skipped: 0,
errors: [],
}
let isCommitted = false
vi.mocked(api.uploadCaddyfile).mockResolvedValue(mockResponse)
vi.mocked(api.getImportStatus).mockImplementation(async () => {
if (isCommitted) return { has_pending: false }
return { has_pending: true, session: mockSession }
})
vi.mocked(api.getImportPreview).mockResolvedValue(mockResponse)
vi.mocked(api.commitImport).mockImplementation(async () => {
isCommitted = true
return mockCommitResult
})
const { result } = renderHook(() => useImport(), { wrapper: createWrapper() })
await act(async () => {
await result.current.upload('test')
})
await waitFor(() => {
expect(result.current.session).toEqual(mockSession)
})
await act(async () => {
await result.current.commit({}, {})
})
expect(result.current.commitResult).toEqual(mockCommitResult)
act(() => {
result.current.clearCommitResult()
})
expect(result.current.commitResult).toBeNull()
expect(result.current.commitSuccess).toBe(false)
})
})