import { summarizeWinter, validateWinterCheck, WinterSummarySource, winterYearFor, } from './winter-check-rules'; const d = (value: string) => new Date(`${value}T12:00:00.000Z`); describe('winterYearFor', () => { it('assigns autumn dates to the winter starting that year', () => { expect(winterYearFor(d('2026-09-15'))).toBe(2026); expect(winterYearFor(d('2026-07-01'))).toBe(2026); }); it('assigns spring dates to the winter that started the year before', () => { expect(winterYearFor(d('2027-03-20'))).toBe(2026); expect(winterYearFor(d('2027-06-30'))).toBe(2026); }); }); describe('validateWinterCheck', () => { it('accepts a plain autumn check', () => { expect( validateWinterCheck({ season: 'AUTUMN', outcome: null, lossCause: null }), ).toBeNull(); }); it('rejects an outcome on an autumn check', () => { expect( validateWinterCheck({ season: 'AUTUMN', outcome: 'SURVIVED', lossCause: null, }), ).toMatch(/Einwinterung/); }); it('requires an outcome on a spring check', () => { expect( validateWinterCheck({ season: 'SPRING', outcome: null, lossCause: null }), ).toMatch(/Ergebnis/); }); it('requires a loss cause exactly when the colony was lost', () => { expect( validateWinterCheck({ season: 'SPRING', outcome: 'LOST', lossCause: null, }), ).toMatch(/Verlustursache/); expect( validateWinterCheck({ season: 'SPRING', outcome: 'SURVIVED', lossCause: 'DEAD_OR_EMPTY', }), ).toMatch(/Verlustursache/); expect( validateWinterCheck({ season: 'SPRING', outcome: 'LOST', lossCause: 'QUEEN_PROBLEM', }), ).toBeNull(); }); }); function check(overrides: Partial): WinterSummarySource { return { colonyId: 'c1', apiaryId: 'a', apiaryName: 'Stand A', season: 'AUTUMN', outcome: null, lossCause: null, ...overrides, }; } describe('summarizeWinter', () => { it('counts outcomes against the colonies that went into winter', () => { const summary = summarizeWinter([ check({ colonyId: 'c1' }), check({ colonyId: 'c1', season: 'SPRING', outcome: 'SURVIVED' }), check({ colonyId: 'c2' }), check({ colonyId: 'c2', season: 'SPRING', outcome: 'LOST', lossCause: 'DEAD_OR_EMPTY', }), check({ colonyId: 'c3' }), check({ colonyId: 'c3', season: 'SPRING', outcome: 'WEAK' }), check({ colonyId: 'c4' }), ]); expect(summary.total).toMatchObject({ wintered: 4, survived: 1, weak: 1, lost: 1, lostByCause: { DEAD_OR_EMPTY: 1, QUEEN_PROBLEM: 0, NATURAL_DISASTER: 0 }, pendingSpring: 1, springWithoutAutumn: 0, lossRatePercent: 25, isProvisional: true, }); }); it('never counts a colony without spring check as survived', () => { const summary = summarizeWinter([check({ colonyId: 'c1' })]); expect(summary.total.survived).toBe(0); expect(summary.total.pendingSpring).toBe(1); expect(summary.total.isProvisional).toBe(true); }); it('keeps spring checks without autumn check out of the loss rate', () => { const summary = summarizeWinter([ check({ colonyId: 'c1' }), check({ colonyId: 'c1', season: 'SPRING', outcome: 'SURVIVED' }), check({ colonyId: 'c9', season: 'SPRING', outcome: 'LOST', lossCause: 'NATURAL_DISASTER', }), ]); expect(summary.total).toMatchObject({ wintered: 1, lost: 0, springWithoutAutumn: 1, lossRatePercent: 0, isProvisional: false, }); }); it('reports no loss rate when nothing was wintered', () => { expect(summarizeWinter([]).total.lossRatePercent).toBeNull(); }); it('groups by apiary, sorted by name', () => { const summary = summarizeWinter([ check({ colonyId: 'c1', apiaryId: 'b', apiaryName: 'Wald' }), check({ colonyId: 'c2', apiaryId: 'a', apiaryName: 'Obstwiese' }), ]); expect(summary.apiaries.map((apiary) => apiary.apiaryName)).toEqual([ 'Obstwiese', 'Wald', ]); }); });