import { beforeEach, describe, expect, it, vi } from 'vitest' import { createPinia, setActivePinia } from 'pinia' import { api, setAccessToken, type Admin } from '../api' import { useAuth } from './auth' vi.mock('../api', () => ({ api: { login: vi.fn(), me: vi.fn(), logout: vi.fn() }, setAccessToken: vi.fn() })) let storage: Map const viewer: Admin = { id: 'viewer', username: 'viewer', displayName: '只读', role: 'viewer' } beforeEach(() => { vi.clearAllMocks(); storage = new Map(); vi.stubGlobal('sessionStorage', { getItem: (k: string) => storage.get(k) ?? null, setItem: (k: string, v: string) => storage.set(k, v), removeItem: (k: string) => storage.delete(k) }); setActivePinia(createPinia()) }) describe('管理员会话', () => { it('只读角色登录后仍没有写权限', async () => { vi.mocked(api.login).mockResolvedValue({ accessToken: 'jwt', expiresIn: 3600, user: viewer }); const auth = useAuth(); await auth.login('viewer', 'password'); expect(auth.canOperate).toBe(false); expect(auth.canAdmin).toBe(false); expect(storage.get('vdi.accessToken')).toBe('jwt') }) it('同时触发恢复只请求一次;令牌失效清除会话', async () => { storage.set('vdi.accessToken', 'expired'); vi.mocked(api.me).mockRejectedValue(new Error('expired')); const auth = useAuth(); await Promise.all([auth.restore(), auth.restore()]); expect(api.me).toHaveBeenCalledTimes(1); expect(auth.user).toBeNull(); expect(storage.has('vdi.accessToken')).toBe(false); expect(setAccessToken).toHaveBeenLastCalledWith('') }) it('退出接口失败也移除本地登录凭据', async () => { vi.mocked(api.login).mockResolvedValue({ accessToken: 'jwt', expiresIn: 3600, user: viewer }); vi.mocked(api.logout).mockRejectedValue(new Error('offline')); const auth = useAuth(); await auth.login('viewer', 'password'); await expect(auth.logout()).rejects.toThrow('offline'); expect(auth.token).toBe(''); expect(auth.user).toBeNull(); expect(storage.has('vdi.accessToken')).toBe(false) }) })