template-saas/apps/backend/dist/modules/superadmin/__tests__/superadmin.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

302 lines
14 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 superadmin_service_1 = require("../superadmin.service");
const tenant_entity_1 = require("../../tenants/entities/tenant.entity");
const user_entity_1 = require("../../auth/entities/user.entity");
const subscription_entity_1 = require("../../billing/entities/subscription.entity");
describe('SuperadminService', () => {
let service;
let tenantRepository;
let userRepository;
let subscriptionRepository;
const mockTenant = {
id: 'tenant-123',
name: 'Test Company',
slug: 'test-company',
domain: 'test.example.com',
logo_url: 'https://example.com/logo.png',
status: 'active',
plan_id: 'plan-123',
trial_ends_at: new Date('2026-02-01'),
settings: { theme: 'dark' },
metadata: {},
created_at: new Date('2026-01-01'),
updated_at: new Date('2026-01-01'),
};
const mockUser = {
id: 'user-123',
tenant_id: 'tenant-123',
email: 'test@example.com',
first_name: 'John',
last_name: 'Doe',
status: 'active',
created_at: new Date('2026-01-01'),
updated_at: new Date('2026-01-01'),
};
const mockSubscription = {
id: 'sub-123',
tenant_id: 'tenant-123',
plan_id: 'plan-123',
status: 'active',
created_at: new Date('2026-01-01'),
updated_at: new Date('2026-01-01'),
};
const mockQueryBuilder = {
where: jest.fn().mockReturnThis(),
andWhere: jest.fn().mockReturnThis(),
leftJoinAndSelect: jest.fn().mockReturnThis(),
getCount: jest.fn().mockResolvedValue(5),
getMany: jest.fn().mockResolvedValue([]),
};
beforeEach(async () => {
const module = await testing_1.Test.createTestingModule({
providers: [
superadmin_service_1.SuperadminService,
{
provide: (0, typeorm_1.getRepositoryToken)(tenant_entity_1.Tenant),
useValue: {
findOne: jest.fn(),
find: jest.fn(),
findAndCount: jest.fn(),
save: jest.fn(),
create: jest.fn(),
remove: jest.fn(),
count: jest.fn(),
createQueryBuilder: jest.fn().mockReturnValue(mockQueryBuilder),
},
},
{
provide: (0, typeorm_1.getRepositoryToken)(user_entity_1.User),
useValue: {
findOne: jest.fn(),
find: jest.fn(),
findAndCount: jest.fn(),
count: jest.fn(),
createQueryBuilder: jest.fn().mockReturnValue(mockQueryBuilder),
},
},
{
provide: (0, typeorm_1.getRepositoryToken)(subscription_entity_1.Subscription),
useValue: {
findOne: jest.fn(),
find: jest.fn(),
createQueryBuilder: jest.fn().mockReturnValue(mockQueryBuilder),
},
},
],
}).compile();
service = module.get(superadmin_service_1.SuperadminService);
tenantRepository = module.get((0, typeorm_1.getRepositoryToken)(tenant_entity_1.Tenant));
userRepository = module.get((0, typeorm_1.getRepositoryToken)(user_entity_1.User));
subscriptionRepository = module.get((0, typeorm_1.getRepositoryToken)(subscription_entity_1.Subscription));
});
afterEach(() => {
jest.clearAllMocks();
});
describe('listTenants', () => {
it('should return paginated tenants with stats', async () => {
tenantRepository.findAndCount.mockResolvedValue([[mockTenant], 1]);
userRepository.count.mockResolvedValue(5);
subscriptionRepository.findOne.mockResolvedValue(mockSubscription);
const result = await service.listTenants({ page: 1, limit: 10 });
expect(result.data).toHaveLength(1);
expect(result.total).toBe(1);
expect(result.page).toBe(1);
expect(result.limit).toBe(10);
expect(result.totalPages).toBe(1);
expect(result.data[0].userCount).toBe(5);
});
it('should handle search parameter', async () => {
tenantRepository.findAndCount.mockResolvedValue([[], 0]);
await service.listTenants({ page: 1, limit: 10, search: 'test' });
expect(tenantRepository.findAndCount).toHaveBeenCalledWith(expect.objectContaining({
where: expect.objectContaining({
name: expect.anything(),
}),
}));
});
it('should handle status filter', async () => {
tenantRepository.findAndCount.mockResolvedValue([[], 0]);
await service.listTenants({ page: 1, limit: 10, status: 'active' });
expect(tenantRepository.findAndCount).toHaveBeenCalledWith(expect.objectContaining({
where: expect.objectContaining({
status: 'active',
}),
}));
});
});
describe('getTenant', () => {
it('should return tenant with stats', async () => {
tenantRepository.findOne.mockResolvedValue(mockTenant);
userRepository.count.mockResolvedValue(5);
subscriptionRepository.findOne.mockResolvedValue(mockSubscription);
const result = await service.getTenant('tenant-123');
expect(result.id).toBe('tenant-123');
expect(result.userCount).toBe(5);
expect(result.subscription).toBeDefined();
});
it('should throw NotFoundException if tenant not found', async () => {
tenantRepository.findOne.mockResolvedValue(null);
await expect(service.getTenant('non-existent')).rejects.toThrow(common_1.NotFoundException);
});
});
describe('createTenant', () => {
const createDto = {
name: 'New Company',
slug: 'new-company',
domain: 'new.example.com',
status: 'trial',
};
it('should create a new tenant', async () => {
tenantRepository.findOne.mockResolvedValue(null);
tenantRepository.create.mockReturnValue({ ...mockTenant, ...createDto });
tenantRepository.save.mockResolvedValue({ ...mockTenant, ...createDto });
const result = await service.createTenant(createDto);
expect(result.name).toBe('New Company');
expect(tenantRepository.create).toHaveBeenCalled();
expect(tenantRepository.save).toHaveBeenCalled();
});
it('should throw ConflictException if slug exists', async () => {
tenantRepository.findOne.mockResolvedValue(mockTenant);
await expect(service.createTenant(createDto)).rejects.toThrow(common_1.ConflictException);
});
});
describe('updateTenant', () => {
const updateDto = { name: 'Updated Company' };
it('should update a tenant', async () => {
const tenantCopy = { ...mockTenant };
tenantRepository.findOne.mockResolvedValue(tenantCopy);
tenantRepository.save.mockResolvedValue({ ...tenantCopy, ...updateDto });
const result = await service.updateTenant('tenant-123', updateDto);
expect(result.name).toBe('Updated Company');
expect(tenantRepository.save).toHaveBeenCalled();
});
it('should throw NotFoundException if tenant not found', async () => {
tenantRepository.findOne.mockResolvedValue(null);
await expect(service.updateTenant('non-existent', updateDto)).rejects.toThrow(common_1.NotFoundException);
});
});
describe('updateTenantStatus', () => {
it('should update tenant status', async () => {
const tenantCopy = { ...mockTenant };
tenantRepository.findOne.mockResolvedValue(tenantCopy);
tenantRepository.save.mockResolvedValue({ ...tenantCopy, status: 'suspended' });
const result = await service.updateTenantStatus('tenant-123', {
status: 'suspended',
reason: 'Non-payment',
});
expect(tenantRepository.save).toHaveBeenCalled();
});
it('should throw NotFoundException if tenant not found', async () => {
tenantRepository.findOne.mockResolvedValue(null);
await expect(service.updateTenantStatus('non-existent', { status: 'suspended' })).rejects.toThrow(common_1.NotFoundException);
});
it('should store reason in metadata', async () => {
const tenantCopy = { ...mockTenant, metadata: {} };
tenantRepository.findOne.mockResolvedValue(tenantCopy);
tenantRepository.save.mockImplementation(async (t) => t);
await service.updateTenantStatus('tenant-123', {
status: 'suspended',
reason: 'Policy violation',
});
const savedTenant = tenantRepository.save.mock.calls[0][0];
expect(savedTenant.metadata?.statusChangeReason).toBe('Policy violation');
});
});
describe('deleteTenant', () => {
it('should delete a tenant without users', async () => {
tenantRepository.findOne.mockResolvedValue(mockTenant);
userRepository.count.mockResolvedValue(0);
tenantRepository.remove.mockResolvedValue(mockTenant);
await service.deleteTenant('tenant-123');
expect(tenantRepository.remove).toHaveBeenCalledWith(mockTenant);
});
it('should throw NotFoundException if tenant not found', async () => {
tenantRepository.findOne.mockResolvedValue(null);
await expect(service.deleteTenant('non-existent')).rejects.toThrow(common_1.NotFoundException);
});
it('should throw BadRequestException if tenant has users', async () => {
tenantRepository.findOne.mockResolvedValue(mockTenant);
userRepository.count.mockResolvedValue(5);
await expect(service.deleteTenant('tenant-123')).rejects.toThrow(common_1.BadRequestException);
});
});
describe('getTenantUsers', () => {
it('should return paginated users for a tenant', async () => {
tenantRepository.findOne.mockResolvedValue(mockTenant);
userRepository.findAndCount.mockResolvedValue([[mockUser], 1]);
const result = await service.getTenantUsers('tenant-123', 1, 10);
expect(result.data).toHaveLength(1);
expect(result.total).toBe(1);
expect(result.page).toBe(1);
});
it('should throw NotFoundException if tenant not found', async () => {
tenantRepository.findOne.mockResolvedValue(null);
await expect(service.getTenantUsers('non-existent')).rejects.toThrow(common_1.NotFoundException);
});
});
describe('getDashboardStats', () => {
it('should return dashboard statistics', async () => {
tenantRepository.count
.mockResolvedValueOnce(100)
.mockResolvedValueOnce(80)
.mockResolvedValueOnce(15)
.mockResolvedValueOnce(5);
userRepository.count.mockResolvedValue(500);
mockQueryBuilder.getCount.mockResolvedValue(10);
const result = await service.getDashboardStats();
expect(result.totalTenants).toBe(100);
expect(result.activeTenants).toBe(80);
expect(result.trialTenants).toBe(15);
expect(result.suspendedTenants).toBe(5);
expect(result.totalUsers).toBe(500);
expect(result.newTenantsThisMonth).toBe(10);
});
});
describe('getStatusDistribution', () => {
it('should return status distribution', async () => {
tenantRepository.count
.mockResolvedValueOnce(100)
.mockResolvedValueOnce(60)
.mockResolvedValueOnce(20)
.mockResolvedValueOnce(15)
.mockResolvedValueOnce(5);
const result = await service.getStatusDistribution();
expect(result).toHaveLength(4);
expect(result.find(s => s.status === 'Active')?.count).toBe(60);
});
it('should calculate percentages correctly', async () => {
tenantRepository.count
.mockResolvedValueOnce(100)
.mockResolvedValueOnce(50)
.mockResolvedValueOnce(30)
.mockResolvedValueOnce(15)
.mockResolvedValueOnce(5);
const result = await service.getStatusDistribution();
expect(result.find(s => s.status === 'Active')?.percentage).toBe(50);
expect(result.find(s => s.status === 'Trial')?.percentage).toBe(30);
});
});
describe('getTenantGrowth', () => {
it('should return growth data for specified months', async () => {
mockQueryBuilder.getCount.mockResolvedValue(10);
const result = await service.getTenantGrowth(6);
expect(result).toHaveLength(6);
expect(result[0]).toHaveProperty('month');
expect(result[0]).toHaveProperty('count');
});
});
describe('getUserGrowth', () => {
it('should return user growth data', async () => {
mockQueryBuilder.getCount.mockResolvedValue(20);
const result = await service.getUserGrowth(3);
expect(result).toHaveLength(3);
expect(result[0]).toHaveProperty('month');
expect(result[0]).toHaveProperty('count');
});
});
});
//# sourceMappingURL=superadmin.service.spec.js.map