- HERENCIA-SIMCO.md actualizado con directivas v3.7 y v3.8 - Actualizaciones de configuracion Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
250 lines
12 KiB
JavaScript
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
|