Some checks failed
Deploy to Staging / Build Images (pull_request) Successful in 6m41s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 52s
Deploy to Staging / Verify Staging (pull_request) Failing after 4m7s
Deploy to Staging / Notify Staging Ready (pull_request) Has been skipped
Deploy to Staging / Notify Staging Failure (pull_request) Successful in 9s
Backend test fixtures: - Replace auth0|xxx format with UUID in all test userId values - Update admin tests for new id/userProfileId schema - Add missing deletionRequestedAt/deletionScheduledFor to auth test mocks - Fix admin integration test supertest usage (app.server) Frontend: - AdminUser type: auth0Sub -> id + userProfileId - admin.api.ts: all user management methods use userId (UUID) params - useUsers/useAdmins hooks: auth0Sub -> userId/id in mutations - AdminUsersPage + AdminUsersMobileScreen: user.auth0Sub -> user.id - Remove encodeURIComponent (UUIDs don't need encoding) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
309 lines
9.7 KiB
TypeScript
309 lines
9.7 KiB
TypeScript
/**
|
|
* @ai-summary Integration tests for audit log wiring across features
|
|
* @ai-context Verifies audit logging is properly integrated into auth, vehicle, admin, and backup features
|
|
*/
|
|
|
|
import { Pool } from 'pg';
|
|
import { appConfig } from '../../../core/config/config-loader';
|
|
import { AuditLogService } from '../domain/audit-log.service';
|
|
import { AuditLogRepository } from '../data/audit-log.repository';
|
|
|
|
describe('AuditLog Feature Integration', () => {
|
|
let pool: Pool;
|
|
let repository: AuditLogRepository;
|
|
let service: AuditLogService;
|
|
const createdIds: string[] = [];
|
|
|
|
beforeAll(async () => {
|
|
pool = new Pool({
|
|
connectionString: appConfig.getDatabaseUrl(),
|
|
});
|
|
repository = new AuditLogRepository(pool);
|
|
service = new AuditLogService(repository);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
// Cleanup test data
|
|
if (createdIds.length > 0) {
|
|
await pool.query(`DELETE FROM audit_logs WHERE id = ANY($1::uuid[])`, [createdIds]);
|
|
}
|
|
await pool.end();
|
|
});
|
|
|
|
describe('Vehicle logging integration', () => {
|
|
it('should create audit log with vehicle category and correct resource', async () => {
|
|
const userId = '550e8400-e29b-41d4-a716-446655440000';
|
|
const vehicleId = 'vehicle-uuid-123';
|
|
const entry = await service.info(
|
|
'vehicle',
|
|
userId,
|
|
'Vehicle created: 2024 Toyota Camry',
|
|
'vehicle',
|
|
vehicleId,
|
|
{ vin: '1HGBH41JXMN109186', make: 'Toyota', model: 'Camry', year: 2024 }
|
|
);
|
|
|
|
createdIds.push(entry.id);
|
|
|
|
expect(entry.category).toBe('vehicle');
|
|
expect(entry.severity).toBe('info');
|
|
expect(entry.userId).toBe(userId);
|
|
expect(entry.action).toContain('Vehicle created');
|
|
expect(entry.resourceType).toBe('vehicle');
|
|
expect(entry.resourceId).toBe(vehicleId);
|
|
expect(entry.details).toHaveProperty('vin');
|
|
expect(entry.details).toHaveProperty('make', 'Toyota');
|
|
});
|
|
|
|
it('should log vehicle update with correct fields', async () => {
|
|
const userId = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
|
|
const vehicleId = 'vehicle-uuid-456';
|
|
const entry = await service.info(
|
|
'vehicle',
|
|
userId,
|
|
'Vehicle updated: 2024 Toyota Camry',
|
|
'vehicle',
|
|
vehicleId,
|
|
{ updatedFields: ['color', 'licensePlate'] }
|
|
);
|
|
|
|
createdIds.push(entry.id);
|
|
|
|
expect(entry.category).toBe('vehicle');
|
|
expect(entry.action).toContain('Vehicle updated');
|
|
expect(entry.details).toHaveProperty('updatedFields');
|
|
});
|
|
|
|
it('should log vehicle deletion with vehicle info', async () => {
|
|
const userId = '7c9e6679-7425-40de-944b-e07fc1f90ae7';
|
|
const vehicleId = 'vehicle-uuid-789';
|
|
const entry = await service.info(
|
|
'vehicle',
|
|
userId,
|
|
'Vehicle deleted: 2024 Toyota Camry',
|
|
'vehicle',
|
|
vehicleId,
|
|
{ vin: '1HGBH41JXMN109186', make: 'Toyota', model: 'Camry', year: 2024 }
|
|
);
|
|
|
|
createdIds.push(entry.id);
|
|
|
|
expect(entry.category).toBe('vehicle');
|
|
expect(entry.action).toContain('Vehicle deleted');
|
|
expect(entry.resourceId).toBe(vehicleId);
|
|
});
|
|
});
|
|
|
|
describe('Auth logging integration', () => {
|
|
it('should create audit log with auth category for signup', async () => {
|
|
const userId = '550e8400-e29b-41d4-a716-446655440000';
|
|
const entry = await service.info(
|
|
'auth',
|
|
userId,
|
|
'User signup: test@example.com',
|
|
'user',
|
|
userId,
|
|
{ email: 'test@example.com', ipAddress: '192.168.1.1' }
|
|
);
|
|
|
|
createdIds.push(entry.id);
|
|
|
|
expect(entry.category).toBe('auth');
|
|
expect(entry.severity).toBe('info');
|
|
expect(entry.userId).toBe(userId);
|
|
expect(entry.action).toContain('signup');
|
|
expect(entry.resourceType).toBe('user');
|
|
});
|
|
|
|
it('should create audit log for password reset request', async () => {
|
|
const userId = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
|
|
const entry = await service.info(
|
|
'auth',
|
|
userId,
|
|
'Password reset requested',
|
|
'user',
|
|
userId
|
|
);
|
|
|
|
createdIds.push(entry.id);
|
|
|
|
expect(entry.category).toBe('auth');
|
|
expect(entry.action).toBe('Password reset requested');
|
|
});
|
|
});
|
|
|
|
describe('Admin logging integration', () => {
|
|
it('should create audit log for admin user creation', async () => {
|
|
const adminId = '7c9e6679-7425-40de-944b-e07fc1f90ae7';
|
|
const targetAdminId = '8f14e45f-ceea-367f-a27f-c9a6d0c67e0e';
|
|
const entry = await service.info(
|
|
'admin',
|
|
adminId,
|
|
'Admin user created: newadmin@example.com',
|
|
'admin_user',
|
|
targetAdminId,
|
|
{ email: 'newadmin@example.com', role: 'admin' }
|
|
);
|
|
|
|
createdIds.push(entry.id);
|
|
|
|
expect(entry.category).toBe('admin');
|
|
expect(entry.severity).toBe('info');
|
|
expect(entry.userId).toBe(adminId);
|
|
expect(entry.action).toContain('Admin user created');
|
|
expect(entry.resourceType).toBe('admin_user');
|
|
expect(entry.details).toHaveProperty('role', 'admin');
|
|
});
|
|
|
|
it('should create audit log for admin revocation', async () => {
|
|
const adminId = '7c9e6679-7425-40de-944b-e07fc1f90ae7';
|
|
const targetAdminId = 'a1b2c3d4-e5f6-7890-1234-567890abcdef';
|
|
const entry = await service.info(
|
|
'admin',
|
|
adminId,
|
|
'Admin user revoked: revoked@example.com',
|
|
'admin_user',
|
|
targetAdminId,
|
|
{ email: 'revoked@example.com' }
|
|
);
|
|
|
|
createdIds.push(entry.id);
|
|
|
|
expect(entry.category).toBe('admin');
|
|
expect(entry.action).toContain('Admin user revoked');
|
|
});
|
|
|
|
it('should create audit log for admin reinstatement', async () => {
|
|
const adminId = '7c9e6679-7425-40de-944b-e07fc1f90ae7';
|
|
const targetAdminId = 'b2c3d4e5-f6a7-8901-2345-678901bcdef0';
|
|
const entry = await service.info(
|
|
'admin',
|
|
adminId,
|
|
'Admin user reinstated: reinstated@example.com',
|
|
'admin_user',
|
|
targetAdminId,
|
|
{ email: 'reinstated@example.com' }
|
|
);
|
|
|
|
createdIds.push(entry.id);
|
|
|
|
expect(entry.category).toBe('admin');
|
|
expect(entry.action).toContain('Admin user reinstated');
|
|
});
|
|
});
|
|
|
|
describe('Backup/System logging integration', () => {
|
|
it('should create audit log for backup creation', async () => {
|
|
const adminId = '7c9e6679-7425-40de-944b-e07fc1f90ae7';
|
|
const backupId = 'backup-uuid-123';
|
|
const entry = await service.info(
|
|
'system',
|
|
adminId,
|
|
'Backup created: Manual backup',
|
|
'backup',
|
|
backupId,
|
|
{ name: 'Manual backup', includeDocuments: true }
|
|
);
|
|
|
|
createdIds.push(entry.id);
|
|
|
|
expect(entry.category).toBe('system');
|
|
expect(entry.severity).toBe('info');
|
|
expect(entry.action).toContain('Backup created');
|
|
expect(entry.resourceType).toBe('backup');
|
|
expect(entry.resourceId).toBe(backupId);
|
|
});
|
|
|
|
it('should create audit log for backup restore', async () => {
|
|
const adminId = '7c9e6679-7425-40de-944b-e07fc1f90ae7';
|
|
const backupId = 'backup-uuid-456';
|
|
const entry = await service.info(
|
|
'system',
|
|
adminId,
|
|
'Backup restored: backup-uuid-456',
|
|
'backup',
|
|
backupId,
|
|
{ safetyBackupId: 'safety-backup-uuid' }
|
|
);
|
|
|
|
createdIds.push(entry.id);
|
|
|
|
expect(entry.category).toBe('system');
|
|
expect(entry.action).toContain('Backup restored');
|
|
});
|
|
|
|
it('should create error-level audit log for backup failure', async () => {
|
|
const adminId = '7c9e6679-7425-40de-944b-e07fc1f90ae7';
|
|
const backupId = 'backup-uuid-789';
|
|
const entry = await service.error(
|
|
'system',
|
|
adminId,
|
|
'Backup failed: Daily backup',
|
|
'backup',
|
|
backupId,
|
|
{ error: 'Disk full' }
|
|
);
|
|
|
|
createdIds.push(entry.id);
|
|
|
|
expect(entry.category).toBe('system');
|
|
expect(entry.severity).toBe('error');
|
|
expect(entry.action).toContain('Backup failed');
|
|
expect(entry.details).toHaveProperty('error', 'Disk full');
|
|
});
|
|
|
|
it('should create error-level audit log for restore failure', async () => {
|
|
const adminId = '7c9e6679-7425-40de-944b-e07fc1f90ae7';
|
|
const backupId = 'backup-uuid-restore-fail';
|
|
const entry = await service.error(
|
|
'system',
|
|
adminId,
|
|
'Backup restore failed: backup-uuid-restore-fail',
|
|
'backup',
|
|
backupId,
|
|
{ error: 'Corrupted archive', safetyBackupId: 'safety-uuid' }
|
|
);
|
|
|
|
createdIds.push(entry.id);
|
|
|
|
expect(entry.category).toBe('system');
|
|
expect(entry.severity).toBe('error');
|
|
expect(entry.action).toContain('restore failed');
|
|
});
|
|
});
|
|
|
|
describe('Cross-feature audit log queries', () => {
|
|
it('should be able to filter logs by category', async () => {
|
|
// Search for vehicle logs
|
|
const vehicleResult = await service.search(
|
|
{ category: 'vehicle' },
|
|
{ limit: 100, offset: 0 }
|
|
);
|
|
|
|
expect(vehicleResult.logs.length).toBeGreaterThan(0);
|
|
expect(vehicleResult.logs.every((log) => log.category === 'vehicle')).toBe(true);
|
|
});
|
|
|
|
it('should be able to search across all categories', async () => {
|
|
const result = await service.search(
|
|
{ search: 'created' },
|
|
{ limit: 100, offset: 0 }
|
|
);
|
|
|
|
expect(result.logs.length).toBeGreaterThan(0);
|
|
// Should find logs from vehicle and admin categories
|
|
const categories = new Set(result.logs.map((log) => log.category));
|
|
expect(categories.size).toBeGreaterThanOrEqual(1);
|
|
});
|
|
|
|
it('should be able to filter by severity across categories', async () => {
|
|
const errorResult = await service.search(
|
|
{ severity: 'error' },
|
|
{ limit: 100, offset: 0 }
|
|
);
|
|
|
|
expect(errorResult.logs.every((log) => log.severity === 'error')).toBe(true);
|
|
});
|
|
});
|
|
});
|