"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); const testing_1 = require("@nestjs/testing"); const typeorm_1 = require("@nestjs/typeorm"); const jwt_1 = require("@nestjs/jwt"); const config_1 = require("@nestjs/config"); const typeorm_2 = require("typeorm"); const common_1 = require("@nestjs/common"); const bcrypt = __importStar(require("bcrypt")); const auth_service_1 = require("../services/auth.service"); const entities_1 = require("../entities"); jest.mock('bcrypt'); const mockedBcrypt = bcrypt; describe('AuthService', () => { let service; let userRepository; let sessionRepository; let tokenRepository; let jwtService; let configService; const mockUser = { id: '550e8400-e29b-41d4-a716-446655440000', tenant_id: '550e8400-e29b-41d4-a716-446655440001', email: 'test@example.com', password_hash: 'hashed_password', first_name: 'Test', last_name: 'User', status: 'active', email_verified: true, }; const mockTenantId = '550e8400-e29b-41d4-a716-446655440001'; beforeEach(async () => { const mockUserRepo = { findOne: jest.fn(), create: jest.fn(), save: jest.fn(), update: jest.fn(), }; const mockSessionRepo = { findOne: jest.fn(), save: jest.fn(), update: jest.fn(), }; const mockTokenRepo = { findOne: jest.fn(), save: jest.fn(), update: jest.fn(), }; const mockJwtService = { sign: jest.fn(), verify: jest.fn(), }; const mockConfigService = { get: jest.fn(), }; const mockDataSource = { createQueryRunner: jest.fn(), }; const module = await testing_1.Test.createTestingModule({ providers: [ auth_service_1.AuthService, { provide: (0, typeorm_1.getRepositoryToken)(entities_1.User), useValue: mockUserRepo }, { provide: (0, typeorm_1.getRepositoryToken)(entities_1.Session), useValue: mockSessionRepo }, { provide: (0, typeorm_1.getRepositoryToken)(entities_1.Token), useValue: mockTokenRepo }, { provide: jwt_1.JwtService, useValue: mockJwtService }, { provide: config_1.ConfigService, useValue: mockConfigService }, { provide: typeorm_2.DataSource, useValue: mockDataSource }, ], }).compile(); service = module.get(auth_service_1.AuthService); userRepository = module.get((0, typeorm_1.getRepositoryToken)(entities_1.User)); sessionRepository = module.get((0, typeorm_1.getRepositoryToken)(entities_1.Session)); tokenRepository = module.get((0, typeorm_1.getRepositoryToken)(entities_1.Token)); jwtService = module.get(jwt_1.JwtService); configService = module.get(config_1.ConfigService); }); afterEach(() => { jest.clearAllMocks(); }); describe('register', () => { const registerDto = { email: 'newuser@example.com', password: 'SecurePass123!', first_name: 'New', last_name: 'User', }; it('should register a new user successfully', async () => { userRepository.findOne.mockResolvedValue(null); mockedBcrypt.hash.mockResolvedValue('hashed_password'); userRepository.create.mockReturnValue({ ...mockUser, email: registerDto.email, status: 'pending_verification', }); userRepository.save.mockResolvedValue({ ...mockUser, email: registerDto.email, status: 'pending_verification', }); sessionRepository.save.mockResolvedValue({}); tokenRepository.save.mockResolvedValue({}); jwtService.sign.mockReturnValueOnce('access_token').mockReturnValueOnce('refresh_token'); configService.get.mockReturnValue('15m'); const result = await service.register(registerDto, mockTenantId); expect(result).toHaveProperty('user'); expect(result).toHaveProperty('accessToken'); expect(result).toHaveProperty('refreshToken'); expect(userRepository.findOne).toHaveBeenCalled(); expect(userRepository.save).toHaveBeenCalled(); }); it('should throw ConflictException if email already exists', async () => { userRepository.findOne.mockResolvedValue(mockUser); await expect(service.register(registerDto, mockTenantId)).rejects.toThrow(common_1.ConflictException); }); }); describe('login', () => { const loginDto = { email: 'test@example.com', password: 'password123', }; it('should login user successfully', async () => { userRepository.findOne.mockResolvedValue(mockUser); mockedBcrypt.compare.mockResolvedValue(true); userRepository.save.mockResolvedValue(mockUser); sessionRepository.save.mockResolvedValue({}); jwtService.sign.mockReturnValueOnce('access_token').mockReturnValueOnce('refresh_token'); configService.get.mockReturnValue('15m'); const result = await service.login(loginDto, mockTenantId); expect(result).toHaveProperty('user'); expect(result).toHaveProperty('accessToken'); expect(result).toHaveProperty('refreshToken'); expect(result.user).not.toHaveProperty('password_hash'); }); it('should throw UnauthorizedException for invalid email', async () => { userRepository.findOne.mockResolvedValue(null); await expect(service.login(loginDto, mockTenantId)).rejects.toThrow(common_1.UnauthorizedException); }); it('should throw UnauthorizedException for invalid password', async () => { userRepository.findOne.mockResolvedValue(mockUser); mockedBcrypt.compare.mockResolvedValue(false); await expect(service.login(loginDto, mockTenantId)).rejects.toThrow(common_1.UnauthorizedException); }); it('should throw UnauthorizedException for suspended user', async () => { userRepository.findOne.mockResolvedValue({ ...mockUser, status: 'suspended', }); mockedBcrypt.compare.mockResolvedValue(true); await expect(service.login(loginDto, mockTenantId)).rejects.toThrow(common_1.UnauthorizedException); }); it('should throw UnauthorizedException for inactive user', async () => { userRepository.findOne.mockResolvedValue({ ...mockUser, status: 'inactive', }); mockedBcrypt.compare.mockResolvedValue(true); await expect(service.login(loginDto, mockTenantId)).rejects.toThrow(common_1.UnauthorizedException); }); }); describe('logout', () => { it('should invalidate session successfully', async () => { sessionRepository.update.mockResolvedValue({ affected: 1 }); await service.logout(mockUser.id, 'session_token'); expect(sessionRepository.update).toHaveBeenCalledWith({ user_id: mockUser.id, session_token: 'session_token' }, { is_active: false }); }); }); describe('logoutAll', () => { it('should invalidate all sessions for user', async () => { sessionRepository.update.mockResolvedValue({ affected: 3 }); await service.logoutAll(mockUser.id); expect(sessionRepository.update).toHaveBeenCalledWith({ user_id: mockUser.id }, { is_active: false }); }); }); describe('changePassword', () => { const changePasswordDto = { currentPassword: 'oldPassword123', newPassword: 'newPassword456', }; it('should change password successfully', async () => { userRepository.findOne.mockResolvedValue(mockUser); mockedBcrypt.compare.mockResolvedValue(true); mockedBcrypt.hash.mockResolvedValue('new_hashed_password'); userRepository.update.mockResolvedValue({ affected: 1 }); const result = await service.changePassword(mockUser.id, changePasswordDto); expect(result).toHaveProperty('message'); expect(userRepository.update).toHaveBeenCalled(); }); it('should throw NotFoundException if user not found', async () => { userRepository.findOne.mockResolvedValue(null); await expect(service.changePassword('invalid-id', changePasswordDto)).rejects.toThrow(common_1.NotFoundException); }); it('should throw BadRequestException for incorrect current password', async () => { userRepository.findOne.mockResolvedValue(mockUser); mockedBcrypt.compare.mockResolvedValue(false); await expect(service.changePassword(mockUser.id, changePasswordDto)).rejects.toThrow(common_1.BadRequestException); }); it('should throw BadRequestException if new password same as current', async () => { const samePasswordDto = { currentPassword: 'samePassword', newPassword: 'samePassword', }; userRepository.findOne.mockResolvedValue(mockUser); mockedBcrypt.compare.mockResolvedValue(true); await expect(service.changePassword(mockUser.id, samePasswordDto)).rejects.toThrow(common_1.BadRequestException); }); }); describe('requestPasswordReset', () => { it('should create reset token for existing user', async () => { userRepository.findOne.mockResolvedValue(mockUser); tokenRepository.save.mockResolvedValue({}); const result = await service.requestPasswordReset(mockUser.email, mockTenantId); expect(result).toHaveProperty('message'); expect(tokenRepository.save).toHaveBeenCalled(); }); it('should return success message even for non-existing email (security)', async () => { userRepository.findOne.mockResolvedValue(null); const result = await service.requestPasswordReset('nonexistent@example.com', mockTenantId); expect(result).toHaveProperty('message'); expect(tokenRepository.save).not.toHaveBeenCalled(); }); }); describe('resetPassword', () => { const mockToken = { id: 'token-id', user_id: mockUser.id, tenant_id: mockTenantId, token_type: 'password_reset', is_used: false, expires_at: new Date(Date.now() + 3600000), }; it('should reset password successfully', async () => { tokenRepository.findOne.mockResolvedValue(mockToken); mockedBcrypt.hash.mockResolvedValue('new_hashed_password'); userRepository.update.mockResolvedValue({ affected: 1 }); tokenRepository.update.mockResolvedValue({ affected: 1 }); sessionRepository.update.mockResolvedValue({ affected: 1 }); const result = await service.resetPassword('valid_token', 'newPassword123', mockTenantId); expect(result).toHaveProperty('message'); expect(userRepository.update).toHaveBeenCalled(); expect(tokenRepository.update).toHaveBeenCalled(); }); it('should throw BadRequestException for invalid token', async () => { tokenRepository.findOne.mockResolvedValue(null); await expect(service.resetPassword('invalid_token', 'newPassword123', mockTenantId)).rejects.toThrow(common_1.BadRequestException); }); it('should throw BadRequestException for expired token', async () => { tokenRepository.findOne.mockResolvedValue({ ...mockToken, expires_at: new Date(Date.now() - 3600000), }); await expect(service.resetPassword('expired_token', 'newPassword123', mockTenantId)).rejects.toThrow(common_1.BadRequestException); }); }); describe('verifyEmail', () => { const mockToken = { id: 'token-id', user_id: mockUser.id, tenant_id: mockTenantId, token_type: 'email_verification', is_used: false, expires_at: new Date(Date.now() + 3600000), }; it('should verify email successfully', async () => { tokenRepository.findOne.mockResolvedValue(mockToken); userRepository.update.mockResolvedValue({ affected: 1 }); tokenRepository.update.mockResolvedValue({ affected: 1 }); const result = await service.verifyEmail('valid_token', mockTenantId); expect(result).toHaveProperty('message'); expect(userRepository.update).toHaveBeenCalledWith({ id: mockToken.user_id }, expect.objectContaining({ email_verified: true, status: 'active', })); }); it('should throw BadRequestException for invalid token', async () => { tokenRepository.findOne.mockResolvedValue(null); await expect(service.verifyEmail('invalid_token', mockTenantId)).rejects.toThrow(common_1.BadRequestException); }); }); describe('validateUser', () => { it('should return user if active', async () => { userRepository.findOne.mockResolvedValue(mockUser); const result = await service.validateUser(mockUser.id); expect(result).toEqual(mockUser); }); it('should return null if user not found or not active', async () => { userRepository.findOne.mockResolvedValue(null); const result = await service.validateUser('invalid-id'); expect(result).toBeNull(); }); }); describe('getProfile', () => { it('should return sanitized user profile', async () => { userRepository.findOne.mockResolvedValue(mockUser); const result = await service.getProfile(mockUser.id); expect(result).not.toHaveProperty('password_hash'); expect(result).toHaveProperty('email'); }); it('should throw NotFoundException if user not found', async () => { userRepository.findOne.mockResolvedValue(null); await expect(service.getProfile('invalid-id')).rejects.toThrow(common_1.NotFoundException); }); }); describe('refreshToken', () => { const mockSession = { id: 'session-id', user_id: mockUser.id, tenant_id: mockTenantId, refresh_token_hash: 'hashed_refresh_token', is_active: true, expires_at: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), }; it('should refresh tokens successfully with valid refresh token', async () => { jwtService.verify.mockReturnValue({ sub: mockUser.id, email: mockUser.email, tenant_id: mockTenantId, }); configService.get.mockReturnValue('test-secret'); userRepository.findOne.mockResolvedValue(mockUser); sessionRepository.findOne.mockResolvedValue(mockSession); sessionRepository.update.mockResolvedValue({ affected: 1 }); jwtService.sign.mockReturnValueOnce('new_access_token').mockReturnValueOnce('new_refresh_token'); const result = await service.refreshToken('valid_refresh_token', '127.0.0.1', 'Mozilla/5.0'); expect(result).toHaveProperty('accessToken', 'new_access_token'); expect(result).toHaveProperty('refreshToken', 'new_refresh_token'); expect(jwtService.verify).toHaveBeenCalled(); expect(sessionRepository.update).toHaveBeenCalled(); }); it('should throw UnauthorizedException when user not found', async () => { jwtService.verify.mockReturnValue({ sub: 'non-existent-user-id', email: 'test@example.com', tenant_id: mockTenantId, }); configService.get.mockReturnValue('test-secret'); userRepository.findOne.mockResolvedValue(null); await expect(service.refreshToken('valid_token_but_user_deleted')).rejects.toThrow(common_1.UnauthorizedException); }); it('should throw UnauthorizedException when session not found', async () => { jwtService.verify.mockReturnValue({ sub: mockUser.id, email: mockUser.email, tenant_id: mockTenantId, }); configService.get.mockReturnValue('test-secret'); userRepository.findOne.mockResolvedValue(mockUser); sessionRepository.findOne.mockResolvedValue(null); await expect(service.refreshToken('token_with_no_session')).rejects.toThrow(common_1.UnauthorizedException); }); it('should throw UnauthorizedException when session is expired', async () => { const expiredSession = { ...mockSession, expires_at: new Date(Date.now() - 3600000), }; jwtService.verify.mockReturnValue({ sub: mockUser.id, email: mockUser.email, tenant_id: mockTenantId, }); configService.get.mockReturnValue('test-secret'); userRepository.findOne.mockResolvedValue(mockUser); sessionRepository.findOne.mockResolvedValue(expiredSession); sessionRepository.update.mockResolvedValue({ affected: 1 }); await expect(service.refreshToken('token_with_expired_session')).rejects.toThrow(common_1.UnauthorizedException); }); it('should throw UnauthorizedException for invalid JWT token', async () => { jwtService.verify.mockImplementation(() => { throw new Error('Invalid token'); }); configService.get.mockReturnValue('test-secret'); await expect(service.refreshToken('invalid_jwt_token')).rejects.toThrow(common_1.UnauthorizedException); }); it('should deactivate expired session when detected', async () => { const expiredSession = { ...mockSession, expires_at: new Date(Date.now() - 3600000), }; jwtService.verify.mockReturnValue({ sub: mockUser.id, email: mockUser.email, tenant_id: mockTenantId, }); configService.get.mockReturnValue('test-secret'); userRepository.findOne.mockResolvedValue(mockUser); sessionRepository.findOne.mockResolvedValue(expiredSession); sessionRepository.update.mockResolvedValue({ affected: 1 }); await expect(service.refreshToken('token')).rejects.toThrow(common_1.UnauthorizedException); expect(sessionRepository.update).toHaveBeenCalledWith({ id: expiredSession.id }, { is_active: false }); }); }); describe('verifyEmail - additional cases', () => { const mockToken = { id: 'token-id', user_id: mockUser.id, tenant_id: mockTenantId, token_type: 'email_verification', is_used: false, expires_at: new Date(Date.now() + 3600000), }; it('should throw BadRequestException for expired verification token', async () => { tokenRepository.findOne.mockResolvedValue({ ...mockToken, expires_at: new Date(Date.now() - 3600000), }); await expect(service.verifyEmail('expired_verification_token', mockTenantId)).rejects.toThrow(common_1.BadRequestException); }); }); describe('register - additional cases', () => { const registerDto = { email: 'newuser@example.com', password: 'SecurePass123!', }; it('should register user with IP and userAgent metadata', async () => { userRepository.findOne.mockResolvedValue(null); mockedBcrypt.hash.mockResolvedValue('hashed_password'); userRepository.create.mockReturnValue({ ...mockUser, email: registerDto.email, status: 'pending_verification', }); userRepository.save.mockResolvedValue({ ...mockUser, email: registerDto.email, status: 'pending_verification', }); sessionRepository.save.mockResolvedValue({}); tokenRepository.save.mockResolvedValue({}); jwtService.sign.mockReturnValueOnce('access_token').mockReturnValueOnce('refresh_token'); configService.get.mockReturnValue('15m'); const ip = '192.168.1.100'; const userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'; const result = await service.register(registerDto, mockTenantId, ip, userAgent); expect(result).toHaveProperty('user'); expect(result).toHaveProperty('accessToken'); expect(sessionRepository.save).toHaveBeenCalledWith(expect.objectContaining({ ip_address: ip, user_agent: userAgent, })); }); it('should register user without optional fields', async () => { userRepository.findOne.mockResolvedValue(null); mockedBcrypt.hash.mockResolvedValue('hashed_password'); userRepository.create.mockReturnValue({ ...mockUser, email: registerDto.email, first_name: null, last_name: null, phone: null, status: 'pending_verification', }); userRepository.save.mockResolvedValue({ ...mockUser, email: registerDto.email, status: 'pending_verification', }); sessionRepository.save.mockResolvedValue({}); tokenRepository.save.mockResolvedValue({}); jwtService.sign.mockReturnValueOnce('access_token').mockReturnValueOnce('refresh_token'); configService.get.mockReturnValue('15m'); const result = await service.register(registerDto, mockTenantId); expect(result).toHaveProperty('user'); expect(userRepository.create).toHaveBeenCalledWith(expect.objectContaining({ first_name: null, last_name: null, phone: null, })); }); it('should create email verification token on registration', async () => { userRepository.findOne.mockResolvedValue(null); mockedBcrypt.hash.mockResolvedValue('hashed_password'); userRepository.create.mockReturnValue({ ...mockUser, email: registerDto.email, status: 'pending_verification', }); userRepository.save.mockResolvedValue({ ...mockUser, email: registerDto.email, status: 'pending_verification', }); sessionRepository.save.mockResolvedValue({}); tokenRepository.save.mockResolvedValue({}); jwtService.sign.mockReturnValueOnce('access_token').mockReturnValueOnce('refresh_token'); configService.get.mockReturnValue('15m'); await service.register(registerDto, mockTenantId); expect(tokenRepository.save).toHaveBeenCalledWith(expect.objectContaining({ token_type: 'email_verification', })); }); }); describe('login - additional cases', () => { const loginDto = { email: 'test@example.com', password: 'password123', }; it('should update last_login_at and last_login_ip on successful login', async () => { userRepository.findOne.mockResolvedValue(mockUser); mockedBcrypt.compare.mockResolvedValue(true); userRepository.save.mockResolvedValue(mockUser); sessionRepository.save.mockResolvedValue({}); jwtService.sign.mockReturnValueOnce('access_token').mockReturnValueOnce('refresh_token'); configService.get.mockReturnValue('15m'); const ip = '10.0.0.1'; await service.login(loginDto, mockTenantId, ip); expect(userRepository.save).toHaveBeenCalledWith(expect.objectContaining({ last_login_ip: ip, })); }); it('should detect device type from userAgent - mobile', async () => { userRepository.findOne.mockResolvedValue(mockUser); mockedBcrypt.compare.mockResolvedValue(true); userRepository.save.mockResolvedValue(mockUser); sessionRepository.save.mockResolvedValue({}); jwtService.sign.mockReturnValueOnce('access_token').mockReturnValueOnce('refresh_token'); configService.get.mockReturnValue('15m'); await service.login(loginDto, mockTenantId, '127.0.0.1', 'Mozilla/5.0 (iPhone; CPU iPhone OS)'); expect(sessionRepository.save).toHaveBeenCalledWith(expect.objectContaining({ device_type: 'mobile', })); }); it('should detect device type from userAgent - tablet', async () => { userRepository.findOne.mockResolvedValue(mockUser); mockedBcrypt.compare.mockResolvedValue(true); userRepository.save.mockResolvedValue(mockUser); sessionRepository.save.mockResolvedValue({}); jwtService.sign.mockReturnValueOnce('access_token').mockReturnValueOnce('refresh_token'); configService.get.mockReturnValue('15m'); await service.login(loginDto, mockTenantId, '127.0.0.1', 'Mozilla/5.0 (iPad; CPU OS)'); expect(sessionRepository.save).toHaveBeenCalledWith(expect.objectContaining({ device_type: 'tablet', })); }); it('should detect device type from userAgent - desktop', async () => { userRepository.findOne.mockResolvedValue(mockUser); mockedBcrypt.compare.mockResolvedValue(true); userRepository.save.mockResolvedValue(mockUser); sessionRepository.save.mockResolvedValue({}); jwtService.sign.mockReturnValueOnce('access_token').mockReturnValueOnce('refresh_token'); configService.get.mockReturnValue('15m'); await service.login(loginDto, mockTenantId, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'); expect(sessionRepository.save).toHaveBeenCalledWith(expect.objectContaining({ device_type: 'desktop', })); }); it('should detect device type as unknown when no userAgent', async () => { userRepository.findOne.mockResolvedValue(mockUser); mockedBcrypt.compare.mockResolvedValue(true); userRepository.save.mockResolvedValue(mockUser); sessionRepository.save.mockResolvedValue({}); jwtService.sign.mockReturnValueOnce('access_token').mockReturnValueOnce('refresh_token'); configService.get.mockReturnValue('15m'); await service.login(loginDto, mockTenantId, '127.0.0.1'); expect(sessionRepository.save).toHaveBeenCalledWith(expect.objectContaining({ device_type: 'unknown', })); }); it('should login with Android device', async () => { userRepository.findOne.mockResolvedValue(mockUser); mockedBcrypt.compare.mockResolvedValue(true); userRepository.save.mockResolvedValue(mockUser); sessionRepository.save.mockResolvedValue({}); jwtService.sign.mockReturnValueOnce('access_token').mockReturnValueOnce('refresh_token'); configService.get.mockReturnValue('15m'); await service.login(loginDto, mockTenantId, '127.0.0.1', 'Mozilla/5.0 (Linux; Android 10)'); expect(sessionRepository.save).toHaveBeenCalledWith(expect.objectContaining({ device_type: 'mobile', })); }); }); describe('requestPasswordReset - additional cases', () => { it('should create token with correct expiry time (1 hour)', async () => { const beforeCall = Date.now(); userRepository.findOne.mockResolvedValue(mockUser); tokenRepository.save.mockResolvedValue({}); await service.requestPasswordReset(mockUser.email, mockTenantId); const savedToken = tokenRepository.save.mock.calls[0][0]; expect(savedToken.expires_at).toBeDefined(); const expiryTime = savedToken.expires_at.getTime(); const expectedMinExpiry = beforeCall + 60 * 60 * 1000 - 1000; const expectedMaxExpiry = beforeCall + 60 * 60 * 1000 + 1000; expect(expiryTime).toBeGreaterThanOrEqual(expectedMinExpiry); expect(expiryTime).toBeLessThanOrEqual(expectedMaxExpiry); expect(savedToken.token_type).toBe('password_reset'); }); }); describe('resetPassword - additional cases', () => { const mockToken = { id: 'token-id', user_id: mockUser.id, tenant_id: mockTenantId, token_type: 'password_reset', is_used: false, expires_at: new Date(Date.now() + 3600000), }; it('should invalidate all sessions after password reset', async () => { tokenRepository.findOne.mockResolvedValue(mockToken); mockedBcrypt.hash.mockResolvedValue('new_hashed_password'); userRepository.update.mockResolvedValue({ affected: 1 }); tokenRepository.update.mockResolvedValue({ affected: 1 }); sessionRepository.update.mockResolvedValue({ affected: 3 }); await service.resetPassword('valid_token', 'newPassword123', mockTenantId); expect(sessionRepository.update).toHaveBeenCalledWith({ user_id: mockToken.user_id }, { is_active: false }); }); it('should mark token as used with timestamp', async () => { const beforeCall = Date.now(); tokenRepository.findOne.mockResolvedValue(mockToken); mockedBcrypt.hash.mockResolvedValue('new_hashed_password'); userRepository.update.mockResolvedValue({ affected: 1 }); tokenRepository.update.mockResolvedValue({ affected: 1 }); sessionRepository.update.mockResolvedValue({ affected: 1 }); await service.resetPassword('valid_token', 'newPassword123', mockTenantId); expect(tokenRepository.update).toHaveBeenCalledWith({ id: mockToken.id }, expect.objectContaining({ is_used: true, })); const updateCall = tokenRepository.update.mock.calls[0][1]; expect(updateCall.used_at).toBeDefined(); expect(updateCall.used_at.getTime()).toBeGreaterThanOrEqual(beforeCall); }); }); describe('validateUser - additional cases', () => { it('should query for active users only', async () => { userRepository.findOne.mockResolvedValue(mockUser); await service.validateUser(mockUser.id); expect(userRepository.findOne).toHaveBeenCalledWith({ where: { id: mockUser.id, status: 'active' }, }); }); }); }); //# sourceMappingURL=auth.service.spec.js.map