mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-07 06:59:01 +00:00
195 lines
6.5 KiB
TypeScript
195 lines
6.5 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { API_PATHS } from "@/api/api-paths";
|
|
|
|
const apiGet = vi.fn();
|
|
const apiPost = vi.fn();
|
|
const apiPut = vi.fn();
|
|
const apiPatch = vi.fn();
|
|
|
|
vi.mock("@/api/api", () => ({
|
|
apiGet: (...args: unknown[]) => apiGet(...args),
|
|
apiPost: (...args: unknown[]) => apiPost(...args),
|
|
apiPut: (...args: unknown[]) => apiPut(...args),
|
|
apiPatch: (...args: unknown[]) => apiPatch(...args),
|
|
}));
|
|
|
|
vi.mock("ky", () => ({
|
|
isHTTPError: (error: unknown) =>
|
|
error !== null && typeof error === "object" && "__kyHTTPError" in error,
|
|
}));
|
|
|
|
import { vendorCompanyRosterApi } from "@/domain/vendors/api/vendor-company-roster-api";
|
|
import { isVendorRosterConflictError } from "@/domain/vendors/lib/vendor-roster-conflict";
|
|
|
|
function httpError(status: number, body: unknown): Error {
|
|
const error = new Error("HTTPError") as Error & {
|
|
__kyHTTPError: true;
|
|
response: { status: number; json: () => Promise<unknown>; text: () => Promise<string> };
|
|
};
|
|
error.__kyHTTPError = true;
|
|
error.response = {
|
|
status,
|
|
json: () => Promise.resolve(body),
|
|
text: () => Promise.resolve(JSON.stringify(body)),
|
|
};
|
|
return error;
|
|
}
|
|
|
|
describe("vendorCompanyRosterApi", () => {
|
|
beforeEach(() => {
|
|
apiGet.mockReset();
|
|
apiPost.mockReset();
|
|
apiPut.mockReset();
|
|
apiPatch.mockReset();
|
|
});
|
|
|
|
it("fetches by vendorId or companyId with the correct query params", async () => {
|
|
apiGet.mockResolvedValueOnce({ data: { companyId: 5, name: "Solo Co" } });
|
|
|
|
await vendorCompanyRosterApi.get({ vendorId: 7 });
|
|
|
|
expect(apiGet).toHaveBeenCalledWith(API_PATHS.vendorCompanyRoster.base, {
|
|
searchParams: { vendorId: "7" },
|
|
});
|
|
|
|
apiGet.mockResolvedValueOnce({ data: { companyId: 5, name: "Solo Co" } });
|
|
await vendorCompanyRosterApi.get({ companyId: 5 });
|
|
expect(apiGet).toHaveBeenLastCalledWith(API_PATHS.vendorCompanyRoster.base, {
|
|
searchParams: { companyId: "5" },
|
|
});
|
|
});
|
|
|
|
it("posts the canonicalized create payload", async () => {
|
|
apiPost.mockResolvedValueOnce({ data: { companyId: 9, name: "Solo Co" } });
|
|
|
|
await vendorCompanyRosterApi.create({
|
|
name: "Solo Co",
|
|
companyPhone: "314-555-0100",
|
|
technicians: [{ contactName: "Adam", phone: "3145550198", preferredContact: "Phone" }],
|
|
});
|
|
|
|
expect(apiPost).toHaveBeenCalledWith(
|
|
API_PATHS.vendorCompanyRoster.base,
|
|
expect.objectContaining({
|
|
name: "Solo Co",
|
|
companyPhone: "(314) 555-0100",
|
|
technicians: [expect.objectContaining({ phone: "(314) 555-0198" })],
|
|
}),
|
|
);
|
|
expect(apiPost.mock.calls[0]?.[1]).not.toHaveProperty("rowVersion");
|
|
});
|
|
|
|
it("puts the rowVersion on the update payload", async () => {
|
|
apiPut.mockResolvedValueOnce({ data: { companyId: 5, name: "Solo Co" } });
|
|
|
|
await vendorCompanyRosterApi.update(5, { name: "Solo Co" }, "rv-1");
|
|
|
|
expect(apiPut).toHaveBeenCalledWith(
|
|
API_PATHS.vendorCompanyRoster.byCompany(5),
|
|
expect.objectContaining({ name: "Solo Co", rowVersion: "rv-1" }),
|
|
);
|
|
});
|
|
|
|
it("throws an open-work-order conflict on a 409 with blocked work orders", async () => {
|
|
apiPut.mockRejectedValueOnce(
|
|
httpError(409, {
|
|
blockedWorkOrders: [
|
|
{ workOrderId: 501, workOrderNumber: "WO-501", workOrderTitle: "Boiler" },
|
|
],
|
|
}),
|
|
);
|
|
|
|
await expect(vendorCompanyRosterApi.update(5, { name: "Solo Co" }, "rv-1")).rejects.toSatisfy(
|
|
(error: unknown) => {
|
|
if (!isVendorRosterConflictError(error)) return false;
|
|
return error.conflict.kind === "open-work-orders";
|
|
},
|
|
);
|
|
});
|
|
|
|
it("throws a stale conflict on a 409 without blocked work orders", async () => {
|
|
apiPut.mockRejectedValueOnce(httpError(409, { message: "rowversion mismatch" }));
|
|
|
|
await expect(vendorCompanyRosterApi.update(5, { name: "Solo Co" }, "rv-1")).rejects.toSatisfy(
|
|
(error: unknown) => {
|
|
if (!isVendorRosterConflictError(error)) return false;
|
|
return error.conflict.kind === "stale";
|
|
},
|
|
);
|
|
});
|
|
|
|
it("rethrows non-conflict errors untouched", async () => {
|
|
const generic = new Error("boom");
|
|
apiPost.mockRejectedValueOnce(generic);
|
|
|
|
await expect(vendorCompanyRosterApi.create({ name: "Solo Co" })).rejects.toBe(generic);
|
|
});
|
|
|
|
it("patches the additive payload without a technician id and without PUT", async () => {
|
|
apiPatch.mockResolvedValueOnce({ data: { companyId: 5, name: "Solo Co" } });
|
|
|
|
await vendorCompanyRosterApi.addTechnicians(5, {
|
|
rowVersion: "rv-1",
|
|
addTechnicians: [
|
|
{
|
|
id: 99,
|
|
contactName: "Adam",
|
|
phone: "3145550198",
|
|
email: "",
|
|
preferredContact: "Phone",
|
|
tradeSpecialties: "Plumbing",
|
|
isActive: true,
|
|
},
|
|
],
|
|
});
|
|
|
|
expect(apiPatch).toHaveBeenCalledWith(
|
|
API_PATHS.vendorCompanyRoster.byCompany(5),
|
|
expect.objectContaining({
|
|
rowVersion: "rv-1",
|
|
addTechnicians: [expect.objectContaining({ contactName: "Adam", phone: "(314) 555-0198" })],
|
|
}),
|
|
);
|
|
const body = apiPatch.mock.calls[0]?.[1] as Record<string, unknown>;
|
|
expect(body).not.toHaveProperty("companyFields");
|
|
expect((body.addTechnicians as Array<Record<string, unknown>>)[0]).not.toHaveProperty("id");
|
|
expect(apiPut).not.toHaveBeenCalled();
|
|
expect(apiPost).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("includes companyFields on the additive patch when provided", async () => {
|
|
apiPatch.mockResolvedValueOnce({ data: { companyId: 5, name: "Solo Co" } });
|
|
|
|
await vendorCompanyRosterApi.addTechnicians(5, {
|
|
rowVersion: "rv-2",
|
|
addTechnicians: [
|
|
{ contactName: "Adam", phone: "", email: "", tradeSpecialties: "", isActive: true },
|
|
],
|
|
companyFields: { notes: "Updated notes" },
|
|
});
|
|
|
|
expect(apiPatch).toHaveBeenCalledWith(
|
|
API_PATHS.vendorCompanyRoster.byCompany(5),
|
|
expect.objectContaining({
|
|
rowVersion: "rv-2",
|
|
companyFields: { notes: "Updated notes" },
|
|
}),
|
|
);
|
|
});
|
|
|
|
it("throws a stale conflict on a 409 from the additive patch", async () => {
|
|
apiPatch.mockRejectedValueOnce(httpError(409, { message: "rowversion mismatch" }));
|
|
|
|
await expect(
|
|
vendorCompanyRosterApi.addTechnicians(5, {
|
|
rowVersion: "rv-1",
|
|
addTechnicians: [
|
|
{ contactName: "Adam", phone: "", email: "", tradeSpecialties: "", isActive: true },
|
|
],
|
|
}),
|
|
).rejects.toSatisfy((error: unknown) => {
|
|
if (!isVendorRosterConflictError(error)) return false;
|
|
return error.conflict.kind === "stale";
|
|
});
|
|
});
|
|
});
|