import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' import { api, clearCache, cancelAllRequests } from '@/services/api' import axios from 'axios' // 模拟axios vi.mock('axios') const mockedAxios = vi.mocked(axios) // 模拟localStorage const localStorageMock = { getItem: vi.fn(), setItem: vi.fn(), removeItem: vi.fn(), clear: vi.fn() } global.localStorage = localStorageMock // 模拟console方法 const consoleSpy = { error: vi.spyOn(console, 'error').mockImplementation(() => {}), warn: vi.spyOn(console, 'warn').mockImplementation(() => {}), log: vi.spyOn(console, 'log').mockImplementation(() => {}) } // 模拟window.performance Object.defineProperty(window, 'performance', { value: { now: vi.fn(() => Date.now()) } }) describe('API Service', () => { beforeEach(() => { // 重置所有模拟 vi.clearAllMocks() // 模拟axios.create返回值 mockedAxios.create.mockReturnValue({ request: vi.fn(), interceptors: { request: { use: vi.fn() }, response: { use: vi.fn() } } }) }) afterEach(() => { // 清理所有请求 cancelAllRequests() clearCache() }) describe('API实例', () => { it('应该正确创建API实例', () => { expect(api).toBeDefined() expect(typeof api.get).toBe('function') expect(typeof api.post).toBe('function') expect(typeof api.put).toBe('function') expect(typeof api.delete).toBe('function') expect(typeof api.request).toBe('function') }) it('应该设置正确的baseURL', () => { // 由于我们模拟了axios.create,这里我们检查调用参数 expect(mockedAxios.create).toHaveBeenCalledWith( expect.objectContaining({ baseURL: 'https://localhost:7001/api', timeout: 15000 }) ) }) }) describe('请求拦截器', () => { it('应该在请求前添加时间戳', async () => { // 模拟请求 const mockRequest = { url: '/test', method: 'get', headers: {} } // 模拟请求拦截器 const requestInterceptor = mockedAxios.create().interceptors.request.use.mock.calls[0][0] const processedRequest = requestInterceptor(mockRequest) // 检查是否添加了时间戳 expect(processedRequest.metadata).toBeDefined() expect(processedRequest.metadata.startTime).toBeDefined() }) it('应该在请求前添加CSRF令牌', async () => { // 模拟localStorage中的CSRF令牌 localStorageMock.getItem.mockReturnValue('test-csrf-token') // 模拟请求 const mockRequest = { url: '/test', method: 'post', headers: {} } // 模拟请求拦截器 const requestInterceptor = mockedAxios.create().interceptors.request.use.mock.calls[0][0] const processedRequest = requestInterceptor(mockRequest) // 检查是否添加了CSRF令牌 expect(processedRequest.headers['X-CSRF-Token']).toBe('test-csrf-token') }) it('应该在请求前添加认证令牌', async () => { // 模拟localStorage中的认证令牌 localStorageMock.getItem.mockImplementation((key) => { if (key === 'auth_token') return 'test-auth-token' return null }) // 模拟请求 const mockRequest = { url: '/test', method: 'get', headers: {} } // 模拟请求拦截器 const requestInterceptor = mockedAxios.create().interceptors.request.use.mock.calls[0][0] const processedRequest = requestInterceptor(mockRequest) // 检查是否添加了认证令牌 expect(processedRequest.headers.Authorization).toBe('Bearer test-auth-token') }) }) describe('响应拦截器', () => { it('应该正确处理成功响应', async () => { // 模拟响应 const mockResponse = { data: { success: true, data: { id: 1, name: 'Test' } }, status: 200, statusText: 'OK', headers: {}, config: { url: '/test', method: 'get', metadata: { startTime: Date.now() - 100 } } } // 模拟响应拦截器 const responseInterceptor = mockedAxios.create().interceptors.response.use.mock.calls[0][0] const processedResponse = responseInterceptor(mockResponse) // 检查响应处理 expect(processedResponse.data).toEqual({ success: true, data: { id: 1, name: 'Test' } }) }) it('应该记录请求耗时', async () => { // 模拟响应 const mockResponse = { data: { success: true, data: { id: 1, name: 'Test' } }, status: 200, statusText: 'OK', headers: {}, config: { url: '/test', method: 'get', metadata: { startTime: Date.now() - 100 } } } // 模拟响应拦截器 const responseInterceptor = mockedAxios.create().interceptors.response.use.mock.calls[0][0] responseInterceptor(mockResponse) // 检查是否记录了请求耗时 expect(consoleSpy.log).toHaveBeenCalledWith( expect.stringContaining('请求完成'), expect.anything() ) }) it('应该缓存GET请求响应', async () => { // 模拟响应 const mockResponse = { data: { success: true, data: { id: 1, name: 'Test' } }, status: 200, statusText: 'OK', headers: {}, config: { url: '/test', method: 'get', metadata: { startTime: Date.now() - 100 } } } // 模拟响应拦截器 const responseInterceptor = mockedAxios.create().interceptors.response.use.mock.calls[0][0] responseInterceptor(mockResponse) // 检查是否缓存了响应 expect(localStorageMock.setItem).toHaveBeenCalledWith( expect.stringContaining('api_cache_'), expect.any(String) ) }) }) describe('错误处理', () => { it('应该正确处理网络错误', async () => { // 模拟网络错误 const networkError = new Error('Network Error') networkError.code = 'NETWORK_ERROR' // 模拟错误拦截器 const errorInterceptor = mockedAxios.create().interceptors.response.use.mock.calls[0][1] // 处理错误 const result = errorInterceptor(networkError) // 检查错误处理 expect(result).rejects.toThrow() expect(consoleSpy.error).toHaveBeenCalledWith( '网络错误', expect.any(Error) ) }) it('应该正确处理超时错误', async () => { // 模拟超时错误 const timeoutError = new Error('Timeout') timeoutError.code = 'ECONNABORTED' // 模拟错误拦截器 const errorInterceptor = mockedAxios.create().interceptors.response.use.mock.calls[0][1] // 处理错误 const result = errorInterceptor(timeoutError) // 检查错误处理 expect(result).rejects.toThrow() expect(consoleSpy.error).toHaveBeenCalledWith( '请求超时', expect.any(Error) ) }) it('应该正确处理服务器错误', async () => { // 模拟服务器错误响应 const serverError = { response: { status: 500, statusText: 'Internal Server Error', data: { message: '服务器内部错误' } } } // 模拟错误拦截器 const errorInterceptor = mockedAxios.create().interceptors.response.use.mock.calls[0][1] // 处理错误 const result = errorInterceptor(serverError) // 检查错误处理 expect(result).rejects.toThrow() expect(consoleSpy.error).toHaveBeenCalledWith( '服务器错误', expect.any(Object) ) }) it('应该正确处理401未授权错误', async () => { // 模拟401错误响应 const unauthorizedError = { response: { status: 401, statusText: 'Unauthorized', data: { message: '未授权访问' } } } // 模拟错误拦截器 const errorInterceptor = mockedAxios.create().interceptors.response.use.mock.calls[0][1] // 处理错误 const result = errorInterceptor(unauthorizedError) // 检查错误处理 expect(result).rejects.toThrow() expect(consoleSpy.error).toHaveBeenCalledWith( '未授权访问', expect.any(Object) ) }) }) describe('缓存管理', () => { it('应该正确缓存GET请求', () => { // 模拟缓存数据 const cacheKey = 'api_cache_/test' const cacheData = { data: { id: 1, name: 'Test' }, timestamp: Date.now(), expiry: Date.now() + 300000 // 5分钟后过期 } // 设置缓存 localStorageMock.setItem.mockReturnValue() localStorageMock.getItem.mockReturnValue(JSON.stringify(cacheData)) // 检查缓存设置 localStorageMock.setItem(cacheKey, JSON.stringify(cacheData)) expect(localStorageMock.setItem).toHaveBeenCalledWith(cacheKey, JSON.stringify(cacheData)) }) it('应该正确清除缓存', () => { // 调用清除缓存函数 clearCache() // 检查是否清除了所有缓存 expect(localStorageMock.removeItem).toHaveBeenCalledWith('api_cache_/test') }) }) describe('请求取消', () => { it('应该正确取消重复请求', () => { // 模拟请求配置 const requestConfig = { url: '/test', method: 'get' } // 模拟CancelToken const cancelToken = { token: 'test-token', cancel: vi.fn() } // 检查是否创建了CancelToken expect(cancelToken.token).toBe('test-token') }) it('应该正确取消所有请求', () => { // 调用取消所有请求函数 cancelAllRequests() // 检查是否取消了所有请求 // 这里需要根据实际实现进行检查 }) }) })