import { describe, expect, it } from "vitest"; import { ancestorsOf, breadcrumbLabel, buildOrgMaps, divisionOf, subtreeOf, type OrgUnit } from "@/lib/org"; // Der Baum ersetzt die drei festen Ebenen des Altmodells. Damit hängt an // dieser Datei mehr als vorher: eine Einheit falsch verkettet, und ein Filter // „Bereich Produktion" liefert stillschweigend zu wenig — nicht gar nichts, // was auffallen würde. const units: OrgUnit[] = [ { id: "gmbh", org_number: "10000000", name: "Alpenwerk", parent_id: null, unit_type: "Gesellschaft" }, { id: "prod", org_number: "20100000", name: "Produktion", parent_id: "gmbh", unit_type: "Bereich" }, { id: "fert", org_number: "21100000", name: "Fertigung", parent_id: "prod", unit_type: "Abteilung" }, { id: "mont", org_number: "22001000", name: "Montage", parent_id: "fert", unit_type: "Team" }, { id: "cnc", org_number: "22002000", name: "CNC", parent_id: "fert", unit_type: "Team" }, { id: "it", org_number: "20200000", name: "IT", parent_id: "gmbh", unit_type: "Bereich" }, ]; const locations = [{ id: "loc-1", name: "Wien-Hernals", country: "Österreich" }]; const maps = buildOrgMaps(units, locations); describe("buildOrgMaps", () => { it("listet Eltern vor ihren Kindern", () => { // Die Reihenfolge ist eine Zusage: der Filter im Mitarbeiterlisten-Select // rückt danach ein, und ein Einfügen in die Datenbank verlässt sich darauf. const position = new Map(maps.unitList.map((u, i) => [u.id, i])); for (const u of maps.unitList) { if (!u.parent_id) continue; expect(position.get(u.parent_id)!, `${u.name} steht vor ${u.parent_id}`).toBeLessThan(position.get(u.id)!); } }); it("misst die Tiefe ab der Wurzel", () => { expect(maps.depthOf.get("gmbh")).toBe(0); expect(maps.depthOf.get("prod")).toBe(1); expect(maps.depthOf.get("mont")).toBe(3); }); }); describe("ancestorsOf", () => { it("gibt die Kette von der Wurzel bis zur Einheit selbst", () => { expect(ancestorsOf(maps, "mont").map((u) => u.name)).toEqual(["Alpenwerk", "Produktion", "Fertigung", "Montage"]); }); it("bricht bei einem Ring ab, statt endlos zu laufen", () => { // parent_id ist eine gewöhnliche Spalte; ein fehlerhafter Import kann // einen Ring erzeugen, und jede Auswertung hier ist rekursiv. const ringMaps = buildOrgMaps( [ { id: "a", org_number: "1", name: "A", parent_id: "b", unit_type: "Bereich" }, { id: "b", org_number: "2", name: "B", parent_id: "a", unit_type: "Bereich" }, ], [] ); expect(ancestorsOf(ringMaps, "a")).toHaveLength(2); }); it("liefert nichts für eine unbekannte Einheit", () => { expect(ancestorsOf(maps, "gibtesnicht")).toEqual([]); expect(ancestorsOf(maps, null)).toEqual([]); }); }); describe("subtreeOf", () => { it("schliesst die Einheit selbst und alles darunter ein", () => { // Genau das meint der Filter „Bereich Produktion": im Bereich selbst // sitzt nur die Bereichsleitung, alle anderen hängen tiefer. expect(new Set(subtreeOf(maps, "prod"))).toEqual(new Set(["prod", "fert", "mont", "cnc"])); }); it("ist für ein Blatt die Einheit allein", () => { expect(subtreeOf(maps, "mont")).toEqual(["mont"]); }); }); describe("divisionOf", () => { it("findet den Bereich einer tief hängenden Einheit", () => { expect(divisionOf(maps, "mont")?.name).toBe("Produktion"); }); it("gibt für eine Bereichsleitung ihren eigenen Bereich", () => { expect(divisionOf(maps, "prod")?.name).toBe("Produktion"); }); it("hat für die Gesellschaft selbst keinen Bereich", () => { // Die Geschäftsführung sitzt über allen Bereichen, nicht in einem. expect(divisionOf(maps, "gmbh")).toBeUndefined(); }); }); describe("breadcrumbLabel", () => { it("lässt die Gesellschaft weg, die in jeder Zeile gleich wäre", () => { expect(breadcrumbLabel(maps, "mont")).toBe("Produktion › Fertigung › Montage"); }); it("endet bei der Einheit, an der die Person tatsächlich hängt", () => { expect(breadcrumbLabel(maps, "prod")).toBe("Produktion"); }); it("fällt auf einen Strich zurück, wenn nichts auflösbar ist", () => { expect(breadcrumbLabel(maps, null)).toBe("–"); expect(breadcrumbLabel(maps, "gmbh")).toBe("–"); }); });