template-saas/apps/backend/dist/modules/onboarding/__tests__/onboarding.service.spec.js
rckrdmrd 50a821a415
Some checks failed
CI / Backend CI (push) Has been cancelled
CI / Frontend CI (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / CI Summary (push) Has been cancelled
[SIMCO-V38] feat: Actualizar a SIMCO v3.8.0
- HERENCIA-SIMCO.md actualizado con directivas v3.7 y v3.8
- Actualizaciones de configuracion

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 08:53:08 -06:00

250 lines
12 KiB
JavaScript

"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