import { describe, it, expect, vi, beforeEach } from 'vitest' import { setActivePinia, createPinia } from 'pinia' import { useUserStore } from '@/store/user' import * as loginApi from '@/api/login' import * as auth from '@/utils/auth' import { mockLoginResponse, mockAdminInfo, wrapResponse, wrapErrorResponse } from '../../fixtures' // Mock modules vi.mock('@/api/login', () => ({ login: vi.fn(), getInfo: vi.fn(), logout: vi.fn() })) vi.mock('@/utils/auth', () => ({ getToken: vi.fn(() => ''), setToken: vi.fn(), removeToken: vi.fn(), setRefreshToken: vi.fn(), removeRefreshToken: vi.fn() })) describe('User Store', () => { beforeEach(() => { setActivePinia(createPinia()) vi.clearAllMocks() }) describe('loginAction', () => { it('should login successfully and store token', async () => { const mockResponse = wrapResponse(mockLoginResponse) vi.mocked(loginApi.login).mockResolvedValue(mockResponse) const store = useUserStore() const result = await store.loginAction({ username: 'admin', password: 'password' }) expect(result.code).toBe(200) expect(store.token).toBe(mockLoginResponse.token) expect(store.userInfo?.username).toBe(mockLoginResponse.admin.username) expect(auth.setToken).toHaveBeenCalledWith(mockLoginResponse.token) expect(auth.setRefreshToken).toHaveBeenCalledWith(mockLoginResponse.refreshToken) }) it('should not store token on login failure', async () => { const mockResponse = wrapErrorResponse('用户名或密码错误', 401) as any vi.mocked(loginApi.login).mockResolvedValue(mockResponse) const store = useUserStore() const result = await store.loginAction({ username: 'wrong', password: 'wrong' }) expect(result.code).toBe(401) expect(store.token).toBe('') expect(auth.setToken).not.toHaveBeenCalled() }) }) describe('getUserInfo', () => { it('should fetch and store user info', async () => { const mockResponse = wrapResponse(mockAdminInfo) vi.mocked(loginApi.getInfo).mockResolvedValue(mockResponse) const store = useUserStore() const result = await store.getUserInfo() expect(result.code).toBe(200) expect(store.userInfo?.username).toBe(mockAdminInfo.username) expect(store.userInfo?.role).toBe(mockAdminInfo.role) }) }) describe('logoutAction', () => { it('should clear token and user info on logout', async () => { vi.mocked(loginApi.logout).mockResolvedValue(wrapResponse(null)) const store = useUserStore() // Set initial state store.token = mockLoginResponse.token store.userInfo = mockAdminInfo await store.logoutAction() expect(store.token).toBe('') expect(store.userInfo).toBeNull() expect(auth.removeToken).toHaveBeenCalled() expect(auth.removeRefreshToken).toHaveBeenCalled() }) it('should clear state in finally block even if logout API fails', async () => { vi.mocked(loginApi.logout).mockRejectedValue(new Error('Network error')) const store = useUserStore() store.token = mockLoginResponse.token store.userInfo = mockAdminInfo // logoutAction uses try/finally without catch, so error will propagate // but finally block still clears the state try { await store.logoutAction() } catch { // Expected to throw } // State should be cleared in finally block expect(store.token).toBe('') expect(store.userInfo).toBeNull() expect(auth.removeToken).toHaveBeenCalled() expect(auth.removeRefreshToken).toHaveBeenCalled() }) }) describe('resetToken', () => { it('should clear all auth state', () => { const store = useUserStore() store.token = mockLoginResponse.token store.userInfo = mockAdminInfo store.resetToken() expect(store.token).toBe('') expect(store.userInfo).toBeNull() expect(auth.removeToken).toHaveBeenCalled() expect(auth.removeRefreshToken).toHaveBeenCalled() }) }) })