87 lines
3.7 KiB
TypeScript
87 lines
3.7 KiB
TypeScript
// @vitest-environment node
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
vi.mock("../../db.server", () => ({
|
|
db: {
|
|
groupUser: {
|
|
create: vi.fn(),
|
|
createMany: vi.fn(),
|
|
deleteMany: vi.fn(),
|
|
},
|
|
$transaction: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
import { db } from "../../db.server";
|
|
import { createUserGroup, createUserGroups, deleteGroupUsers, deleteUserGroup, replaceGroupUsers } from "./userGroups.db.server";
|
|
|
|
const dbMock = vi.mocked(db);
|
|
|
|
describe("user group persistence", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
vi.mocked(dbMock.$transaction).mockImplementation((async (callback: (tx: typeof dbMock) => Promise<unknown>) => callback(dbMock)) as never);
|
|
});
|
|
|
|
it("creates and deletes single group memberships", async () => {
|
|
vi.mocked(dbMock.groupUser.create).mockResolvedValueOnce({ id: "group_user_1" } as never);
|
|
vi.mocked(dbMock.groupUser.deleteMany).mockResolvedValueOnce({ count: 1 } as never);
|
|
vi.mocked(dbMock.groupUser.deleteMany).mockResolvedValueOnce({ count: 2 } as never);
|
|
|
|
await expect(createUserGroup("user_1", "group_1")).resolves.toEqual({ id: "group_user_1" });
|
|
await expect(deleteUserGroup("user_1", "group_1")).resolves.toEqual({ count: 1 });
|
|
await expect(deleteGroupUsers("group_1", "tenant_1")).resolves.toEqual({ count: 2 });
|
|
|
|
expect(dbMock.groupUser.create).toHaveBeenCalledWith({ data: { userId: "user_1", groupId: "group_1" } });
|
|
expect(dbMock.groupUser.deleteMany).toHaveBeenNthCalledWith(1, { where: { userId: "user_1", groupId: "group_1" } });
|
|
expect(dbMock.groupUser.deleteMany).toHaveBeenNthCalledWith(2, { where: { groupId: "group_1", group: { tenantId: "tenant_1" } } });
|
|
});
|
|
|
|
it("bulk-creates deduplicated group memberships with database duplicate protection", async () => {
|
|
vi.mocked(dbMock.groupUser.createMany).mockResolvedValueOnce({ count: 2 } as never);
|
|
|
|
await expect(createUserGroups("group_1", ["user_1", "user_2", "user_1"])).resolves.toEqual({ count: 2 });
|
|
|
|
expect(dbMock.groupUser.createMany).toHaveBeenCalledWith({
|
|
data: [
|
|
{ userId: "user_1", groupId: "group_1" },
|
|
{ userId: "user_2", groupId: "group_1" },
|
|
],
|
|
skipDuplicates: true,
|
|
});
|
|
});
|
|
|
|
it("skips bulk membership writes when no users are selected", async () => {
|
|
await expect(createUserGroups("group_1", [])).resolves.toEqual({ count: 0 });
|
|
|
|
expect(dbMock.groupUser.createMany).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("replaces group memberships inside one transaction", async () => {
|
|
vi.mocked(dbMock.groupUser.deleteMany).mockResolvedValueOnce({ count: 3 } as never);
|
|
vi.mocked(dbMock.groupUser.createMany).mockResolvedValueOnce({ count: 2 } as never);
|
|
|
|
await expect(replaceGroupUsers("group_1", "tenant_1", ["user_1", "user_2", "user_1"])).resolves.toEqual({ count: 2 });
|
|
|
|
expect(dbMock.$transaction).toHaveBeenCalledTimes(1);
|
|
expect(dbMock.groupUser.deleteMany).toHaveBeenCalledWith({ where: { groupId: "group_1", group: { tenantId: "tenant_1" } } });
|
|
expect(dbMock.groupUser.createMany).toHaveBeenCalledWith({
|
|
data: [
|
|
{ userId: "user_1", groupId: "group_1" },
|
|
{ userId: "user_2", groupId: "group_1" },
|
|
],
|
|
skipDuplicates: true,
|
|
});
|
|
});
|
|
|
|
it("replaces group memberships with an empty set without bulk insert", async () => {
|
|
vi.mocked(dbMock.groupUser.deleteMany).mockResolvedValueOnce({ count: 3 } as never);
|
|
|
|
await expect(replaceGroupUsers("group_1", "tenant_1", [])).resolves.toEqual({ count: 0 });
|
|
|
|
expect(dbMock.$transaction).toHaveBeenCalledTimes(1);
|
|
expect(dbMock.groupUser.deleteMany).toHaveBeenCalledWith({ where: { groupId: "group_1", group: { tenantId: "tenant_1" } } });
|
|
expect(dbMock.groupUser.createMany).not.toHaveBeenCalled();
|
|
});
|
|
});
|