"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const testing_1 = require("@nestjs/testing"); const typeorm_1 = require("@nestjs/typeorm"); const common_1 = require("@nestjs/common"); const onboarding_service_1 = require("../onboarding.service"); const tenant_entity_1 = require("../../tenants/entities/tenant.entity"); const user_entity_1 = require("../../auth/entities/user.entity"); const token_entity_1 = require("../../auth/entities/token.entity"); const subscription_entity_1 = require("../../billing/entities/subscription.entity"); const email_service_1 = require("../../email/services/email.service"); const audit_service_1 = require("../../audit/services/audit.service"); describe('OnboardingService', () => { let service; let tenantRepo; let userRepo; let tokenRepo; let subscriptionRepo; let emailService; let auditService; const mockTenantId = '550e8400-e29b-41d4-a716-446655440001'; const mockUserId = '550e8400-e29b-41d4-a716-446655440002'; const mockPlanId = '550e8400-e29b-41d4-a716-446655440003'; const mockTenant = { id: mockTenantId, name: 'Test Company', slug: 'test-company', status: 'trial', logo_url: null, settings: null, plan_id: null, }; const mockUser = { id: mockUserId, tenant_id: mockTenantId, email: 'test@example.com', first_name: 'Test', last_name: 'User', status: 'active', }; const mockSubscription = { id: 'sub-001', tenant_id: mockTenantId, plan_id: mockPlanId, status: subscription_entity_1.SubscriptionStatus.ACTIVE, }; beforeEach(async () => { const mockTenantRepo = { findOne: jest.fn(), save: jest.fn(), }; const mockUserRepo = { findOne: jest.fn(), count: jest.fn(), }; const mockTokenRepo = { count: jest.fn(), }; const mockSubscriptionRepo = { findOne: jest.fn(), }; const mockEmailService = { sendTemplateEmail: jest.fn(), }; const mockAuditService = { createAuditLog: jest.fn(), }; const module = await testing_1.Test.createTestingModule({ providers: [ onboarding_service_1.OnboardingService, { provide: (0, typeorm_1.getRepositoryToken)(tenant_entity_1.Tenant), useValue: mockTenantRepo }, { provide: (0, typeorm_1.getRepositoryToken)(user_entity_1.User), useValue: mockUserRepo }, { provide: (0, typeorm_1.getRepositoryToken)(token_entity_1.Token), useValue: mockTokenRepo }, { provide: (0, typeorm_1.getRepositoryToken)(subscription_entity_1.Subscription), useValue: mockSubscriptionRepo }, { provide: email_service_1.EmailService, useValue: mockEmailService }, { provide: audit_service_1.AuditService, useValue: mockAuditService }, ], }).compile(); service = module.get(onboarding_service_1.OnboardingService); tenantRepo = module.get((0, typeorm_1.getRepositoryToken)(tenant_entity_1.Tenant)); userRepo = module.get((0, typeorm_1.getRepositoryToken)(user_entity_1.User)); tokenRepo = module.get((0, typeorm_1.getRepositoryToken)(token_entity_1.Token)); subscriptionRepo = module.get((0, typeorm_1.getRepositoryToken)(subscription_entity_1.Subscription)); emailService = module.get(email_service_1.EmailService); auditService = module.get(audit_service_1.AuditService); }); afterEach(() => { jest.clearAllMocks(); }); describe('getStatus', () => { it('should return onboarding status for tenant with all data', async () => { tenantRepo.findOne.mockResolvedValue(mockTenant); tokenRepo.count.mockResolvedValue(3); userRepo.count.mockResolvedValue(2); subscriptionRepo.findOne.mockResolvedValue(mockSubscription); const result = await service.getStatus(mockTenantId); expect(result).toBeDefined(); expect(result.completed).toBe(false); expect(result.data.company).toBeDefined(); expect(result.data.company?.name).toBe('Test Company'); expect(result.data.company?.slug).toBe('test-company'); expect(result.data.team.invitesSent).toBe(3); expect(result.data.team.membersJoined).toBe(1); expect(result.data.plan.selected).toBe(true); expect(result.data.plan.planId).toBe(mockPlanId); }); it('should return step 1 when company info is missing', async () => { const tenantWithoutInfo = { ...mockTenant, name: '', slug: '' }; tenantRepo.findOne.mockResolvedValue(tenantWithoutInfo); tokenRepo.count.mockResolvedValue(0); userRepo.count.mockResolvedValue(1); subscriptionRepo.findOne.mockResolvedValue(null); const result = await service.getStatus(mockTenantId); expect(result.step).toBe(1); expect(result.data.company).toBeNull(); }); it('should return step 3 when plan is not selected', async () => { tenantRepo.findOne.mockResolvedValue(mockTenant); tokenRepo.count.mockResolvedValue(0); userRepo.count.mockResolvedValue(1); subscriptionRepo.findOne.mockResolvedValue(null); const result = await service.getStatus(mockTenantId); expect(result.step).toBe(3); expect(result.data.plan.selected).toBe(false); }); it('should return step 4 when ready for completion', async () => { tenantRepo.findOne.mockResolvedValue(mockTenant); tokenRepo.count.mockResolvedValue(2); userRepo.count.mockResolvedValue(3); subscriptionRepo.findOne.mockResolvedValue(mockSubscription); const result = await service.getStatus(mockTenantId); expect(result.step).toBe(4); }); it('should return completed=true when tenant is active', async () => { const activeTenant = { ...mockTenant, status: 'active' }; tenantRepo.findOne.mockResolvedValue(activeTenant); tokenRepo.count.mockResolvedValue(0); userRepo.count.mockResolvedValue(1); subscriptionRepo.findOne.mockResolvedValue(mockSubscription); const result = await service.getStatus(mockTenantId); expect(result.completed).toBe(true); expect(result.step).toBe(4); }); it('should throw BadRequestException when tenant not found', async () => { tenantRepo.findOne.mockResolvedValue(null); await expect(service.getStatus(mockTenantId)).rejects.toThrow(common_1.BadRequestException); }); }); describe('completeOnboarding', () => { it('should complete onboarding successfully', async () => { tenantRepo.findOne.mockResolvedValue(mockTenant); userRepo.findOne.mockResolvedValue(mockUser); tenantRepo.save.mockResolvedValue({ ...mockTenant, status: 'active' }); auditService.createAuditLog.mockResolvedValue({}); emailService.sendTemplateEmail.mockResolvedValue({ success: true }); const result = await service.completeOnboarding(mockTenantId, mockUserId); expect(result.success).toBe(true); expect(result.redirectUrl).toBe('/dashboard'); expect(tenantRepo.save).toHaveBeenCalled(); expect(auditService.createAuditLog).toHaveBeenCalledWith(expect.objectContaining({ tenant_id: mockTenantId, user_id: mockUserId, action: 'update', entity_type: 'tenant', })); expect(emailService.sendTemplateEmail).toHaveBeenCalledWith(expect.objectContaining({ templateKey: 'welcome', })); }); it('should return success if tenant already active', async () => { const activeTenant = { ...mockTenant, status: 'active' }; tenantRepo.findOne.mockResolvedValue(activeTenant); const result = await service.completeOnboarding(mockTenantId, mockUserId); expect(result.success).toBe(true); expect(result.redirectUrl).toBe('/dashboard'); expect(tenantRepo.save).not.toHaveBeenCalled(); }); it('should throw BadRequestException when tenant not found', async () => { tenantRepo.findOne.mockResolvedValue(null); await expect(service.completeOnboarding(mockTenantId, mockUserId)).rejects.toThrow(common_1.BadRequestException); }); it('should throw BadRequestException when user not found', async () => { const trialTenant = { ...mockTenant, status: 'trial' }; tenantRepo.findOne.mockResolvedValue(trialTenant); userRepo.findOne.mockResolvedValue(null); await expect(service.completeOnboarding(mockTenantId, mockUserId)).rejects.toThrow(common_1.BadRequestException); }); it('should complete onboarding even if email fails', async () => { tenantRepo.findOne.mockResolvedValue(mockTenant); userRepo.findOne.mockResolvedValue(mockUser); tenantRepo.save.mockResolvedValue({ ...mockTenant, status: 'active' }); auditService.createAuditLog.mockResolvedValue({}); emailService.sendTemplateEmail.mockRejectedValue(new Error('Email failed')); const result = await service.completeOnboarding(mockTenantId, mockUserId); expect(result.success).toBe(true); expect(result.redirectUrl).toBe('/dashboard'); }); it('should update tenant status from trial to active', async () => { const trialTenant = { ...mockTenant, status: 'trial' }; tenantRepo.findOne.mockResolvedValue(trialTenant); userRepo.findOne.mockResolvedValue(mockUser); tenantRepo.save.mockImplementation(async (tenant) => tenant); auditService.createAuditLog.mockResolvedValue({}); emailService.sendTemplateEmail.mockResolvedValue({ success: true }); await service.completeOnboarding(mockTenantId, mockUserId); expect(tenantRepo.save).toHaveBeenCalledWith(expect.objectContaining({ status: 'active', })); }); }); describe('team data calculation', () => { it('should return 0 members joined when only owner exists', async () => { tenantRepo.findOne.mockResolvedValue(mockTenant); tokenRepo.count.mockResolvedValue(0); userRepo.count.mockResolvedValue(1); subscriptionRepo.findOne.mockResolvedValue(null); const result = await service.getStatus(mockTenantId); expect(result.data.team.membersJoined).toBe(0); }); it('should count invitations correctly', async () => { tenantRepo.findOne.mockResolvedValue(mockTenant); tokenRepo.count.mockResolvedValue(5); userRepo.count.mockResolvedValue(1); subscriptionRepo.findOne.mockResolvedValue(null); const result = await service.getStatus(mockTenantId); expect(result.data.team.invitesSent).toBe(5); }); }); describe('plan data calculation', () => { it('should return selected=false when no subscription', async () => { tenantRepo.findOne.mockResolvedValue(mockTenant); tokenRepo.count.mockResolvedValue(0); userRepo.count.mockResolvedValue(1); subscriptionRepo.findOne.mockResolvedValue(null); const result = await service.getStatus(mockTenantId); expect(result.data.plan.selected).toBe(false); expect(result.data.plan.planId).toBeNull(); }); it('should return selected=true with planId when subscription exists', async () => { tenantRepo.findOne.mockResolvedValue(mockTenant); tokenRepo.count.mockResolvedValue(0); userRepo.count.mockResolvedValue(1); subscriptionRepo.findOne.mockResolvedValue(mockSubscription); const result = await service.getStatus(mockTenantId); expect(result.data.plan.selected).toBe(true); expect(result.data.plan.planId).toBe(mockPlanId); }); }); }); //# sourceMappingURL=onboarding.service.spec.js.map