- WinterCheck per colony, winter and season (autumn/spring), every field editable; loss causes limited to the COLOSS monitoring categories. - Summary per winter and apiary: loss rate lost/wintered, marked provisional while colonies are not yet checked in spring - an unchecked colony never counts as survived. - A loss marks the colony deceased only on explicit request, in the same transaction and audited. - Permissions winter-checks:read/create/update granted by migration to beekeeper and staff roles; winter checks included in backups. - Page "Überwinterung": next action first, one button per row until the colony is wintered, German number format. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
107 lines
2.8 KiB
TypeScript
107 lines
2.8 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import type { Colony } from "@/lib/colonies/types";
|
|
import type { WinterCheckWithColony } from "./types";
|
|
import { buildWinterRows, winterLabel, winterYearFor } from "./winter";
|
|
|
|
function colony(overrides: Partial<Colony>): Colony {
|
|
return {
|
|
id: "c1",
|
|
code: "V-01",
|
|
apiaryId: "a",
|
|
hiveType: null,
|
|
originText: null,
|
|
colonyType: "PRODUCTION_COLONY",
|
|
status: "ACTIVE",
|
|
strength: null,
|
|
broodStatus: null,
|
|
feedStatus: null,
|
|
healthStatus: null,
|
|
notes: null,
|
|
currentQueenId: null,
|
|
createdAt: "",
|
|
updatedAt: "",
|
|
deactivatedAt: null,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
const deadColony = {
|
|
id: "c1",
|
|
code: "V-01",
|
|
status: "DECEASED",
|
|
apiaryId: "a",
|
|
apiary: { id: "a", name: "Obstwiese" },
|
|
};
|
|
|
|
function check(overrides: Partial<WinterCheckWithColony>): WinterCheckWithColony {
|
|
return {
|
|
id: "w1",
|
|
colonyId: "c1",
|
|
userId: "u",
|
|
winterYear: 2026,
|
|
season: "AUTUMN",
|
|
date: "2026-10-01T00:00:00.000Z",
|
|
occupiedSeams: null,
|
|
feedStockKg: null,
|
|
queenPresent: null,
|
|
outcome: null,
|
|
lossCause: null,
|
|
notes: null,
|
|
createdAt: "",
|
|
updatedAt: "",
|
|
colony: { ...deadColony, status: "ACTIVE" },
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe("winterYearFor / winterLabel", () => {
|
|
it("maps autumn and spring to the same winter", () => {
|
|
expect(winterYearFor(new Date(2026, 9, 1))).toBe(2026);
|
|
expect(winterYearFor(new Date(2027, 2, 20))).toBe(2026);
|
|
expect(winterLabel(2026)).toBe("Winter 2026/27");
|
|
});
|
|
});
|
|
|
|
describe("buildWinterRows", () => {
|
|
it("lists living colonies without records and hides old dead ones", () => {
|
|
const groups = buildWinterRows(
|
|
[colony({ id: "c1" }), colony({ id: "c2", code: "V-02", status: "DECEASED" })],
|
|
[],
|
|
new Map([["a", "Obstwiese"]]),
|
|
);
|
|
expect(groups).toHaveLength(1);
|
|
expect(groups[0].rows.map((row) => row.colonyId)).toEqual(["c1"]);
|
|
});
|
|
|
|
it("keeps a colony that died this winter, with its loss record", () => {
|
|
const groups = buildWinterRows(
|
|
[],
|
|
[
|
|
check({ colony: deadColony }),
|
|
check({
|
|
id: "w2",
|
|
season: "SPRING",
|
|
outcome: "LOST",
|
|
lossCause: "DEAD_OR_EMPTY",
|
|
colony: deadColony,
|
|
}),
|
|
],
|
|
new Map(),
|
|
);
|
|
const [row] = groups[0].rows;
|
|
expect(row).toMatchObject({ isLiving: false });
|
|
expect(row.autumn?.id).toBe("w1");
|
|
expect(row.spring?.outcome).toBe("LOST");
|
|
expect(groups[0].apiaryName).toBe("Obstwiese");
|
|
});
|
|
|
|
it("sorts colony codes naturally", () => {
|
|
const groups = buildWinterRows(
|
|
[colony({ id: "c10", code: "V-10" }), colony({ id: "c2", code: "V-2" })],
|
|
[],
|
|
new Map([["a", "Obstwiese"]]),
|
|
);
|
|
expect(groups[0].rows.map((row) => row.code)).toEqual(["V-2", "V-10"]);
|
|
});
|
|
});
|