"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