user.service.test.ts 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. import { UserService } from '../user.service';
  2. import { DataSource, Repository } from 'typeorm';
  3. import { UserEntity as User } from '../user.entity';
  4. import { Role } from '../role.entity';
  5. import * as bcrypt from 'bcrypt';
  6. import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
  7. // Mock TypeORM 数据源和仓库
  8. vi.mock('typeorm', async (importOriginal) => {
  9. const actual = await importOriginal()
  10. return {
  11. ...actual,
  12. DataSource: vi.fn().mockImplementation(() => ({
  13. getRepository: vi.fn()
  14. })),
  15. Repository: vi.fn()
  16. }
  17. });
  18. // Mock bcrypt
  19. vi.mock('bcrypt', () => ({
  20. hash: vi.fn().mockResolvedValue('hashed_password'),
  21. compare: vi.fn().mockResolvedValue(true)
  22. }));
  23. describe('UserService', () => {
  24. let userService: UserService;
  25. let mockDataSource: any;
  26. let mockUserRepository: any;
  27. let mockRoleRepository: any;
  28. beforeEach(() => {
  29. // 创建模拟的仓库实例
  30. mockUserRepository = {
  31. create: vi.fn(),
  32. save: vi.fn(),
  33. findOne: vi.fn(),
  34. update: vi.fn(),
  35. delete: vi.fn(),
  36. createQueryBuilder: vi.fn(),
  37. find: vi.fn(),
  38. findByIds: vi.fn()
  39. } as any;
  40. mockRoleRepository = {
  41. findByIds: vi.fn()
  42. } as any;
  43. // 创建模拟的数据源
  44. mockDataSource = {
  45. getRepository: vi.fn()
  46. } as any;
  47. // 设置数据源返回模拟的仓库
  48. mockDataSource.getRepository
  49. .mockReturnValueOnce(mockUserRepository)
  50. .mockReturnValueOnce(mockRoleRepository);
  51. userService = new UserService(mockDataSource);
  52. });
  53. afterEach(() => {
  54. vi.clearAllMocks();
  55. });
  56. describe('createUser', () => {
  57. it('应该成功创建用户并哈希密码', async () => {
  58. const userData = {
  59. username: 'testuser',
  60. password: 'password123',
  61. email: 'test@example.com'
  62. };
  63. const mockUser = { id: 1, ...userData, password: 'hashed_password' } as User;
  64. mockUserRepository.create.mockReturnValue(mockUser);
  65. mockUserRepository.save.mockResolvedValue(mockUser);
  66. const result = await userService.createUser(userData);
  67. expect(bcrypt.hash).toHaveBeenCalledWith('password123', 10);
  68. expect(mockUserRepository.create).toHaveBeenCalledWith({
  69. ...userData,
  70. password: 'hashed_password'
  71. });
  72. expect(mockUserRepository.save).toHaveBeenCalledWith(mockUser);
  73. expect(result).toEqual(mockUser);
  74. });
  75. it('应该在创建用户失败时抛出错误', async () => {
  76. const userData = { username: 'testuser', password: 'password123' };
  77. const error = new Error('Database error');
  78. mockUserRepository.create.mockImplementation(() => {
  79. throw error;
  80. });
  81. await expect(userService.createUser(userData)).rejects.toThrow('Failed to create user');
  82. });
  83. });
  84. describe('getUserById', () => {
  85. it('应该通过ID成功获取用户', async () => {
  86. const mockUser = { id: 1, username: 'testuser' } as User;
  87. mockUserRepository.findOne.mockResolvedValue(mockUser);
  88. const result = await userService.getUserById(1);
  89. expect(mockUserRepository.findOne).toHaveBeenCalledWith({
  90. where: { id: 1 },
  91. relations: ['roles']
  92. });
  93. expect(result).toEqual(mockUser);
  94. });
  95. it('应该在用户不存在时返回null', async () => {
  96. mockUserRepository.findOne.mockResolvedValue(null);
  97. const result = await userService.getUserById(999);
  98. expect(result).toBeNull();
  99. });
  100. });
  101. describe('getUsersWithPagination', () => {
  102. it('应该成功获取分页用户列表', async () => {
  103. const mockUsers = [{ id: 1 }, { id: 2 }] as User[];
  104. const total = 2;
  105. const mockQueryBuilder = {
  106. leftJoinAndSelect: vi.fn().mockReturnThis(),
  107. skip: vi.fn().mockReturnThis(),
  108. take: vi.fn().mockReturnThis(),
  109. where: vi.fn().mockReturnThis(),
  110. getManyAndCount: vi.fn().mockResolvedValue([mockUsers, total])
  111. };
  112. mockUserRepository.createQueryBuilder.mockReturnValue(mockQueryBuilder as any);
  113. const result = await userService.getUsersWithPagination({
  114. page: 1,
  115. pageSize: 10
  116. });
  117. expect(mockQueryBuilder.skip).toHaveBeenCalledWith(0);
  118. expect(mockQueryBuilder.take).toHaveBeenCalledWith(10);
  119. expect(result).toEqual([mockUsers, total]);
  120. });
  121. it('应该支持关键词搜索', async () => {
  122. const mockQueryBuilder = {
  123. leftJoinAndSelect: vi.fn().mockReturnThis(),
  124. skip: vi.fn().mockReturnThis(),
  125. take: vi.fn().mockReturnThis(),
  126. where: vi.fn().mockReturnThis(),
  127. getManyAndCount: vi.fn().mockResolvedValue([[], 0])
  128. };
  129. mockUserRepository.createQueryBuilder.mockReturnValue(mockQueryBuilder as any);
  130. await userService.getUsersWithPagination({
  131. page: 1,
  132. pageSize: 10,
  133. keyword: 'test'
  134. });
  135. expect(mockQueryBuilder.where).toHaveBeenCalledWith(
  136. 'user.username LIKE :keyword OR user.nickname LIKE :keyword OR user.phone LIKE :keyword',
  137. { keyword: '%test%' }
  138. );
  139. });
  140. });
  141. describe('verifyPassword', () => {
  142. it('应该验证密码正确', async () => {
  143. const user = { password: 'hashed_password' } as User;
  144. const result = await userService.verifyPassword(user, 'password123');
  145. expect(bcrypt.compare).toHaveBeenCalledWith('password123', 'hashed_password');
  146. expect(result).toBe(true);
  147. });
  148. it('应该验证密码错误', async () => {
  149. vi.mocked(bcrypt.compare).mockResolvedValueOnce(false);
  150. const user = { password: 'hashed_password' } as User;
  151. const result = await userService.verifyPassword(user, 'wrong_password');
  152. expect(result).toBe(false);
  153. });
  154. });
  155. });