feat: add opportunity workflows, delete routes, company sites, algorithms, and expanded test coverage
This commit is contained in:
@@ -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");
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user