196 lines
8.2 KiB
TypeScript
196 lines
8.2 KiB
TypeScript
import { beforeEach, describe, expect, it } from "vitest";
|
||
import { Betriebsmodus } from "@/generated/prisma/enums";
|
||
import {
|
||
versionMitAufbau,
|
||
versionsHistorie,
|
||
vorlageHolen,
|
||
vorlagenFilterwerte,
|
||
vorlagenListe,
|
||
} from "@/server/queries/vorlagen";
|
||
import { datenbankLeeren, datenbankVerfuegbar, testPrisma } from "./helpers/db";
|
||
|
||
/**
|
||
* Mandantensicht auf die Vorlagen (#009).
|
||
*
|
||
* Diese Datei gibt es, weil der unabhängige Review zwei Lücken gefunden hat, die
|
||
* kein Test sehen konnte: der Suchfilter hat die Kundenbedingung überschrieben
|
||
* (zwei `OR`-Schlüssel im selben Objekt, der zweite gewinnt), und Historie wie
|
||
* Aufbau einer Version haben überhaupt nicht gefiltert. Beides fiel nicht auf,
|
||
* weil es zu `src/server/queries/vorlagen.ts` keinen einzigen Test gab.
|
||
*/
|
||
|
||
async function aufbau() {
|
||
const eigene = await testPrisma.kunde.create({
|
||
data: { name: "Eigene Organisation", kuerzel: "EIGEN", istEigeneOrganisation: true },
|
||
});
|
||
const meiner = await testPrisma.kunde.create({ data: { name: "Meiner", kuerzel: "MEIN" } });
|
||
const fremder = await testPrisma.kunde.create({ data: { name: "Fremder", kuerzel: "FREMD" } });
|
||
await testPrisma.einstellung.create({
|
||
data: { id: "global", modus: Betriebsmodus.DIENSTLEISTUNG },
|
||
});
|
||
|
||
const techniker = await testPrisma.benutzer.create({
|
||
data: { email: "t@firma.example", name: "Techniker", rolle: "TECHNIKER" },
|
||
});
|
||
await testPrisma.benutzerKunde.create({
|
||
data: { benutzerId: techniker.id, kundeId: meiner.id },
|
||
});
|
||
|
||
async function vorlageMit(name: string, kundeId: string | null) {
|
||
const vorlage = await testPrisma.vorlage.create({
|
||
data: { name, kategorie: "Geheim", schlagworte: ["intern"], kundeId },
|
||
});
|
||
const version = await testPrisma.vorlagenVersion.create({
|
||
data: { vorlageId: vorlage.id, version: 1, status: "ENTWURF" },
|
||
});
|
||
const abschnitt = await testPrisma.vorlagenAbschnitt.create({
|
||
data: { versionId: version.id, titel: `Abschnitt von ${name}` },
|
||
});
|
||
await testPrisma.vorlagenSchritt.create({
|
||
data: { abschnittId: abschnitt.id, titel: "Ein Schritt" },
|
||
});
|
||
await testPrisma.vorlagenVersion.update({
|
||
where: { id: version.id },
|
||
data: { status: "VEROEFFENTLICHT", veroeffentlichtAm: new Date() },
|
||
});
|
||
return { vorlage, versionId: version.id };
|
||
}
|
||
|
||
return {
|
||
techniker,
|
||
admin: { id: "admin", rolle: "ADMIN" as const },
|
||
meine: await vorlageMit("Geheimprojekt Meiner", meiner.id),
|
||
fremde: await vorlageMit("Geheimprojekt Fremder", fremder.id),
|
||
globale: await vorlageMit("Geheimprojekt Global", null),
|
||
eigene,
|
||
};
|
||
}
|
||
|
||
describe.runIf(datenbankVerfuegbar)("Mandantensicht auf Vorlagen", () => {
|
||
beforeEach(async () => {
|
||
await datenbankLeeren();
|
||
process.env["OIDC_CUSTOMER_GROUP_PREFIX"] = "lz-kunde-";
|
||
});
|
||
|
||
describe("Liste", () => {
|
||
it("zeigt einem gebundenen Techniker nur seine und die globalen Vorlagen", async () => {
|
||
const { techniker } = await aufbau();
|
||
const namen = (await vorlagenListe(techniker)).map((z) => z.name).sort();
|
||
expect(namen).toEqual(["Geheimprojekt Global", "Geheimprojekt Meiner"]);
|
||
});
|
||
|
||
it("lässt die Mandantengrenze auch beim Suchen stehen", async () => {
|
||
// Genau der Blocker: der Suchfilter brachte ein zweites `OR` mit und
|
||
// überschrieb die Kundenbedingung — ein Buchstabe im Suchfeld genügte.
|
||
const { techniker } = await aufbau();
|
||
const namen = (await vorlagenListe(techniker, { suche: "Geheim" })).map((z) => z.name).sort();
|
||
expect(namen).toEqual(["Geheimprojekt Global", "Geheimprojekt Meiner"]);
|
||
expect(namen).not.toContain("Geheimprojekt Fremder");
|
||
});
|
||
|
||
it("lässt sie auch bei Kategorie- und Schlagwortfilter stehen", async () => {
|
||
const { techniker } = await aufbau();
|
||
for (const filter of [{ kategorie: "Geheim" }, { schlagwort: "intern" }]) {
|
||
const namen = (await vorlagenListe(techniker, filter)).map((z) => z.name);
|
||
expect(namen, JSON.stringify(filter)).not.toContain("Geheimprojekt Fremder");
|
||
}
|
||
});
|
||
|
||
it("lässt sie auch bei allen Filtern gleichzeitig stehen", async () => {
|
||
const { techniker } = await aufbau();
|
||
const namen = (
|
||
await vorlagenListe(techniker, {
|
||
suche: "Geheim",
|
||
kategorie: "Geheim",
|
||
schlagwort: "intern",
|
||
auchArchivierte: true,
|
||
})
|
||
).map((z) => z.name);
|
||
expect(namen).not.toContain("Geheimprojekt Fremder");
|
||
});
|
||
|
||
it("zeigt einem Administrator alles", async () => {
|
||
const { admin } = await aufbau();
|
||
expect(await vorlagenListe(admin, { suche: "Geheim" })).toHaveLength(3);
|
||
});
|
||
|
||
it("blendet fremde Kategorien und Schlagworte aus den Filterwerten aus", async () => {
|
||
const { techniker, fremde } = await aufbau();
|
||
await testPrisma.vorlage.update({
|
||
where: { id: fremde.vorlage.id },
|
||
data: { kategorie: "Nur-fuer-Fremde", schlagworte: ["geheimwort"] },
|
||
});
|
||
const werte = await vorlagenFilterwerte(techniker);
|
||
expect(werte.kategorien).not.toContain("Nur-fuer-Fremde");
|
||
expect(werte.schlagworte).not.toContain("geheimwort");
|
||
});
|
||
});
|
||
|
||
describe("Historie und Aufbau", () => {
|
||
it("zeigt die Historie einer fremden Vorlage nicht", async () => {
|
||
const { techniker, fremde, meine } = await aufbau();
|
||
expect(await versionsHistorie(fremde.vorlage.id, techniker)).toEqual([]);
|
||
expect(await versionsHistorie(meine.vorlage.id, techniker)).toHaveLength(1);
|
||
});
|
||
|
||
it("gibt den Aufbau einer fremden Version nicht heraus", async () => {
|
||
// Sonst stünden Abschnittstitel, Schritte und Befehle eines fremden Kunden
|
||
// offen — die Vorlage ist der innere Ablauf seines Betriebs.
|
||
const { techniker, fremde, meine, globale } = await aufbau();
|
||
expect(await versionMitAufbau(fremde.versionId, techniker)).toBeUndefined();
|
||
expect(await versionMitAufbau(meine.versionId, techniker)).toBeDefined();
|
||
expect(await versionMitAufbau(globale.versionId, techniker)).toBeDefined();
|
||
});
|
||
|
||
it("gibt einem Administrator alles heraus", async () => {
|
||
const { admin, fremde } = await aufbau();
|
||
expect(await versionMitAufbau(fremde.versionId, admin)).toBeDefined();
|
||
});
|
||
|
||
it("schränkt ohne gesetztes Präfix niemanden ein", async () => {
|
||
delete process.env["OIDC_CUSTOMER_GROUP_PREFIX"];
|
||
const { techniker, fremde } = await aufbau();
|
||
expect(await versionMitAufbau(fremde.versionId, techniker)).toBeDefined();
|
||
expect(await vorlagenListe(techniker)).toHaveLength(3);
|
||
});
|
||
});
|
||
|
||
describe("Einzelne Vorlage", () => {
|
||
it("gibt eine fremde Vorlage nicht heraus", async () => {
|
||
// Die Vorlagenseite haengt allein an dieser Funktion. Gaebe sie die Zeile
|
||
// heraus, staenden Name, Beschreibung und Versionsgeschichte offen – die
|
||
// Kennung steht in jedem Link, den jemand weitergibt.
|
||
const { techniker, fremde, meine, globale } = await aufbau();
|
||
expect(await vorlageHolen(fremde.vorlage.id, techniker)).toBeUndefined();
|
||
expect(await vorlageHolen(meine.vorlage.id, techniker)).toBeDefined();
|
||
expect(await vorlageHolen(globale.vorlage.id, techniker)).toBeDefined();
|
||
});
|
||
|
||
it("gibt einem Administrator auch eine fremde heraus", async () => {
|
||
const { admin, fremde } = await aufbau();
|
||
expect(await vorlageHolen(fremde.vorlage.id, admin)).toBeDefined();
|
||
});
|
||
|
||
it("liefert dieselben Felder wie die Liste", async () => {
|
||
// Zwei Lesemodelle fuer dieselbe Zeile laufen auseinander; deshalb bauen
|
||
// beide aus derselben Auswahl. Der Test haelt das fest.
|
||
const { techniker, meine } = await aufbau();
|
||
const ausListe = (await vorlagenListe(techniker, { auchArchivierte: true })).find(
|
||
(zeile) => zeile.id === meine.vorlage.id,
|
||
);
|
||
expect(await vorlageHolen(meine.vorlage.id, techniker)).toEqual(ausListe);
|
||
});
|
||
|
||
it("findet auch eine archivierte Vorlage", async () => {
|
||
// Die Seite muss sie zeigen koennen, sonst liesse sich nichts freigeben.
|
||
const { techniker, meine } = await aufbau();
|
||
await testPrisma.vorlage.update({
|
||
where: { id: meine.vorlage.id },
|
||
data: { archiviert: true },
|
||
});
|
||
const zeile = await vorlageHolen(meine.vorlage.id, techniker);
|
||
expect(zeile?.archiviert).toBe(true);
|
||
});
|
||
});
|
||
});
|