Files
alpenwerk-hr/tests/unit/lolyo.test.ts
Andrei Laas 676cecbf15
Some checks failed
CI / Migrationen auf leerer Datenbank (push) Has been cancelled
CI / Lint, Typen, Tests, Build (push) Has been cancelled
Export fuer LOLYO, und die Zielsystem-Exporte in einem eigenen Abschnitt
LOLYO ist die dritte Datei, die in ein fremdes System geladen wird. Spalten,
Trennzeichen und Schreibweise kommen aus der Vorlage des Anbieters, Zeichen fuer
Zeichen -- einschliesslich der gemischten Kopfzeile ("Titel prefix" deutsch,
"Title Suffix" englisch). Semikolon und kein BOM: das BOM haenge unsichtbar am
Namen der ersten Spalte, und "Benutzer/Code" kaeme in keiner Zuordnung mehr vor.

Der Benutzercode ist die Alpenwerk-UUID. Anders als bei Cornerstone ist das
hier richtig: LOLYO kennt die Person noch nicht und vergibt keine eigene
Kennung, die wir treffen muessten.

Diese Datei legt Konten an, samt Passwort in der Zeile. Das Startpasswort steht
deshalb in der Route und nicht in lib/lolyo.ts: eine Route ist serverseitig, eine
lib-Datei waere nur so lange sicher, wie niemand sie aus einem Client-Bauteil
heraus einbindet -- und das saehe man dem Buendel nicht an. Dieselbe Ueberlegung
steht in lib/passwort.ts.

Die drei Zielsystem-Exporte stehen jetzt unter einer gemeinsamen Ueberschrift,
getrennt vom Datenexport darueber. Der gibt Zahlen heraus, die jemand ansieht;
diese legen im Zielsystem Datensaetze an. Nebeneinander in einer Reihe
gleichartiger Karten war das nicht zu sehen. Die Auswahl steht damit einmal
statt dreimal da.

Der erklaerende Text unter dem Cornerstone-Export entfaellt auf Wunsch des
Kunden.
2026-09-28 13:45:38 +02:00

131 lines
4.4 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { toCsv } from "@/lib/export";
import {
anrede,
baueLolyoZeile,
LOLYO_CSV,
LOLYO_EMAIL_BESTAETIGT,
LOLYO_GRUPPE,
LOLYO_SPALTEN,
LOLYO_SPRACHE,
lolyoSpalten,
type LolyoQuelle,
} from "@/lib/lolyo";
// Die Datei legt in LOLYO Konten an. Eine falsche Spalte fällt hier nicht auf,
// sondern dort — als Konto, das niemandem gehört, oder als Zeile, die der
// Import abweist.
// Die Kopfzeile aus der Vorlage des Anbieters, Zeichen für Zeichen.
const VORGABE = "Benutzer/Code;Email;Passwort;Titel prefix;Vorname;Nachname;Title Suffix;Position;Gruppen;Telefon;Anrede;Sprache;E-Mail bestätigt";
const UUID = "8f2b1c40-9d3e-4a77-b2c1-5e6f70a81234";
const PASSWORT = "Startpasswort1!";
function person(teil: Partial<LolyoQuelle> = {}): LolyoQuelle {
return {
id: UUID,
company_email: "max.alpentest@alpenwerk-firmentest.com",
title_prefix: ["Mag."],
title_suffix: ["MBA"],
first_name: "Max",
last_name: "Alpentest",
job_title: "HR-Manager",
phone: "+4366412345678",
gender: "m",
...teil,
};
}
const zeile = (teil: Partial<LolyoQuelle> = {}) => baueLolyoZeile(person(teil), PASSWORT);
describe("Spalten", () => {
it("stehen genau so und in genau der Reihenfolge wie in der Vorlage", () => {
expect(LOLYO_SPALTEN.join(";")).toBe(VORGABE);
});
it("sind 13", () => {
expect(LOLYO_SPALTEN).toHaveLength(13);
});
it("werden alle gefüllt — keine Spalte ohne Zuordnung", () => {
const z = zeile();
for (const spalte of LOLYO_SPALTEN) {
expect(z, `Spalte "${spalte}" fehlt in der Zeile`).toHaveProperty(spalte);
}
expect(Object.keys(z).sort()).toEqual([...LOLYO_SPALTEN].sort());
});
});
describe("Die Zuordnung", () => {
it("nimmt als Benutzercode die Alpenwerk-UUID", () => {
expect(zeile()["Benutzer/Code"]).toBe(UUID);
});
it("nimmt die Firmenadresse und nie die private", () => {
// Die Datei legt Konten bei einem fremden Anbieter an. Eine Privatadresse
// hat dort nichts verloren; ohne Firmenadresse bleibt die Zelle leer.
expect(zeile().Email).toBe("max.alpentest@alpenwerk-firmentest.com");
expect(zeile({ company_email: null }).Email).toBe("");
});
it("trägt das Passwort ein, das die Route hereinreicht", () => {
expect(zeile().Passwort).toBe(PASSWORT);
});
it("setzt Titel, Name und Position aus Alpenwerk", () => {
const z = zeile();
expect(z["Titel prefix"]).toBe("Mag.");
expect(z.Vorname).toBe("Max");
expect(z.Nachname).toBe("Alpentest");
expect(z["Title Suffix"]).toBe("MBA");
expect(z.Position).toBe("HR-Manager");
});
it("fügt mehrere Titel zusammen und lässt keine zu einer leeren Zelle werden", () => {
expect(zeile({ title_prefix: ["Dr.", "Mag."] })["Titel prefix"]).toBe("Dr. Mag.");
expect(zeile({ title_prefix: [], title_suffix: [] })["Titel prefix"]).toBe("");
});
it("setzt Gruppe, Sprache und Bestätigung fest", () => {
const z = zeile();
expect(z.Gruppen).toBe(LOLYO_GRUPPE);
expect(z.Sprache).toBe(LOLYO_SPRACHE);
expect(z["E-Mail bestätigt"]).toBe(LOLYO_EMAIL_BESTAETIGT);
});
it("leitet die Anrede aus dem Geschlecht ab", () => {
expect(anrede("m")).toBe("Herr");
expect(anrede("w")).toBe("Frau");
// Lieber leer als geraten: eine falsche Anrede steht in jeder Nachricht,
// die die App verschickt.
expect(anrede("")).toBe("");
});
it("lässt eine fehlende Telefonnummer leer", () => {
expect(zeile({ phone: null }).Telefon).toBe("");
});
});
describe("Die Datei", () => {
it("ist semikolongetrennt und ohne BOM", () => {
// Das BOM hinge unsichtbar am Namen der ersten Spalte, und
// „Benutzer/Code" käme beim Einlesen in keiner Zuordnung mehr vor.
const csv = toCsv([zeile()], lolyoSpalten(), LOLYO_CSV);
expect(csv.startsWith("Benutzer/Code;")).toBe(true);
expect(csv.charCodeAt(0)).not.toBe(0xfeff);
});
it("beginnt mit der Kopfzeile der Vorlage", () => {
const csv = toCsv([], lolyoSpalten(), LOLYO_CSV);
expect(csv.split("\r\n")[0]).toBe(VORGABE);
});
it("maskiert eine Telefonnummer, die Excel als Formel läse", () => {
// „+43…" beginnt mit einem Pluszeichen — ohne Schutz macht Excel daraus
// beim Öffnen eine Formel und zeigt einen Fehler statt der Nummer.
const csv = toCsv([zeile()], lolyoSpalten(), LOLYO_CSV);
expect(csv).toContain("'+4366412345678");
});
});