Files
alpenwerk-hr/tests/unit/report-criteria.test.ts
Andrei Laas 779beb4478 Hay-Grade statt Verwendungsgruppe A--F
A--F stammte aus der Spezifikation, nicht vom Kunden: sechs erfundene Stufen
mit erfundenen Beschreibungen. Der Kunde bewertet nach Hay und hat die Liste
geschickt -- 13 Stufen plus einen Generic Grade.

Dieselbe Spalte fuellt im Cornerstone-Extrakt die Grade ID. Solange dort A--F
steht, ist die Datei in jeder Zeile falsch, ohne dass es beim Erzeugen
auffaellt: das Zielsystem kennt diese Kennungen nicht.

Der Typ heisst weiter paygrade_type, ist aber jetzt eine Domain ueber text mit
CHECK statt eines Aufzaehlungstyps. Ein Enum laesst sich nicht umschreiben --
Werte entfernen geht gar nicht, und add value darf im selben Vorgang, der den
neuen Wert schreibt, nicht benutzt werden. Wichtiger: die rund zehn
SQL-Funktionen, die den Wert nach paygrade_type umwandeln, bleiben unveraendert
gueltig. Jede von ihnen neu zu erzeugen hiesse, zehnmal die Gelegenheit zu
haben, aus einer veralteten Vorlage zu kopieren.

Zwei Funktionen muessen doch angefasst werden, beide per Punktaenderung an der
laufenden Definition statt per Kopie aus einer Datei: der Vorgabewert B in
hire_employee und die Beschriftung im Protokoll von promote_employee.

Der Bestand bekommt den Generic Grade. Aus A--F liesse sich kein Hay-Grade
ableiten: andere Einteilung, andere Anzahl. Geraten saehe im Extrakt genauso
aus wie erhoben.
2026-09-28 11:13:41 +02:00

100 lines
4.0 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
anzahlKriterien,
AUSWAHL_KRITERIEN,
JANEIN_KRITERIEN,
kriterienParameter,
LEERE_CRITERIA,
parseCriteria,
passtImSpeicher,
ZEITRAUM_KRITERIEN,
} from "@/lib/report-criteria";
function aus(params: Record<string, string>) {
return parseCriteria((k) => params[k]);
}
describe("parseCriteria", () => {
it("nimmt bekannte Werte an", () => {
const c = aus({ employment: "Teilzeit", paygrade: "HG13", gender: "w" });
expect(c.auswahl).toEqual({ employment: "Teilzeit", paygrade: "HG13", gender: "w" });
});
it("verwirft Werte, die nicht in der Liste stehen", () => {
// Die Werte landen in SQL-Vergleichen und im Dateinamen des Downloads,
// also in einem Content-Disposition-Header — was nicht in der Liste
// steht, darf nicht durchkommen.
const c = aus({ employment: "Vollzeit'; drop table employees; --", paygrade: "HG21", gender: "x" });
expect(c.auswahl).toEqual({});
});
it("nimmt ja/nein an und sonst nichts", () => {
const c = aus({ dienstwagen: "ja", betriebsrat: "vielleicht", clevel: "nein" });
expect(c.jaNein).toEqual({ dienstwagen: "ja", clevel: "nein" });
});
it("prüft Zeiträume auf echte Kalenderdaten", () => {
const c = aus({ entryFrom: "2024-01-01", entryTo: "2024-02-31", exitTo: "gestern" });
// Der 31. Februar ist kein Datum, auch wenn er wie eines aussieht.
expect(c.zeitraum.entry).toEqual({ von: "2024-01-01", bis: undefined });
expect(c.zeitraum.exit).toBeUndefined();
});
it("nimmt Stunden mit Komma und weist Unsinn ab", () => {
expect(aus({ hoursFrom: "20,5" }).stundenVon).toBe(20.5);
expect(aus({ hoursFrom: "-5" }).stundenVon).toBeUndefined();
expect(aus({ hoursTo: "500" }).stundenBis).toBeUndefined();
expect(aus({ hoursTo: "keine" }).stundenBis).toBeUndefined();
});
it("liefert ohne Parameter nichts Gesetztes", () => {
expect(anzahlKriterien(aus({}))).toBe(0);
expect(anzahlKriterien(LEERE_CRITERIA)).toBe(0);
});
it("zählt jedes gesetzte Kriterium einzeln", () => {
const c = aus({ employment: "Teilzeit", dienstwagen: "ja", entryFrom: "2024-01-01", entryTo: "2024-12-31", hoursTo: "30" });
expect(anzahlKriterien(c)).toBe(5);
});
});
describe("kriterienParameter", () => {
it("nennt jeden Parameter, den parseCriteria liest", () => {
// „Zurücksetzen" räumt anhand dieser Liste auf. Fehlt darin ein Name,
// bleibt ein unsichtbares Kriterium in der Adresszeile stehen und
// schränkt den Export weiter ein, ohne dass es jemand sieht.
const namen = kriterienParameter();
const alle = {
...Object.fromEntries(AUSWAHL_KRITERIEN.map((k) => [k.key, k.optionen[0].wert])),
...Object.fromEntries(JANEIN_KRITERIEN.map((k) => [k.key, "ja"])),
...Object.fromEntries(ZEITRAUM_KRITERIEN.flatMap((k) => [[`${k.key}From`, "2024-01-01"], [`${k.key}To`, "2024-12-31"]])),
hoursFrom: "10",
hoursTo: "38.5",
};
expect(anzahlKriterien(aus(alle))).toBeGreaterThan(0);
const uebrig = aus(Object.fromEntries(Object.entries(alle).filter(([k]) => !namen.includes(k))));
expect(anzahlKriterien(uebrig)).toBe(0);
});
});
describe("passtImSpeicher", () => {
const zeile = { work_days: ["Mo", "Di", "Mi"], dependentsCount: 2 };
it("lässt ohne Kriterien alles durch", () => {
expect(passtImSpeicher(zeile, LEERE_CRITERIA)).toBe(true);
});
it("prüft den Wochentag gegen die Arbeitstage", () => {
expect(passtImSpeicher(zeile, aus({ weekday: "Di" }))).toBe(true);
expect(passtImSpeicher(zeile, aus({ weekday: "Fr" }))).toBe(false);
});
it("unterscheidet mit und ohne Angehörige", () => {
expect(passtImSpeicher(zeile, aus({ dependents: "ja" }))).toBe(true);
expect(passtImSpeicher(zeile, aus({ dependents: "nein" }))).toBe(false);
expect(passtImSpeicher({ ...zeile, dependentsCount: 0 }, aus({ dependents: "nein" }))).toBe(true);
expect(passtImSpeicher({ ...zeile, dependentsCount: 0 }, aus({ dependents: "ja" }))).toBe(false);
});
});