feat(api): add AdminModule with user and workspace management endpoints (#555)
Some checks failed
ci/woodpecker/push/api Pipeline failed
Some checks failed
ci/woodpecker/push/api Pipeline failed
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
This commit was merged in pull request #555.
This commit is contained in:
258
apps/api/src/admin/admin.controller.spec.ts
Normal file
258
apps/api/src/admin/admin.controller.spec.ts
Normal file
@@ -0,0 +1,258 @@
|
||||
import { describe, it, expect, beforeEach, vi } from "vitest";
|
||||
import { Test, TestingModule } from "@nestjs/testing";
|
||||
import { AdminController } from "./admin.controller";
|
||||
import { AdminService } from "./admin.service";
|
||||
import { AuthGuard } from "../auth/guards/auth.guard";
|
||||
import { AdminGuard } from "../auth/guards/admin.guard";
|
||||
import { WorkspaceMemberRole } from "@prisma/client";
|
||||
import type { ExecutionContext } from "@nestjs/common";
|
||||
|
||||
describe("AdminController", () => {
|
||||
let controller: AdminController;
|
||||
let service: AdminService;
|
||||
|
||||
const mockAdminService = {
|
||||
listUsers: vi.fn(),
|
||||
inviteUser: vi.fn(),
|
||||
updateUser: vi.fn(),
|
||||
deactivateUser: vi.fn(),
|
||||
createWorkspace: vi.fn(),
|
||||
updateWorkspace: vi.fn(),
|
||||
};
|
||||
|
||||
const mockAuthGuard = {
|
||||
canActivate: vi.fn((context: ExecutionContext) => {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
request.user = {
|
||||
id: "550e8400-e29b-41d4-a716-446655440001",
|
||||
email: "admin@example.com",
|
||||
name: "Admin User",
|
||||
};
|
||||
return true;
|
||||
}),
|
||||
};
|
||||
|
||||
const mockAdminGuard = {
|
||||
canActivate: vi.fn(() => true),
|
||||
};
|
||||
|
||||
const mockAdminId = "550e8400-e29b-41d4-a716-446655440001";
|
||||
const mockUserId = "550e8400-e29b-41d4-a716-446655440002";
|
||||
const mockWorkspaceId = "550e8400-e29b-41d4-a716-446655440003";
|
||||
|
||||
const mockAdminUser = {
|
||||
id: mockAdminId,
|
||||
email: "admin@example.com",
|
||||
name: "Admin User",
|
||||
};
|
||||
|
||||
const mockUserResponse = {
|
||||
id: mockUserId,
|
||||
name: "Test User",
|
||||
email: "test@example.com",
|
||||
emailVerified: false,
|
||||
image: null,
|
||||
createdAt: new Date("2026-01-01"),
|
||||
deactivatedAt: null,
|
||||
isLocalAuth: false,
|
||||
invitedAt: null,
|
||||
invitedBy: null,
|
||||
workspaceMemberships: [],
|
||||
};
|
||||
|
||||
const mockWorkspaceResponse = {
|
||||
id: mockWorkspaceId,
|
||||
name: "Test Workspace",
|
||||
ownerId: mockAdminId,
|
||||
settings: {},
|
||||
createdAt: new Date("2026-01-01"),
|
||||
updatedAt: new Date("2026-01-01"),
|
||||
memberCount: 1,
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [AdminController],
|
||||
providers: [
|
||||
{
|
||||
provide: AdminService,
|
||||
useValue: mockAdminService,
|
||||
},
|
||||
],
|
||||
})
|
||||
.overrideGuard(AuthGuard)
|
||||
.useValue(mockAuthGuard)
|
||||
.overrideGuard(AdminGuard)
|
||||
.useValue(mockAdminGuard)
|
||||
.compile();
|
||||
|
||||
controller = module.get<AdminController>(AdminController);
|
||||
service = module.get<AdminService>(AdminService);
|
||||
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("should be defined", () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
|
||||
describe("listUsers", () => {
|
||||
it("should return paginated users", async () => {
|
||||
const paginatedResult = {
|
||||
data: [mockUserResponse],
|
||||
meta: { total: 1, page: 1, limit: 50, totalPages: 1 },
|
||||
};
|
||||
mockAdminService.listUsers.mockResolvedValue(paginatedResult);
|
||||
|
||||
const result = await controller.listUsers({ page: 1, limit: 50 });
|
||||
|
||||
expect(result).toEqual(paginatedResult);
|
||||
expect(service.listUsers).toHaveBeenCalledWith(1, 50);
|
||||
});
|
||||
|
||||
it("should use default pagination", async () => {
|
||||
const paginatedResult = {
|
||||
data: [],
|
||||
meta: { total: 0, page: 1, limit: 50, totalPages: 0 },
|
||||
};
|
||||
mockAdminService.listUsers.mockResolvedValue(paginatedResult);
|
||||
|
||||
await controller.listUsers({});
|
||||
|
||||
expect(service.listUsers).toHaveBeenCalledWith(undefined, undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe("inviteUser", () => {
|
||||
it("should invite a user", async () => {
|
||||
const inviteDto = { email: "new@example.com" };
|
||||
const invitationResponse = {
|
||||
userId: "new-id",
|
||||
invitationToken: "token",
|
||||
email: "new@example.com",
|
||||
invitedAt: new Date(),
|
||||
};
|
||||
mockAdminService.inviteUser.mockResolvedValue(invitationResponse);
|
||||
|
||||
const result = await controller.inviteUser(inviteDto, mockAdminUser);
|
||||
|
||||
expect(result).toEqual(invitationResponse);
|
||||
expect(service.inviteUser).toHaveBeenCalledWith(inviteDto, mockAdminId);
|
||||
});
|
||||
|
||||
it("should invite a user with workspace and role", async () => {
|
||||
const inviteDto = {
|
||||
email: "new@example.com",
|
||||
workspaceId: mockWorkspaceId,
|
||||
role: WorkspaceMemberRole.ADMIN,
|
||||
};
|
||||
mockAdminService.inviteUser.mockResolvedValue({
|
||||
userId: "new-id",
|
||||
invitationToken: "token",
|
||||
email: "new@example.com",
|
||||
invitedAt: new Date(),
|
||||
});
|
||||
|
||||
await controller.inviteUser(inviteDto, mockAdminUser);
|
||||
|
||||
expect(service.inviteUser).toHaveBeenCalledWith(inviteDto, mockAdminId);
|
||||
});
|
||||
});
|
||||
|
||||
describe("updateUser", () => {
|
||||
it("should update a user", async () => {
|
||||
const updateDto = { name: "Updated Name" };
|
||||
mockAdminService.updateUser.mockResolvedValue({
|
||||
...mockUserResponse,
|
||||
name: "Updated Name",
|
||||
});
|
||||
|
||||
const result = await controller.updateUser(mockUserId, updateDto);
|
||||
|
||||
expect(result.name).toBe("Updated Name");
|
||||
expect(service.updateUser).toHaveBeenCalledWith(mockUserId, updateDto);
|
||||
});
|
||||
|
||||
it("should deactivate a user via update", async () => {
|
||||
const deactivatedAt = "2026-02-28T00:00:00.000Z";
|
||||
const updateDto = { deactivatedAt };
|
||||
mockAdminService.updateUser.mockResolvedValue({
|
||||
...mockUserResponse,
|
||||
deactivatedAt: new Date(deactivatedAt),
|
||||
});
|
||||
|
||||
const result = await controller.updateUser(mockUserId, updateDto);
|
||||
|
||||
expect(result.deactivatedAt).toEqual(new Date(deactivatedAt));
|
||||
});
|
||||
});
|
||||
|
||||
describe("deactivateUser", () => {
|
||||
it("should soft-delete a user", async () => {
|
||||
mockAdminService.deactivateUser.mockResolvedValue({
|
||||
...mockUserResponse,
|
||||
deactivatedAt: new Date(),
|
||||
});
|
||||
|
||||
const result = await controller.deactivateUser(mockUserId);
|
||||
|
||||
expect(result.deactivatedAt).toBeDefined();
|
||||
expect(service.deactivateUser).toHaveBeenCalledWith(mockUserId);
|
||||
});
|
||||
});
|
||||
|
||||
describe("createWorkspace", () => {
|
||||
it("should create a workspace", async () => {
|
||||
const createDto = { name: "New Workspace", ownerId: mockAdminId };
|
||||
mockAdminService.createWorkspace.mockResolvedValue(mockWorkspaceResponse);
|
||||
|
||||
const result = await controller.createWorkspace(createDto);
|
||||
|
||||
expect(result).toEqual(mockWorkspaceResponse);
|
||||
expect(service.createWorkspace).toHaveBeenCalledWith(createDto);
|
||||
});
|
||||
|
||||
it("should create workspace with settings", async () => {
|
||||
const createDto = {
|
||||
name: "New Workspace",
|
||||
ownerId: mockAdminId,
|
||||
settings: { feature: true },
|
||||
};
|
||||
mockAdminService.createWorkspace.mockResolvedValue({
|
||||
...mockWorkspaceResponse,
|
||||
settings: { feature: true },
|
||||
});
|
||||
|
||||
const result = await controller.createWorkspace(createDto);
|
||||
|
||||
expect(result.settings).toEqual({ feature: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe("updateWorkspace", () => {
|
||||
it("should update a workspace", async () => {
|
||||
const updateDto = { name: "Updated Workspace" };
|
||||
mockAdminService.updateWorkspace.mockResolvedValue({
|
||||
...mockWorkspaceResponse,
|
||||
name: "Updated Workspace",
|
||||
});
|
||||
|
||||
const result = await controller.updateWorkspace(mockWorkspaceId, updateDto);
|
||||
|
||||
expect(result.name).toBe("Updated Workspace");
|
||||
expect(service.updateWorkspace).toHaveBeenCalledWith(mockWorkspaceId, updateDto);
|
||||
});
|
||||
|
||||
it("should update workspace settings", async () => {
|
||||
const updateDto = { settings: { notifications: false } };
|
||||
mockAdminService.updateWorkspace.mockResolvedValue({
|
||||
...mockWorkspaceResponse,
|
||||
settings: { notifications: false },
|
||||
});
|
||||
|
||||
const result = await controller.updateWorkspace(mockWorkspaceId, updateDto);
|
||||
|
||||
expect(result.settings).toEqual({ notifications: false });
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user