Bisher liessen sich diese acht Objekttypen nur anlegen, nie nachträglich korrigieren – Aufgaben hatten zwar schon einen PATCH-Endpunkt, aber keine Bearbeiten-UI, die übrigen sieben hatten weder Backend- noch Frontend-Unterstützung. Jetzt bekommt jeder Typ einen PATCH :id Endpunkt (UpdateXDto per PartialType(PickType(...)) nach demselben Muster wie UpdateColonyDto/UpdateApiaryDto, mit Audit-Log-Eintrag inkl. Vorher-/Nachher-Stand) sowie eine Bearbeiten-UI: - Aufgaben: Dialog auf der Aufgabenliste - Durchsichten/Behandlungen/Fütterungen/Varroa-Messungen: Dialog pro Listeneintrag auf der Volk-Detailseite - Ernten: Dialog auf der eigenen Ernte-Detailseite - Schleuderungen: Dialog direkt in der Rückverfolgbarkeits-Karte der Honigcharge (keine eigene Detailseite nötig); Erntezeilen dort verlinken jetzt zur Ernte-Detailseite statt sie zu duplizieren - Abfüllungen: Dialog auf der Abfüllungs-Detailseite, bewusst nur Datum/MHD/Losnummer editierbar – Glasgröße/-anzahl bleiben unveränderlich, weil sie bereits unumkehrbar die Honigcharge-Menge reduziert haben Relationsstiftende Fremdschlüssel (colonyId, apiaryId/colonyIds, harvestIds, honeyBatchId) bleiben überall bewusst nicht editierbar, konsistent mit dem bestehenden apiaryId-Ausschluss bei UpdateColonyDto.
99 lines
2.6 KiB
TypeScript
99 lines
2.6 KiB
TypeScript
import { NotFoundException } from '@nestjs/common';
|
|
import { FeedingsService } from './feedings.service';
|
|
import { PrismaService } from '../prisma/prisma.service';
|
|
import { AuditLogService } from '../audit/audit-log.service';
|
|
|
|
describe('FeedingsService', () => {
|
|
let service: FeedingsService;
|
|
let prisma: {
|
|
feeding: {
|
|
findMany: jest.Mock;
|
|
findUnique: jest.Mock;
|
|
create: jest.Mock;
|
|
update: jest.Mock;
|
|
groupBy: jest.Mock;
|
|
};
|
|
};
|
|
let auditLog: { record: jest.Mock };
|
|
|
|
const feeding = { id: 'feeding-1', colonyId: 'colony-1', date: new Date() };
|
|
|
|
beforeEach(() => {
|
|
prisma = {
|
|
feeding: {
|
|
findMany: jest.fn(),
|
|
findUnique: jest.fn(),
|
|
create: jest.fn(),
|
|
update: jest.fn(),
|
|
groupBy: jest.fn(),
|
|
},
|
|
};
|
|
auditLog = { record: jest.fn() };
|
|
service = new FeedingsService(
|
|
prisma as unknown as PrismaService,
|
|
auditLog as unknown as AuditLogService,
|
|
);
|
|
});
|
|
|
|
it('findById throws NotFoundException when missing', async () => {
|
|
prisma.feeding.findUnique.mockResolvedValue(null);
|
|
await expect(service.findById('missing')).rejects.toBeInstanceOf(
|
|
NotFoundException,
|
|
);
|
|
});
|
|
|
|
it('create stores the acting user as the feeder', async () => {
|
|
prisma.feeding.create.mockResolvedValue(feeding);
|
|
|
|
await service.create(
|
|
{
|
|
colonyId: 'colony-1',
|
|
date: '2026-01-01',
|
|
feedType: 'SUGAR_WATER',
|
|
amount: 2,
|
|
unit: 'l',
|
|
},
|
|
'user-1',
|
|
);
|
|
|
|
const [[createArg]] = prisma.feeding.create.mock.calls as [
|
|
[{ data: { colonyId: string; userId: string } }],
|
|
];
|
|
expect(createArg.data.colonyId).toBe('colony-1');
|
|
expect(createArg.data.userId).toBe('user-1');
|
|
});
|
|
|
|
it('update writes an audit log entry with before and after data', async () => {
|
|
prisma.feeding.findUnique.mockResolvedValue(feeding);
|
|
const updated = { ...feeding, notes: 'geändert' };
|
|
prisma.feeding.update.mockResolvedValue(updated);
|
|
|
|
const result = await service.update(
|
|
'feeding-1',
|
|
{ notes: 'geändert' },
|
|
'user-1',
|
|
);
|
|
|
|
expect(result).toBe(updated);
|
|
expect(auditLog.record).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
entityType: 'Feeding',
|
|
entityId: 'feeding-1',
|
|
action: 'UPDATE',
|
|
beforeData: feeding,
|
|
afterData: updated,
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('summaryByColony sums amounts per unit', async () => {
|
|
prisma.feeding.groupBy.mockResolvedValue([
|
|
{ unit: 'l', _sum: { amount: 5.5 } },
|
|
]);
|
|
|
|
const summary = await service.summaryByColony('colony-1');
|
|
|
|
expect(summary).toEqual([{ unit: 'l', totalAmount: 5.5 }]);
|
|
});
|
|
});
|