/** * @ai-summary Admin service unit tests * @ai-context Tests business logic for admin management */ import { AdminService } from '../../domain/admin.service'; import { AdminRepository } from '../../data/admin.repository'; // Mock the audit log service jest.mock('../../../audit-log', () => ({ auditLogService: { info: jest.fn().mockResolvedValue(undefined), warn: jest.fn().mockResolvedValue(undefined), error: jest.fn().mockResolvedValue(undefined), }, })); describe('AdminService', () => { let adminService: AdminService; let mockRepository: jest.Mocked; beforeEach(() => { mockRepository = { getAdminById: jest.fn(), getAdminByUserProfileId: jest.fn(), getAdminByEmail: jest.fn(), getAllAdmins: jest.fn(), getActiveAdmins: jest.fn(), createAdmin: jest.fn(), revokeAdmin: jest.fn(), reinstateAdmin: jest.fn(), logAuditAction: jest.fn(), getAuditLogs: jest.fn(), } as any; adminService = new AdminService(mockRepository); }); describe('getAdminById', () => { it('should return admin when found', async () => { const mockAdmin = { id: '7c9e6679-7425-40de-944b-e07fc1f90ae7', userProfileId: '7c9e6679-7425-40de-944b-e07fc1f90ae7', email: 'admin@motovaultpro.com', role: 'admin' as const, createdAt: new Date(), createdBy: '550e8400-e29b-41d4-a716-446655440000', revokedAt: null, updatedAt: new Date(), }; mockRepository.getAdminById.mockResolvedValue(mockAdmin); const result = await adminService.getAdminById('7c9e6679-7425-40de-944b-e07fc1f90ae7'); expect(result).toEqual(mockAdmin); expect(mockRepository.getAdminById).toHaveBeenCalledWith('7c9e6679-7425-40de-944b-e07fc1f90ae7'); }); it('should return null when admin not found', async () => { mockRepository.getAdminById.mockResolvedValue(null); const result = await adminService.getAdminById('00000000-0000-0000-0000-000000000000'); expect(result).toBeNull(); }); }); describe('createAdmin', () => { it('should create new admin and log audit', async () => { const newAdminId = '8f14e45f-ceea-367f-a27f-c9a6d0c67e0e'; const creatorId = '7c9e6679-7425-40de-944b-e07fc1f90ae7'; const mockAdmin = { id: newAdminId, userProfileId: newAdminId, email: 'newadmin@motovaultpro.com', role: 'admin' as const, createdAt: new Date(), createdBy: creatorId, revokedAt: null, updatedAt: new Date(), }; mockRepository.getAdminByEmail.mockResolvedValue(null); mockRepository.createAdmin.mockResolvedValue(mockAdmin); mockRepository.logAuditAction.mockResolvedValue({} as any); const result = await adminService.createAdmin( 'newadmin@motovaultpro.com', 'admin', newAdminId, creatorId ); expect(result).toEqual(mockAdmin); expect(mockRepository.createAdmin).toHaveBeenCalled(); expect(mockRepository.logAuditAction).toHaveBeenCalledWith( creatorId, 'CREATE', mockAdmin.id, 'admin_user', mockAdmin.email, expect.any(Object) ); }); it('should reject if admin already exists', async () => { const existingId = '7c9e6679-7425-40de-944b-e07fc1f90ae7'; const existingAdmin = { id: existingId, userProfileId: existingId, email: 'admin@motovaultpro.com', role: 'admin' as const, createdAt: new Date(), createdBy: '550e8400-e29b-41d4-a716-446655440000', revokedAt: null, updatedAt: new Date(), }; mockRepository.getAdminByEmail.mockResolvedValue(existingAdmin); await expect( adminService.createAdmin('admin@motovaultpro.com', 'admin', '8f14e45f-ceea-367f-a27f-c9a6d0c67e0e', existingId) ).rejects.toThrow('already exists'); }); }); describe('revokeAdmin', () => { it('should revoke admin when multiple active admins exist', async () => { const toRevokeId = 'a1b2c3d4-e5f6-7890-1234-567890abcdef'; const admin1Id = '6ba7b810-9dad-11d1-80b4-00c04fd430c8'; const admin2Id = '8f14e45f-ceea-367f-a27f-c9a6d0c67e0e'; const revokedAdmin = { id: toRevokeId, userProfileId: toRevokeId, email: 'toadmin@motovaultpro.com', role: 'admin' as const, createdAt: new Date(), createdBy: '550e8400-e29b-41d4-a716-446655440000', revokedAt: new Date(), updatedAt: new Date(), }; const activeAdmins = [ { id: admin1Id, userProfileId: admin1Id, email: 'admin1@motovaultpro.com', role: 'admin' as const, createdAt: new Date(), createdBy: '550e8400-e29b-41d4-a716-446655440000', revokedAt: null, updatedAt: new Date(), }, { id: admin2Id, userProfileId: admin2Id, email: 'admin2@motovaultpro.com', role: 'admin' as const, createdAt: new Date(), createdBy: '550e8400-e29b-41d4-a716-446655440000', revokedAt: null, updatedAt: new Date(), }, ]; mockRepository.getActiveAdmins.mockResolvedValue(activeAdmins); mockRepository.revokeAdmin.mockResolvedValue(revokedAdmin); mockRepository.logAuditAction.mockResolvedValue({} as any); const result = await adminService.revokeAdmin(toRevokeId, admin1Id); expect(result).toEqual(revokedAdmin); expect(mockRepository.revokeAdmin).toHaveBeenCalledWith(toRevokeId); expect(mockRepository.logAuditAction).toHaveBeenCalled(); }); it('should prevent revoking last active admin', async () => { const lastAdminId = '7c9e6679-7425-40de-944b-e07fc1f90ae7'; const lastAdmin = { id: lastAdminId, userProfileId: lastAdminId, email: 'last@motovaultpro.com', role: 'admin' as const, createdAt: new Date(), createdBy: '550e8400-e29b-41d4-a716-446655440000', revokedAt: null, updatedAt: new Date(), }; mockRepository.getActiveAdmins.mockResolvedValue([lastAdmin]); await expect( adminService.revokeAdmin(lastAdminId, lastAdminId) ).rejects.toThrow('Cannot revoke the last active admin'); }); }); describe('reinstateAdmin', () => { it('should reinstate revoked admin and log audit', async () => { const reinstateId = 'b2c3d4e5-f6a7-8901-2345-678901bcdef0'; const adminActorId = '7c9e6679-7425-40de-944b-e07fc1f90ae7'; const reinstatedAdmin = { id: reinstateId, userProfileId: reinstateId, email: 'reinstate@motovaultpro.com', role: 'admin' as const, createdAt: new Date(), createdBy: '550e8400-e29b-41d4-a716-446655440000', revokedAt: null, updatedAt: new Date(), }; mockRepository.reinstateAdmin.mockResolvedValue(reinstatedAdmin); mockRepository.logAuditAction.mockResolvedValue({} as any); const result = await adminService.reinstateAdmin(reinstateId, adminActorId); expect(result).toEqual(reinstatedAdmin); expect(mockRepository.reinstateAdmin).toHaveBeenCalledWith(reinstateId); expect(mockRepository.logAuditAction).toHaveBeenCalledWith( adminActorId, 'REINSTATE', reinstateId, 'admin_user', reinstatedAdmin.email ); }); }); });