user.service.test.ts 5.6 KB

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