Files
ComfyUI_frontend/tests-ui/tests/services/networkClientAdapter.test.ts
bymyself 0d6e5a75a9 feat: Add header registry infrastructure
- Create TypeScript interfaces for header providers
- Implement header registry with priority-based ordering
- Add network client adapters that integrate with the registry
- Add comprehensive unit tests

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-17 13:43:58 -07:00

290 lines
7.9 KiB
TypeScript

import axios from 'axios'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { headerRegistry } from '@/services/headerRegistry'
import {
createAxiosWithHeaders,
fetchWithHeaders
} from '@/services/networkClientAdapter'
import type { IHeaderProvider } from '@/types/headerTypes'
// Mock axios
vi.mock('axios')
// Mock fetch globally
const mockFetch = vi.fn()
vi.stubGlobal('fetch', mockFetch)
describe('networkClientAdapter', () => {
beforeEach(() => {
vi.clearAllMocks()
headerRegistry.clear()
})
afterEach(() => {
vi.restoreAllMocks()
})
describe('createAxiosWithHeaders', () => {
it('should create an axios instance with header injection', async () => {
// Setup mock axios instance
const mockInterceptors = {
request: {
use: vi.fn()
},
response: {
use: vi.fn()
}
}
const mockAxiosInstance = {
interceptors: mockInterceptors,
get: vi.fn(),
post: vi.fn()
}
vi.mocked(axios.create).mockReturnValue(mockAxiosInstance as any)
// Create instance
createAxiosWithHeaders({ baseURL: 'https://api.example.com' })
// Verify axios.create was called with config
expect(axios.create).toHaveBeenCalledWith({
baseURL: 'https://api.example.com'
})
// Verify interceptor was added
expect(mockInterceptors.request.use).toHaveBeenCalledWith(
expect.any(Function),
expect.any(Function)
)
})
it('should inject headers from registry on request', async () => {
// Setup header provider
const provider: IHeaderProvider = {
provideHeaders: vi.fn().mockReturnValue({
'X-Custom-Header': 'custom-value'
})
}
headerRegistry.registerHeaderProvider(provider)
// Setup mock axios
const mockInterceptors = {
request: {
use: vi.fn()
},
response: {
use: vi.fn()
}
}
const mockAxiosInstance = {
interceptors: mockInterceptors
}
vi.mocked(axios.create).mockReturnValue(mockAxiosInstance as any)
// Create instance
createAxiosWithHeaders()
// Get the interceptor function
const [interceptorFn] = mockInterceptors.request.use.mock.calls[0]
// Test the interceptor
const config = {
url: '/api/test',
method: 'POST',
data: { foo: 'bar' },
headers: {
'Content-Type': 'application/json'
}
}
const result = await interceptorFn(config)
// Verify provider was called with correct context
expect(provider.provideHeaders).toHaveBeenCalledWith({
url: '/api/test',
method: 'POST',
body: { foo: 'bar' },
config
})
// Verify headers were merged
expect(result.headers).toEqual({
'Content-Type': 'application/json',
'X-Custom-Header': 'custom-value'
})
})
it('should handle interceptor errors', async () => {
// Setup mock axios
const mockInterceptors = {
request: {
use: vi.fn()
},
response: {
use: vi.fn()
}
}
const mockAxiosInstance = {
interceptors: mockInterceptors
}
vi.mocked(axios.create).mockReturnValue(mockAxiosInstance as any)
// Create instance
createAxiosWithHeaders()
// Get the error handler
const [, errorHandler] = mockInterceptors.request.use.mock.calls[0]
// Test error handling
const error = new Error('Request error')
await expect(errorHandler(error)).rejects.toThrow('Request error')
})
})
describe('fetchWithHeaders', () => {
it('should inject headers from registry into fetch requests', async () => {
// Setup header provider
const provider: IHeaderProvider = {
provideHeaders: vi.fn().mockReturnValue({
'X-Api-Key': 'test-key',
'X-Request-ID': '12345'
})
}
headerRegistry.registerHeaderProvider(provider)
// Setup fetch mock
mockFetch.mockResolvedValue(new Response('OK'))
// Make request
await fetchWithHeaders('https://api.example.com/data', {
method: 'GET',
headers: {
Accept: 'application/json'
}
})
// Verify provider was called
expect(provider.provideHeaders).toHaveBeenCalledWith({
url: 'https://api.example.com/data',
method: 'GET',
body: undefined
})
// Verify fetch was called with merged headers
expect(mockFetch).toHaveBeenCalledWith(
'https://api.example.com/data',
expect.objectContaining({
method: 'GET',
headers: expect.any(Headers)
})
)
// Check the headers
const [, init] = mockFetch.mock.calls[0]
const headers = init.headers as Headers
expect(headers.get('Accept')).toBe('application/json')
expect(headers.get('X-Api-Key')).toBe('test-key')
expect(headers.get('X-Request-ID')).toBe('12345')
})
it('should handle URL objects', async () => {
const provider: IHeaderProvider = {
provideHeaders: vi.fn().mockReturnValue({})
}
headerRegistry.registerHeaderProvider(provider)
mockFetch.mockResolvedValue(new Response('OK'))
const url = new URL('https://api.example.com/test')
await fetchWithHeaders(url)
expect(provider.provideHeaders).toHaveBeenCalledWith({
url: 'https://api.example.com/test',
method: 'GET',
body: undefined
})
})
it('should handle Request objects', async () => {
const provider: IHeaderProvider = {
provideHeaders: vi.fn().mockReturnValue({
'X-Custom': 'value'
})
}
headerRegistry.registerHeaderProvider(provider)
mockFetch.mockResolvedValue(new Response('OK'))
const request = new Request('https://api.example.com/test', {
method: 'POST',
body: JSON.stringify({ data: 'test' })
})
await fetchWithHeaders(request)
expect(provider.provideHeaders).toHaveBeenCalledWith({
url: 'https://api.example.com/test',
method: 'POST',
body: undefined // init.body is undefined when using Request object
})
// Verify headers were added
const [, init] = mockFetch.mock.calls[0]
const headers = init.headers as Headers
expect(headers.get('X-Custom')).toBe('value')
})
it('should convert header values to strings', async () => {
const provider: IHeaderProvider = {
provideHeaders: vi.fn().mockReturnValue({
'X-Number': 123,
'X-Boolean': true,
'X-String': 'test'
})
}
headerRegistry.registerHeaderProvider(provider)
mockFetch.mockResolvedValue(new Response('OK'))
await fetchWithHeaders('https://api.example.com')
const [, init] = mockFetch.mock.calls[0]
const headers = init.headers as Headers
expect(headers.get('X-Number')).toBe('123')
expect(headers.get('X-Boolean')).toBe('true')
expect(headers.get('X-String')).toBe('test')
})
it('should preserve existing headers and let registry override', async () => {
const provider: IHeaderProvider = {
provideHeaders: vi.fn().mockReturnValue({
'X-Override': 'new-value',
'X-New': 'added'
})
}
headerRegistry.registerHeaderProvider(provider)
mockFetch.mockResolvedValue(new Response('OK'))
await fetchWithHeaders('https://api.example.com', {
headers: {
'X-Override': 'old-value',
'X-Existing': 'keep-me'
}
})
const [, init] = mockFetch.mock.calls[0]
const headers = init.headers as Headers
expect(headers.get('X-Override')).toBe('new-value') // Registry wins
expect(headers.get('X-Existing')).toBe('keep-me')
expect(headers.get('X-New')).toBe('added')
})
})
})