|
|
@@ -0,0 +1,142 @@
|
|
|
+import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
+import { AuthService, User, UserService } from '../../src/auth.service.js';
|
|
|
+import { EnableStatus } from '@d8d/shared-types';
|
|
|
+
|
|
|
+describe('认证服务', () => {
|
|
|
+ let authService: AuthService;
|
|
|
+ let mockUserService: UserService;
|
|
|
+
|
|
|
+ const mockUser: User = {
|
|
|
+ id: 1,
|
|
|
+ username: 'testuser',
|
|
|
+ nickname: '测试用户',
|
|
|
+ isDisabled: EnableStatus.ENABLED,
|
|
|
+ roles: [{ name: 'user' }]
|
|
|
+ };
|
|
|
+
|
|
|
+ beforeEach(() => {
|
|
|
+ mockUserService = {
|
|
|
+ getUserByUsername: vi.fn(),
|
|
|
+ verifyPassword: vi.fn(),
|
|
|
+ createUser: vi.fn()
|
|
|
+ };
|
|
|
+
|
|
|
+ authService = new AuthService(mockUserService);
|
|
|
+ vi.stubEnv('JWT_SECRET', 'test-secret');
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('登录', () => {
|
|
|
+ it('应该成功登录', async () => {
|
|
|
+ vi.mocked(mockUserService.getUserByUsername).mockResolvedValue(mockUser);
|
|
|
+ vi.mocked(mockUserService.verifyPassword).mockResolvedValue(true);
|
|
|
+
|
|
|
+ const result = await authService.login('testuser', 'password');
|
|
|
+
|
|
|
+ expect(result.token).toBeDefined();
|
|
|
+ expect(result.user).toEqual(mockUser);
|
|
|
+ expect(mockUserService.getUserByUsername).toHaveBeenCalledWith('testuser');
|
|
|
+ expect(mockUserService.verifyPassword).toHaveBeenCalledWith(mockUser, 'password');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('用户不存在时应该抛出错误', async () => {
|
|
|
+ vi.mocked(mockUserService.getUserByUsername).mockResolvedValue(null);
|
|
|
+
|
|
|
+ await expect(authService.login('nonexistent', 'password'))
|
|
|
+ .rejects
|
|
|
+ .toThrow('用户不存在');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('用户被禁用时应该抛出错误', async () => {
|
|
|
+ const disabledUser = { ...mockUser, isDisabled: EnableStatus.DISABLED };
|
|
|
+ vi.mocked(mockUserService.getUserByUsername).mockResolvedValue(disabledUser);
|
|
|
+
|
|
|
+ await expect(authService.login('testuser', 'password'))
|
|
|
+ .rejects
|
|
|
+ .toThrow('用户账户已被禁用');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('密码错误时应该抛出错误', async () => {
|
|
|
+ vi.mocked(mockUserService.getUserByUsername).mockResolvedValue(mockUser);
|
|
|
+ vi.mocked(mockUserService.verifyPassword).mockResolvedValue(false);
|
|
|
+
|
|
|
+ await expect(authService.login('testuser', 'wrongpassword'))
|
|
|
+ .rejects
|
|
|
+ .toThrow('密码错误');
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('生成token', () => {
|
|
|
+ it('应该成功生成token', () => {
|
|
|
+ const token = authService.generateToken(mockUser);
|
|
|
+
|
|
|
+ expect(token).toBeDefined();
|
|
|
+ expect(typeof token).toBe('string');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该使用自定义过期时间生成token', () => {
|
|
|
+ const token = authService.generateToken(mockUser, '2h');
|
|
|
+
|
|
|
+ expect(token).toBeDefined();
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('验证token', () => {
|
|
|
+ it('应该成功验证token', () => {
|
|
|
+ const token = authService.generateToken(mockUser);
|
|
|
+ const payload = authService.verifyToken(token);
|
|
|
+
|
|
|
+ expect(payload.id).toBe(mockUser.id);
|
|
|
+ expect(payload.username).toBe(mockUser.username);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('无效token应该抛出错误', () => {
|
|
|
+ const invalidToken = 'invalid.token.here';
|
|
|
+
|
|
|
+ expect(() => {
|
|
|
+ authService.verifyToken(invalidToken);
|
|
|
+ }).toThrow('无效的token');
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('权限验证', () => {
|
|
|
+ it('应该验证用户有权限', () => {
|
|
|
+ const hasPermission = authService.hasPermission(mockUser, 'user');
|
|
|
+
|
|
|
+ expect(hasPermission).toBe(true);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该验证用户没有权限', () => {
|
|
|
+ const hasPermission = authService.hasPermission(mockUser, 'admin');
|
|
|
+
|
|
|
+ expect(hasPermission).toBe(false);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该验证用户有任意一个权限', () => {
|
|
|
+ const hasAnyPermission = authService.hasAnyPermission(mockUser, ['user', 'admin']);
|
|
|
+
|
|
|
+ expect(hasAnyPermission).toBe(true);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该验证用户没有任意一个权限', () => {
|
|
|
+ const hasAnyPermission = authService.hasAnyPermission(mockUser, ['admin', 'superuser']);
|
|
|
+
|
|
|
+ expect(hasAnyPermission).toBe(false);
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('登出', () => {
|
|
|
+ it('应该成功登出', async () => {
|
|
|
+ const token = authService.generateToken(mockUser);
|
|
|
+
|
|
|
+ await expect(authService.logout(token)).resolves.toBeUndefined();
|
|
|
+ });
|
|
|
+
|
|
|
+ it('无效token登出时应该抛出错误', async () => {
|
|
|
+ const invalidToken = 'invalid.token.here';
|
|
|
+
|
|
|
+ await expect(authService.logout(invalidToken))
|
|
|
+ .rejects
|
|
|
+ .toThrow('无效的token');
|
|
|
+ });
|
|
|
+ });
|
|
|
+});
|