feat(vorlagen): Versionierung, Unveraenderlichkeit und JSON-Austausch (#23)
This commit was merged in pull request #23.
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import { Betriebsmodus } from "@/generated/prisma/enums";
|
||||
import {
|
||||
versionMitAufbau,
|
||||
versionsHistorie,
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user