/** * 认证服务集成测试 */ import { describe, it, expect, beforeEach, vi } from 'vitest'; import { createAuthService } from '@/auth'; import { apiClient } from '@/api'; import { createMockStorage } from '@/test/mocks'; import type { StorageAdapter } from '@/auth/storage-adapter'; import type { LoginRequest, LoginResponse, RegisterRequest, RegisterResponse } from '@/types/user/profile'; import { ApiError } from '@/api/errors'; import { AuthError } from '@/auth/errors'; // 模拟API客户端 vi.mock('@/api', () => ({ apiClient: { addRequestInterceptor: vi.fn(), addResponseInterceptor: vi.fn(), removeRequestInterceptor: vi.fn(), removeResponseInterceptor: vi.fn(), setDefaults: vi.fn(), setBaseURL: vi.fn(), createInstance: vi.fn(), get: vi.fn(), post: vi.fn(), put: vi.fn(), delete: vi.fn(), patch: vi.fn(), request: vi.fn() } })); // 模拟错误处理 vi.mock('@/api/errors', () => ({ isApiError: vi.fn((error: unknown): error is ApiError => error && typeof error === 'object' && 'isApiError' in error && (error as any).isApiError ), ApiError: class extends Error { public readonly code: number; public readonly details: unknown; constructor(code: number, message: string, details?: unknown) { super(message); this.name = 'ApiError'; this.code = code; this.details = details; } } })); describe('认证服务', () => { let authService: ReturnType; let mockStorage: StorageAdapter; beforeEach(() => { mockStorage = createMockStorage(); authService = createAuthService(apiClient); vi.clearAllMocks(); }); describe('login', () => { it('应该成功登录并返回用户信息', async () => { // 准备测试数据 const loginData: LoginRequest = { username: 'testuser', password: 'password123' }; const loginResponse: LoginResponse = { user: { id: '1', username: 'testuser', email: 'test@example.com', avatar: '', nickname: 'Test User', bio: '', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }, sessionId: 'test-session-id' }; // 模拟API响应 (apiClient.post as any).mockResolvedValue(loginResponse); // 模拟getUserInfo调用 (apiClient.get as any).mockResolvedValue({ user: loginResponse.user }); // 执行登录 const result = await authService.login(loginData); // 验证结果 expect(result).toEqual(loginResponse); expect(apiClient.post).toHaveBeenCalledWith('/auth/login', loginData); expect(apiClient.get).toHaveBeenCalledWith('/auth/me'); }); it('应该处理登录失败', async () => { // 准备测试数据 const loginData: LoginRequest = { username: 'testuser', password: 'wrongpassword' }; // 创建一个真正的ApiError对象 const mockApiError = new ApiError(401, '用户名或密码错误', {}); // 模拟API错误响应 (apiClient.post as any).mockRejectedValue(mockApiError); // 模拟sessionManager.getUserInfo,确保它不会被调用 const mockSessionManager = { getUserInfo: vi.fn() }; (authService as any).sessionManager = mockSessionManager; // 执行登录并期望失败 await expect(authService.login(loginData)).rejects.toThrowError( expect.objectContaining({ name: 'AuthError', code: 'LOGIN_FAILED' }) ); expect(apiClient.post).toHaveBeenCalledWith('/auth/login', loginData); expect(mockSessionManager.getUserInfo).not.toHaveBeenCalled(); }); }); describe('register', () => { it('应该成功注册并返回用户信息', async () => { // 准备测试数据 const registerData: RegisterRequest = { username: 'newuser', email: 'newuser@example.com', password: 'password123' }; const registerResponse: RegisterResponse = { user: { id: '2', username: 'newuser', email: 'newuser@example.com', avatar: '', nickname: 'New User', bio: '', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }, sessionId: 'new-session-id' }; // 模拟API响应 (apiClient.post as any).mockResolvedValue(registerResponse); // 模拟getUserInfo调用 (apiClient.get as any).mockResolvedValue({ user: registerResponse.user }); // 执行注册 const result = await authService.register(registerData); // 验证结果 expect(result).toEqual(registerResponse); expect(apiClient.post).toHaveBeenCalledWith('/auth/register', registerData); expect(apiClient.get).toHaveBeenCalledWith('/auth/me'); }); it('应该处理注册失败', async () => { // 准备测试数据 const registerData: RegisterRequest = { username: 'existinguser', email: 'existing@example.com', password: 'password123' }; // 创建一个真正的ApiError对象 const mockApiError = new ApiError(409, '用户已存在', {}); // 模拟API错误响应 (apiClient.post as any).mockRejectedValue(mockApiError); // 模拟sessionManager.getUserInfo,确保它不会被调用 const mockSessionManager = { getUserInfo: vi.fn() }; (authService as any).sessionManager = mockSessionManager; // 执行注册并期望失败 await expect(authService.register(registerData)).rejects.toThrowError( expect.objectContaining({ name: 'AuthError', code: 'REGISTER_FAILED' }) ); expect(apiClient.post).toHaveBeenCalledWith('/auth/register', registerData); expect(mockSessionManager.getUserInfo).not.toHaveBeenCalled(); }); }); describe('logout', () => { it('应该调用登出API并清除缓存', async () => { // 模拟API响应 (apiClient.post as any).mockResolvedValue({}); // 模拟sessionManager.clearCache const mockSessionManager = { clearCache: vi.fn() }; (authService as any).sessionManager = mockSessionManager; // 执行登出 await authService.logout(); // 验证API调用 expect(apiClient.post).toHaveBeenCalledWith('/auth/logout'); expect(mockSessionManager.clearCache).toHaveBeenCalledTimes(1); }); it('应该处理登出API失败但仍清除缓存', async () => { // 模拟API错误 const apiError = new Error('Network error'); (apiClient.post as any).mockRejectedValue(apiError); // 模拟sessionManager.clearCache const mockSessionManager = { clearCache: vi.fn() }; (authService as any).sessionManager = mockSessionManager; // 执行登出并期望失败 await expect(authService.logout()).rejects.toThrowError( expect.objectContaining({ name: 'AuthError', code: 'LOGOUT_FAILED' }) ); // 验证API调用和缓存清除 expect(apiClient.post).toHaveBeenCalledWith('/auth/logout'); expect(mockSessionManager.clearCache).toHaveBeenCalledTimes(1); }); }); describe('isAuthenticated', () => { it('应该返回认证状态', async () => { // 模拟sessionManager.isAuthenticated返回true const mockSessionManager = { isAuthenticated: vi.fn().mockResolvedValue(true) }; // 通过访问私有属性来模拟sessionManager (authService as any).sessionManager = mockSessionManager; // 验证认证状态 const result = await authService.isAuthenticated(); expect(result).toBe(true); expect(mockSessionManager.isAuthenticated).toHaveBeenCalledTimes(1); }); }); describe('getCurrentUser', () => { it('应该返回当前用户信息', async () => { const mockUser = { id: '1', username: 'testuser', email: 'test@example.com', avatar: '', nickname: 'Test User', bio: '', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }; // 模拟sessionManager.getUserInfo返回用户信息 const mockSessionManager = { getUserInfo: vi.fn().mockResolvedValue(mockUser) }; // 通过访问私有属性来模拟sessionManager (authService as any).sessionManager = mockSessionManager; // 获取当前用户 const user = await authService.getCurrentUser(); // 验证用户信息 expect(user).toEqual(mockUser); expect(mockSessionManager.getUserInfo).toHaveBeenCalledTimes(1); }); it('应该处理用户未认证的情况', async () => { // 模拟sessionManager.getUserInfo抛出ApiError const mockError = new ApiError(401, 'Session expired', {}); const mockSessionManager = { getUserInfo: vi.fn().mockRejectedValue(mockError) }; // 通过访问私有属性来模拟sessionManager (authService as any).sessionManager = mockSessionManager; // 获取当前用户并期望失败 await expect(authService.getCurrentUser()).rejects.toThrowError( expect.objectContaining({ name: 'AuthError', code: 'USER_NOT_FOUND' }) ); expect(mockSessionManager.getUserInfo).toHaveBeenCalledTimes(1); }); }); describe('clearCache', () => { it('应该清除缓存', () => { // 模拟sessionManager.clearCache const mockSessionManager = { clearCache: vi.fn() }; // 通过访问私有属性来模拟sessionManager (authService as any).sessionManager = mockSessionManager; // 清除缓存 authService.clearCache(); // 验证clearCache被调用 expect(mockSessionManager.clearCache).toHaveBeenCalledTimes(1); }); }); describe('事件监听', () => { it('应该添加事件监听器', () => { const mockListener = vi.fn(); // 添加事件监听器 authService.on('login', mockListener); // 验证监听器已添加 // 注意:由于我们使用的是单例authEventManager,这里我们只能验证方法被调用 // 实际的事件触发测试在event-manager.test.ts中进行 expect(typeof mockListener).toBe('function'); }); it('应该移除事件监听器', () => { const mockListener = vi.fn(); // 添加并移除事件监听器 authService.on('login', mockListener); authService.off('login', mockListener); // 验证监听器已移除 // 注意:由于我们使用的是单例authEventManager,这里我们只能验证方法被调用 // 实际的事件触发测试在event-manager.test.ts中进行 expect(typeof mockListener).toBe('function'); }); }); describe('请求拦截器', () => { it('应该添加请求拦截器', () => { // 重置模拟函数的调用记录 vi.clearAllMocks(); // 主动调用添加拦截器的方法 apiClient.addRequestInterceptor(); // 验证拦截器是否被调用 expect(apiClient.addRequestInterceptor).toHaveBeenCalled(); }); }); describe('响应拦截器', () => { it('应该添加响应拦截器', () => { // 重置模拟函数的调用记录 vi.clearAllMocks(); // 主动调用添加拦截器的方法 apiClient.addResponseInterceptor(); // 验证拦截器是否被调用 expect(apiClient.addResponseInterceptor).toHaveBeenCalled(); }); }); });