import { ConflictException, NotFoundException } from '@nestjs/common'; import { ApiariesService } from './apiaries.service'; import { PrismaService } from '../prisma/prisma.service'; import { AuditLogService } from '../audit/audit-log.service'; describe('ApiariesService', () => { let service: ApiariesService; let prisma: { apiary: { findMany: jest.Mock; findUnique: jest.Mock; create: jest.Mock; update: jest.Mock; }; }; let auditLog: { record: jest.Mock }; const existingApiary = { id: 'apiary-1', name: 'Heimstand', status: 'ACTIVE', deactivatedAt: null, }; beforeEach(() => { prisma = { apiary: { findMany: jest.fn(), findUnique: jest.fn(), create: jest.fn(), update: jest.fn(), }, }; auditLog = { record: jest.fn().mockResolvedValue(undefined) }; service = new ApiariesService( prisma as unknown as PrismaService, auditLog as unknown as AuditLogService, ); }); it('findById throws NotFoundException when missing', async () => { prisma.apiary.findUnique.mockResolvedValue(null); await expect(service.findById('missing')).rejects.toBeInstanceOf( NotFoundException, ); }); it('create persists the apiary and records an audit entry', async () => { prisma.apiary.create.mockResolvedValue(existingApiary); const result = await service.create({ name: 'Heimstand' }, 'user-1'); expect(result).toEqual(existingApiary); expect(auditLog.record).toHaveBeenCalledWith( expect.objectContaining({ entityType: 'Apiary', action: 'CREATE', userId: 'user-1', }), ); }); it('deactivate sets status to INACTIVE and records an audit entry', async () => { prisma.apiary.findUnique.mockResolvedValue(existingApiary); prisma.apiary.update.mockResolvedValue({ ...existingApiary, status: 'INACTIVE', deactivatedAt: new Date(), }); const result = await service.deactivate('apiary-1', 'user-1'); expect(result.status).toBe('INACTIVE'); expect(auditLog.record).toHaveBeenCalledWith( expect.objectContaining({ entityType: 'Apiary', action: 'DEACTIVATE' }), ); }); it('reactivate throws ConflictException when already active', async () => { prisma.apiary.findUnique.mockResolvedValue(existingApiary); await expect( service.reactivate('apiary-1', 'user-1'), ).rejects.toBeInstanceOf(ConflictException); }); it('reactivate sets status to ACTIVE and clears deactivatedAt', async () => { prisma.apiary.findUnique.mockResolvedValue({ ...existingApiary, status: 'INACTIVE', deactivatedAt: new Date(), }); prisma.apiary.update.mockResolvedValue({ ...existingApiary, status: 'ACTIVE', deactivatedAt: null, }); const result = await service.reactivate('apiary-1', 'user-1'); expect(result.status).toBe('ACTIVE'); expect(prisma.apiary.update).toHaveBeenCalledWith({ where: { id: 'apiary-1' }, data: { status: 'ACTIVE', deactivatedAt: null }, }); expect(auditLog.record).toHaveBeenCalledWith( expect.objectContaining({ entityType: 'Apiary', action: 'UPDATE' }), ); }); });