"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const testing_1 = require("@nestjs/testing"); const typeorm_1 = require("@nestjs/typeorm"); const audit_service_1 = require("../services/audit.service"); const audit_log_entity_1 = require("../entities/audit-log.entity"); const activity_log_entity_1 = require("../entities/activity-log.entity"); describe('AuditService', () => { let service; let auditLogRepo; let activityLogRepo; const mockTenantId = '550e8400-e29b-41d4-a716-446655440001'; const mockUserId = '550e8400-e29b-41d4-a716-446655440002'; const mockAuditLog = { id: 'audit-001', tenant_id: mockTenantId, user_id: mockUserId, action: audit_log_entity_1.AuditAction.CREATE, entity_type: 'user', entity_id: 'user-001', new_values: { email: 'test@example.com' }, changed_fields: ['email'], ip_address: '192.168.1.1', created_at: new Date(), }; const mockActivityLog = { id: 'activity-001', tenant_id: mockTenantId, user_id: mockUserId, activity_type: activity_log_entity_1.ActivityType.PAGE_VIEW, resource_type: 'dashboard', description: 'Viewed dashboard', ip_address: '192.168.1.1', created_at: new Date(), }; beforeEach(async () => { const mockAuditLogRepo = { create: jest.fn(), save: jest.fn(), findOne: jest.fn(), find: jest.fn(), count: jest.fn(), createQueryBuilder: jest.fn(), }; const mockActivityLogRepo = { create: jest.fn(), save: jest.fn(), findOne: jest.fn(), find: jest.fn(), createQueryBuilder: jest.fn(), }; const module = await testing_1.Test.createTestingModule({ providers: [ audit_service_1.AuditService, { provide: (0, typeorm_1.getRepositoryToken)(audit_log_entity_1.AuditLog), useValue: mockAuditLogRepo }, { provide: (0, typeorm_1.getRepositoryToken)(activity_log_entity_1.ActivityLog), useValue: mockActivityLogRepo }, ], }).compile(); service = module.get(audit_service_1.AuditService); auditLogRepo = module.get((0, typeorm_1.getRepositoryToken)(audit_log_entity_1.AuditLog)); activityLogRepo = module.get((0, typeorm_1.getRepositoryToken)(activity_log_entity_1.ActivityLog)); }); afterEach(() => { jest.clearAllMocks(); }); describe('createAuditLog', () => { it('should create audit log successfully', async () => { auditLogRepo.create.mockReturnValue(mockAuditLog); auditLogRepo.save.mockResolvedValue(mockAuditLog); const result = await service.createAuditLog({ tenant_id: mockTenantId, user_id: mockUserId, action: audit_log_entity_1.AuditAction.CREATE, entity_type: 'user', entity_id: 'user-001', new_values: { email: 'test@example.com' }, }); expect(result).toEqual(mockAuditLog); expect(auditLogRepo.create).toHaveBeenCalled(); expect(auditLogRepo.save).toHaveBeenCalled(); }); it('should detect changed fields', async () => { auditLogRepo.create.mockReturnValue(mockAuditLog); auditLogRepo.save.mockResolvedValue(mockAuditLog); await service.createAuditLog({ tenant_id: mockTenantId, user_id: mockUserId, action: audit_log_entity_1.AuditAction.UPDATE, entity_type: 'user', entity_id: 'user-001', old_values: { email: 'old@example.com', name: 'Old Name' }, new_values: { email: 'new@example.com', name: 'Old Name' }, }); expect(auditLogRepo.create).toHaveBeenCalledWith(expect.objectContaining({ changed_fields: ['email'], })); }); it('should handle null old/new values', async () => { auditLogRepo.create.mockReturnValue(mockAuditLog); auditLogRepo.save.mockResolvedValue(mockAuditLog); await service.createAuditLog({ tenant_id: mockTenantId, action: audit_log_entity_1.AuditAction.DELETE, entity_type: 'user', entity_id: 'user-001', }); expect(auditLogRepo.create).toHaveBeenCalledWith(expect.objectContaining({ changed_fields: [], })); }); it('should include request metadata', async () => { auditLogRepo.create.mockReturnValue(mockAuditLog); auditLogRepo.save.mockResolvedValue(mockAuditLog); await service.createAuditLog({ tenant_id: mockTenantId, user_id: mockUserId, action: audit_log_entity_1.AuditAction.READ, entity_type: 'document', ip_address: '192.168.1.1', user_agent: 'Mozilla/5.0', endpoint: '/api/documents/1', http_method: 'GET', response_status: 200, duration_ms: 150, }); expect(auditLogRepo.create).toHaveBeenCalledWith(expect.objectContaining({ ip_address: '192.168.1.1', endpoint: '/api/documents/1', duration_ms: 150, })); }); }); describe('queryAuditLogs', () => { it('should return paginated audit logs', async () => { const qb = { where: jest.fn().mockReturnThis(), andWhere: jest.fn().mockReturnThis(), orderBy: jest.fn().mockReturnThis(), skip: jest.fn().mockReturnThis(), take: jest.fn().mockReturnThis(), getManyAndCount: jest.fn().mockResolvedValue([[mockAuditLog], 1]), }; auditLogRepo.createQueryBuilder.mockReturnValue(qb); const result = await service.queryAuditLogs(mockTenantId, {}); expect(result.data).toHaveLength(1); expect(result.total).toBe(1); expect(result.page).toBe(1); expect(result.limit).toBe(20); }); it('should filter by user_id', async () => { const qb = { where: jest.fn().mockReturnThis(), andWhere: jest.fn().mockReturnThis(), orderBy: jest.fn().mockReturnThis(), skip: jest.fn().mockReturnThis(), take: jest.fn().mockReturnThis(), getManyAndCount: jest.fn().mockResolvedValue([[], 0]), }; auditLogRepo.createQueryBuilder.mockReturnValue(qb); await service.queryAuditLogs(mockTenantId, { user_id: mockUserId }); expect(qb.andWhere).toHaveBeenCalledWith('audit.user_id = :user_id', { user_id: mockUserId, }); }); it('should filter by action', async () => { const qb = { where: jest.fn().mockReturnThis(), andWhere: jest.fn().mockReturnThis(), orderBy: jest.fn().mockReturnThis(), skip: jest.fn().mockReturnThis(), take: jest.fn().mockReturnThis(), getManyAndCount: jest.fn().mockResolvedValue([[], 0]), }; auditLogRepo.createQueryBuilder.mockReturnValue(qb); await service.queryAuditLogs(mockTenantId, { action: audit_log_entity_1.AuditAction.CREATE }); expect(qb.andWhere).toHaveBeenCalledWith('audit.action = :action', { action: audit_log_entity_1.AuditAction.CREATE, }); }); it('should filter by entity_type', async () => { const qb = { where: jest.fn().mockReturnThis(), andWhere: jest.fn().mockReturnThis(), orderBy: jest.fn().mockReturnThis(), skip: jest.fn().mockReturnThis(), take: jest.fn().mockReturnThis(), getManyAndCount: jest.fn().mockResolvedValue([[], 0]), }; auditLogRepo.createQueryBuilder.mockReturnValue(qb); await service.queryAuditLogs(mockTenantId, { entity_type: 'user' }); expect(qb.andWhere).toHaveBeenCalledWith('audit.entity_type = :entity_type', { entity_type: 'user', }); }); it('should filter by date range', async () => { const qb = { where: jest.fn().mockReturnThis(), andWhere: jest.fn().mockReturnThis(), orderBy: jest.fn().mockReturnThis(), skip: jest.fn().mockReturnThis(), take: jest.fn().mockReturnThis(), getManyAndCount: jest.fn().mockResolvedValue([[], 0]), }; auditLogRepo.createQueryBuilder.mockReturnValue(qb); const from_date = '2026-01-01'; const to_date = '2026-01-31'; await service.queryAuditLogs(mockTenantId, { from_date, to_date }); expect(qb.andWhere).toHaveBeenCalledWith('audit.created_at BETWEEN :from_date AND :to_date', { from_date, to_date }); }); it('should handle pagination correctly', async () => { const qb = { where: jest.fn().mockReturnThis(), andWhere: jest.fn().mockReturnThis(), orderBy: jest.fn().mockReturnThis(), skip: jest.fn().mockReturnThis(), take: jest.fn().mockReturnThis(), getManyAndCount: jest.fn().mockResolvedValue([[], 100]), }; auditLogRepo.createQueryBuilder.mockReturnValue(qb); const result = await service.queryAuditLogs(mockTenantId, { page: 3, limit: 10, }); expect(qb.skip).toHaveBeenCalledWith(20); expect(qb.take).toHaveBeenCalledWith(10); expect(result.totalPages).toBe(10); }); }); describe('getAuditLogById', () => { it('should return audit log by id', async () => { auditLogRepo.findOne.mockResolvedValue(mockAuditLog); const result = await service.getAuditLogById(mockTenantId, 'audit-001'); expect(result).toEqual(mockAuditLog); expect(auditLogRepo.findOne).toHaveBeenCalledWith({ where: { id: 'audit-001', tenant_id: mockTenantId }, }); }); it('should return null when not found', async () => { auditLogRepo.findOne.mockResolvedValue(null); const result = await service.getAuditLogById(mockTenantId, 'invalid'); expect(result).toBeNull(); }); }); describe('getEntityAuditHistory', () => { it('should return audit history for entity', async () => { auditLogRepo.find.mockResolvedValue([mockAuditLog]); const result = await service.getEntityAuditHistory(mockTenantId, 'user', 'user-001'); expect(result).toHaveLength(1); expect(auditLogRepo.find).toHaveBeenCalledWith({ where: { tenant_id: mockTenantId, entity_type: 'user', entity_id: 'user-001', }, order: { created_at: 'DESC' }, }); }); it('should return empty array for no history', async () => { auditLogRepo.find.mockResolvedValue([]); const result = await service.getEntityAuditHistory(mockTenantId, 'document', 'doc-999'); expect(result).toHaveLength(0); }); }); describe('createActivityLog', () => { it('should create activity log successfully', async () => { activityLogRepo.create.mockReturnValue(mockActivityLog); activityLogRepo.save.mockResolvedValue(mockActivityLog); const result = await service.createActivityLog(mockTenantId, mockUserId, { activity_type: activity_log_entity_1.ActivityType.PAGE_VIEW, resource_type: 'dashboard', description: 'Viewed dashboard', }, { ip_address: '192.168.1.1' }); expect(result).toEqual(mockActivityLog); expect(activityLogRepo.create).toHaveBeenCalled(); }); it('should include session context', async () => { activityLogRepo.create.mockReturnValue(mockActivityLog); activityLogRepo.save.mockResolvedValue(mockActivityLog); await service.createActivityLog(mockTenantId, mockUserId, { activity_type: activity_log_entity_1.ActivityType.FEATURE_USE, description: 'Used export feature', }, { ip_address: '192.168.1.1', user_agent: 'Mozilla/5.0', session_id: 'session-001', }); expect(activityLogRepo.create).toHaveBeenCalledWith(expect.objectContaining({ session_id: 'session-001', })); }); }); describe('queryActivityLogs', () => { it('should return paginated activity logs', async () => { const qb = { where: jest.fn().mockReturnThis(), andWhere: jest.fn().mockReturnThis(), orderBy: jest.fn().mockReturnThis(), skip: jest.fn().mockReturnThis(), take: jest.fn().mockReturnThis(), getManyAndCount: jest .fn() .mockResolvedValue([[mockActivityLog], 1]), }; activityLogRepo.createQueryBuilder.mockReturnValue(qb); const result = await service.queryActivityLogs(mockTenantId, {}); expect(result.data).toHaveLength(1); expect(result.total).toBe(1); }); it('should filter by activity_type', async () => { const qb = { where: jest.fn().mockReturnThis(), andWhere: jest.fn().mockReturnThis(), orderBy: jest.fn().mockReturnThis(), skip: jest.fn().mockReturnThis(), take: jest.fn().mockReturnThis(), getManyAndCount: jest.fn().mockResolvedValue([[], 0]), }; activityLogRepo.createQueryBuilder.mockReturnValue(qb); await service.queryActivityLogs(mockTenantId, { activity_type: activity_log_entity_1.ActivityType.PAGE_VIEW, }); expect(qb.andWhere).toHaveBeenCalledWith('activity.activity_type = :activity_type', { activity_type: activity_log_entity_1.ActivityType.PAGE_VIEW }); }); it('should filter by resource_type', async () => { const qb = { where: jest.fn().mockReturnThis(), andWhere: jest.fn().mockReturnThis(), orderBy: jest.fn().mockReturnThis(), skip: jest.fn().mockReturnThis(), take: jest.fn().mockReturnThis(), getManyAndCount: jest.fn().mockResolvedValue([[], 0]), }; activityLogRepo.createQueryBuilder.mockReturnValue(qb); await service.queryActivityLogs(mockTenantId, { resource_type: 'document' }); expect(qb.andWhere).toHaveBeenCalledWith('activity.resource_type = :resource_type', { resource_type: 'document' }); }); }); describe('getUserActivitySummary', () => { it('should return activity summary by type', async () => { const qb = { select: jest.fn().mockReturnThis(), addSelect: jest.fn().mockReturnThis(), where: jest.fn().mockReturnThis(), andWhere: jest.fn().mockReturnThis(), groupBy: jest.fn().mockReturnThis(), getRawMany: jest.fn().mockResolvedValue([ { activity_type: activity_log_entity_1.ActivityType.PAGE_VIEW, count: '50' }, { activity_type: activity_log_entity_1.ActivityType.FEATURE_USE, count: '25' }, ]), }; activityLogRepo.createQueryBuilder.mockReturnValue(qb); const result = await service.getUserActivitySummary(mockTenantId, mockUserId, 30); expect(result).toHaveLength(2); expect(result[0]).toEqual({ activity_type: activity_log_entity_1.ActivityType.PAGE_VIEW, count: 50, }); }); it('should use default 30 days', async () => { const qb = { select: jest.fn().mockReturnThis(), addSelect: jest.fn().mockReturnThis(), where: jest.fn().mockReturnThis(), andWhere: jest.fn().mockReturnThis(), groupBy: jest.fn().mockReturnThis(), getRawMany: jest.fn().mockResolvedValue([]), }; activityLogRepo.createQueryBuilder.mockReturnValue(qb); await service.getUserActivitySummary(mockTenantId, mockUserId); expect(qb.andWhere).toHaveBeenCalledWith('activity.created_at >= :fromDate', expect.any(Object)); }); }); describe('getAuditStats', () => { it('should return audit statistics', async () => { auditLogRepo.count.mockResolvedValue(100); const actionsByTypeQb = { select: jest.fn().mockReturnThis(), addSelect: jest.fn().mockReturnThis(), where: jest.fn().mockReturnThis(), andWhere: jest.fn().mockReturnThis(), groupBy: jest.fn().mockReturnThis(), getRawMany: jest.fn().mockResolvedValue([ { action: audit_log_entity_1.AuditAction.CREATE, count: '30' }, { action: audit_log_entity_1.AuditAction.UPDATE, count: '50' }, { action: audit_log_entity_1.AuditAction.DELETE, count: '20' }, ]), }; const topUsersQb = { select: jest.fn().mockReturnThis(), addSelect: jest.fn().mockReturnThis(), where: jest.fn().mockReturnThis(), andWhere: jest.fn().mockReturnThis(), groupBy: jest.fn().mockReturnThis(), orderBy: jest.fn().mockReturnThis(), limit: jest.fn().mockReturnThis(), getRawMany: jest.fn().mockResolvedValue([ { user_id: mockUserId, count: '45' }, { user_id: 'user-002', count: '30' }, ]), }; auditLogRepo.createQueryBuilder .mockReturnValueOnce(actionsByTypeQb) .mockReturnValueOnce(topUsersQb); const result = await service.getAuditStats(mockTenantId, 7); expect(result.total_actions).toBe(100); expect(result.actions_by_type).toHaveLength(3); expect(result.top_users).toHaveLength(2); }); it('should use default 7 days', async () => { auditLogRepo.count.mockResolvedValue(0); const qb = { select: jest.fn().mockReturnThis(), addSelect: jest.fn().mockReturnThis(), where: jest.fn().mockReturnThis(), andWhere: jest.fn().mockReturnThis(), groupBy: jest.fn().mockReturnThis(), orderBy: jest.fn().mockReturnThis(), limit: jest.fn().mockReturnThis(), getRawMany: jest.fn().mockResolvedValue([]), }; auditLogRepo.createQueryBuilder.mockReturnValue(qb); await service.getAuditStats(mockTenantId); expect(auditLogRepo.count).toHaveBeenCalled(); }); }); }); //# sourceMappingURL=audit.service.spec.js.map