feat(queens): verdrängte Königin bekommt ihren Endstatus automatisch
Entscheidung Inhaber: Zieht eine neue Königin ein, wird die bisherige „Ersetzt“; Vereinigen und Auflösen eines Volks setzen dessen Königin auf „Ersetzt“, ein Winterverlust auf „Eingegangen“. Nur aus einem verfügbaren Status (ACTIVE, RESERVE, MATED, UNMATED), über updateStatusIn in derselben Transaktion mit Archivdatum und Audit (QueensService.retireIn). Ein von Hand gesetzter Endstatus eines Volks fragt weiter über den Aufräum- Vorschlag nach. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
f9e03a1695
commit
81927d629a
10 files changed
+205
-4
No files matched your search
@@ -1,4 +1,5 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { QueensModule } from '../queens/queens.module';
|
||||
import { ApiaryRestrictionsModule } from '../apiary-restrictions/apiary-restrictions.module';
|
||||
import { ColoniesController } from './colonies.controller';
|
||||
import { ColoniesService } from './colonies.service';
|
||||
@@ -6,7 +7,7 @@ import { ColoniesService } from './colonies.service';
|
||||
// ApiaryRestrictionsModule: the restricted-zone endpoints ride along with the
|
||||
// colony paths that enforce them (AppModule imports ColoniesModule).
|
||||
@Module({
|
||||
imports: [ApiaryRestrictionsModule],
|
||||
imports: [ApiaryRestrictionsModule, QueensModule],
|
||||
controllers: [ColoniesController],
|
||||
providers: [ColoniesService],
|
||||
exports: [ColoniesService],
|
||||
|
||||
@@ -5,11 +5,13 @@ import { AuditLogService } from '../audit/audit-log.service';
|
||||
import { WebhooksService } from '../webhooks/webhooks.service';
|
||||
import { DomainEventsService } from '../domain-events/domain-events.service';
|
||||
import { PermissionResolverService } from '../auth/permissions/permission-resolver.service';
|
||||
import { QueensService } from '../queens/queens.service';
|
||||
import { berlinToday } from '../common/dates';
|
||||
import { expectOnlyIdPayloads } from '../domain-events/herd-payload.spec-helper';
|
||||
|
||||
describe('ColoniesService', () => {
|
||||
let service: ColoniesService;
|
||||
let queens: { retireIn: jest.Mock };
|
||||
let tx: {
|
||||
$queryRaw: jest.Mock;
|
||||
colony: { create: jest.Mock; update: jest.Mock; findUnique: jest.Mock };
|
||||
@@ -94,12 +96,14 @@ describe('ColoniesService', () => {
|
||||
.fn()
|
||||
.mockResolvedValue(new Set(['compliance:manage'])),
|
||||
};
|
||||
queens = { retireIn: jest.fn().mockResolvedValue(undefined) };
|
||||
service = new ColoniesService(
|
||||
prisma as unknown as PrismaService,
|
||||
auditLog as unknown as AuditLogService,
|
||||
webhooks as unknown as WebhooksService,
|
||||
events as unknown as DomainEventsService,
|
||||
resolver as unknown as PermissionResolverService,
|
||||
queens as unknown as QueensService,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -307,6 +311,13 @@ describe('ColoniesService', () => {
|
||||
endDate: null,
|
||||
});
|
||||
expect(historyCloseArg.data.endDate).toBeInstanceOf(Date);
|
||||
// The merged-away queen leaves for the archive as REPLACED.
|
||||
expect(queens.retireIn).toHaveBeenCalledWith(
|
||||
tx,
|
||||
'queen-2',
|
||||
'REPLACED',
|
||||
'user-1',
|
||||
);
|
||||
|
||||
const [[lineageArg]] = tx.colonyLineage.create.mock.calls as [
|
||||
[{ data: { type: string; fromColonyId: string; toColonyId: string } }],
|
||||
@@ -419,6 +430,13 @@ describe('ColoniesService', () => {
|
||||
endDate: null,
|
||||
});
|
||||
expect(historyCloseArg.data.endDate).toBeInstanceOf(Date);
|
||||
// The released queen leaves for the archive as REPLACED.
|
||||
expect(queens.retireIn).toHaveBeenCalledWith(
|
||||
tx,
|
||||
'queen-1',
|
||||
'REPLACED',
|
||||
'user-1',
|
||||
);
|
||||
});
|
||||
|
||||
it('dissolve does not touch queen history for a colony with no queen', async () => {
|
||||
@@ -434,6 +452,7 @@ describe('ColoniesService', () => {
|
||||
await service.dissolve('colony-1', 'user-1');
|
||||
|
||||
expect(tx.queenColonyHistory.updateMany).not.toHaveBeenCalled();
|
||||
expect(queens.retireIn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('reactivate throws ConflictException when already active', async () => {
|
||||
|
||||
@@ -26,6 +26,7 @@ import { lockRowsForShare, lockRowsForUpdate } from '../prisma/row-lock';
|
||||
import { colonyTransitionEvents, lockColony } from './colony-events';
|
||||
import { berlinToday, dayToDate } from '../common/dates';
|
||||
import { PermissionResolverService } from '../auth/permissions/permission-resolver.service';
|
||||
import { QueensService } from '../queens/queens.service';
|
||||
import {
|
||||
assertNoActiveRestriction,
|
||||
RestrictionCheck,
|
||||
@@ -81,6 +82,7 @@ export class ColoniesService {
|
||||
private readonly webhooks: WebhooksService,
|
||||
private readonly events: DomainEventsService,
|
||||
private readonly permissions: PermissionResolverService,
|
||||
private readonly queens: QueensService,
|
||||
) {}
|
||||
|
||||
async findAll(filter: ColonyFilter = {}): Promise<Colony[]> {
|
||||
@@ -526,6 +528,14 @@ export class ColoniesService {
|
||||
merged.id,
|
||||
dayToDate(today),
|
||||
);
|
||||
if (releasedQueenId) {
|
||||
await this.queens.retireIn(
|
||||
tx,
|
||||
releasedQueenId,
|
||||
'REPLACED',
|
||||
actingUserId,
|
||||
);
|
||||
}
|
||||
const mergedAway = await tx.colony.update({
|
||||
where: { id: merged.id },
|
||||
data: { status: 'MERGED', deactivatedAt: new Date() },
|
||||
@@ -576,6 +586,14 @@ export class ColoniesService {
|
||||
this.assertActive(before);
|
||||
const today = berlinToday();
|
||||
const releasedQueenId = await releaseQueenIn(tx, id, dayToDate(today));
|
||||
if (releasedQueenId) {
|
||||
await this.queens.retireIn(
|
||||
tx,
|
||||
releasedQueenId,
|
||||
'REPLACED',
|
||||
actingUserId,
|
||||
);
|
||||
}
|
||||
const updated = await tx.colony.update({
|
||||
where: { id },
|
||||
data: { status: 'DISSOLVED', deactivatedAt: new Date() },
|
||||
|
||||
Reference in new issue
Block a user