fix: restore permissions export compatibility and add regressions

This commit is contained in:
2026-02-27 14:54:26 -06:00
parent 5a6970a4c5
commit cb8c6b3958
8 changed files with 617 additions and 10 deletions
@@ -0,0 +1,301 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
const { mockApi } = vi.hoisted(() => ({
mockApi: {
get: vi.fn(),
post: vi.fn(),
patch: vi.fn(),
put: vi.fn(),
delete: vi.fn(),
},
}));
vi.mock("../axios", () => ({
default: mockApi,
api: mockApi,
}));
import { company } from "./companies";
import { credential } from "./credentials";
import { credentialType } from "./credentialTypes";
import { permission } from "./permissions";
import { procurement } from "./procurement";
import { role } from "./roles";
import { sales } from "./sales";
import { unifi } from "./unifi";
import { users } from "./users";
describe("optima api modules", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("company.fetchMany sends search params", async () => {
mockApi.get.mockResolvedValueOnce({ data: { data: [] } });
await company.fetchMany("token", 2, "acme", 50);
expect(mockApi.get).toHaveBeenCalledWith("/v1/company/companies", {
params: { page: 2, rpp: 50, search: "acme" },
headers: { Authorization: "Bearer token" },
});
});
it("company.count returns count", async () => {
mockApi.get.mockResolvedValueOnce({ data: { data: { count: 17 } } });
const count = await company.count("token");
expect(count).toBe(17);
});
it("credential.fetch and delete call expected routes", async () => {
mockApi.get.mockResolvedValueOnce({ data: { data: { id: "cred-1" } } });
mockApi.delete.mockResolvedValueOnce({ data: { ok: true } });
await credential.fetch("token", "cred-1");
await credential.delete("token", "cred-1");
expect(mockApi.get).toHaveBeenCalledWith(
"/v1/credential/credentials/cred-1",
{
headers: { Authorization: "Bearer token" },
},
);
expect(mockApi.delete).toHaveBeenCalledWith(
"/v1/credential/credentials/cred-1",
{
headers: { Authorization: "Bearer token" },
},
);
});
it("credential.create posts payload", async () => {
const payload = {
name: "VPN",
notes: "notes",
typeId: "type-1",
companyId: "company-1",
fields: [],
};
mockApi.post.mockResolvedValueOnce({ data: { data: payload } });
await credential.create("token", payload as any);
expect(mockApi.post).toHaveBeenCalledWith(
"/v1/credential/credentials",
payload,
{ headers: { Authorization: "Bearer token" } },
);
});
it("credential.updateFields wraps fields payload", async () => {
const fields = [{ id: "f1", name: "Username" }];
mockApi.put.mockResolvedValueOnce({ data: { ok: true } });
await credential.updateFields("token", "cred-1", fields as any);
expect(mockApi.put).toHaveBeenCalledWith(
"/v1/credential/credentials/cred-1/fields",
{ fields },
{ headers: { Authorization: "Bearer token" } },
);
});
it("credential sub-credential methods call expected endpoints", async () => {
mockApi.get.mockResolvedValueOnce({ data: { data: [] } });
mockApi.post.mockResolvedValueOnce({ data: { ok: true } });
mockApi.delete.mockResolvedValueOnce({ data: { ok: true } });
await credential.fetchSubCredentials("token", "cred-1");
await credential.addSubCredential("token", "cred-1", {
fieldId: "fid",
name: "Sub",
fields: [{ fieldId: "f2", value: "v" }],
});
await credential.removeSubCredential("token", "cred-1", "sub-1");
expect(mockApi.get).toHaveBeenCalledWith(
"/v1/credential/credentials/cred-1/sub-credentials",
{ headers: { Authorization: "Bearer token" } },
);
expect(mockApi.post).toHaveBeenCalledWith(
"/v1/credential/credentials/cred-1/sub-credentials",
{ fieldId: "fid", name: "Sub", fields: [{ fieldId: "f2", value: "v" }] },
{ headers: { Authorization: "Bearer token" } },
);
expect(mockApi.delete).toHaveBeenCalledWith(
"/v1/credential/credentials/cred-1/sub-credentials/sub-1",
{ headers: { Authorization: "Bearer token" } },
);
});
it("credentialType create and delete use identifier path", async () => {
mockApi.post.mockResolvedValueOnce({ data: { ok: true } });
mockApi.delete.mockResolvedValueOnce({ data: { ok: true } });
await credentialType.create("token", {
name: "Router",
permissionScope: "router",
fields: [],
} as any);
await credentialType.delete("token", "ctype-1");
expect(mockApi.post).toHaveBeenCalledWith(
"/v1/credential-type",
{ name: "Router", permissionScope: "router", fields: [] },
{ headers: { Authorization: "Bearer token" } },
);
expect(mockApi.delete).toHaveBeenCalledWith("/v1/credential-type/ctype-1", {
headers: { Authorization: "Bearer token" },
});
});
it("permission module endpoints are correct", async () => {
mockApi.get.mockResolvedValue({ data: { data: [] } });
await permission.fetchCategorized("token");
await permission.fetchFlat("token");
await permission.fetchByCategory("token", "company");
expect(mockApi.get).toHaveBeenNthCalledWith(1, "/v1/permissions", {
headers: { Authorization: "Bearer token" },
});
expect(mockApi.get).toHaveBeenNthCalledWith(2, "/v1/permissions/nodes", {
headers: { Authorization: "Bearer token" },
});
expect(mockApi.get).toHaveBeenNthCalledWith(3, "/v1/permissions/company", {
headers: { Authorization: "Bearer token" },
});
});
it("procurement methods build params and count correctly", async () => {
mockApi.get
.mockResolvedValueOnce({ data: { data: [] } })
.mockResolvedValueOnce({ data: { data: { count: 4 } } });
await procurement.fetchMany("token", 3, "switch", 10, true);
const count = await procurement.count("token", true);
expect(mockApi.get).toHaveBeenNthCalledWith(1, "/v1/procurement/items", {
params: { page: 3, rpp: 10, search: "switch", includeInactive: true },
headers: { Authorization: "Bearer token" },
});
expect(mockApi.get).toHaveBeenNthCalledWith(2, "/v1/procurement/count", {
params: { activeOnly: "true" },
headers: { Authorization: "Bearer token" },
});
expect(count).toBe(4);
});
it("procurement link and unlink post target id", async () => {
mockApi.post.mockResolvedValue({ data: { ok: true } });
await procurement.linkItem("token", "item-a", "item-b");
await procurement.unlinkItem("token", "item-a", "item-b");
expect(mockApi.post).toHaveBeenNthCalledWith(
1,
"/v1/procurement/items/item-a/link",
{ targetId: "item-b" },
{ headers: { Authorization: "Bearer token" } },
);
expect(mockApi.post).toHaveBeenNthCalledWith(
2,
"/v1/procurement/items/item-a/unlink",
{ targetId: "item-b" },
{ headers: { Authorization: "Bearer token" } },
);
});
it("role add and remove permissions include payload", async () => {
mockApi.post.mockResolvedValueOnce({ data: { ok: true } });
mockApi.delete.mockResolvedValueOnce({ data: { ok: true } });
await role.addPermissions("token", "role-1", ["a", "b"]);
await role.removePermissions("token", "role-1", ["a"]);
expect(mockApi.post).toHaveBeenCalledWith(
"/v1/role/role-1/permissions",
{ permissions: ["a", "b"] },
{ headers: { Authorization: "Bearer token" } },
);
expect(mockApi.delete).toHaveBeenCalledWith("/v1/role/role-1/permissions", {
headers: { Authorization: "Bearer token" },
data: { permissions: ["a"] },
});
});
it("sales.fetchMany includes includeClosed and search params", async () => {
mockApi.get.mockResolvedValueOnce({ data: { data: [] } });
await sales.fetchMany("token", 1, "fiber", 25, true);
expect(mockApi.get).toHaveBeenCalledWith("/v1/sales/opportunities", {
params: { page: 1, rpp: 25, search: "fiber", includeClosed: true },
headers: { Authorization: "Bearer token" },
});
});
it("users module uses expected endpoints", async () => {
mockApi.get.mockResolvedValue({ data: { data: [] } });
mockApi.post.mockResolvedValueOnce({ data: { data: { results: [] } } });
mockApi.patch.mockResolvedValueOnce({ data: { data: {} } });
mockApi.delete.mockResolvedValueOnce({ data: { data: {} } });
await users.fetchAll("token");
await users.fetch("token", "user-1");
await users.fetchRoles("token", "user-1");
await users.checkPermissions("token", "user-1", ["x"]);
await users.update("token", "user-1", { name: "New Name" });
await users.delete("token", "user-1");
expect(mockApi.get).toHaveBeenCalledWith("/v1/user/users", {
headers: { Authorization: "Bearer token" },
});
expect(mockApi.get).toHaveBeenCalledWith("/v1/user/users/user-1", {
headers: { Authorization: "Bearer token" },
});
expect(mockApi.get).toHaveBeenCalledWith("/v1/user/users/user-1/roles", {
headers: { Authorization: "Bearer token" },
});
expect(mockApi.post).toHaveBeenCalledWith(
"/v1/user/users/user-1/check-permission",
{ permissions: ["x"] },
{ headers: { Authorization: "Bearer token" } },
);
});
it("unifi module hits expected endpoints for site and wifi actions", async () => {
mockApi.get.mockResolvedValue({ data: { data: [] } });
mockApi.post.mockResolvedValue({ data: { ok: true } });
mockApi.patch.mockResolvedValue({ data: { ok: true } });
await unifi.fetchSites("token");
await unifi.syncSites("token");
await unifi.createSite("token", "HQ");
await unifi.linkSite("token", "site-1", "company-1");
await unifi.unlinkSite("token", "site-1");
await unifi.fetchSiteWifi("token", "site-1");
await unifi.updateWifi("token", "site-1", "wlan-1", { name: "New" });
await unifi.fetchPPSKs("token", "site-1", "wlan-1");
await unifi.createPPSK("token", "site-1", "wlan-1", {
key: "abc",
name: "Staff",
});
expect(mockApi.get).toHaveBeenCalledWith("/v1/unifi/sites", {
headers: { Authorization: "Bearer token" },
});
expect(mockApi.post).toHaveBeenCalledWith(
"/v1/unifi/sites/sync",
{},
{ headers: { Authorization: "Bearer token" } },
);
expect(mockApi.patch).toHaveBeenCalledWith(
"/v1/unifi/site/site-1/wifi/wlan-1",
{ name: "New" },
{ headers: { Authorization: "Bearer token" } },
);
});
});