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' // 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 = { code: 200, message: 'success', data: { token: 'test-token', refreshToken: 'refresh-token', admin: { id: 1, username: 'admin', nickname: 'Admin', role: 'admin' } } } 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('test-token') expect(store.userInfo?.username).toBe('admin') expect(auth.setToken).toHaveBeenCalledWith('test-token') expect(auth.setRefreshToken).toHaveBeenCalledWith('refresh-token') }) it('should not store token on login failure', async () => { const mockResponse = { code: 401, message: '用户名或密码错误', data: null } 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 = { code: 200, message: 'success', data: { id: 1, username: 'admin', nickname: 'Admin', role: 'admin' } } vi.mocked(loginApi.getInfo).mockResolvedValue(mockResponse) const store = useUserStore() const result = await store.getUserInfo() expect(result.code).toBe(200) expect(store.userInfo?.username).toBe('admin') expect(store.userInfo?.role).toBe('admin') }) }) describe('logoutAction', () => { it('should clear token and user info on logout', async () => { vi.mocked(loginApi.logout).mockResolvedValue({ code: 200, message: 'success', data: null }) const store = useUserStore() // Set initial state store.token = 'test-token' store.userInfo = { id: 1, username: 'admin', nickname: 'Admin', role: 'admin' } 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 = 'test-token' store.userInfo = { id: 1, username: 'admin', nickname: 'Admin', role: 'admin' } // 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 = 'test-token' store.userInfo = { id: 1, username: 'admin', nickname: 'Admin', role: 'admin' } store.resetToken() expect(store.token).toBe('') expect(store.userInfo).toBeNull() expect(auth.removeToken).toHaveBeenCalled() expect(auth.removeRefreshToken).toHaveBeenCalled() }) }) })