import { BadRequestException, NotFoundException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { DateTime } from '../../common/value-objects/date-time/date-time'; import { Status } from '../../common/value-objects/status/status'; import type { PrivilegeWithDetails } from './privilege'; import { PrivilegesRepository } from './privileges.repository'; import { PrivilegesService } from './privileges.service'; describe('PrivilegesService', () => { let service: PrivilegesService; let repository: jest.Mocked< Pick< PrivilegesRepository, | 'list' | 'findById' | 'create' | 'createMany' | 'update' | 'updateStatus' | 'bulkUpdateStatus' | 'delete' | 'bulkDelete' | 'listKeys' | 'findKeyById' | 'checkPermission' | 'getPermissionsMap' > >; const now = DateTime.fromUnixMs(1_700_000_000_000); const sample: PrivilegeWithDetails = { id: 'priv-1', name: 'Admin', code: 'ADMIN', status: Status.create('draft'), createdAt: now, updatedAt: now, createdBy: 'user-1', updatedBy: 'user-1', details: [], }; beforeEach(async () => { repository = { list: jest.fn(), findById: jest.fn(), create: jest.fn(), createMany: jest.fn(), update: jest.fn(), updateStatus: jest.fn(), bulkUpdateStatus: jest.fn(), delete: jest.fn(), bulkDelete: jest.fn(), listKeys: jest.fn(), findKeyById: jest.fn(), checkPermission: jest.fn(), getPermissionsMap: jest.fn(), }; const moduleRef: TestingModule = await Test.createTestingModule({ providers: [ PrivilegesService, { provide: PrivilegesRepository, useValue: repository }, ], }).compile(); service = moduleRef.get(PrivilegesService); }); it('list maps visible fields and pagination', async () => { repository.list.mockResolvedValue({ data: [sample], total: 1 }); const result = await service.list({ page: 1, limit: 10 }); expect(result.total).toBe(1); expect(result.data[0]).toMatchObject({ id: 'priv-1', status: 'draft', createdAt: now.value, }); expect(service.visibleFields).toContain('status'); }); it('findById throws when missing', async () => { repository.findById.mockResolvedValue(null); await expect(service.findById('missing')).rejects.toBeInstanceOf( NotFoundException, ); }); it('create defaults status to draft', async () => { repository.create.mockResolvedValue(sample); await service.create({ name: 'Admin', code: 'ADMIN', userId: 'user-1', }); expect(repository.create).toHaveBeenCalled(); const arg = repository.create.mock.calls[0][0]; expect(arg.status?.value).toBe('draft'); expect(arg.details).toBeUndefined(); }); it('update rejects status field', async () => { await expect( service.update('priv-1', { status: 'active', userId: 'user-1', }), ).rejects.toBeInstanceOf(BadRequestException); }); it('updateStatus updates via repository', async () => { repository.updateStatus.mockResolvedValue(sample); await service.updateStatus('priv-1', 'active', 'user-1'); expect(repository.updateStatus).toHaveBeenCalledWith( 'priv-1', expect.objectContaining({ value: 'active' }), 'user-1', ); }); it('importCsv creates rows and fails batch on invalid status', async () => { await expect( service.importCsv('name,code,status\nA,A1,nope', 'user-1'), ).rejects.toBeInstanceOf(BadRequestException); expect(repository.createMany).not.toHaveBeenCalled(); }); it('importCsv imports valid rows', async () => { repository.createMany.mockResolvedValue(1); const result = await service.importCsv( 'name,code,status\nAdmin,ADMIN,draft', 'user-1', ); expect(result.imported).toBe(1); expect(repository.createMany).toHaveBeenCalledTimes(1); }); it('checkPermission delegates', async () => { repository.checkPermission.mockResolvedValue(true); await expect( service.checkPermission('user-1', 'PRIVILEGES', 'view'), ).resolves.toBe(true); }); });