import { describe, test, expect, mock, beforeEach } from "bun:test"; import { buildMockCatalogItem, buildMockConstants } from "../setup"; // --------------------------------------------------------------------------- // Stable mock factory // --------------------------------------------------------------------------- function createStablePrismaMock( overrides: Record> = {} ) { return new Proxy( {}, { get(_target, model: string) { if (model === "$connect" || model === "$disconnect") return mock(() => Promise.resolve()); if (overrides[model]) return overrides[model]; return new Proxy({}, { get: () => mock(() => Promise.resolve(null)) }); }, } ); } // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- describe("procurement manager", () => { beforeEach(() => { mock.restore(); }); // ------------------------------------------------------------------- // fetchItem // ------------------------------------------------------------------- describe("fetchItem()", () => { test("returns CatalogItemController by internal ID", async () => { const mockData = { ...buildMockCatalogItem(), linkedItems: [] }; mock.module("../../src/constants", () => buildMockConstants({ prisma: createStablePrismaMock({ catalogItem: { findFirst: mock(() => Promise.resolve(mockData)), }, }), }) ); const { procurement } = await import("../../src/managers/procurement"); const result = await procurement.fetchItem("cat-1"); expect(result).toBeDefined(); expect(result.id).toBe("cat-1"); }); test("looks up by cwCatalogId for numeric identifiers", async () => { const mockData = { ...buildMockCatalogItem(), linkedItems: [] }; const findFirst = mock(() => Promise.resolve(mockData)); mock.module("../../src/constants", () => buildMockConstants({ prisma: createStablePrismaMock({ catalogItem: { findFirst }, }), }) ); const { procurement } = await import("../../src/managers/procurement"); await procurement.fetchItem(500); const where = findFirst.mock.calls[0]?.[0]?.where; expect(where).toHaveProperty("cwCatalogId", 500); }); test("throws 404 when not found", async () => { mock.module("../../src/constants", () => buildMockConstants({ prisma: createStablePrismaMock({ catalogItem: { findFirst: mock(() => Promise.resolve(null)), }, }), }) ); const { procurement } = await import("../../src/managers/procurement"); try { await procurement.fetchItem("nonexistent"); expect(true).toBe(false); } catch (e: any) { expect(e.name).toBe("CatalogItemNotFound"); expect(e.status).toBe(404); } }); }); // ------------------------------------------------------------------- // fetchPages // ------------------------------------------------------------------- describe("fetchPages()", () => { test("returns paginated catalog items", async () => { const items = [ { ...buildMockCatalogItem(), linkedItems: [] }, { ...buildMockCatalogItem({ id: "cat-2" }), linkedItems: [] }, ]; mock.module("../../src/constants", () => buildMockConstants({ prisma: createStablePrismaMock({ catalogItem: { findMany: mock(() => Promise.resolve(items)), findFirst: mock(() => Promise.resolve(null)), }, }), }) ); const { procurement } = await import("../../src/managers/procurement"); const result = await procurement.fetchPages(1, 10); expect(result).toBeArrayOfSize(2); }); test("clamps page to minimum 1", async () => { const findMany = mock(() => Promise.resolve([])); mock.module("../../src/constants", () => buildMockConstants({ prisma: createStablePrismaMock({ catalogItem: { findMany, findFirst: mock(() => Promise.resolve(null)), }, }), }) ); const { procurement } = await import("../../src/managers/procurement"); await procurement.fetchPages(0, 10); const opts = findMany.mock.calls[0]?.[0]; expect(opts.skip).toBe(0); // (max(0,1)-1) * 10 = 0 }); test("falls back to safe pagination when page/rpp are invalid", async () => { const findMany = mock(() => Promise.resolve([])); mock.module("../../src/constants", () => buildMockConstants({ prisma: createStablePrismaMock({ catalogItem: { findMany, findFirst: mock(() => Promise.resolve(null)), }, }), }) ); const { procurement } = await import("../../src/managers/procurement"); await procurement.fetchPages(Number.NaN, Number.NaN); const opts = findMany.mock.calls[0]?.[0]; expect(opts.skip).toBe(0); expect(opts.take).toBe(30); }); test("uses relational manufacturer filter when manufacturer option is provided", async () => { const findMany = mock(() => Promise.resolve([])); mock.module("../../src/constants", () => buildMockConstants({ prisma: createStablePrismaMock({ catalogItem: { findMany, findFirst: mock(() => Promise.resolve(null)), }, }), }) ); const { procurement } = await import("../../src/managers/procurement"); await procurement.fetchPages(1, 20, { manufacturer: "Ubiquiti" }); const where = findMany.mock.calls[0]?.[0]?.where; expect(JSON.stringify(where)).toContain( '"manufacturer":{"is":{"name":{"contains":"Ubiquiti"' ); }); }); // ------------------------------------------------------------------- // search // ------------------------------------------------------------------- describe("search()", () => { test("returns matching catalog items", async () => { const items = [{ ...buildMockCatalogItem(), linkedItems: [] }]; mock.module("../../src/constants", () => buildMockConstants({ prisma: createStablePrismaMock({ catalogItem: { findMany: mock(() => Promise.resolve(items)), findFirst: mock(() => Promise.resolve(null)), }, }), }) ); const { procurement } = await import("../../src/managers/procurement"); const result = await procurement.search("switch", 1, 10); expect(result).toBeArrayOfSize(1); }); test("ignores invalid numeric filters instead of passing NaN to Prisma", async () => { const findMany = mock(() => Promise.resolve([])); mock.module("../../src/constants", () => buildMockConstants({ prisma: createStablePrismaMock({ catalogItem: { findMany, findFirst: mock(() => Promise.resolve(null)), }, }), }) ); const { procurement } = await import("../../src/managers/procurement"); await procurement.search("switch", Number.NaN, Number.NaN, { minPrice: Number.NaN, maxPrice: Number.NaN, }); const where = findMany.mock.calls[0]?.[0]?.where; expect(where).toHaveProperty("OR"); expect(JSON.stringify(where)).not.toContain("NaN"); }); test("uses relational manufacturer search clause", async () => { const findMany = mock(() => Promise.resolve([])); mock.module("../../src/constants", () => buildMockConstants({ prisma: createStablePrismaMock({ catalogItem: { findMany, findFirst: mock(() => Promise.resolve(null)), }, }), }) ); const { procurement } = await import("../../src/managers/procurement"); await procurement.search("wir", 1, 20); const where = findMany.mock.calls[0]?.[0]?.where; expect(JSON.stringify(where)).toContain( '"manufacturer":{"is":{"name":{"contains":"wir"' ); }); }); // ------------------------------------------------------------------- // count // ------------------------------------------------------------------- describe("count()", () => { test("returns total count", async () => { mock.module("../../src/constants", () => buildMockConstants({ prisma: createStablePrismaMock({ catalogItem: { count: mock(() => Promise.resolve(50)), findFirst: mock(() => Promise.resolve(null)), }, }), }) ); const { procurement } = await import("../../src/managers/procurement"); const result = await procurement.count(); expect(result).toBe(50); }); }); // ------------------------------------------------------------------- // countSearch // ------------------------------------------------------------------- describe("countSearch()", () => { test("returns count of matching items", async () => { mock.module("../../src/constants", () => buildMockConstants({ prisma: createStablePrismaMock({ catalogItem: { count: mock(() => Promise.resolve(12)), findFirst: mock(() => Promise.resolve(null)), }, }), }) ); const { procurement } = await import("../../src/managers/procurement"); const result = await procurement.countSearch("switch"); expect(result).toBe(12); }); }); // ------------------------------------------------------------------- // fetchDistinctValues // ------------------------------------------------------------------- describe("fetchDistinctValues()", () => { test("returns sorted distinct category values", async () => { const items = [{ category: "Technology" }, { category: "Accessories" }]; mock.module("../../src/constants", () => buildMockConstants({ prisma: createStablePrismaMock({ catalogItem: { findMany: mock(() => Promise.resolve(items)), findFirst: mock(() => Promise.resolve(null)), }, }), }) ); const { procurement } = await import("../../src/managers/procurement"); const result = await procurement.fetchDistinctValues("category"); expect(result).toEqual(["Technology", "Accessories"]); }); test("filters out null values", async () => { const items = [{ manufacturer: "Ubiquiti" }, { manufacturer: null }]; mock.module("../../src/constants", () => buildMockConstants({ prisma: createStablePrismaMock({ catalogItem: { findMany: mock(() => Promise.resolve(items)), findFirst: mock(() => Promise.resolve(null)), }, }), }) ); const { procurement } = await import("../../src/managers/procurement"); const result = await procurement.fetchDistinctValues("manufacturer"); expect(result).toEqual(["Ubiquiti"]); }); }); // ------------------------------------------------------------------- // fetchLaborCatalogItems // ------------------------------------------------------------------- describe("fetchLaborCatalogItems()", () => { test("throws when labor items not found", async () => { mock.module("../../src/constants", () => buildMockConstants({ prisma: createStablePrismaMock({ catalogItem: { findFirst: mock(() => Promise.resolve(null)), }, }), }) ); const { procurement } = await import("../../src/managers/procurement"); try { await procurement.fetchLaborCatalogItems(); expect(true).toBe(false); } catch (e: any) { expect(e.name).toBe("LaborCatalogProductsNotFound"); expect(e.status).toBe(500); } }); }); });