Files
calendar/packages/trpc/server/routers/viewer/attributes/attributeUtils.test.ts
T

260 lines
7.0 KiB
TypeScript

import { describe, expect, it, vi, beforeEach } from "vitest";
import {
handleSimpleAttribute,
handleSelectAttribute,
processUserAttributes,
removeAttribute,
} from "./attributeUtils";
describe("Attribute Utils", () => {
const mockTx = {
membership: {
findFirst: vi.fn(),
},
attributeToUser: {
findFirst: vi.fn(),
upsert: vi.fn(),
deleteMany: vi.fn(),
},
attributeOption: {
update: vi.fn(),
create: vi.fn(),
},
};
beforeEach(() => {
vi.clearAllMocks();
});
describe("processUserAttributes", () => {
it("should return error if user is not a member", async () => {
mockTx.membership.findFirst.mockResolvedValue(null);
const result = await processUserAttributes(mockTx as any, 1, 1, []);
expect(result).toEqual({
userId: 1,
success: false,
message: "User is not part of your organization",
});
});
it("should skip attributes without type", async () => {
mockTx.membership.findFirst.mockResolvedValue({ id: 1 });
const result = await processUserAttributes(mockTx as any, 1, 1, [{ id: "attr-1", value: "test" }]);
expect(result).toEqual({
userId: 1,
success: true,
});
expect(mockTx.attributeToUser.findFirst).not.toHaveBeenCalled();
});
it("should process TEXT attribute", async () => {
mockTx.membership.findFirst.mockResolvedValue({ id: 1 });
const result = await processUserAttributes(mockTx as any, 1, 1, [
{ id: "attr-1", value: "test", type: "TEXT" },
]);
expect(result).toEqual({
userId: 1,
success: true,
});
expect(mockTx.attributeToUser.findFirst).toHaveBeenCalled();
});
it("should process NUMBER attribute", async () => {
mockTx.membership.findFirst.mockResolvedValue({ id: 1 });
const result = await processUserAttributes(mockTx as any, 1, 1, [
{ id: "attr-1", value: "123", type: "NUMBER" },
]);
expect(result).toEqual({
userId: 1,
success: true,
});
expect(mockTx.attributeToUser.findFirst).toHaveBeenCalled();
});
it("should process SINGLE_SELECT attribute", async () => {
mockTx.membership.findFirst.mockResolvedValue({ id: 1 });
const result = await processUserAttributes(mockTx as any, 1, 1, [
{ id: "attr-1", options: [{ value: "opt-1" }], type: "SINGLE_SELECT" },
]);
expect(result).toEqual({
userId: 1,
success: true,
});
// Should first delete existing options
expect(mockTx.attributeToUser.deleteMany).toHaveBeenCalledWith({
where: {
memberId: 1,
attributeOption: {
attribute: {
id: "attr-1",
},
},
},
});
// Then add the new option
expect(mockTx.attributeToUser.upsert).toHaveBeenCalledWith({
where: {
memberId_attributeOptionId: {
memberId: 1,
attributeOptionId: "opt-1",
},
},
create: {
memberId: 1,
attributeOptionId: "opt-1",
},
update: {},
});
});
it("should process MULTI_SELECT attribute", async () => {
mockTx.membership.findFirst.mockResolvedValue({ id: 1 });
const result = await processUserAttributes(mockTx as any, 1, 1, [
{
id: "attr-1",
options: [{ value: "opt-1" }, { value: "opt-2" }],
type: "MULTI_SELECT",
},
]);
expect(result).toEqual({
userId: 1,
success: true,
});
// Should not delete existing options for multi-select
expect(mockTx.attributeToUser.deleteMany).not.toHaveBeenCalled();
expect(mockTx.attributeToUser.upsert).toHaveBeenCalledTimes(2);
});
it("should handle attribute removal when no value or options provided", async () => {
mockTx.membership.findFirst.mockResolvedValue({ id: 1 });
const result = await processUserAttributes(mockTx as any, 1, 1, [{ id: "attr-1", type: "TEXT" }]);
expect(result).toEqual({
userId: 1,
success: true,
});
expect(mockTx.attributeToUser.deleteMany).toHaveBeenCalled();
});
});
describe("handleSimpleAttribute", () => {
it("should update existing attribute option", async () => {
mockTx.attributeToUser.findFirst.mockResolvedValue({
id: 1,
attributeOption: { id: 2 },
});
await handleSimpleAttribute(mockTx as any, 1, {
id: "attr-1",
value: "test value",
});
expect(mockTx.attributeOption.update).toHaveBeenCalledWith({
where: { id: 2 },
data: {
value: "test value",
slug: "test-value",
},
});
});
it("should create new attribute option if none exists", async () => {
mockTx.attributeToUser.findFirst.mockResolvedValue(null);
await handleSimpleAttribute(mockTx as any, 1, {
id: "attr-1",
value: "new value",
});
expect(mockTx.attributeOption.create).toHaveBeenCalledWith({
data: {
value: "new value",
slug: "new-value",
attribute: {
connect: { id: "attr-1" },
},
assignedUsers: {
create: { memberId: 1 },
},
},
});
});
});
describe("handleSelectAttribute", () => {
it("should remove existing options for SINGLE_SELECT before adding new one", async () => {
await handleSelectAttribute(mockTx as any, 1, {
id: "attr-1",
options: [{ value: "opt-1" }],
type: "SINGLE_SELECT",
});
expect(mockTx.attributeToUser.deleteMany).toHaveBeenCalledWith({
where: {
memberId: 1,
attributeOption: {
attribute: {
id: "attr-1",
},
},
},
});
expect(mockTx.attributeToUser.upsert).toHaveBeenCalledWith({
where: {
memberId_attributeOptionId: {
memberId: 1,
attributeOptionId: "opt-1",
},
},
create: {
memberId: 1,
attributeOptionId: "opt-1",
},
update: {},
});
});
it("should not remove existing options for MULTI_SELECT", async () => {
await handleSelectAttribute(mockTx as any, 1, {
id: "attr-1",
options: [{ value: "opt-1" }, { value: "opt-2" }],
type: "MULTI_SELECT",
});
expect(mockTx.attributeToUser.deleteMany).not.toHaveBeenCalled();
expect(mockTx.attributeToUser.upsert).toHaveBeenCalledTimes(2);
});
});
describe("removeAttribute", () => {
it("should delete all attribute options for user", async () => {
await removeAttribute(mockTx as any, 1, "attr-1");
expect(mockTx.attributeToUser.deleteMany).toHaveBeenCalledWith({
where: {
memberId: 1,
attributeOption: {
attribute: {
id: "attr-1",
},
},
},
});
});
});
});