diff --git a/ApiCore-Backend/src/colonies/colonies.service.spec.ts b/ApiCore-Backend/src/colonies/colonies.service.spec.ts index 14eeeb4..f301d4d 100644 --- a/ApiCore-Backend/src/colonies/colonies.service.spec.ts +++ b/ApiCore-Backend/src/colonies/colonies.service.spec.ts @@ -1,4 +1,4 @@ -import { NotFoundException } from '@nestjs/common'; +import { ConflictException, NotFoundException } from '@nestjs/common'; import { ColoniesService } from './colonies.service'; import { PrismaService } from '../prisma/prisma.service'; import { AuditLogService } from '../audit/audit-log.service'; @@ -143,6 +143,16 @@ describe('ColoniesService', () => { expect(lineageArg.data.toColonyId).toBe('colony-1'); }); + it('move rejects a non-active colony', async () => { + prisma.colony.findUnique.mockResolvedValue({ + ...parentColony, + status: 'DISSOLVED', + }); + await expect( + service.move('colony-1', { apiaryId: 'apiary-2' }, 'user-1'), + ).rejects.toBeInstanceOf(ConflictException); + }); + it('dissolve sets status DISSOLVED and records DEACTIVATE', async () => { prisma.colony.findUnique.mockResolvedValue(parentColony); prisma.colony.update.mockResolvedValue({ diff --git a/ApiCore-Backend/src/colonies/colonies.service.ts b/ApiCore-Backend/src/colonies/colonies.service.ts index 272b044..60ffcbf 100644 --- a/ApiCore-Backend/src/colonies/colonies.service.ts +++ b/ApiCore-Backend/src/colonies/colonies.service.ts @@ -1,4 +1,8 @@ -import { Injectable, NotFoundException } from '@nestjs/common'; +import { + ConflictException, + Injectable, + NotFoundException, +} from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { AuditLogService } from '../audit/audit-log.service'; import { Colony } from '../../generated/prisma'; @@ -27,6 +31,14 @@ export class ColoniesService { return colony; } + private assertActive(colony: Colony): void { + if (colony.status !== 'ACTIVE') { + throw new ConflictException( + `Colony ${colony.id} is not active (status: ${colony.status})`, + ); + } + } + async create(dto: CreateColonyDto, actingUserId: string): Promise { const colony = await this.prisma.colony.create({ data: { ...dto } }); await this.auditLog.record({ @@ -66,6 +78,7 @@ export class ColoniesService { actingUserId: string, ): Promise { const before = await this.findById(id); + this.assertActive(before); const colony = await this.prisma.$transaction(async (tx) => { const updated = await tx.colony.update({ where: { id }, @@ -98,6 +111,7 @@ export class ColoniesService { actingUserId: string, ): Promise { const parent = await this.findById(id); + this.assertActive(parent); const child = await this.prisma.$transaction(async (tx) => { const created = await tx.colony.create({ data: { @@ -134,6 +148,7 @@ export class ColoniesService { actingUserId: string, ): Promise { const survivor = await this.findById(id); + this.assertActive(survivor); await this.prisma.$transaction(async (tx) => { for (const mergedColonyId of dto.mergedColonyIds) { const merged = await tx.colony.findUnique({ @@ -142,6 +157,7 @@ export class ColoniesService { if (!merged) { throw new NotFoundException(`Colony ${mergedColonyId} not found`); } + this.assertActive(merged); await tx.colony.update({ where: { id: mergedColonyId }, data: { status: 'MERGED', deactivatedAt: new Date() }, diff --git a/ApiCore-Backend/test/colonies.e2e-spec.ts b/ApiCore-Backend/test/colonies.e2e-spec.ts index acad51b..d4beda6 100644 --- a/ApiCore-Backend/test/colonies.e2e-spec.ts +++ b/ApiCore-Backend/test/colonies.e2e-spec.ts @@ -188,5 +188,11 @@ describe('Colonies (e2e)', () => { .get(`/api/v1/colonies/${colonyId}`) .set('Authorization', `Bearer ${staffToken}`) .expect(200); + + await request(app.getHttpServer()) + .post(`/api/v1/colonies/${colonyId}/move`) + .set('Authorization', `Bearer ${beekeeperToken}`) + .send({ apiaryId: apiaryAId }) + .expect(409); }); });