import { describe, test, expect } from "vitest"; import { isSellableStatus, type ApiCoreProduct, type ApiCoreHarvestWithApiaryName } from "./apicore-client"; import { slugForApiCoreSource, buildHarvestDescription, buildProductDescription, deriveWeightGrams, } from "./apicore-mapping"; function makeProduct(overrides: Partial = {}): ApiCoreProduct { return { id: "cm-product-1", name: "Sommertracht 500 g", description: null, sku: null, packagingSize: null, weight: null, salesUnit: null, status: "ACTIVE", ...overrides, }; } function makeHarvest( overrides: Partial = {} ): ApiCoreHarvestWithApiaryName { return { id: "cm-harvest-1", apiaryId: "cm-apiary-1", date: "2026-07-14T00:00:00.000Z", honeyType: "Lindenhonig", honeySuperCount: null, grossWeight: null, netWeight: null, notes: null, apiaryName: "Streuobstwiese", ...overrides, }; } describe("slugForApiCoreSource", () => { test("leitet den Slug ausschliesslich aus der ApiCore-ID ab", () => { // Arrange const sourceId = "cm-product-1"; // Act const slug = slugForApiCoreSource(sourceId); // Assert expect(slug).toBe("apicore-cm-product-1"); }); test("bleibt stabil, wenn sich in ApiCore nur der Name aendert", () => { // Arrange: derselbe Datensatz, in ApiCore umbenannt. const before = makeProduct({ name: "Sommertracht 500 g" }); const after = makeProduct({ name: "Sommertracht cremig, 500 g" }); // Act & Assert: sonst wuerde der Sync ein zweites Produkt anlegen. expect(slugForApiCoreSource(after.id)).toBe(slugForApiCoreSource(before.id)); }); }); describe("buildHarvestDescription", () => { test("fasst die vorhandenen Mengenangaben und Notizen zusammen", () => { // Arrange const source = makeHarvest({ honeySuperCount: 12, grossWeight: 48.5, netWeight: 41, notes: " Sehr hell. " }); // Act const description = buildHarvestDescription(source); // Assert expect(description).toBe( "Automatisch aus ApiCore importiert. 12 Honigräume · Bruttogewicht 48.5 kg · Nettogewicht 41 kg · Sehr hell." ); }); test("fordert eine Beschreibung an, wenn ApiCore gar nichts liefert", () => { // Arrange const source = makeHarvest(); // Act const description = buildHarvestDescription(source); // Assert expect(description).toContain("Bitte Beschreibung ergänzen."); }); }); describe("buildProductDescription", () => { test("uebernimmt ApiCores Beschreibung und nennt Artikelnummer und Verkaufseinheit", () => { // Arrange const source = makeProduct({ description: "Cremig gerührt.", sku: "WH-500", salesUnit: "Glas" }); // Act const description = buildProductDescription(source); // Assert expect(description).toBe( "Automatisch aus ApiCore importiert (Artikelnummer WH-500 · Verkaufseinheit Glas). Cremig gerührt." ); }); test("setzt eine Aufforderung ein, statt einen leeren Verkaufstext zu erzeugen", () => { // Arrange: ApiCores description ist eine interne Notiz und oft leer. const source = makeProduct({ description: " " }); // Act const description = buildProductDescription(source); // Assert expect(description).toContain("Bitte Produktbeschreibung für den Shop ergänzen."); }); }); describe("deriveWeightGrams", () => { test("nimmt das Gewicht des Artikels", () => { // Arrange const source = makeProduct({ weight: 500, packagingSize: 250 }); // Act & Assert: weight schlaegt packagingSize. expect(deriveWeightGrams(source)).toBe(500); }); test("faellt auf die Gebindegroesse zurueck, wenn kein Gewicht gepflegt ist", () => { // Arrange const source = makeProduct({ weight: null, packagingSize: 250 }); // Act & Assert expect(deriveWeightGrams(source)).toBe(250); }); test("rundet auf volle Gramm", () => { // Arrange const source = makeProduct({ weight: 499.6 }); // Act & Assert expect(deriveWeightGrams(source)).toBe(500); }); test("gibt 0 zurueck statt zu raten, wenn ApiCore keine Menge kennt", () => { // Arrange const source = makeProduct(); // Act & Assert: 0 ist in Wabenhain kein speicherbares Gewicht und zwingt // damit zur manuellen Pflege, bevor das Produkt verkauft werden kann. expect(deriveWeightGrams(source)).toBe(0); }); test("behandelt 0 und negative Werte wie eine fehlende Angabe", () => { // Arrange const zero = makeProduct({ weight: 0 }); const negative = makeProduct({ weight: -500 }); // Act & Assert expect(deriveWeightGrams(zero)).toBe(0); expect(deriveWeightGrams(negative)).toBe(0); }); }); describe("isSellableStatus", () => { test("erkennt ACTIVE als verkaufsfaehig", () => { expect(isSellableStatus("ACTIVE")).toBe(true); }); test("behandelt DISCONTINUED und unbekannte Status als nicht verkaufsfaehig", () => { // Arrange & Act & Assert: ein kuenftiger, hier noch unbekannter // ApiCore-Status darf nicht versehentlich als Shop-Entwurf durchrutschen. expect(isSellableStatus("DISCONTINUED")).toBe(false); expect(isSellableStatus("SEASONAL")).toBe(false); expect(isSellableStatus("")).toBe(false); }); });