import { describe, it, expect } from "vitest"; import { diffGraph, normalizeGraph, validateGraph, type GraphNode, } from "@/src/modules/marketing/lib/graph"; import { EMAIL_TEMPLATES } from "@/src/modules/marketing/lib/node-config"; const ids = { birthday: "11111111-1111-4111-8111-111111111111", milestone: "22222222-2222-4222-8222-222222222222", mail: "33333333-3333-4333-8333-333333333333", mail2: "44444444-4444-4444-8444-444444444444", }; const mail = ( id = ids.mail, config: Record = { ...EMAIL_TEMPLATES.birthday } ): GraphNode => ({ id, kind: "email", config, position: { x: 300, y: 0 }, emailNodeId: null, isActive: false, }); const birthday = (overrides: Partial = {}): GraphNode => ({ id: ids.birthday, kind: "birthday", config: {}, position: { x: 0, y: 0 }, emailNodeId: ids.mail, isActive: true, ...overrides, }); describe("validateGraph", () => { it("akzeptiert einen verbundenen Trigger mit gültiger E-Mail", () => { expect(validateGraph([birthday(), mail()])).toBeNull(); }); it("erlaubt Geburtstag nur einmal", () => { const second = birthday({ id: ids.milestone }); expect(validateGraph([birthday(), second, mail()])?.message).toMatch(/nur einmal/); }); it("lehnt Verbindungen zu fehlenden E-Mails ab", () => { expect(validateGraph([birthday({ emailNodeId: ids.mail2 }), mail()])?.message).toMatch( /nicht gefunden/ ); }); it("prüft die Werte der Trigger", () => { const milestone: GraphNode = { ...birthday({ id: ids.milestone }), kind: "milestone", config: { bookings: 0 }, }; expect(validateGraph([milestone, mail()])?.nodeId).toBe(ids.milestone); }); it("verlangt einen Rabatt für {code}", () => { const invalid = mail(ids.mail, { ...EMAIL_TEMPLATES.birthday, reward: null, body: "Ihr Code: {code}", }); expect(validateGraph([invalid])?.message).toMatch(/\{code\}/); }); it("lehnt Bausteine ohne gültige ID ab", () => { expect(validateGraph([mail("abc")])?.message).toMatch(/Ungültig/); }); }); describe("diffGraph", () => { it("protokolliert Anlegen, Verbinden und Einschalten", () => { const actions = diffGraph([], [birthday(), mail()]).map((c) => c.action); expect(actions).toEqual( expect.arrayContaining(["node.created", "trigger.connected", "trigger.activated"]) ); }); it("ignoriert reines Verschieben", () => { const before = [birthday(), mail()]; const after = [birthday({ position: { x: 99, y: 99 } }), mail()]; expect(diffGraph(before, after)).toEqual([]); }); it("nennt geänderte Felder einer E-Mail", () => { const before = [mail()]; const after = [mail(ids.mail, { ...EMAIL_TEMPLATES.birthday, subject: "Neu" })]; const [change] = diffGraph(before, after); expect(change.action).toBe("node.updated"); expect(change.summary).toContain("Betreff"); }); it("protokolliert Ausschalten und Löschen", () => { const before = [birthday(), mail(), mail(ids.mail2)]; const after = [birthday({ isActive: false }), mail()]; const actions = diffGraph(before, after).map((c) => c.action); expect(actions).toEqual(expect.arrayContaining(["node.deleted", "trigger.deactivated"])); }); }); describe("normalizeGraph", () => { it("speichert nur geprüfte Werte", () => { const [trigger, email] = normalizeGraph([ birthday({ config: { fremd: "x" } }), { ...mail(ids.mail, { ...EMAIL_TEMPLATES.birthday, subject: " Hallo ", extra: 1 }), isActive: true, }, ]); expect(trigger.config).toEqual({}); expect(email.config).not.toHaveProperty("extra"); expect(email.config.subject).toBe("Hallo"); expect(email.isActive).toBe(false); }); });