/** * 认证状态管理测试 */ import { describe, it, expect, vi, beforeEach } from 'vitest' import { setActivePinia, createPinia } from 'pinia' import { useAuthStore } from '@/stores/auth' // 模拟 API vi.mock('@/api/auth', () => ({ authApi: { login: vi.fn(), register: vi.fn(), logout: vi.fn(), refreshToken: vi.fn(), getUserInfo: vi.fn() } })) // 模拟存储工具 vi.mock('@/utils/storage', () => ({ default: { get: vi.fn(), set: vi.fn(), remove: vi.fn(), clear: vi.fn() } })) // 模拟路由 const mockRouter = { push: vi.fn(), replace: vi.fn() } vi.mock('vue-router', () => ({ useRouter: () => mockRouter })) // 模拟配置 vi.mock('@/config/constants', () => ({ STORAGE_KEYS: { TOKEN: 'auth_token', REFRESH_TOKEN: 'refresh_token', USER_INFO: 'user_info' }, TOKEN_CONFIG: { EXPIRES_IN: 7200, REFRESH_THRESHOLD: 300 } })) describe('useAuthStore', () => { let authStore: ReturnType beforeEach(() => { setActivePinia(createPinia()) authStore = useAuthStore() vi.clearAllMocks() }) describe('initial state', () => { it('should have correct initial state', () => { expect(authStore.token).toBe('') expect(authStore.refreshToken).toBe('') expect(authStore.user).toBeNull() expect(authStore.isAuthenticated).toBe(false) expect(authStore.isTokenExpired).toBe(true) }) }) describe('getters', () => { it('should compute isAuthenticated correctly', () => { expect(authStore.isAuthenticated).toBe(false) authStore.token = 'valid-token' authStore.tokenExpireTime = Date.now() + 3600000 // 1小时后过期 expect(authStore.isAuthenticated).toBe(true) }) it('should compute isTokenExpired correctly', () => { expect(authStore.isTokenExpired).toBe(true) authStore.tokenExpireTime = Date.now() + 3600000 // 1小时后过期 expect(authStore.isTokenExpired).toBe(false) authStore.tokenExpireTime = Date.now() - 3600000 // 1小时前过期 expect(authStore.isTokenExpired).toBe(true) }) it('should compute needsRefresh correctly', () => { authStore.tokenExpireTime = Date.now() + 600000 // 10分钟后过期 expect(authStore.needsRefresh).toBe(true) authStore.tokenExpireTime = Date.now() + 3600000 // 1小时后过期 expect(authStore.needsRefresh).toBe(false) }) it('should compute user properties correctly', () => { authStore.user = { id: '1', username: 'testuser', nickname: 'Test User', email: 'test@example.com', avatar: 'avatar.jpg', role: 'user', createTime: Date.now(), updateTime: Date.now() } expect(authStore.userId).toBe('1') expect(authStore.username).toBe('testuser') expect(authStore.nickname).toBe('Test User') expect(authStore.email).toBe('test@example.com') expect(authStore.avatar).toBe('avatar.jpg') expect(authStore.userRole).toBe('user') }) }) describe('actions', () => { describe('login', () => { it('should login successfully', async () => { const mockResponse = { token: 'new-token', refreshToken: 'new-refresh-token', expiresIn: 7200, user: { id: '1', username: 'testuser', nickname: 'Test User', email: 'test@example.com' } } const { authApi } = await import('@/api/auth') vi.mocked(authApi.login).mockResolvedValue(mockResponse) const loginData = { username: 'testuser', password: 'password123' } const result = await authStore.login(loginData) expect(authApi.login).toHaveBeenCalledWith(loginData) expect(authStore.token).toBe('new-token') expect(authStore.refreshToken).toBe('new-refresh-token') expect(authStore.user).toEqual(mockResponse.user) expect(result).toEqual(mockResponse) }) it('should handle login failure', async () => { const { authApi } = await import('@/api/auth') const error = new Error('Invalid credentials') vi.mocked(authApi.login).mockRejectedValue(error) const loginData = { username: 'testuser', password: 'wrongpassword' } await expect(authStore.login(loginData)).rejects.toThrow('Invalid credentials') expect(authStore.token).toBe('') expect(authStore.user).toBeNull() }) }) describe('register', () => { it('should register successfully', async () => { const mockResponse = { token: 'new-token', refreshToken: 'new-refresh-token', expiresIn: 7200, user: { id: '1', username: 'newuser', nickname: 'New User', email: 'new@example.com' } } const { authApi } = await import('@/api/auth') vi.mocked(authApi.register).mockResolvedValue(mockResponse) const registerData = { username: 'newuser', password: 'password123', email: 'new@example.com', nickname: 'New User' } const result = await authStore.register(registerData) expect(authApi.register).toHaveBeenCalledWith(registerData) expect(authStore.token).toBe('new-token') expect(authStore.user).toEqual(mockResponse.user) expect(result).toEqual(mockResponse) }) }) describe('logout', () => { it('should logout successfully', async () => { // 设置初始状态 authStore.token = 'current-token' authStore.refreshToken = 'current-refresh-token' authStore.user = { id: '1', username: 'testuser' } as any const { authApi } = await import('@/api/auth') vi.mocked(authApi.logout).mockResolvedValue(undefined) await authStore.logout() expect(authApi.logout).toHaveBeenCalled() expect(authStore.token).toBe('') expect(authStore.refreshToken).toBe('') expect(authStore.user).toBeNull() expect(authStore.tokenExpireTime).toBe(0) }) it('should clear state even if API call fails', async () => { authStore.token = 'current-token' authStore.user = { id: '1', username: 'testuser' } as any const { authApi } = await import('@/api/auth') vi.mocked(authApi.logout).mockRejectedValue(new Error('Network error')) await authStore.logout() expect(authStore.token).toBe('') expect(authStore.user).toBeNull() }) }) describe('refreshToken', () => { it('should refresh token successfully', async () => { authStore.refreshToken = 'current-refresh-token' const mockResponse = { token: 'new-token', refreshToken: 'new-refresh-token', expiresIn: 7200 } const { authApi } = await import('@/api/auth') vi.mocked(authApi.refreshToken).mockResolvedValue(mockResponse) const result = await authStore.refreshTokenAction() expect(authApi.refreshToken).toHaveBeenCalledWith('current-refresh-token') expect(authStore.token).toBe('new-token') expect(authStore.refreshToken).toBe('new-refresh-token') expect(result).toEqual(mockResponse) }) it('should handle refresh token failure', async () => { authStore.refreshToken = 'invalid-refresh-token' const { authApi } = await import('@/api/auth') vi.mocked(authApi.refreshToken).mockRejectedValue(new Error('Invalid refresh token')) await expect(authStore.refreshTokenAction()).rejects.toThrow('Invalid refresh token') }) }) describe('updateUserInfo', () => { it('should update user info', async () => { authStore.user = { id: '1', username: 'testuser', nickname: 'Old Name', email: 'old@example.com' } as any const updates = { nickname: 'New Name', email: 'new@example.com' } await authStore.updateUserInfo(updates) expect(authStore.user?.nickname).toBe('New Name') expect(authStore.user?.email).toBe('new@example.com') expect(authStore.user?.username).toBe('testuser') // 保持不变 }) }) describe('checkAuthStatus', () => { it('should return true for valid authentication', () => { authStore.token = 'valid-token' authStore.tokenExpireTime = Date.now() + 3600000 expect(authStore.checkAuthStatus()).toBe(true) }) it('should return false for expired token', () => { authStore.token = 'expired-token' authStore.tokenExpireTime = Date.now() - 3600000 expect(authStore.checkAuthStatus()).toBe(false) }) it('should return false for missing token', () => { authStore.token = '' expect(authStore.checkAuthStatus()).toBe(false) }) }) }) describe('persistence', () => { it('should save state to storage', () => { const storage = require('@/utils/storage').default authStore.token = 'test-token' authStore.refreshToken = 'test-refresh-token' authStore.user = { id: '1', username: 'testuser' } as any authStore.saveToStorage() expect(storage.set).toHaveBeenCalledWith('auth_token', 'test-token') expect(storage.set).toHaveBeenCalledWith('refresh_token', 'test-refresh-token') expect(storage.set).toHaveBeenCalledWith('user_info', authStore.user) }) it('should load state from storage', () => { const storage = require('@/utils/storage').default storage.get.mockImplementation((key: string) => { switch (key) { case 'auth_token': return 'stored-token' case 'refresh_token': return 'stored-refresh-token' case 'user_info': return { id: '1', username: 'storeduser' } default: return null } }) authStore.loadFromStorage() expect(authStore.token).toBe('stored-token') expect(authStore.refreshToken).toBe('stored-refresh-token') expect(authStore.user).toEqual({ id: '1', username: 'storeduser' }) }) }) })