Skip to main content
Glama
client-interceptors.test.tsβ€’10.9 kB
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import axios, { AxiosInstance, AxiosError } from 'axios'; import { createRequestInterceptor, createResponseInterceptor, createErrorInterceptor, addDiagnosticInterceptors, configureStandardInterceptors, } from '@/api/client-interceptors.js'; // Mock the logger to avoid noise in tests vi.mock('@/utils/logger.js', () => ({ debug: vi.fn(), error: vi.fn(), OperationType: { API_CALL: 'API_CALL', }, })); describe('Client Interceptors', () => { let mockClient: AxiosInstance; beforeEach(() => { mockClient = axios.create({ baseURL: 'https://test.example.com', }); vi.clearAllMocks(); }); afterEach(() => { // Clear all interceptors to prevent test interference mockClient.interceptors.request.clear(); mockClient.interceptors.response.clear(); }); describe('createRequestInterceptor', () => { it('should create a request interceptor that redacts authorization header', () => { const interceptor = createRequestInterceptor('TEST'); const config = { baseURL: 'https://api.example.com', url: '/test', method: 'GET' as const, headers: { Authorization: 'Bearer secret-api-key-12345', 'Content-Type': 'application/json', }, }; const result = interceptor(config); expect(result).toBe(config); // Should return the same config object expect(result.headers.Authorization).toBe('Bearer secret-api-key-12345'); // Original should be preserved }); it('should handle missing headers gracefully', () => { const interceptor = createRequestInterceptor(); const config = { baseURL: 'https://api.example.com', url: '/test', method: 'GET' as const, }; const result = interceptor(config); expect(result).toBe(config); }); it('should handle missing authorization header', () => { const interceptor = createRequestInterceptor(); const config = { baseURL: 'https://api.example.com', url: '/test', method: 'GET' as const, headers: { 'Content-Type': 'application/json', }, }; const result = interceptor(config); expect(result).toBe(config); }); }); describe('createResponseInterceptor', () => { it('should create a response interceptor that logs response details', () => { const interceptor = createResponseInterceptor('TEST'); const response = { status: 200, data: { success: true, data: [] }, config: { url: '/test', }, }; const result = interceptor(response as any); expect(result).toBe(response); }); it('should handle response without data', () => { const interceptor = createResponseInterceptor(); const response = { status: 204, data: null, config: { url: '/test', }, }; const result = interceptor(response as any); expect(result).toBe(response); }); it('should extract object keys from response data', () => { const interceptor = createResponseInterceptor(); const response = { status: 200, data: { users: [], companies: [], meta: {} }, config: { url: '/test', }, }; const result = interceptor(response as any); expect(result).toBe(response); }); }); describe('createErrorInterceptor', () => { it('should create an error interceptor that logs and re-throws errors', async () => { const interceptor = createErrorInterceptor('TEST'); const axiosError = new Error('Network Error') as AxiosError; axiosError.response = { status: 500, data: { error: 'Internal Server Error' }, config: { url: '/test', method: 'GET', data: { test: 'payload' }, }, } as any; await expect(interceptor(axiosError)).rejects.toBe(axiosError); }); it('should handle errors without response', async () => { const interceptor = createErrorInterceptor(); const networkError = new Error('Network Error') as AxiosError; await expect(interceptor(networkError)).rejects.toBe(networkError); }); }); describe('addDiagnosticInterceptors', () => { it('should add interceptors when diagnostics are enabled', () => { const initialRequestHandlers = mockClient.interceptors.request.handlers.length; const initialResponseHandlers = mockClient.interceptors.response.handlers.length; addDiagnosticInterceptors(mockClient, { prefix: 'TEST', enableDiagnostics: true, }); expect(mockClient.interceptors.request.handlers.length).toBeGreaterThan( initialRequestHandlers ); expect(mockClient.interceptors.response.handlers.length).toBeGreaterThan( initialResponseHandlers ); }); it('should not add interceptors when diagnostics are disabled', () => { const initialRequestHandlers = mockClient.interceptors.request.handlers.length; const initialResponseHandlers = mockClient.interceptors.response.handlers.length; addDiagnosticInterceptors(mockClient, { enableDiagnostics: false, }); expect(mockClient.interceptors.request.handlers.length).toBe( initialRequestHandlers ); expect(mockClient.interceptors.response.handlers.length).toBe( initialResponseHandlers ); }); it('should allow multiple interceptors when called multiple times', () => { const initialRequestHandlers = mockClient.interceptors.request.handlers?.length || 0; const initialResponseHandlers = mockClient.interceptors.response.handlers?.length || 0; // Call twice to test multiple interceptors addDiagnosticInterceptors(mockClient); addDiagnosticInterceptors(mockClient); // Should have added interceptors each time (Axios allows multiple interceptors) expect( mockClient.interceptors.request.handlers?.length || 0 ).toBeGreaterThan(initialRequestHandlers); expect( mockClient.interceptors.response.handlers?.length || 0 ).toBeGreaterThan(initialResponseHandlers); }); it('should use default options when none provided', () => { addDiagnosticInterceptors(mockClient); // Should have added interceptors with default settings expect(mockClient.interceptors.request.handlers.length).toBeGreaterThan( 0 ); expect(mockClient.interceptors.response.handlers.length).toBeGreaterThan( 0 ); }); }); describe('configureStandardInterceptors', () => { it('should configure both diagnostic and error interceptors by default', () => { const initialRequestHandlers = mockClient.interceptors.request.handlers.length; const initialResponseHandlers = mockClient.interceptors.response.handlers.length; configureStandardInterceptors(mockClient); expect(mockClient.interceptors.request.handlers.length).toBeGreaterThan( initialRequestHandlers ); expect(mockClient.interceptors.response.handlers.length).toBeGreaterThan( initialResponseHandlers ); }); it('should respect enableDiagnostics option', () => { const initialHandlers = mockClient.interceptors.request.handlers.length; configureStandardInterceptors(mockClient, { enableDiagnostics: false, enableErrorHandling: true, }); // Should only add error handling interceptors, not diagnostic ones expect(mockClient.interceptors.request.handlers.length).toBe( initialHandlers ); }); it('should respect enableErrorHandling option', () => { configureStandardInterceptors(mockClient, { enableDiagnostics: true, enableErrorHandling: false, }); // Should have diagnostic interceptors expect(mockClient.interceptors.request.handlers.length).toBeGreaterThan( 0 ); }); it('should work with custom prefix', () => { configureStandardInterceptors(mockClient, { prefix: 'CUSTOM_PREFIX', enableDiagnostics: true, }); expect(mockClient.interceptors.request.handlers.length).toBeGreaterThan( 0 ); expect(mockClient.interceptors.response.handlers.length).toBeGreaterThan( 0 ); }); }); describe('Integration Tests', () => { it('should handle a complete request/response cycle with interceptors', async () => { // Mock a successful response const mockAdapter = vi.fn().mockResolvedValue({ status: 200, data: { result: 'success' }, headers: {}, config: {}, }); mockClient.defaults.adapter = mockAdapter; configureStandardInterceptors(mockClient); const response = await mockClient.get('/test', { headers: { Authorization: 'Bearer test-key' }, }); expect(response.data).toEqual({ result: 'success' }); expect(mockAdapter).toHaveBeenCalled(); }); it('should handle errors properly with interceptors', async () => { const mockError = new Error('API Error') as AxiosError; mockError.response = { status: 400, data: { error: 'Bad Request' }, } as any; const mockAdapter = vi.fn().mockRejectedValue(mockError); mockClient.defaults.adapter = mockAdapter; configureStandardInterceptors(mockClient); await expect(mockClient.get('/test')).rejects.toThrow('API Error'); expect(mockAdapter).toHaveBeenCalled(); }); }); describe('Edge Cases', () => { it('should handle null/undefined config in request interceptor', () => { const interceptor = createRequestInterceptor(); const nullConfig = null as any; expect(() => interceptor(nullConfig)).not.toThrow(); }); it('should handle circular references in response data', () => { const interceptor = createResponseInterceptor(); const circularData: any = { name: 'test' }; circularData.self = circularData; // Create circular reference const response = { status: 200, data: circularData, config: { url: '/test' }, }; expect(() => interceptor(response as any)).not.toThrow(); }); it('should handle very large response objects', () => { const interceptor = createResponseInterceptor(); const largeData = Array.from({ length: 1000 }, (_, i) => ({ id: i, data: `item-${i}`, })); const response = { status: 200, data: { items: largeData }, config: { url: '/test' }, }; expect(() => interceptor(response as any)).not.toThrow(); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kesslerio/attio-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server