feat(#380): Workspace-to-Matrix-Room mapping and provisioning
Some checks failed
ci/woodpecker/push/api Pipeline failed
Some checks failed
ci/woodpecker/push/api Pipeline failed
- Add matrix_room_id column to workspace table (migration) - Create MatrixRoomService for room provisioning and mapping - Auto-create Matrix room on workspace provisioning (when configured) - Support manual room linking for existing workspaces - Unit tests for all mapping operations Refs #380
This commit is contained in:
186
apps/api/src/bridge/matrix/matrix-room.service.spec.ts
Normal file
186
apps/api/src/bridge/matrix/matrix-room.service.spec.ts
Normal file
@@ -0,0 +1,186 @@
|
||||
import { Test, TestingModule } from "@nestjs/testing";
|
||||
import { MatrixRoomService } from "./matrix-room.service";
|
||||
import { MatrixService } from "./matrix.service";
|
||||
import { PrismaService } from "../../prisma/prisma.service";
|
||||
import { vi, describe, it, expect, beforeEach } from "vitest";
|
||||
|
||||
// Mock matrix-bot-sdk to avoid native module import errors
|
||||
vi.mock("matrix-bot-sdk", () => {
|
||||
return {
|
||||
MatrixClient: class MockMatrixClient {},
|
||||
SimpleFsStorageProvider: class MockStorageProvider {
|
||||
constructor(_filename: string) {
|
||||
// No-op for testing
|
||||
}
|
||||
},
|
||||
AutojoinRoomsMixin: {
|
||||
setupOnClient: vi.fn(),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
describe("MatrixRoomService", () => {
|
||||
let service: MatrixRoomService;
|
||||
|
||||
const mockCreateRoom = vi.fn().mockResolvedValue("!new-room:example.com");
|
||||
|
||||
const mockMatrixService = {
|
||||
isConnected: vi.fn().mockReturnValue(true),
|
||||
// Private field accessed by MatrixRoomService.getMatrixClient()
|
||||
client: {
|
||||
createRoom: mockCreateRoom,
|
||||
},
|
||||
};
|
||||
|
||||
const mockPrismaService = {
|
||||
workspace: {
|
||||
findUnique: vi.fn(),
|
||||
update: vi.fn(),
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
process.env.MATRIX_SERVER_NAME = "example.com";
|
||||
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
MatrixRoomService,
|
||||
{
|
||||
provide: PrismaService,
|
||||
useValue: mockPrismaService,
|
||||
},
|
||||
{
|
||||
provide: MatrixService,
|
||||
useValue: mockMatrixService,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<MatrixRoomService>(MatrixRoomService);
|
||||
|
||||
vi.clearAllMocks();
|
||||
// Restore defaults after clearing
|
||||
mockMatrixService.isConnected.mockReturnValue(true);
|
||||
mockCreateRoom.mockResolvedValue("!new-room:example.com");
|
||||
mockPrismaService.workspace.update.mockResolvedValue({});
|
||||
});
|
||||
|
||||
describe("provisionRoom", () => {
|
||||
it("should create a Matrix room and store the mapping", async () => {
|
||||
const roomId = await service.provisionRoom(
|
||||
"workspace-uuid-1",
|
||||
"My Workspace",
|
||||
"my-workspace"
|
||||
);
|
||||
|
||||
expect(roomId).toBe("!new-room:example.com");
|
||||
|
||||
expect(mockCreateRoom).toHaveBeenCalledWith({
|
||||
name: "Mosaic: My Workspace",
|
||||
room_alias_name: "mosaic-my-workspace",
|
||||
topic: "Mosaic workspace: My Workspace",
|
||||
preset: "private_chat",
|
||||
visibility: "private",
|
||||
});
|
||||
|
||||
expect(mockPrismaService.workspace.update).toHaveBeenCalledWith({
|
||||
where: { id: "workspace-uuid-1" },
|
||||
data: { matrixRoomId: "!new-room:example.com" },
|
||||
});
|
||||
});
|
||||
|
||||
it("should return null when Matrix is not configured (no MatrixService)", async () => {
|
||||
// Create a service without MatrixService
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
MatrixRoomService,
|
||||
{
|
||||
provide: PrismaService,
|
||||
useValue: mockPrismaService,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
const serviceWithoutMatrix = module.get<MatrixRoomService>(MatrixRoomService);
|
||||
|
||||
const roomId = await serviceWithoutMatrix.provisionRoom(
|
||||
"workspace-uuid-1",
|
||||
"My Workspace",
|
||||
"my-workspace"
|
||||
);
|
||||
|
||||
expect(roomId).toBeNull();
|
||||
expect(mockCreateRoom).not.toHaveBeenCalled();
|
||||
expect(mockPrismaService.workspace.update).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should return null when Matrix is not connected", async () => {
|
||||
mockMatrixService.isConnected.mockReturnValue(false);
|
||||
|
||||
const roomId = await service.provisionRoom(
|
||||
"workspace-uuid-1",
|
||||
"My Workspace",
|
||||
"my-workspace"
|
||||
);
|
||||
|
||||
expect(roomId).toBeNull();
|
||||
expect(mockCreateRoom).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("getRoomForWorkspace", () => {
|
||||
it("should return the room ID for a mapped workspace", async () => {
|
||||
mockPrismaService.workspace.findUnique.mockResolvedValue({
|
||||
matrixRoomId: "!mapped-room:example.com",
|
||||
});
|
||||
|
||||
const roomId = await service.getRoomForWorkspace("workspace-uuid-1");
|
||||
|
||||
expect(roomId).toBe("!mapped-room:example.com");
|
||||
expect(mockPrismaService.workspace.findUnique).toHaveBeenCalledWith({
|
||||
where: { id: "workspace-uuid-1" },
|
||||
select: { matrixRoomId: true },
|
||||
});
|
||||
});
|
||||
|
||||
it("should return null for an unmapped workspace", async () => {
|
||||
mockPrismaService.workspace.findUnique.mockResolvedValue({
|
||||
matrixRoomId: null,
|
||||
});
|
||||
|
||||
const roomId = await service.getRoomForWorkspace("workspace-uuid-2");
|
||||
|
||||
expect(roomId).toBeNull();
|
||||
});
|
||||
|
||||
it("should return null for a non-existent workspace", async () => {
|
||||
mockPrismaService.workspace.findUnique.mockResolvedValue(null);
|
||||
|
||||
const roomId = await service.getRoomForWorkspace("non-existent-uuid");
|
||||
|
||||
expect(roomId).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("linkWorkspaceToRoom", () => {
|
||||
it("should store the room mapping in the workspace", async () => {
|
||||
await service.linkWorkspaceToRoom("workspace-uuid-1", "!existing-room:example.com");
|
||||
|
||||
expect(mockPrismaService.workspace.update).toHaveBeenCalledWith({
|
||||
where: { id: "workspace-uuid-1" },
|
||||
data: { matrixRoomId: "!existing-room:example.com" },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("unlinkWorkspace", () => {
|
||||
it("should remove the room mapping from the workspace", async () => {
|
||||
await service.unlinkWorkspace("workspace-uuid-1");
|
||||
|
||||
expect(mockPrismaService.workspace.update).toHaveBeenCalledWith({
|
||||
where: { id: "workspace-uuid-1" },
|
||||
data: { matrixRoomId: null },
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user