import { beforeEach, describe, expect, it, vi } from "vitest"; const apiClient = { get: vi.fn(), post: vi.fn(), put: vi.fn(), delete: vi.fn(), }; const fetchAllCursorPages = vi.fn(async () => ({ items: [{ id: "tenant-1", name: "Tenant", slug: "tenant" }], total: 1, })); vi.mock("./apiClient", () => ({ default: apiClient, })); vi.mock("./auth", () => ({ userManager: { getUser: vi.fn(async () => ({ access_token: "access-token" })), }, })); vi.mock("../../../common/core/pagination", () => ({ fetchAllCursorPages, })); describe("orgfront adminApi user tenant payloads", () => { beforeEach(() => { apiClient.get.mockReset(); apiClient.post.mockReset(); apiClient.put.mockReset(); apiClient.delete.mockReset(); apiClient.get.mockResolvedValue({ data: { ok: true } }); apiClient.post.mockResolvedValue({ data: { ok: true } }); apiClient.put.mockResolvedValue({ data: { ok: true } }); apiClient.delete.mockResolvedValue({ data: { ok: true } }); fetchAllCursorPages.mockClear(); window.localStorage.clear(); }); it("routes read APIs to their documented orgfront admin endpoints", async () => { const adminApi = await import("./adminApi"); await adminApi.fetchAuditLogs(10, "cursor-a"); await adminApi.fetchTenants(25, 50, "parent-1", "cursor-b"); await adminApi.fetchAllTenants({ pageSize: 200, parentId: "parent-1" }); await adminApi.fetchTenant("tenant-1"); await adminApi.fetchTenantAdmins("tenant-1"); await adminApi.fetchTenantOwners("tenant-1"); await adminApi.fetchGroups("tenant-1"); await adminApi.fetchGroup("tenant-1", "group-1"); await adminApi.fetchImportProgress("tenant-1", "progress-1"); await adminApi.fetchGroupRoles("tenant-1", "group-1"); await adminApi.fetchApiKeys(20, 40); await adminApi.fetchUsers(30, 60, "admin", "tenant"); await adminApi.fetchUser("user-1"); await adminApi.fetchPasswordPolicy(); await adminApi.fetchUserRpHistory("user-1"); await adminApi.fetchMe(); await adminApi.fetchRelyingParties("tenant-1"); await adminApi.fetchAllRelyingParties(); await adminApi.fetchRelyingParty("client-1"); await adminApi.fetchRPOwners("client-1"); await adminApi.fetchPublicOrgChart("public-token"); await adminApi.fetchOrgChartSnapshot(); expect(apiClient.get).toHaveBeenCalledWith("/v1/audit", { params: { limit: 10, cursor: "cursor-a" }, }); expect(apiClient.get).toHaveBeenCalledWith("/v1/admin/tenants", { params: { limit: 25, offset: 50, parentId: "parent-1", cursor: "cursor-b", }, }); expect(fetchAllCursorPages).toHaveBeenCalledWith( expect.objectContaining({ path: "/v1/admin/tenants", pageSize: 200, params: { parentId: "parent-1" }, }), ); expect(apiClient.get).toHaveBeenCalledWith( "/v1/admin/tenants/tenant-1/organization/group-1/roles", ); expect(apiClient.get).toHaveBeenCalledWith("/v1/public/orgchart", { params: { token: "public-token" }, }); expect(apiClient.get).toHaveBeenCalledWith("/v1/admin/orgchart/snapshot", { params: { cache: "redis" }, }); }); it("routes mutation APIs to their documented orgfront admin endpoints", async () => { const adminApi = await import("./adminApi"); await adminApi.createTenant({ name: "Tenant", slug: "tenant" }); await adminApi.updateTenant("tenant-1", { status: "inactive" }); await adminApi.deleteTenant("tenant-1"); await adminApi.deleteTenantsBulk(["tenant-1"]); await adminApi.approveTenant("tenant-1"); await adminApi.addTenantAdmin("tenant-1", "user-1"); await adminApi.removeTenantAdmin("tenant-1", "user-1"); await adminApi.addTenantOwner("tenant-1", "user-1"); await adminApi.removeTenantOwner("tenant-1", "user-1"); await adminApi.createGroup("tenant-1", { name: "Group" }); await adminApi.deleteGroup("tenant-1", "group-1"); await adminApi.addGroupMember("tenant-1", "group-1", "user-1"); await adminApi.removeGroupMember("tenant-1", "group-1", "user-1"); await adminApi.importOrgChart( "tenant-1", new File(["name"], "org.csv"), "progress-1", ); await adminApi.assignGroupRole("tenant-1", "group-1", "tenant-2", "owner"); await adminApi.removeGroupRole("tenant-1", "group-1", "tenant-2", "owner"); await adminApi.createApiKey({ name: "key", scopes: ["read"] }); await adminApi.deleteApiKey("key-1"); await adminApi.createUser({ email: "user@example.com", name: "User" }); await adminApi.bulkCreateUsers([ { email: "user@example.com", name: "User", metadata: {} }, ]); await adminApi.bulkUpdateUsers({ userIds: ["user-1"], status: "inactive" }); await adminApi.bulkDeleteUsers(["user-1"]); await adminApi.updateUser("user-1", { status: "active" }); await adminApi.deleteUser("user-1"); await adminApi.createRelyingParty("tenant-1", { client_name: "RP", redirect_uris: ["https://rp.example/callback"], }); await adminApi.updateRelyingParty("client-1", { client_name: "RP", redirect_uris: ["https://rp.example/callback"], }); await adminApi.deleteRelyingParty("client-1"); await adminApi.addRPOwner("client-1", "User:user-1"); await adminApi.removeRPOwner("client-1", "User:user-1"); expect(apiClient.post).toHaveBeenCalledWith( "/v1/admin/tenants/tenant-1/organization/group-1/members", { userId: "user-1" }, ); expect(apiClient.post).toHaveBeenCalledWith( "/v1/admin/tenants/tenant-1/organization/import?progressId=progress-1", expect.any(FormData), { headers: { "Content-Type": "multipart/form-data" } }, ); expect(apiClient.put).toHaveBeenCalledWith("/v1/admin/users/user-1", { status: "active", }); expect(apiClient.delete).toHaveBeenCalledWith( "/v1/admin/relying-parties/client-1/owners/User:user-1", ); }); it("sends tenantSlug without remapping it to companyCode when creating a user", async () => { const { createUser } = await import("./adminApi"); await createUser({ email: "user@test.com", name: "Test User", tenantSlug: "test-tenant", }); expect(apiClient.post).toHaveBeenCalledWith( "/v1/admin/users", expect.objectContaining({ tenantSlug: "test-tenant" }), ); expect(apiClient.post.mock.calls[0][1]).not.toHaveProperty("companyCode"); }); it("sends tenantSlug without remapping it to companyCode when updating a user", async () => { const { updateUser } = await import("./adminApi"); await updateUser("user-id", { tenantSlug: "new-tenant" }); expect(apiClient.put).toHaveBeenCalledWith( "/v1/admin/users/user-id", expect.objectContaining({ tenantSlug: "new-tenant" }), ); expect(apiClient.put.mock.calls[0][1]).not.toHaveProperty("companyCode"); }); it("keeps tenantSlug payloads unchanged for bulk user APIs", async () => { const { bulkCreateUsers, bulkUpdateUsers } = await import("./adminApi"); await bulkCreateUsers([ { email: "user@test.com", name: "Test User", tenantSlug: "test-tenant", metadata: {}, }, ]); await bulkUpdateUsers({ userIds: ["user-id"], tenantSlug: "new-tenant", }); expect(apiClient.post.mock.calls[0][1].users[0]).toMatchObject({ tenantSlug: "test-tenant", }); expect(apiClient.post.mock.calls[0][1].users[0]).not.toHaveProperty( "companyCode", ); expect(apiClient.put.mock.calls[0][1]).toMatchObject({ tenantSlug: "new-tenant", }); expect(apiClient.put.mock.calls[0][1]).not.toHaveProperty("companyCode"); }); });