feat(backend): Statistik- und Reporting-Endpunkte (Modul 27)
Aggregations-Endpunkte fuer Voelker, Koeniginnen, Bienenstaende, Varroa, Honig und Fuetterung nach Lastenheft Abschnitt 4.26.
This commit is contained in:
1 parent
8080a7f5ec
commit
e8177432b0
6 files changed
+706
No files matched your search
@@ -0,0 +1,216 @@
|
||||
import { StatisticsService } from './statistics.service';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
|
||||
describe('StatisticsService', () => {
|
||||
let service: StatisticsService;
|
||||
let prisma: {
|
||||
colony: { groupBy: jest.Mock; findMany: jest.Mock };
|
||||
inspection: { findMany: jest.Mock };
|
||||
queen: { findMany: jest.Mock };
|
||||
queenEvaluation: { findMany: jest.Mock };
|
||||
apiary: { findMany: jest.Mock };
|
||||
varroaMeasurement: { findMany: jest.Mock };
|
||||
treatment: { groupBy: jest.Mock };
|
||||
harvest: { findMany: jest.Mock };
|
||||
honeyBatch: { findMany: jest.Mock };
|
||||
feeding: { findMany: jest.Mock };
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
prisma = {
|
||||
colony: {
|
||||
groupBy: jest.fn().mockResolvedValue([]),
|
||||
findMany: jest.fn().mockResolvedValue([]),
|
||||
},
|
||||
inspection: { findMany: jest.fn().mockResolvedValue([]) },
|
||||
queen: { findMany: jest.fn().mockResolvedValue([]) },
|
||||
queenEvaluation: { findMany: jest.fn().mockResolvedValue([]) },
|
||||
apiary: { findMany: jest.fn().mockResolvedValue([]) },
|
||||
varroaMeasurement: { findMany: jest.fn().mockResolvedValue([]) },
|
||||
treatment: { groupBy: jest.fn().mockResolvedValue([]) },
|
||||
harvest: { findMany: jest.fn().mockResolvedValue([]) },
|
||||
honeyBatch: { findMany: jest.fn().mockResolvedValue([]) },
|
||||
feeding: { findMany: jest.fn().mockResolvedValue([]) },
|
||||
};
|
||||
service = new StatisticsService(prisma as unknown as PrismaService);
|
||||
});
|
||||
|
||||
describe('colonies', () => {
|
||||
it('groups losses by year using deactivatedAt, falling back to updatedAt', async () => {
|
||||
prisma.colony.groupBy.mockResolvedValueOnce([
|
||||
{ status: 'ACTIVE', _count: { _all: 5 } },
|
||||
]);
|
||||
prisma.colony.findMany.mockResolvedValueOnce([
|
||||
{
|
||||
deactivatedAt: new Date('2025-09-01'),
|
||||
updatedAt: new Date('2025-09-01'),
|
||||
},
|
||||
{ deactivatedAt: null, updatedAt: new Date('2024-03-01') },
|
||||
]);
|
||||
|
||||
const result = await service.colonies();
|
||||
|
||||
expect(result.byStatus).toEqual([{ status: 'ACTIVE', count: 5 }]);
|
||||
expect(result.lossesByYear).toEqual({ '2025': 1, '2024': 1 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('queens', () => {
|
||||
it('averages evaluation dimensions per queen and sorts by honey yield descending', async () => {
|
||||
prisma.queen.findMany.mockResolvedValueOnce([
|
||||
{
|
||||
id: 'q1',
|
||||
code: 'K-001',
|
||||
status: 'ACTIVE',
|
||||
createdAt: new Date('2024-01-01'),
|
||||
deactivatedAt: null,
|
||||
},
|
||||
{
|
||||
id: 'q2',
|
||||
code: 'K-002',
|
||||
status: 'ACTIVE',
|
||||
createdAt: new Date('2024-01-01'),
|
||||
deactivatedAt: null,
|
||||
},
|
||||
]);
|
||||
prisma.queenEvaluation.findMany.mockResolvedValueOnce([
|
||||
{
|
||||
queenId: 'q1',
|
||||
colonyStrength: 3,
|
||||
gentleness: 3,
|
||||
combBuild: 3,
|
||||
swarmingTendency: 3,
|
||||
broodPattern: 3,
|
||||
honeyYield: 2,
|
||||
},
|
||||
{
|
||||
queenId: 'q2',
|
||||
colonyStrength: 5,
|
||||
gentleness: 5,
|
||||
combBuild: 5,
|
||||
swarmingTendency: 5,
|
||||
broodPattern: 5,
|
||||
honeyYield: 5,
|
||||
},
|
||||
]);
|
||||
|
||||
const result = await service.queens();
|
||||
|
||||
expect(result.evaluationAverages.map((row) => row.queenId)).toEqual([
|
||||
'q2',
|
||||
'q1',
|
||||
]);
|
||||
expect(result.evaluationAverages[0].honeyYield).toBe(5);
|
||||
});
|
||||
|
||||
it('computes average lifespan only from retired queens', async () => {
|
||||
prisma.queen.findMany.mockResolvedValueOnce([
|
||||
{
|
||||
id: 'q1',
|
||||
code: 'K-001',
|
||||
status: 'RETIRED',
|
||||
createdAt: new Date('2024-01-01'),
|
||||
deactivatedAt: new Date('2024-01-11'),
|
||||
},
|
||||
{
|
||||
id: 'q2',
|
||||
code: 'K-002',
|
||||
status: 'ACTIVE',
|
||||
createdAt: new Date('2024-01-01'),
|
||||
deactivatedAt: null,
|
||||
},
|
||||
]);
|
||||
|
||||
const result = await service.queens();
|
||||
|
||||
expect(result.averageLifespanDays).toBe(10);
|
||||
});
|
||||
});
|
||||
|
||||
describe('apiaries', () => {
|
||||
it('computes honey totals and colony counts per apiary', async () => {
|
||||
prisma.apiary.findMany.mockResolvedValueOnce([
|
||||
{
|
||||
id: 'a1',
|
||||
name: 'Wabenhain Nord',
|
||||
colonies: [
|
||||
{ id: 'c1', status: 'ACTIVE' },
|
||||
{ id: 'c2', status: 'DECEASED' },
|
||||
],
|
||||
harvests: [{ netWeight: 10 }, { netWeight: 5 }],
|
||||
},
|
||||
]);
|
||||
|
||||
const result = await service.apiaries();
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
apiaryId: 'a1',
|
||||
name: 'Wabenhain Nord',
|
||||
activeColonies: 1,
|
||||
lostColonies: 1,
|
||||
totalHoneyKg: 15,
|
||||
averageHoneyKgPerColony: 7.5,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('varroa', () => {
|
||||
it('returns measurements and treatment counts per colony', async () => {
|
||||
prisma.varroaMeasurement.findMany.mockResolvedValueOnce([
|
||||
{
|
||||
colonyId: 'c1',
|
||||
date: new Date('2025-07-01'),
|
||||
miteCount: 4,
|
||||
method: 'PUDERZUCKER',
|
||||
},
|
||||
]);
|
||||
prisma.treatment.groupBy.mockResolvedValueOnce([
|
||||
{ colonyId: 'c1', _count: { _all: 2 } },
|
||||
]);
|
||||
|
||||
const result = await service.varroa();
|
||||
|
||||
expect(result.measurements).toHaveLength(1);
|
||||
expect(result.treatmentCountByColony).toEqual([
|
||||
{ colonyId: 'c1', treatmentCount: 2 },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('honey', () => {
|
||||
it('sums yield per year and per apiary', async () => {
|
||||
prisma.harvest.findMany.mockResolvedValueOnce([
|
||||
{ apiaryId: 'a1', date: new Date('2025-06-01'), netWeight: 10 },
|
||||
{ apiaryId: 'a1', date: new Date('2024-06-01'), netWeight: 6 },
|
||||
{ apiaryId: 'a2', date: new Date('2025-06-01'), netWeight: 4 },
|
||||
]);
|
||||
prisma.honeyBatch.findMany.mockResolvedValueOnce([
|
||||
{ amount: 8, createdAt: new Date() },
|
||||
]);
|
||||
|
||||
const result = await service.honey();
|
||||
|
||||
expect(result.totalHarvestKg).toBe(20);
|
||||
expect(result.totalHoneyBatchKg).toBe(8);
|
||||
expect(result.yieldPerYear).toEqual({ '2025': 14, '2024': 6 });
|
||||
expect(result.yieldPerApiary).toEqual({ a1: 16, a2: 4 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('feeding', () => {
|
||||
it('sums feeding amount per colony and per season', async () => {
|
||||
prisma.feeding.findMany.mockResolvedValueOnce([
|
||||
{ colonyId: 'c1', date: new Date('2025-09-01'), amount: 5, unit: 'l' },
|
||||
{ colonyId: 'c1', date: new Date('2025-09-15'), amount: 3, unit: 'l' },
|
||||
{ colonyId: 'c2', date: new Date('2024-09-01'), amount: 2, unit: 'l' },
|
||||
]);
|
||||
|
||||
const result = await service.feeding();
|
||||
|
||||
expect(result.byColony).toEqual({ c1: 8, c2: 2 });
|
||||
expect(result.bySeason).toEqual({ '2025': 8, '2024': 2 });
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user