157 lines
5.0 KiB
TypeScript
157 lines
5.0 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const mocks = vi.hoisted(() => ({
|
|
tenantUserFindMany: vi.fn(),
|
|
groupFindMany: vi.fn(),
|
|
createGroupChat: vi.fn(),
|
|
getOrCreateDirectMessage: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("~/utils/db/.server/tenantRls.server", () => ({
|
|
withTenantRLS: vi.fn(async (_tenantId: string, operation: (tx: unknown) => Promise<unknown>) =>
|
|
operation({ tenantUser: { findMany: mocks.tenantUserFindMany }, group: { findMany: mocks.groupFindMany } })
|
|
),
|
|
}));
|
|
|
|
vi.mock("@hivegrid/chat/lifecycle.server", () => ({
|
|
createGroupChat: mocks.createGroupChat,
|
|
getOrCreateDirectMessage: mocks.getOrCreateDirectMessage,
|
|
}));
|
|
|
|
import { createChatChannel, loadChatChannelCreatePage } from "./ChatChannelCreateService.server";
|
|
|
|
const context = {
|
|
tenantId: "tenant_1",
|
|
userId: "user_current",
|
|
tenantUserId: "tenant_user_current",
|
|
permissions: ["chat.create_channel", "chat.create_dm"],
|
|
hasModule: true as const,
|
|
};
|
|
|
|
describe("AK-P15b chat channel create service", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mocks.tenantUserFindMany.mockResolvedValue([
|
|
{
|
|
userId: "user_current",
|
|
user: { email: "raffael@example.test", firstName: "Raffael", lastName: "Sheikh", avatar: null },
|
|
},
|
|
{
|
|
userId: "user_mara",
|
|
user: { email: "mara@example.test", firstName: "Mara", lastName: "Klein", avatar: " https://example.test/mara.png " },
|
|
},
|
|
]);
|
|
mocks.groupFindMany.mockResolvedValue([
|
|
{
|
|
id: "group_south",
|
|
name: "Team Süd",
|
|
description: "Montage",
|
|
users: [{ userId: "user_mara" }],
|
|
},
|
|
]);
|
|
mocks.createGroupChat.mockResolvedValue({ chatId: "chat_group" });
|
|
mocks.getOrCreateDirectMessage.mockResolvedValue({ chatId: "chat_dm", dmPairKey: "pair", created: true });
|
|
});
|
|
|
|
it("loads only active tenant users inside tenant RLS and excludes the viewer from options", async () => {
|
|
const vm = await loadChatChannelCreatePage({ context, requestedMode: "group" });
|
|
|
|
expect(mocks.tenantUserFindMany).toHaveBeenCalledWith({
|
|
where: {
|
|
tenantId: "tenant_1",
|
|
status: 2,
|
|
user: { active: true, pendingDeletion: false, pseudonymized: false },
|
|
},
|
|
select: {
|
|
userId: true,
|
|
user: { select: { email: true, firstName: true, lastName: true, avatar: true } },
|
|
},
|
|
orderBy: [{ user: { firstName: "asc" } }, { user: { lastName: "asc" } }, { user: { email: "asc" } }],
|
|
});
|
|
expect(mocks.groupFindMany).toHaveBeenCalledWith({
|
|
where: { tenantId: "tenant_1" },
|
|
select: {
|
|
id: true,
|
|
name: true,
|
|
description: true,
|
|
users: {
|
|
where: {
|
|
user: {
|
|
active: true,
|
|
pendingDeletion: false,
|
|
pseudonymized: false,
|
|
tenants: { some: { tenantId: "tenant_1", status: 2 } },
|
|
},
|
|
},
|
|
select: { userId: true },
|
|
},
|
|
},
|
|
orderBy: [{ name: "asc" }, { id: "asc" }],
|
|
});
|
|
expect(vm.status).toBe("ready");
|
|
if (vm.status !== "ready") throw new Error("fixture must be ready");
|
|
expect(vm.data.userOptions).toEqual([
|
|
{
|
|
userId: "user_mara",
|
|
displayName: "Mara Klein",
|
|
email: "mara@example.test",
|
|
avatar: { imageUrl: "https://example.test/mara.png", fallbackLabel: "MK" },
|
|
isCurrentUser: false,
|
|
},
|
|
]);
|
|
expect(vm.data.groupOptions).toEqual([
|
|
{ groupId: "group_south", name: "Team Süd", description: "Montage", memberCount: 1 },
|
|
]);
|
|
});
|
|
|
|
it("passes group privacy and initial members into the atomic lifecycle", async () => {
|
|
await expect(
|
|
createChatChannel({
|
|
context,
|
|
requestId: "request_group",
|
|
submission: {
|
|
mode: "group",
|
|
name: "Montage Nord",
|
|
description: "Abstimmung",
|
|
privacy: "private",
|
|
initialUserIds: ["user_mara"],
|
|
initialGroupIds: ["group_south"],
|
|
syncGroupMembership: true,
|
|
},
|
|
})
|
|
).resolves.toEqual({ chatId: "chat_group" });
|
|
|
|
expect(mocks.createGroupChat).toHaveBeenCalledWith({
|
|
tenantId: "tenant_1",
|
|
userId: "user_current",
|
|
permissions: context.permissions,
|
|
requestId: "request_group",
|
|
name: "Montage Nord",
|
|
description: "Abstimmung",
|
|
isPrivate: true,
|
|
notificationDefault: "all_messages",
|
|
initialMemberUserIds: ["user_mara"],
|
|
initialGroupIds: ["group_south"],
|
|
syncGroupMembership: true,
|
|
});
|
|
});
|
|
|
|
it("uses the canonical get-or-create lifecycle for direct messages", async () => {
|
|
await expect(
|
|
createChatChannel({
|
|
context,
|
|
requestId: "request_dm",
|
|
submission: { mode: "dm", peerUserId: "user_mara" },
|
|
})
|
|
).resolves.toEqual({ chatId: "chat_dm", dmPairKey: "pair", created: true });
|
|
|
|
expect(mocks.getOrCreateDirectMessage).toHaveBeenCalledWith({
|
|
tenantId: "tenant_1",
|
|
userId: "user_current",
|
|
permissions: context.permissions,
|
|
requestId: "request_dm",
|
|
peerUserId: "user_mara",
|
|
});
|
|
});
|
|
});
|