- Implement DockerHandler to handle API requests for listing Docker containers. - Create DockerService to interact with Docker API and retrieve container information. - Add routes for Docker container management in the API. - Introduce frontend API integration for Docker container listing. - Enhance ProxyHostForm to allow quick selection of Docker containers. - Update Docker-related tests to ensure functionality and error handling. - Modify Docker Compose files to enable Docker socket access for local and remote environments. - Add TypeScript configurations for improved build processes.
217 lines
7.2 KiB
TypeScript
217 lines
7.2 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 { useProxyHosts } from '../useProxyHosts'
|
|
import * as api from '../../api/proxyHosts'
|
|
|
|
// Mock the API
|
|
vi.mock('../../api/proxyHosts', () => ({
|
|
getProxyHosts: vi.fn(),
|
|
createProxyHost: vi.fn(),
|
|
updateProxyHost: vi.fn(),
|
|
deleteProxyHost: vi.fn(),
|
|
}))
|
|
|
|
const createMockHost = (overrides: Partial<api.ProxyHost> = {}): api.ProxyHost => ({
|
|
uuid: '1',
|
|
domain_names: 'test.com',
|
|
forward_scheme: 'http',
|
|
forward_host: 'localhost',
|
|
forward_port: 8080,
|
|
ssl_forced: false,
|
|
http2_support: false,
|
|
hsts_enabled: false,
|
|
hsts_subdomains: false,
|
|
block_exploits: false,
|
|
websocket_support: false,
|
|
locations: [],
|
|
enabled: true,
|
|
created_at: '2025-01-01T00:00:00Z',
|
|
updated_at: '2025-01-01T00:00:00Z',
|
|
...overrides,
|
|
})
|
|
|
|
const createWrapper = () => {
|
|
const queryClient = new QueryClient({
|
|
defaultOptions: {
|
|
queries: {
|
|
retry: false,
|
|
},
|
|
},
|
|
})
|
|
return ({ children }: { children: React.ReactNode }) => (
|
|
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
)
|
|
}
|
|
|
|
describe('useProxyHosts', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
afterEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
it('loads proxy hosts on mount', async () => {
|
|
const mockHosts = [
|
|
createMockHost({ uuid: '1', domain_names: 'test.com', enabled: true, forward_host: 'localhost', forward_port: 8080 }),
|
|
createMockHost({ uuid: '2', domain_names: 'app.com', enabled: true, forward_host: 'localhost', forward_port: 3000 }),
|
|
]
|
|
|
|
vi.mocked(api.getProxyHosts).mockResolvedValue(mockHosts)
|
|
|
|
const { result } = renderHook(() => useProxyHosts(), { wrapper: createWrapper() })
|
|
|
|
expect(result.current.loading).toBe(true)
|
|
expect(result.current.hosts).toEqual([])
|
|
|
|
await waitFor(() => {
|
|
expect(result.current.loading).toBe(false)
|
|
})
|
|
|
|
expect(result.current.hosts).toEqual(mockHosts)
|
|
expect(result.current.error).toBeNull()
|
|
expect(api.getProxyHosts).toHaveBeenCalledOnce()
|
|
})
|
|
|
|
it('handles loading errors', async () => {
|
|
const mockError = new Error('Failed to fetch')
|
|
vi.mocked(api.getProxyHosts).mockRejectedValue(mockError)
|
|
|
|
const { result } = renderHook(() => useProxyHosts(), { wrapper: createWrapper() })
|
|
|
|
await waitFor(() => {
|
|
expect(result.current.loading).toBe(false)
|
|
})
|
|
|
|
expect(result.current.error).toBe('Failed to fetch')
|
|
expect(result.current.hosts).toEqual([])
|
|
})
|
|
|
|
it('creates a new proxy host', async () => {
|
|
vi.mocked(api.getProxyHosts).mockResolvedValue([])
|
|
const newHost = { domain_names: 'new.com', forward_host: 'localhost', forward_port: 9000 }
|
|
const createdHost = createMockHost({ uuid: '3', ...newHost, enabled: true })
|
|
|
|
vi.mocked(api.createProxyHost).mockImplementation(async () => {
|
|
vi.mocked(api.getProxyHosts).mockResolvedValue([createdHost])
|
|
return createdHost
|
|
})
|
|
|
|
const { result } = renderHook(() => useProxyHosts(), { wrapper: createWrapper() })
|
|
|
|
await waitFor(() => {
|
|
expect(result.current.loading).toBe(false)
|
|
})
|
|
|
|
await act(async () => {
|
|
await result.current.createHost(newHost)
|
|
})
|
|
|
|
expect(api.createProxyHost).toHaveBeenCalledWith(newHost)
|
|
await waitFor(() => {
|
|
expect(result.current.hosts).toContainEqual(createdHost)
|
|
})
|
|
})
|
|
|
|
it('updates an existing proxy host', async () => {
|
|
const existingHost = createMockHost({ uuid: '1', domain_names: 'test.com', enabled: true, forward_host: 'localhost', forward_port: 8080 })
|
|
let hosts = [existingHost]
|
|
vi.mocked(api.getProxyHosts).mockImplementation(() => Promise.resolve(hosts))
|
|
|
|
vi.mocked(api.updateProxyHost).mockImplementation(async (_, data) => {
|
|
hosts = [{ ...existingHost, ...data }]
|
|
return hosts[0]
|
|
})
|
|
|
|
const { result } = renderHook(() => useProxyHosts(), { wrapper: createWrapper() })
|
|
|
|
await waitFor(() => {
|
|
expect(result.current.loading).toBe(false)
|
|
})
|
|
|
|
await act(async () => {
|
|
await result.current.updateHost('1', { domain_names: 'updated.com' })
|
|
})
|
|
|
|
expect(api.updateProxyHost).toHaveBeenCalledWith('1', { domain_names: 'updated.com' })
|
|
await waitFor(() => {
|
|
expect(result.current.hosts[0].domain_names).toBe('updated.com')
|
|
})
|
|
})
|
|
|
|
it('deletes a proxy host', async () => {
|
|
const hosts = [
|
|
createMockHost({ uuid: '1', domain_names: 'test.com', enabled: true, forward_host: 'localhost', forward_port: 8080 }),
|
|
createMockHost({ uuid: '2', domain_names: 'app.com', enabled: true, forward_host: 'localhost', forward_port: 3000 }),
|
|
]
|
|
vi.mocked(api.getProxyHosts).mockResolvedValue(hosts)
|
|
vi.mocked(api.deleteProxyHost).mockImplementation(async (uuid) => {
|
|
const remaining = hosts.filter(h => h.uuid !== uuid)
|
|
vi.mocked(api.getProxyHosts).mockResolvedValue(remaining)
|
|
})
|
|
|
|
const { result } = renderHook(() => useProxyHosts(), { wrapper: createWrapper() })
|
|
|
|
await waitFor(() => {
|
|
expect(result.current.loading).toBe(false)
|
|
})
|
|
|
|
await act(async () => {
|
|
await result.current.deleteHost('1')
|
|
})
|
|
|
|
expect(api.deleteProxyHost).toHaveBeenCalledWith('1')
|
|
await waitFor(() => {
|
|
expect(result.current.hosts).toHaveLength(1)
|
|
expect(result.current.hosts[0].uuid).toBe('2')
|
|
})
|
|
})
|
|
|
|
it('handles create errors', async () => {
|
|
vi.mocked(api.getProxyHosts).mockResolvedValue([])
|
|
const mockError = new Error('Failed to create')
|
|
vi.mocked(api.createProxyHost).mockRejectedValue(mockError)
|
|
|
|
const { result } = renderHook(() => useProxyHosts(), { wrapper: createWrapper() })
|
|
|
|
await waitFor(() => {
|
|
expect(result.current.loading).toBe(false)
|
|
})
|
|
|
|
await expect(result.current.createHost({ domain_names: 'test.com', forward_host: 'localhost', forward_port: 8080 })).rejects.toThrow('Failed to create')
|
|
})
|
|
|
|
it('handles update errors', async () => {
|
|
const host = createMockHost({ uuid: '1', domain_names: 'test.com', enabled: true, forward_host: 'localhost', forward_port: 8080 })
|
|
vi.mocked(api.getProxyHosts).mockResolvedValue([host])
|
|
const mockError = new Error('Failed to update')
|
|
vi.mocked(api.updateProxyHost).mockRejectedValue(mockError)
|
|
|
|
const { result } = renderHook(() => useProxyHosts(), { wrapper: createWrapper() })
|
|
|
|
await waitFor(() => {
|
|
expect(result.current.loading).toBe(false)
|
|
})
|
|
|
|
await expect(result.current.updateHost('1', { domain_names: 'updated.com' })).rejects.toThrow('Failed to update')
|
|
})
|
|
|
|
it('handles delete errors', async () => {
|
|
const host = createMockHost({ uuid: '1', domain_names: 'test.com', enabled: true, forward_host: 'localhost', forward_port: 8080 })
|
|
vi.mocked(api.getProxyHosts).mockResolvedValue([host])
|
|
const mockError = new Error('Failed to delete')
|
|
vi.mocked(api.deleteProxyHost).mockRejectedValue(mockError)
|
|
|
|
const { result } = renderHook(() => useProxyHosts(), { wrapper: createWrapper() })
|
|
|
|
await waitFor(() => {
|
|
expect(result.current.loading).toBe(false)
|
|
})
|
|
|
|
await expect(result.current.deleteHost('1')).rejects.toThrow('Failed to delete')
|
|
})
|
|
})
|