Files
stack/apps/api/src/workspaces/workspaces.controller.spec.ts
Jason Woltje 8388d49786
Some checks are pending
ci/woodpecker/push/api Pipeline is running
feat(api): add workspace member management endpoints (#556)
Co-authored-by: Jason Woltje <jason@diversecanvas.com>
Co-committed-by: Jason Woltje <jason@diversecanvas.com>
2026-02-28 18:01:05 +00:00

150 lines
4.7 KiB
TypeScript

import { describe, it, expect, beforeEach, vi } from "vitest";
import { Test, TestingModule } from "@nestjs/testing";
import { WorkspacesController } from "./workspaces.controller";
import { WorkspacesService } from "./workspaces.service";
import { AuthGuard } from "../auth/guards/auth.guard";
import { WorkspaceGuard, PermissionGuard } from "../common/guards";
import { WorkspaceMemberRole } from "@prisma/client";
import type { AuthUser } from "@mosaic/shared";
describe("WorkspacesController", () => {
let controller: WorkspacesController;
let service: WorkspacesService;
const mockWorkspacesService = {
getUserWorkspaces: vi.fn(),
addMember: vi.fn(),
updateMemberRole: vi.fn(),
removeMember: vi.fn(),
};
const mockUser: AuthUser = {
id: "user-1",
email: "test@example.com",
name: "Test User",
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [WorkspacesController],
providers: [
{
provide: WorkspacesService,
useValue: mockWorkspacesService,
},
],
})
.overrideGuard(AuthGuard)
.useValue({ canActivate: () => true })
.overrideGuard(WorkspaceGuard)
.useValue({ canActivate: () => true })
.overrideGuard(PermissionGuard)
.useValue({ canActivate: () => true })
.compile();
controller = module.get<WorkspacesController>(WorkspacesController);
service = module.get<WorkspacesService>(WorkspacesService);
vi.clearAllMocks();
});
describe("GET /api/workspaces", () => {
it("should call service with authenticated user id", async () => {
mockWorkspacesService.getUserWorkspaces.mockResolvedValueOnce([]);
await controller.getUserWorkspaces(mockUser);
expect(service.getUserWorkspaces).toHaveBeenCalledWith("user-1");
});
it("should return workspace list from service", async () => {
const mockWorkspaces = [
{
id: "ws-1",
name: "My Workspace",
ownerId: "user-1",
role: WorkspaceMemberRole.OWNER,
createdAt: new Date("2026-01-01"),
},
];
mockWorkspacesService.getUserWorkspaces.mockResolvedValueOnce(mockWorkspaces);
const result = await controller.getUserWorkspaces(mockUser);
expect(result).toEqual(mockWorkspaces);
});
it("should propagate service errors", async () => {
mockWorkspacesService.getUserWorkspaces.mockRejectedValueOnce(new Error("Database error"));
await expect(controller.getUserWorkspaces(mockUser)).rejects.toThrow("Database error");
});
});
describe("POST /api/workspaces/:id/members", () => {
it("should call service with workspace id, actor id, and add member dto", async () => {
const workspaceId = "ws-1";
const addMemberDto = {
userId: "user-2",
role: WorkspaceMemberRole.MEMBER,
};
const mockMember = {
workspaceId,
userId: "user-2",
role: WorkspaceMemberRole.MEMBER,
joinedAt: new Date("2026-02-01"),
};
mockWorkspacesService.addMember.mockResolvedValueOnce(mockMember);
const result = await controller.addMember(workspaceId, addMemberDto, mockUser);
expect(result).toEqual(mockMember);
expect(service.addMember).toHaveBeenCalledWith(workspaceId, mockUser.id, addMemberDto);
});
});
describe("PATCH /api/workspaces/:id/members/:userId", () => {
it("should call service with workspace id, actor id, target user id, and role dto", async () => {
const workspaceId = "ws-1";
const targetUserId = "user-2";
const updateRoleDto = {
role: WorkspaceMemberRole.ADMIN,
};
const mockMember = {
workspaceId,
userId: targetUserId,
role: WorkspaceMemberRole.ADMIN,
joinedAt: new Date("2026-02-01"),
};
mockWorkspacesService.updateMemberRole.mockResolvedValueOnce(mockMember);
const result = await controller.updateMemberRole(
workspaceId,
targetUserId,
updateRoleDto,
mockUser
);
expect(result).toEqual(mockMember);
expect(service.updateMemberRole).toHaveBeenCalledWith(
workspaceId,
mockUser.id,
targetUserId,
updateRoleDto
);
});
});
describe("DELETE /api/workspaces/:id/members/:userId", () => {
it("should call service with workspace id, actor id, and target user id", async () => {
const workspaceId = "ws-1";
const targetUserId = "user-2";
mockWorkspacesService.removeMember.mockResolvedValueOnce(undefined);
await controller.removeMember(workspaceId, targetUserId, mockUser);
expect(service.removeMember).toHaveBeenCalledWith(workspaceId, mockUser.id, targetUserId);
});
});
});