import { describe, test, expect, mock, beforeEach } from "bun:test"; import { buildMockCWActivity } from "../setup"; // --------------------------------------------------------------------------- // Stable mock factory (same pattern as generatedQuotesManager.test.ts) // --------------------------------------------------------------------------- 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("activities manager", () => { beforeEach(() => { mock.restore(); }); // ------------------------------------------------------------------- // fetchItem // ------------------------------------------------------------------- describe("fetchItem()", () => { test("returns an ActivityController on success", async () => { const cwData = buildMockCWActivity(); mock.module("../../src/modules/cw-utils/activities/activities", () => ({ activityCw: { fetch: mock(() => Promise.resolve(cwData)), fetchByCompany: mock(() => Promise.resolve([])), fetchByOpportunity: mock(() => Promise.resolve([])), delete: mock(() => Promise.resolve()), countItems: mock(() => Promise.resolve(0)), update: mock(() => Promise.resolve(cwData)), }, })); mock.module("../../src/constants", () => ({ prisma: createStablePrismaMock(), connectWiseApi: { get: mock(() => Promise.resolve({ data: {} })), post: mock(() => Promise.resolve({ data: {} })), patch: mock(() => Promise.resolve({ data: {} })), delete: mock(() => Promise.resolve({ data: {} })), }, })); const { activities } = await import("../../src/managers/activities"); const result = await activities.fetchItem(5001); expect(result).toBeDefined(); expect(result.cwActivityId).toBe(5001); }); test("throws GenericError on failure", async () => { mock.module("../../src/modules/cw-utils/activities/activities", () => ({ activityCw: { fetch: mock(() => Promise.reject(new Error("CW API down"))), }, })); mock.module("../../src/constants", () => ({ prisma: createStablePrismaMock(), connectWiseApi: { get: mock(() => Promise.resolve({ data: {} })), }, })); const { activities } = await import("../../src/managers/activities"); try { await activities.fetchItem(9999); expect(true).toBe(false); } catch (e: any) { expect(e.name).toBe("FetchActivityError"); expect(e.status).toBeDefined(); } }); }); // ------------------------------------------------------------------- // fetchPages // ------------------------------------------------------------------- describe("fetchPages()", () => { test("returns array of ActivityControllers", async () => { const cwData = [buildMockCWActivity(), buildMockCWActivity({ id: 5002 })]; mock.module("../../src/constants", () => ({ prisma: createStablePrismaMock(), connectWiseApi: { get: mock(() => Promise.resolve({ data: cwData })), post: mock(() => Promise.resolve({ data: {} })), }, })); mock.module("../../src/modules/cw-utils/activities/activities", () => ({ activityCw: {}, })); const { activities } = await import("../../src/managers/activities"); const result = await activities.fetchPages(1, 10); expect(result).toBeArrayOfSize(2); }); test("clamps page to minimum 1", async () => { const getMock = mock(() => Promise.resolve({ data: [] })); mock.module("../../src/constants", () => ({ prisma: createStablePrismaMock(), connectWiseApi: { get: getMock }, })); mock.module("../../src/modules/cw-utils/activities/activities", () => ({ activityCw: {}, })); const { activities } = await import("../../src/managers/activities"); await activities.fetchPages(-5, 10); const url = getMock.mock.calls[0]?.[0] as string; expect(url).toContain("page=1"); }); }); // ------------------------------------------------------------------- // fetchByCompany // ------------------------------------------------------------------- describe("fetchByCompany()", () => { test("returns ActivityControllers for a company", async () => { const items = [buildMockCWActivity()]; mock.module("../../src/modules/cw-utils/activities/activities", () => ({ activityCw: { fetchByCompany: mock(() => Promise.resolve(items)), }, })); mock.module("../../src/constants", () => ({ prisma: createStablePrismaMock(), connectWiseApi: { get: mock(() => Promise.resolve({ data: {} })), }, })); const { activities } = await import("../../src/managers/activities"); const result = await activities.fetchByCompany(123); expect(result).toBeArrayOfSize(1); }); }); // ------------------------------------------------------------------- // fetchByOpportunity // ------------------------------------------------------------------- describe("fetchByOpportunity()", () => { test("returns ActivityControllers for an opportunity", async () => { const items = [buildMockCWActivity()]; mock.module("../../src/modules/cw-utils/activities/activities", () => ({ activityCw: { fetchByOpportunity: mock(() => Promise.resolve(items)), }, })); mock.module("../../src/constants", () => ({ prisma: createStablePrismaMock(), connectWiseApi: { get: mock(() => Promise.resolve({ data: {} })), }, })); const { activities } = await import("../../src/managers/activities"); const result = await activities.fetchByOpportunity(1001); expect(result).toBeArrayOfSize(1); }); }); // ------------------------------------------------------------------- // delete // ------------------------------------------------------------------- describe("delete()", () => { test("delegates to activityCw.delete", async () => { const deleteMock = mock(() => Promise.resolve()); mock.module("../../src/modules/cw-utils/activities/activities", () => ({ activityCw: { delete: deleteMock }, })); mock.module("../../src/constants", () => ({ prisma: createStablePrismaMock(), connectWiseApi: { get: mock(() => Promise.resolve({ data: {} })), }, })); const { activities } = await import("../../src/managers/activities"); await activities.delete(5001); expect(deleteMock).toHaveBeenCalledWith(5001); }); test("throws GenericError on failure", async () => { mock.module("../../src/modules/cw-utils/activities/activities", () => ({ activityCw: { delete: mock(() => Promise.reject(new Error("fail"))), }, })); mock.module("../../src/constants", () => ({ prisma: createStablePrismaMock(), connectWiseApi: { get: mock(() => Promise.resolve({ data: {} })), }, })); const { activities } = await import("../../src/managers/activities"); try { await activities.delete(9999); expect(true).toBe(false); } catch (e: any) { expect(e.name).toBe("DeleteActivityError"); } }); }); // ------------------------------------------------------------------- // count // ------------------------------------------------------------------- describe("count()", () => { test("returns count from activityCw", async () => { mock.module("../../src/modules/cw-utils/activities/activities", () => ({ activityCw: { countItems: mock(() => Promise.resolve(42)), }, })); mock.module("../../src/constants", () => ({ prisma: createStablePrismaMock(), connectWiseApi: { get: mock(() => Promise.resolve({ data: {} })), }, })); const { activities } = await import("../../src/managers/activities"); const result = await activities.count(); expect(result).toBe(42); }); }); });