import { describe, it, expect, beforeEach, vi } from 'vitest'; import { UserService } from '../../src/services/user.service'; import { UserEntity } from '../../src/entities/user.entity'; import { Role } from '../../src/entities/role.entity'; import { DataSource, Repository } from 'typeorm'; // Mock DataSource const mockDataSource = { getRepository: vi.fn() } as unknown as DataSource; // Mock repositories const mockUserRepository = { create: vi.fn(), save: vi.fn(), findOne: vi.fn(), update: vi.fn(), delete: vi.fn(), find: vi.fn(), findByIds: vi.fn() } as unknown as Repository; const mockRoleRepository = { findByIds: vi.fn() } as unknown as Repository; describe('UserService', () => { let userService: UserService; beforeEach(() => { vi.clearAllMocks(); // Setup mock repositories vi.mocked(mockDataSource.getRepository) .mockReturnValueOnce(mockUserRepository) .mockReturnValueOnce(mockRoleRepository); userService = new UserService(mockDataSource); }); describe('createUser', () => { it('should create a user with hashed password', async () => { const userData = { username: 'testuser', password: 'password123', email: 'test@example.com' }; const mockUser = { id: 1, ...userData, password: 'hashed_password' } as UserEntity; vi.mocked(mockUserRepository.create).mockReturnValue(mockUser); vi.mocked(mockUserRepository.save).mockResolvedValue(mockUser); const result = await userService.createUser(userData); expect(mockUserRepository.create).toHaveBeenCalledWith({ ...userData, password: expect.not.stringMatching('password123') // Password should be hashed }); expect(mockUserRepository.save).toHaveBeenCalledWith(mockUser); expect(result).toEqual(mockUser); }); it('should throw error when creation fails', async () => { const userData = { username: 'testuser', password: 'password123' }; vi.mocked(mockUserRepository.create).mockImplementation(() => { throw new Error('Database error'); }); await expect(userService.createUser(userData)).rejects.toThrow('Failed to create user'); }); }); describe('getUserById', () => { it('should return user by id', async () => { const mockUser = { id: 1, username: 'testuser', roles: [] } as UserEntity; vi.mocked(mockUserRepository.findOne).mockResolvedValue(mockUser); const result = await userService.getUserById(1); expect(mockUserRepository.findOne).toHaveBeenCalledWith({ where: { id: 1 }, relations: ['roles'] }); expect(result).toEqual(mockUser); }); it('should return null when user not found', async () => { vi.mocked(mockUserRepository.findOne).mockResolvedValue(null); const result = await userService.getUserById(999); expect(result).toBeNull(); }); }); describe('getUserByUsername', () => { it('should return user by username', async () => { const mockUser = { id: 1, username: 'testuser', roles: [] } as UserEntity; vi.mocked(mockUserRepository.findOne).mockResolvedValue(mockUser); const result = await userService.getUserByUsername('testuser'); expect(mockUserRepository.findOne).toHaveBeenCalledWith({ where: { username: 'testuser' }, relations: ['roles'] }); expect(result).toEqual(mockUser); }); }); describe('updateUser', () => { it('should update user with hashed password', async () => { const updateData = { password: 'newpassword', email: 'new@example.com' }; const updatedUser = { id: 1, username: 'testuser', password: 'hashed_newpassword', email: 'new@example.com', roles: [] } as UserEntity; vi.mocked(mockUserRepository.update).mockResolvedValue({ affected: 1 } as any); vi.mocked(mockUserRepository.findOne).mockResolvedValue(updatedUser); const result = await userService.updateUser(1, updateData); expect(mockUserRepository.update).toHaveBeenCalledWith(1, { ...updateData, password: expect.not.stringMatching('newpassword') // Password should be hashed }); expect(result).toEqual(updatedUser); }); }); describe('deleteUser', () => { it('should delete user successfully', async () => { vi.mocked(mockUserRepository.delete).mockResolvedValue({ affected: 1 } as any); const result = await userService.deleteUser(1); expect(mockUserRepository.delete).toHaveBeenCalledWith(1); expect(result).toBe(true); }); it('should return false when user not found', async () => { vi.mocked(mockUserRepository.delete).mockResolvedValue({ affected: 0 } as any); const result = await userService.deleteUser(999); expect(result).toBe(false); }); }); describe('assignRoles', () => { it('should assign roles to user', async () => { const mockUser = { id: 1, username: 'testuser', roles: [] } as UserEntity; const mockRoles = [ { id: 1, name: 'admin' } as Role, { id: 2, name: 'user' } as Role ]; vi.mocked(mockUserRepository.findOne).mockResolvedValue(mockUser); vi.mocked(mockRoleRepository.findByIds).mockResolvedValue(mockRoles); vi.mocked(mockUserRepository.save).mockResolvedValue({ ...mockUser, roles: mockRoles } as UserEntity); const result = await userService.assignRoles(1, [1, 2]); expect(mockRoleRepository.findByIds).toHaveBeenCalledWith([1, 2]); expect(mockUserRepository.save).toHaveBeenCalledWith({ ...mockUser, roles: mockRoles }); expect(result?.roles).toEqual(mockRoles); }); it('should return null when user not found', async () => { vi.mocked(mockUserRepository.findOne).mockResolvedValue(null); const result = await userService.assignRoles(999, [1, 2]); expect(result).toBeNull(); }); }); describe('getUsers', () => { it('should return all users', async () => { const mockUsers = [ { id: 1, username: 'user1', roles: [] }, { id: 2, username: 'user2', roles: [] } ] as UserEntity[]; vi.mocked(mockUserRepository.find).mockResolvedValue(mockUsers); const result = await userService.getUsers(); expect(mockUserRepository.find).toHaveBeenCalledWith({ relations: ['roles'] }); expect(result).toEqual(mockUsers); }); }); describe('getUserByAccount', () => { it('should return user by username or email', async () => { const mockUser = { id: 1, username: 'testuser', email: 'test@example.com', roles: [] } as UserEntity; vi.mocked(mockUserRepository.findOne).mockResolvedValue(mockUser); const result = await userService.getUserByAccount('testuser'); expect(mockUserRepository.findOne).toHaveBeenCalledWith({ where: [{ username: 'testuser' }, { email: 'testuser' }], relations: ['roles'] }); expect(result).toEqual(mockUser); }); }); });