Files
HiveGrid-Wireframes/hivegrid-cloud/apps/web/app/utils/db/permissions/groups.db.server.test.ts
T

134 lines
5.2 KiB
TypeScript

// @vitest-environment node
import { beforeEach, describe, expect, it, vi } from "vitest";
vi.mock("../../db.server", () => ({
db: {
group: {
create: vi.fn(),
delete: vi.fn(),
deleteMany: vi.fn(),
findFirst: vi.fn(),
findMany: vi.fn(),
update: vi.fn(),
updateMany: vi.fn(),
},
groupUser: {
createMany: vi.fn(),
deleteMany: vi.fn(),
},
},
}));
import { db } from "../../db.server";
import { addUsersToGroup, createGroup, deleteGroup, getAllGroups, getGroup, getGroups, getMyGroups, removeUsersFromGroup, updateGroup } from "./groups.db.server";
const dbMock = vi.mocked(db);
describe("group persistence", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("lists tenant groups and selected groups with users included", async () => {
vi.mocked(dbMock.group.findMany).mockResolvedValue([] as never);
await getAllGroups("tenant_1");
await getGroups("tenant_1", ["group_1", "group_2"]);
expect(dbMock.group.findMany).toHaveBeenNthCalledWith(
1,
expect.objectContaining({
where: { tenantId: "tenant_1" },
include: expect.objectContaining({ users: expect.any(Object) }),
orderBy: [{ name: "asc" }],
})
);
expect(dbMock.group.findMany).toHaveBeenNthCalledWith(
2,
expect.objectContaining({
where: { tenantId: "tenant_1", id: { in: ["group_1", "group_2"] } },
include: expect.objectContaining({ users: expect.any(Object) }),
orderBy: [{ name: "asc" }],
})
);
});
it("limits my-groups lookup to created or joined groups in the tenant", async () => {
vi.mocked(dbMock.group.findMany).mockResolvedValueOnce([] as never);
await getMyGroups("user_1", "tenant_1");
expect(dbMock.group.findMany).toHaveBeenCalledWith(
expect.objectContaining({
where: {
tenantId: "tenant_1",
OR: [{ createdByUserId: "user_1" }, { users: { some: { userId: "user_1" } } }],
},
include: expect.objectContaining({ users: expect.any(Object) }),
orderBy: [{ name: "asc" }],
})
);
});
it("gets groups with optional tenant scope", async () => {
vi.mocked(dbMock.group.findFirst).mockResolvedValue({ id: "group_1" } as never);
await getGroup("group_1");
await getGroup("group_1", "tenant_1");
expect(dbMock.group.findFirst).toHaveBeenNthCalledWith(1, expect.objectContaining({ where: { id: "group_1" } }));
expect(dbMock.group.findFirst).toHaveBeenNthCalledWith(2, expect.objectContaining({ where: { id: "group_1", tenantId: "tenant_1" } }));
});
it("creates and mutates groups with tenant-scoped bulk operations when a tenant is provided", async () => {
vi.mocked(dbMock.group.create).mockResolvedValueOnce({ id: "group_1" } as never);
vi.mocked(dbMock.group.updateMany).mockResolvedValueOnce({ count: 1 } as never);
vi.mocked(dbMock.group.update).mockResolvedValueOnce({ id: "group_1" } as never);
vi.mocked(dbMock.group.deleteMany).mockResolvedValueOnce({ count: 1 } as never);
vi.mocked(dbMock.group.delete).mockResolvedValueOnce({ id: "group_1" } as never);
const data = { createdByUserId: "user_1", tenantId: "tenant_1", name: "Team", description: "Core team", color: 1 };
await createGroup(data);
await updateGroup("group_1", { name: "Ops", description: "Ops team", color: 2 }, "tenant_1");
await updateGroup("group_1", { name: "Ops", description: "Ops team", color: 2 });
await deleteGroup("group_1", "tenant_1");
await deleteGroup("group_1");
expect(dbMock.group.create).toHaveBeenCalledWith({ data });
expect(dbMock.group.updateMany).toHaveBeenCalledWith({
where: { id: "group_1", tenantId: "tenant_1" },
data: { name: "Ops", description: "Ops team", color: 2 },
});
expect(dbMock.group.update).toHaveBeenCalledWith({
where: { id: "group_1" },
data: { name: "Ops", description: "Ops team", color: 2 },
});
expect(dbMock.group.deleteMany).toHaveBeenCalledWith({ where: { id: "group_1", tenantId: "tenant_1" } });
expect(dbMock.group.delete).toHaveBeenCalledWith({ where: { id: "group_1" } });
});
it("adds users to a group idempotently (skipDuplicates) and removes them in bulk", async () => {
vi.mocked(dbMock.groupUser.createMany).mockResolvedValueOnce({ count: 2 } as never);
vi.mocked(dbMock.groupUser.deleteMany).mockResolvedValueOnce({ count: 1 } as never);
await addUsersToGroup("group_1", ["user_1", "user_2"]);
await removeUsersFromGroup("group_1", ["user_1"]);
expect(dbMock.groupUser.createMany).toHaveBeenCalledWith({
data: [
{ groupId: "group_1", userId: "user_1" },
{ groupId: "group_1", userId: "user_2" },
],
skipDuplicates: true,
});
expect(dbMock.groupUser.deleteMany).toHaveBeenCalledWith({ where: { groupId: "group_1", userId: { in: ["user_1"] } } });
});
it("short-circuits bulk membership ops on an empty user list", async () => {
expect(await addUsersToGroup("group_1", [])).toEqual({ count: 0 });
expect(await removeUsersFromGroup("group_1", [])).toEqual({ count: 0 });
expect(dbMock.groupUser.createMany).not.toHaveBeenCalled();
expect(dbMock.groupUser.deleteMany).not.toHaveBeenCalled();
});
});