feat: add opportunity workflows, delete routes, company sites, algorithms, and expanded test coverage

This commit is contained in:
2026-03-09 02:56:08 -05:00
parent c0a4d4f919
commit f53b390e18
50 changed files with 8837 additions and 63 deletions
+182
View File
@@ -0,0 +1,182 @@
import { describe, test, expect, mock, beforeEach } from "bun:test";
// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------
describe("cw.opportunityService", () => {
beforeEach(() => {
mock.restore();
});
// -------------------------------------------------------------------
// submitTimeEntry
// -------------------------------------------------------------------
describe("submitTimeEntry()", () => {
test("submits time entry and returns success", async () => {
const postMock = mock(() => Promise.resolve({ data: { id: 9001 } }));
mock.module("../../src/constants", () => ({
connectWiseApi: { post: postMock },
prisma: new Proxy(
{},
{
get: () => mock(() => Promise.resolve(null)),
},
),
}));
mock.module(
"../../src/modules/cw-utils/opportunities/opportunities",
() => ({
opportunityCw: {
update: mock(() => Promise.resolve({})),
},
}),
);
const { submitTimeEntry } =
await import("../../src/services/cw.opportunityService");
const result = await submitTimeEntry({
activityId: 100,
cwMemberId: 10,
timeStart: "2026-03-01T09:00:00.000Z",
timeEnd: "2026-03-01T10:00:00.000Z",
notes: "Design review",
});
expect(result.success).toBe(true);
expect(result.cwTimeEntryId).toBe(9001);
expect(result.message).toContain("9001");
});
test("strips milliseconds from ISO timestamps", async () => {
const postMock = mock(() => Promise.resolve({ data: { id: 9001 } }));
mock.module("../../src/constants", () => ({
connectWiseApi: { post: postMock },
prisma: new Proxy(
{},
{
get: () => mock(() => Promise.resolve(null)),
},
),
}));
mock.module(
"../../src/modules/cw-utils/opportunities/opportunities",
() => ({
opportunityCw: { update: mock(() => Promise.resolve({})) },
}),
);
const { submitTimeEntry } =
await import("../../src/services/cw.opportunityService");
await submitTimeEntry({
activityId: 100,
cwMemberId: 10,
timeStart: "2026-03-01T09:00:00.123Z",
timeEnd: "2026-03-01T10:00:00.456Z",
notes: "test",
});
const body = postMock.mock.calls[0]?.[1];
expect(body.timeStart).toBe("2026-03-01T09:00:00Z");
expect(body.timeEnd).toBe("2026-03-01T10:00:00Z");
});
test("returns failure on API error", async () => {
mock.module("../../src/constants", () => ({
connectWiseApi: {
post: mock(() => Promise.reject(new Error("CW down"))),
},
prisma: new Proxy(
{},
{
get: () => mock(() => Promise.resolve(null)),
},
),
}));
mock.module(
"../../src/modules/cw-utils/opportunities/opportunities",
() => ({
opportunityCw: { update: mock(() => Promise.resolve({})) },
}),
);
const { submitTimeEntry } =
await import("../../src/services/cw.opportunityService");
const result = await submitTimeEntry({
activityId: 100,
cwMemberId: 10,
timeStart: "2026-03-01T09:00:00Z",
timeEnd: "2026-03-01T10:00:00Z",
notes: "test",
});
expect(result.success).toBe(false);
expect(result.cwTimeEntryId).toBeNull();
expect(result.message).toContain("Failed");
});
});
// -------------------------------------------------------------------
// syncOpportunityStatus
// -------------------------------------------------------------------
describe("syncOpportunityStatus()", () => {
test("syncs status to CW and returns success", async () => {
const updateMock = mock(() => Promise.resolve({}));
mock.module("../../src/constants", () => ({
connectWiseApi: { post: mock(() => Promise.resolve({ data: {} })) },
prisma: new Proxy(
{},
{
get: () => mock(() => Promise.resolve(null)),
},
),
}));
mock.module(
"../../src/modules/cw-utils/opportunities/opportunities",
() => ({
opportunityCw: { update: updateMock },
}),
);
const { syncOpportunityStatus } =
await import("../../src/services/cw.opportunityService");
const result = await syncOpportunityStatus({
opportunityId: 1001,
statusCwId: 24,
});
expect(result.success).toBe(true);
expect(result.message).toContain("1001");
});
test("returns failure on API error", async () => {
mock.module("../../src/constants", () => ({
connectWiseApi: { post: mock(() => Promise.resolve({ data: {} })) },
prisma: new Proxy(
{},
{
get: () => mock(() => Promise.resolve(null)),
},
),
}));
mock.module(
"../../src/modules/cw-utils/opportunities/opportunities",
() => ({
opportunityCw: {
update: mock(() => Promise.reject(new Error("API fail"))),
},
}),
);
const { syncOpportunityStatus } =
await import("../../src/services/cw.opportunityService");
const result = await syncOpportunityStatus({
opportunityId: 1001,
statusCwId: 24,
});
expect(result.success).toBe(false);
expect(result.message).toContain("Failed");
});
});
});