Files
formbricks-formbricks/apps/web/lib/user/service.test.ts
2025-10-20 14:28:14 +00:00

258 lines
7.7 KiB
TypeScript

import { IdentityProvider, Prisma } from "@prisma/client";
import { afterEach, describe, expect, test, vi } from "vitest";
import { prisma } from "@formbricks/database";
import { PrismaErrorType } from "@formbricks/database/types/error";
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors";
import { TOrganization } from "@formbricks/types/organizations";
import { TUserLocale, TUserUpdateInput } from "@formbricks/types/user";
import { deleteOrganization, getOrganizationsWhereUserIsSingleOwner } from "@/lib/organization/service";
import { deleteUser, getUser, getUserByEmail, getUsersWithOrganization, updateUser } from "./service";
vi.mock("@formbricks/database", () => ({
prisma: {
user: {
findUnique: vi.fn(),
findFirst: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
findMany: vi.fn(),
},
},
}));
vi.mock("@/lib/organization/service", () => ({
getOrganizationsWhereUserIsSingleOwner: vi.fn(),
deleteOrganization: vi.fn(),
}));
describe("User Service", () => {
afterEach(() => {
vi.clearAllMocks();
});
const mockPrismaUser = {
id: "user1",
name: "Test User",
email: "test@example.com",
emailVerified: new Date(),
createdAt: new Date(),
updatedAt: new Date(),
twoFactorEnabled: false,
identityProvider: IdentityProvider.email,
notificationSettings: {
alert: {},
unsubscribedOrganizationIds: [],
},
locale: "en-US" as TUserLocale,
lastLoginAt: new Date(),
isActive: true,
twoFactorSecret: null,
backupCodes: null,
password: null,
identityProviderAccountId: null,
groupId: null,
};
const mockOrganizations: TOrganization[] = [
{
id: "org1",
name: "Organization 1",
createdAt: new Date(),
updatedAt: new Date(),
billing: {
stripeCustomerId: null,
plan: "free",
period: "monthly",
limits: {
projects: 3,
monthly: {
responses: 1500,
miu: 2000,
},
},
periodStart: new Date(),
},
isAIEnabled: false,
},
{
id: "org2",
name: "Organization 2",
createdAt: new Date(),
updatedAt: new Date(),
billing: {
stripeCustomerId: null,
plan: "free",
period: "monthly",
limits: {
projects: 3,
monthly: {
responses: 1500,
miu: 2000,
},
},
periodStart: new Date(),
},
isAIEnabled: false,
},
];
describe("getUser", () => {
test("should return user when found", async () => {
vi.mocked(prisma.user.findUnique).mockResolvedValue(mockPrismaUser);
const result = await getUser("user1");
expect(result).toEqual(mockPrismaUser);
expect(prisma.user.findUnique).toHaveBeenCalledWith({
where: { id: "user1" },
select: expect.any(Object),
});
});
test("should return null when user not found", async () => {
vi.mocked(prisma.user.findUnique).mockResolvedValue(null);
const result = await getUser("nonexistent");
expect(result).toBeNull();
});
test("should throw DatabaseError when prisma throws", async () => {
const prismaError = new Prisma.PrismaClientKnownRequestError("Database error", {
code: "P2002",
clientVersion: "5.0.0",
});
vi.mocked(prisma.user.findUnique).mockRejectedValue(prismaError);
await expect(getUser("user1")).rejects.toThrow(DatabaseError);
});
});
describe("getUserByEmail", () => {
test("should return user when found by email", async () => {
vi.mocked(prisma.user.findFirst).mockResolvedValue(mockPrismaUser);
const result = await getUserByEmail("test@example.com");
expect(result).toEqual(mockPrismaUser);
expect(prisma.user.findFirst).toHaveBeenCalledWith({
where: { email: "test@example.com" },
select: expect.any(Object),
});
});
test("should return null when user not found by email", async () => {
vi.mocked(prisma.user.findFirst).mockResolvedValue(null);
const result = await getUserByEmail("nonexistent@example.com");
expect(result).toBeNull();
});
test("should throw DatabaseError when prisma throws", async () => {
const prismaError = new Prisma.PrismaClientKnownRequestError("Database error", {
code: "P2002",
clientVersion: "5.0.0",
});
vi.mocked(prisma.user.findFirst).mockRejectedValue(prismaError);
await expect(getUserByEmail("test@example.com")).rejects.toThrow(DatabaseError);
});
});
describe("updateUser", () => {
test("should update user successfully", async () => {
const updatedPrismaUser = {
...mockPrismaUser,
name: "Updated User",
};
const updateData: TUserUpdateInput = {
name: "Updated User",
};
vi.mocked(prisma.user.update).mockResolvedValue(updatedPrismaUser);
const result = await updateUser("user1", updateData);
expect(result).toEqual(updatedPrismaUser);
expect(prisma.user.update).toHaveBeenCalledWith({
where: { id: "user1" },
data: updateData,
select: expect.any(Object),
});
});
test("should throw ResourceNotFoundError when user not found", async () => {
const prismaError = new Prisma.PrismaClientKnownRequestError("Record not found", {
code: PrismaErrorType.RecordDoesNotExist,
clientVersion: "5.0.0",
});
vi.mocked(prisma.user.update).mockRejectedValue(prismaError);
await expect(updateUser("nonexistent", { name: "New Name" })).rejects.toThrow(ResourceNotFoundError);
});
});
describe("deleteUser", () => {
test("should delete user and their organizations when they are single owner", async () => {
vi.mocked(prisma.user.delete).mockResolvedValue(mockPrismaUser);
vi.mocked(getOrganizationsWhereUserIsSingleOwner).mockResolvedValue(mockOrganizations);
vi.mocked(deleteOrganization).mockResolvedValue();
const result = await deleteUser("user1");
expect(result).toEqual(mockPrismaUser);
expect(getOrganizationsWhereUserIsSingleOwner).toHaveBeenCalledWith("user1");
expect(deleteOrganization).toHaveBeenCalledWith("org1");
expect(prisma.user.delete).toHaveBeenCalledWith({
where: { id: "user1" },
select: expect.any(Object),
});
});
test("should throw DatabaseError when prisma throws", async () => {
const prismaError = new Prisma.PrismaClientKnownRequestError("Database error", {
code: "P2002",
clientVersion: "5.0.0",
});
vi.mocked(getOrganizationsWhereUserIsSingleOwner).mockResolvedValue([]);
vi.mocked(prisma.user.delete).mockRejectedValue(prismaError);
await expect(deleteUser("user1")).rejects.toThrow(DatabaseError);
});
});
describe("getUsersWithOrganization", () => {
test("should return users in an organization", async () => {
const mockUsers = [mockPrismaUser];
vi.mocked(prisma.user.findMany).mockResolvedValue(mockUsers);
const result = await getUsersWithOrganization("org1");
expect(result).toEqual(mockUsers);
expect(prisma.user.findMany).toHaveBeenCalledWith({
where: {
memberships: {
some: {
organizationId: "org1",
},
},
},
select: expect.any(Object),
});
});
test("should throw DatabaseError when prisma throws", async () => {
const prismaError = new Prisma.PrismaClientKnownRequestError("Database error", {
code: "P2002",
clientVersion: "5.0.0",
});
vi.mocked(prisma.user.findMany).mockRejectedValue(prismaError);
await expect(getUsersWithOrganization("org1")).rejects.toThrow(DatabaseError);
});
});
});