"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); }); }); }); //# sourceMappingURL=auth.service.spec.js.map