diff --git a/apps/web/app/(app)/environments/[environmentId]/settings/(account)/profile/lib/user.test.ts b/apps/web/app/(app)/environments/[environmentId]/settings/(account)/profile/lib/user.test.ts index b6a786c673..0a04d12ea1 100644 --- a/apps/web/app/(app)/environments/[environmentId]/settings/(account)/profile/lib/user.test.ts +++ b/apps/web/app/(app)/environments/[environmentId]/settings/(account)/profile/lib/user.test.ts @@ -1,14 +1,7 @@ import { beforeEach, describe, expect, test, vi } from "vitest"; import { prisma } from "@formbricks/database"; -import { InvalidInputError, ResourceNotFoundError } from "@formbricks/types/errors"; -import { verifyUserPassword } from "@/lib/user/password"; -import { verifyPassword as mockVerifyPasswordImported } from "@/modules/auth/lib/utils"; import { getIsEmailUnique } from "./user"; -vi.mock("@/modules/auth/lib/utils", () => ({ - verifyPassword: vi.fn(), -})); - vi.mock("@formbricks/database", () => ({ prisma: { user: { @@ -18,92 +11,12 @@ vi.mock("@formbricks/database", () => ({ })); const mockPrismaUserFindUnique = vi.mocked(prisma.user.findUnique); -const mockVerifyPasswordUtil = vi.mocked(mockVerifyPasswordImported); describe("User Library Tests", () => { beforeEach(() => { vi.resetAllMocks(); }); - describe("verifyUserPassword", () => { - const userId = "test-user-id"; - const password = "test-password"; - - test("should return true for correct password", async () => { - mockPrismaUserFindUnique.mockResolvedValue({ - password: "hashed-password", - identityProvider: "email", - } as any); - mockVerifyPasswordUtil.mockResolvedValue(true); - - const result = await verifyUserPassword(userId, password); - expect(result).toBe(true); - expect(mockPrismaUserFindUnique).toHaveBeenCalledWith({ - where: { id: userId }, - select: { password: true, identityProvider: true }, - }); - expect(mockVerifyPasswordUtil).toHaveBeenCalledWith(password, "hashed-password"); - }); - - test("should return false for incorrect password", async () => { - mockPrismaUserFindUnique.mockResolvedValue({ - password: "hashed-password", - identityProvider: "email", - } as any); - mockVerifyPasswordUtil.mockResolvedValue(false); - - const result = await verifyUserPassword(userId, password); - expect(result).toBe(false); - expect(mockPrismaUserFindUnique).toHaveBeenCalledWith({ - where: { id: userId }, - select: { password: true, identityProvider: true }, - }); - expect(mockVerifyPasswordUtil).toHaveBeenCalledWith(password, "hashed-password"); - }); - - test("should throw ResourceNotFoundError if user not found", async () => { - mockPrismaUserFindUnique.mockResolvedValue(null); - - await expect(verifyUserPassword(userId, password)).rejects.toThrow(ResourceNotFoundError); - await expect(verifyUserPassword(userId, password)).rejects.toThrow(`user with ID ${userId} not found`); - expect(mockPrismaUserFindUnique).toHaveBeenCalledWith({ - where: { id: userId }, - select: { password: true, identityProvider: true }, - }); - expect(mockVerifyPasswordUtil).not.toHaveBeenCalled(); - }); - - test("should throw InvalidInputError if identityProvider is not email", async () => { - mockPrismaUserFindUnique.mockResolvedValue({ - password: "hashed-password", - identityProvider: "google", // Not 'email' - } as any); - - await expect(verifyUserPassword(userId, password)).rejects.toThrow(InvalidInputError); - await expect(verifyUserPassword(userId, password)).rejects.toThrow("Password is not set for this user"); - expect(mockPrismaUserFindUnique).toHaveBeenCalledWith({ - where: { id: userId }, - select: { password: true, identityProvider: true }, - }); - expect(mockVerifyPasswordUtil).not.toHaveBeenCalled(); - }); - - test("should throw InvalidInputError if password is not set for email provider", async () => { - mockPrismaUserFindUnique.mockResolvedValue({ - password: null, // Password not set - identityProvider: "email", - } as any); - - await expect(verifyUserPassword(userId, password)).rejects.toThrow(InvalidInputError); - await expect(verifyUserPassword(userId, password)).rejects.toThrow("Password is not set for this user"); - expect(mockPrismaUserFindUnique).toHaveBeenCalledWith({ - where: { id: userId }, - select: { password: true, identityProvider: true }, - }); - expect(mockVerifyPasswordUtil).not.toHaveBeenCalled(); - }); - }); - describe("getIsEmailUnique", () => { const email = "test@example.com"; diff --git a/apps/web/lib/user/password.test.ts b/apps/web/lib/user/password.test.ts new file mode 100644 index 0000000000..a9950624e2 --- /dev/null +++ b/apps/web/lib/user/password.test.ts @@ -0,0 +1,95 @@ +import { beforeEach, describe, expect, test, vi } from "vitest"; +import { prisma } from "@formbricks/database"; +import { InvalidInputError, ResourceNotFoundError } from "@formbricks/types/errors"; +import { verifyPassword as mockVerifyPasswordImported } from "@/modules/auth/lib/utils"; +import { getUserAuthenticationData, verifyUserPassword } from "./password"; + +vi.mock("server-only", () => ({})); + +vi.mock("@/modules/auth/lib/utils", () => ({ + verifyPassword: vi.fn(), +})); + +vi.mock("@formbricks/database", () => ({ + prisma: { + user: { + findUnique: vi.fn(), + }, + }, +})); + +const mockPrismaUserFindUnique = vi.mocked(prisma.user.findUnique); +const mockVerifyPassword = vi.mocked(mockVerifyPasswordImported); + +describe("user password helpers", () => { + const userId = "test-user-id"; + + beforeEach(() => { + vi.resetAllMocks(); + }); + + test("returns authentication data for an existing user", async () => { + const authenticationData = { + email: "user@example.com", + password: "hashed-password", + identityProvider: "email", + identityProviderAccountId: null, + }; + mockPrismaUserFindUnique.mockResolvedValue(authenticationData as any); + + await expect(getUserAuthenticationData(userId)).resolves.toEqual(authenticationData); + expect(mockPrismaUserFindUnique).toHaveBeenCalledWith({ + where: { id: userId }, + select: { + email: true, + password: true, + identityProvider: true, + identityProviderAccountId: true, + }, + }); + }); + + test("throws when authentication data cannot find the user", async () => { + mockPrismaUserFindUnique.mockResolvedValue(null); + + await expect(getUserAuthenticationData(userId)).rejects.toThrow(ResourceNotFoundError); + }); + + test("verifies a password against the stored hash", async () => { + mockPrismaUserFindUnique.mockResolvedValue({ + email: "user@example.com", + password: "hashed-password", + identityProvider: "email", + identityProviderAccountId: null, + } as any); + mockVerifyPassword.mockResolvedValue(true); + + await expect(verifyUserPassword(userId, "plain-password")).resolves.toBe(true); + expect(mockVerifyPassword).toHaveBeenCalledWith("plain-password", "hashed-password"); + }); + + test("returns false when the password does not match", async () => { + mockPrismaUserFindUnique.mockResolvedValue({ + email: "user@example.com", + password: "hashed-password", + identityProvider: "email", + identityProviderAccountId: null, + } as any); + mockVerifyPassword.mockResolvedValue(false); + + await expect(verifyUserPassword(userId, "wrong-password")).resolves.toBe(false); + expect(mockVerifyPassword).toHaveBeenCalledWith("wrong-password", "hashed-password"); + }); + + test("throws when the user does not have a password", async () => { + mockPrismaUserFindUnique.mockResolvedValue({ + email: "sso-user@example.com", + password: null, + identityProvider: "google", + identityProviderAccountId: "google-account-id", + } as any); + + await expect(verifyUserPassword(userId, "plain-password")).rejects.toThrow(InvalidInputError); + expect(mockVerifyPassword).not.toHaveBeenCalled(); + }); +}); diff --git a/apps/web/modules/ee/sso/lib/providers.test.ts b/apps/web/modules/ee/sso/lib/providers.test.ts index be40fd76e0..db9b7b909f 100644 --- a/apps/web/modules/ee/sso/lib/providers.test.ts +++ b/apps/web/modules/ee/sso/lib/providers.test.ts @@ -51,7 +51,7 @@ describe("SSO Providers", () => { expect((samlProvider as any).authorization?.url).toBe("https://test-app.com/api/auth/saml/authorize"); expect(samlProvider.token).toBe("https://test-app.com/api/auth/saml/token"); expect(samlProvider.userinfo).toBe("https://test-app.com/api/auth/saml/userinfo"); - expect((googleProvider as any).options?.allowDangerousEmailAccountLinking).toBe(true); - expect(samlProvider.allowDangerousEmailAccountLinking).toBe(true); + expect((googleProvider as any).options?.allowDangerousEmailAccountLinking).toBeUndefined(); + expect(samlProvider.allowDangerousEmailAccountLinking).toBeUndefined(); }); });