import { describe, test, expect } from "bun:test"; import { OpportunityController } from "../../../src/controllers/OpportunityController"; import { ActivityController } from "../../../src/controllers/ActivityController"; import { CompanyController } from "../../../src/controllers/CompanyController"; import { buildMockOpportunity, buildMockCompany, buildMockCWActivity, } from "../../setup"; describe("OpportunityController", () => { // ------------------------------------------------------------------- // Constructor // ------------------------------------------------------------------- describe("constructor", () => { test("sets core identification fields", () => { const ctrl = new OpportunityController(buildMockOpportunity()); expect(ctrl.id).toBe("opp-1"); expect(ctrl.cwOpportunityId).toBe(1001); expect(ctrl.name).toBe("Test Opportunity"); expect(ctrl.notes).toBe("Some notes"); }); test("sets type, stage, status references", () => { const ctrl = new OpportunityController(buildMockOpportunity()); expect(ctrl.typeName).toBe("New Business"); expect(ctrl.typeCwId).toBe(1); expect(ctrl.stageName).toBe("Proposal"); expect(ctrl.stageCwId).toBe(2); expect(ctrl.statusName).toBe("Active"); expect(ctrl.statusCwId).toBe(3); }); test("sets priority, rating, source", () => { const ctrl = new OpportunityController(buildMockOpportunity()); expect(ctrl.priorityName).toBe("High"); expect(ctrl.priorityCwId).toBe(4); expect(ctrl.ratingName).toBe("Hot"); expect(ctrl.ratingCwId).toBe(5); expect(ctrl.source).toBe("Referral"); }); test("sets sales rep fields", () => { const ctrl = new OpportunityController(buildMockOpportunity()); expect(ctrl.primarySalesRepName).toBe("John"); expect(ctrl.primarySalesRepIdentifier).toBe("jroberts"); expect(ctrl.primarySalesRepCwId).toBe(10); expect(ctrl.secondarySalesRepName).toBeNull(); }); test("sets company/contact/site fields", () => { const ctrl = new OpportunityController(buildMockOpportunity()); expect(ctrl.companyCwId).toBe(123); expect(ctrl.companyName).toBe("Test Company"); expect(ctrl.contactCwId).toBe(200); expect(ctrl.contactName).toBe("Jane Doe"); expect(ctrl.siteCwId).toBe(300); expect(ctrl.siteName).toBe("Main Office"); }); test("sets financial and location fields", () => { const ctrl = new OpportunityController(buildMockOpportunity()); expect(ctrl.totalSalesTax).toBe(50.0); expect(ctrl.customerPO).toBe("PO-12345"); expect(ctrl.locationName).toBe("HQ"); expect(ctrl.departmentName).toBe("Sales"); }); test("sets date fields", () => { const ctrl = new OpportunityController(buildMockOpportunity()); expect(ctrl.expectedCloseDate).toBeInstanceOf(Date); expect(ctrl.pipelineChangeDate).toBeInstanceOf(Date); expect(ctrl.dateBecameLead).toBeInstanceOf(Date); expect(ctrl.closedDate).toBeNull(); expect(ctrl.closedFlag).toBe(false); }); test("accepts company controller via opts", () => { const company = new CompanyController(buildMockCompany()); const ctrl = new OpportunityController(buildMockOpportunity(), { company, }); const json = ctrl.toJson(); // Company should be a full object, not just {id, name} expect(json.company.id).toBe("company-1"); expect(json.company.name).toBe("Test Company"); }); test("accepts activities via opts", () => { const activities = [new ActivityController(buildMockCWActivity())]; const ctrl = new OpportunityController(buildMockOpportunity(), { activities, }); const json = ctrl.toJson(); expect(json.activities).toHaveLength(1); expect(json.activities[0].cwActivityId).toBe(5001); }); test("accepts customFields via opts", () => { const customFields = [ { id: 1, caption: "Custom1", type: "Text", entryMethod: "EntryField", numberOfDecimals: 0, value: "test", }, ]; const ctrl = new OpportunityController(buildMockOpportunity(), { customFields, }); const json = ctrl.toJson(); expect(json.customFields).toHaveLength(1); }); test("has empty activities/customFields without opts", () => { const ctrl = new OpportunityController(buildMockOpportunity()); const json = ctrl.toJson(); expect(json.activities).toEqual([]); expect(json.customFields).toEqual([]); }); }); // ------------------------------------------------------------------- // mapCwToDb (static) // ------------------------------------------------------------------- describe("mapCwToDb()", () => { const cwOpportunity = { id: 1001, name: "CW Opp", notes: "CW notes", type: { id: 1, name: "New Business" }, stage: { id: 2, name: "Proposal" }, status: { id: 3, name: "Active" }, priority: { id: 4, name: "High" }, rating: null, source: "Web", campaign: null, primarySalesRep: { id: 10, identifier: "jroberts", name: "John" }, secondarySalesRep: null, company: { id: 123, identifier: "TestCo", name: "Test Co" }, contact: { id: 200, name: "Jane" }, site: { id: 300, name: "Main" }, customerPO: "PO-1", totalSalesTax: 25.5, location: { id: 400, name: "HQ" }, department: { id: 500, name: "Sales" }, expectedCloseDate: "2026-04-01T00:00:00Z", pipelineChangeDate: "2026-02-15T00:00:00Z", dateBecameLead: "2026-01-01T00:00:00Z", closedDate: null, closedFlag: false, closedBy: null, customFields: [], _info: { lastUpdated: "2026-02-28T12:00:00Z" }, } as any; test("maps name and notes", () => { const result = OpportunityController.mapCwToDb(cwOpportunity); expect(result.name).toBe("CW Opp"); expect(result.notes).toBe("CW notes"); }); test("maps type, stage, status references", () => { const result = OpportunityController.mapCwToDb(cwOpportunity); expect(result.typeId).toBe(1); expect(result.stageId).toBe(2); expect(result.statusId).toBe(3); }); test("maps null references to null", () => { const result = OpportunityController.mapCwToDb(cwOpportunity); expect(result.ratingName).toBeNull(); expect(result.ratingCwId).toBeNull(); expect(result.campaignName).toBeNull(); }); test("maps sales rep fields", () => { const result = OpportunityController.mapCwToDb(cwOpportunity); expect(result.primarySalesRepName).toBe("John"); expect(result.primarySalesRepIdentifier).toBe("jroberts"); expect(result.secondarySalesRepName).toBeNull(); }); test("maps dates to Date objects", () => { const result = OpportunityController.mapCwToDb(cwOpportunity); expect(result.expectedCloseDate).toBeInstanceOf(Date); expect(result.closedDate).toBeNull(); }); test("maps closedFlag", () => { const result = OpportunityController.mapCwToDb(cwOpportunity); expect(result.closedFlag).toBe(false); }); test("maps cwLastUpdated from _info", () => { const result = OpportunityController.mapCwToDb(cwOpportunity); expect(result.cwLastUpdated).toBeInstanceOf(Date); }); }); // ------------------------------------------------------------------- // toJson // ------------------------------------------------------------------- describe("toJson()", () => { test("returns core fields", () => { const ctrl = new OpportunityController(buildMockOpportunity()); const json = ctrl.toJson(); expect(json.id).toBe("opp-1"); expect(json.cwOpportunityId).toBe(1001); expect(json.name).toBe("Test Opportunity"); }); test("formats type as reference object", () => { const ctrl = new OpportunityController(buildMockOpportunity()); const json = ctrl.toJson(); expect(json.type).toEqual({ id: 1, name: "New Business" }); }); test("formats stage as reference object", () => { const ctrl = new OpportunityController(buildMockOpportunity()); const json = ctrl.toJson(); expect(json.stage).toEqual({ id: 2, name: "Proposal" }); }); test("formats status as reference object", () => { const ctrl = new OpportunityController(buildMockOpportunity()); const json = ctrl.toJson(); expect(json.status).toEqual({ id: 3, name: "Active" }); }); test("formats primarySalesRep with identifier", () => { const ctrl = new OpportunityController(buildMockOpportunity()); const json = ctrl.toJson(); expect(json.primarySalesRep).toEqual({ id: 10, identifier: "jroberts", name: "John", }); }); test("secondarySalesRep is null when not set", () => { const ctrl = new OpportunityController(buildMockOpportunity()); const json = ctrl.toJson(); expect(json.secondarySalesRep).toBeNull(); }); test("contact formats as reference object", () => { const ctrl = new OpportunityController(buildMockOpportunity()); const json = ctrl.toJson(); expect(json.contact).toEqual({ id: 200, name: "Jane Doe" }); }); test("company falls back to CW reference when no controller", () => { const ctrl = new OpportunityController(buildMockOpportunity()); const json = ctrl.toJson(); expect(json.company).toEqual({ id: 123, name: "Test Company" }); }); test("includes financial data", () => { const ctrl = new OpportunityController(buildMockOpportunity()); const json = ctrl.toJson(); expect(json.totalSalesTax).toBe(50.0); expect(json.customerPO).toBe("PO-12345"); }); test("includes dates", () => { const ctrl = new OpportunityController(buildMockOpportunity()); const json = ctrl.toJson(); expect(json.expectedCloseDate).toBeInstanceOf(Date); expect(json.closedFlag).toBe(false); }); test("includes timestamps", () => { const ctrl = new OpportunityController(buildMockOpportunity()); const json = ctrl.toJson(); expect(json.createdAt).toBeInstanceOf(Date); expect(json.updatedAt).toBeInstanceOf(Date); }); }); });