✅ Fixed WebhookService Tests (2 tests failing → 100% passing) - Increased timeout to 20s for retry test (handles 3 retries × 5s delays) - Fixed signature verification test with correct 64-char hex signature - All 7 webhook tests now passing ✅ Fixed Frontend TypeScript Errors - Updated tsconfig.json with complete path aliases (@/types/*, @/hooks/*, @/utils/*, @/pages/*) - Added explicit type annotations in useBookings.ts (prev: Set<string>) - Fixed BookingFilters.tsx with proper type casts (s: BookingStatus) - Fixed CarrierMonitoring.tsx with error callback types - Zero TypeScript compilation errors 📊 Test Results - Test Suites: 8 passed, 8 total (100%) - Tests: 92 passed, 92 total (100%) - Coverage: ~82% for Phase 3 services, 100% for domain entities 📝 Documentation Updated - TEST_COVERAGE_REPORT.md: Updated to reflect 100% success rate - IMPLEMENTATION_SUMMARY.md: Marked all issues as resolved 🎯 Phase 3 Status: COMPLETE - All 13/13 features implemented - All tests passing - Production ready 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
160 lines
4.4 KiB
TypeScript
160 lines
4.4 KiB
TypeScript
/**
|
|
* Audit Service Tests
|
|
*/
|
|
|
|
import { Test, TestingModule } from '@nestjs/testing';
|
|
import { AuditService } from './audit.service';
|
|
import { AUDIT_LOG_REPOSITORY, AuditLogRepository } from '../../domain/ports/out/audit-log.repository';
|
|
import { AuditAction, AuditStatus, AuditLog } from '../../domain/entities/audit-log.entity';
|
|
|
|
describe('AuditService', () => {
|
|
let service: AuditService;
|
|
let repository: jest.Mocked<AuditLogRepository>;
|
|
|
|
beforeEach(async () => {
|
|
const mockRepository: jest.Mocked<AuditLogRepository> = {
|
|
save: jest.fn(),
|
|
findById: jest.fn(),
|
|
findByFilters: jest.fn(),
|
|
count: jest.fn(),
|
|
findByResource: jest.fn(),
|
|
findRecentByOrganization: jest.fn(),
|
|
findByUser: jest.fn(),
|
|
};
|
|
|
|
const module: TestingModule = await Test.createTestingModule({
|
|
providers: [
|
|
AuditService,
|
|
{
|
|
provide: AUDIT_LOG_REPOSITORY,
|
|
useValue: mockRepository,
|
|
},
|
|
],
|
|
}).compile();
|
|
|
|
service = module.get<AuditService>(AuditService);
|
|
repository = module.get(AUDIT_LOG_REPOSITORY);
|
|
});
|
|
|
|
describe('log', () => {
|
|
it('should create and save an audit log', async () => {
|
|
const input = {
|
|
action: AuditAction.BOOKING_CREATED,
|
|
status: AuditStatus.SUCCESS,
|
|
userId: 'user-123',
|
|
userEmail: 'user@example.com',
|
|
organizationId: 'org-123',
|
|
};
|
|
|
|
await service.log(input);
|
|
|
|
expect(repository.save).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
action: AuditAction.BOOKING_CREATED,
|
|
status: AuditStatus.SUCCESS,
|
|
userId: 'user-123',
|
|
})
|
|
);
|
|
});
|
|
|
|
it('should not throw error if save fails', async () => {
|
|
repository.save.mockRejectedValue(new Error('Database error'));
|
|
|
|
const input = {
|
|
action: AuditAction.BOOKING_CREATED,
|
|
status: AuditStatus.SUCCESS,
|
|
userId: 'user-123',
|
|
userEmail: 'user@example.com',
|
|
organizationId: 'org-123',
|
|
};
|
|
|
|
await expect(service.log(input)).resolves.not.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('logSuccess', () => {
|
|
it('should log a successful action', async () => {
|
|
await service.logSuccess(
|
|
AuditAction.BOOKING_CREATED,
|
|
'user-123',
|
|
'user@example.com',
|
|
'org-123',
|
|
{ resourceType: 'booking', resourceId: 'booking-123' }
|
|
);
|
|
|
|
expect(repository.save).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
status: AuditStatus.SUCCESS,
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('logFailure', () => {
|
|
it('should log a failed action with error message', async () => {
|
|
await service.logFailure(
|
|
AuditAction.BOOKING_CREATED,
|
|
'user-123',
|
|
'user@example.com',
|
|
'org-123',
|
|
'Validation failed',
|
|
{ resourceType: 'booking' }
|
|
);
|
|
|
|
expect(repository.save).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
status: AuditStatus.FAILURE,
|
|
errorMessage: 'Validation failed',
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('getAuditLogs', () => {
|
|
it('should return audit logs with filters', async () => {
|
|
const mockLogs = [
|
|
AuditLog.create({
|
|
id: '1',
|
|
action: AuditAction.BOOKING_CREATED,
|
|
status: AuditStatus.SUCCESS,
|
|
userId: 'user-123',
|
|
userEmail: 'user@example.com',
|
|
organizationId: 'org-123',
|
|
}),
|
|
];
|
|
|
|
repository.findByFilters.mockResolvedValue(mockLogs);
|
|
repository.count.mockResolvedValue(1);
|
|
|
|
const result = await service.getAuditLogs({ organizationId: 'org-123' });
|
|
|
|
expect(result.logs).toEqual(mockLogs);
|
|
expect(result.total).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe('getResourceAuditTrail', () => {
|
|
it('should return audit trail for a resource', async () => {
|
|
const mockLogs = [
|
|
AuditLog.create({
|
|
id: '1',
|
|
action: AuditAction.BOOKING_CREATED,
|
|
status: AuditStatus.SUCCESS,
|
|
userId: 'user-123',
|
|
userEmail: 'user@example.com',
|
|
organizationId: 'org-123',
|
|
resourceType: 'booking',
|
|
resourceId: 'booking-123',
|
|
}),
|
|
];
|
|
|
|
repository.findByResource.mockResolvedValue(mockLogs);
|
|
|
|
const result = await service.getResourceAuditTrail('booking', 'booking-123');
|
|
|
|
expect(result).toEqual(mockLogs);
|
|
expect(repository.findByResource).toHaveBeenCalledWith('booking', 'booking-123');
|
|
});
|
|
});
|
|
});
|