diff --git a/apps/web/src/lib/api/admin.test.ts b/apps/web/src/lib/api/admin.test.ts new file mode 100644 index 0000000..3f16ad9 --- /dev/null +++ b/apps/web/src/lib/api/admin.test.ts @@ -0,0 +1,60 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import * as client from "./client"; +import { fetchAdminUsers, inviteUser, updateUser, deactivateUser } from "./admin"; + +vi.mock("./client"); + +beforeEach((): void => { + vi.clearAllMocks(); +}); + +describe("fetchAdminUsers", (): void => { + it("calls admin/users endpoint without params when none provided", async (): Promise => { + vi.mocked(client.apiGet).mockResolvedValueOnce({ data: [], meta: {} } as never); + await fetchAdminUsers(); + expect(client.apiGet).toHaveBeenCalledWith("/api/admin/users"); + }); + + it("appends page and limit params when provided", async (): Promise => { + vi.mocked(client.apiGet).mockResolvedValueOnce({ data: [], meta: {} } as never); + await fetchAdminUsers(2, 50); + expect(client.apiGet).toHaveBeenCalledWith("/api/admin/users?page=2&limit=50"); + }); + + it("throws on API error", async (): Promise => { + vi.mocked(client.apiGet).mockRejectedValueOnce(new Error("Network error")); + await expect(fetchAdminUsers()).rejects.toThrow("Network error"); + }); +}); + +describe("inviteUser", (): void => { + it("posts to invite endpoint", async (): Promise => { + vi.mocked(client.apiPost).mockResolvedValueOnce({ id: "inv-1" } as never); + await inviteUser({ + email: "a@b.com", + name: "Alice", + workspaceId: "ws-1", + role: "MEMBER" as never, + }); + expect(client.apiPost).toHaveBeenCalledWith( + "/api/admin/users/invite", + expect.objectContaining({ email: "a@b.com" }) + ); + }); +}); + +describe("updateUser", (): void => { + it("patches correct endpoint with dto", async (): Promise => { + vi.mocked(client.apiPatch).mockResolvedValueOnce({ id: "u1", name: "Bob" } as never); + await updateUser("u1", { name: "Bob" }); + expect(client.apiPatch).toHaveBeenCalledWith("/api/admin/users/u1", { name: "Bob" }); + }); +}); + +describe("deactivateUser", (): void => { + it("deletes correct endpoint", async (): Promise => { + vi.mocked(client.apiDelete).mockResolvedValueOnce({} as never); + await deactivateUser("u1"); + expect(client.apiDelete).toHaveBeenCalledWith("/api/admin/users/u1"); + }); +}); diff --git a/apps/web/src/lib/api/teams.test.ts b/apps/web/src/lib/api/teams.test.ts new file mode 100644 index 0000000..dff229a --- /dev/null +++ b/apps/web/src/lib/api/teams.test.ts @@ -0,0 +1,53 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import * as client from "./client"; +import { fetchTeams, createTeam, fetchTeamMembers } from "./teams"; + +vi.mock("./client"); + +const localStorageMock = { + getItem: vi.fn().mockReturnValue("ws-1"), + setItem: vi.fn(), + clear: vi.fn(), + removeItem: vi.fn(), + length: 0, + key: vi.fn(), +}; +Object.defineProperty(window, "localStorage", { value: localStorageMock }); + +beforeEach((): void => { + vi.clearAllMocks(); + localStorageMock.getItem.mockReturnValue("ws-1"); +}); + +describe("fetchTeams", (): void => { + it("calls teams endpoint for active workspace", async (): Promise => { + vi.mocked(client.apiGet).mockResolvedValueOnce([] as never); + await fetchTeams(); + expect(client.apiGet).toHaveBeenCalledWith("/api/workspaces/ws-1/teams", "ws-1"); + }); + + it("throws if no workspace id in localStorage", async (): Promise => { + localStorageMock.getItem.mockReturnValue(null); + await expect(fetchTeams()).rejects.toThrow(); + }); +}); + +describe("createTeam", (): void => { + it("posts to teams endpoint", async (): Promise => { + vi.mocked(client.apiPost).mockResolvedValueOnce({ id: "t1", name: "Dev" } as never); + await createTeam({ name: "Dev" }); + expect(client.apiPost).toHaveBeenCalledWith( + "/api/workspaces/ws-1/teams", + expect.objectContaining({ name: "Dev" }), + "ws-1" + ); + }); +}); + +describe("fetchTeamMembers", (): void => { + it("calls members endpoint for team", async (): Promise => { + vi.mocked(client.apiGet).mockResolvedValueOnce([] as never); + await fetchTeamMembers("t-1"); + expect(client.apiGet).toHaveBeenCalledWith("/api/workspaces/ws-1/teams/t-1/members", "ws-1"); + }); +}); diff --git a/apps/web/src/lib/api/workspaces.test.ts b/apps/web/src/lib/api/workspaces.test.ts new file mode 100644 index 0000000..5cae481 --- /dev/null +++ b/apps/web/src/lib/api/workspaces.test.ts @@ -0,0 +1,65 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import * as client from "./client"; +import { + fetchUserWorkspaces, + fetchWorkspaceMembers, + addWorkspaceMember, + updateWorkspaceMemberRole, + removeWorkspaceMember, +} from "./workspaces"; + +vi.mock("./client"); + +beforeEach((): void => { + vi.clearAllMocks(); +}); + +describe("fetchUserWorkspaces", (): void => { + it("calls correct endpoint", async (): Promise => { + vi.mocked(client.apiGet).mockResolvedValueOnce([] as never); + await fetchUserWorkspaces(); + expect(client.apiGet).toHaveBeenCalledWith("/api/workspaces"); + }); +}); + +describe("fetchWorkspaceMembers", (): void => { + it("calls correct endpoint with workspace id", async (): Promise => { + vi.mocked(client.apiGet).mockResolvedValueOnce([] as never); + await fetchWorkspaceMembers("ws-1"); + expect(client.apiGet).toHaveBeenCalledWith("/api/workspaces/ws-1/members"); + }); + + it("throws on error", async (): Promise => { + vi.mocked(client.apiGet).mockRejectedValueOnce(new Error("Forbidden")); + await expect(fetchWorkspaceMembers("ws-1")).rejects.toThrow("Forbidden"); + }); +}); + +describe("addWorkspaceMember", (): void => { + it("posts to correct endpoint", async (): Promise => { + vi.mocked(client.apiPost).mockResolvedValueOnce({} as never); + await addWorkspaceMember("ws-1", { userId: "u1", role: "MEMBER" as never }); + expect(client.apiPost).toHaveBeenCalledWith("/api/workspaces/ws-1/members", { + userId: "u1", + role: "MEMBER", + }); + }); +}); + +describe("updateWorkspaceMemberRole", (): void => { + it("patches correct endpoint", async (): Promise => { + vi.mocked(client.apiPatch).mockResolvedValueOnce({} as never); + await updateWorkspaceMemberRole("ws-1", "u1", { role: "ADMIN" as never }); + expect(client.apiPatch).toHaveBeenCalledWith("/api/workspaces/ws-1/members/u1", { + role: "ADMIN", + }); + }); +}); + +describe("removeWorkspaceMember", (): void => { + it("calls delete on correct endpoint", async (): Promise => { + vi.mocked(client.apiDelete).mockResolvedValueOnce(undefined as never); + await removeWorkspaceMember("ws-1", "u1"); + expect(client.apiDelete).toHaveBeenCalledWith("/api/workspaces/ws-1/members/u1"); + }); +});