Files
calendar/packages/trpc/server/routers/viewer/admin/lockUserAccount.handler.test.ts
T
Syed Ali ShahbazGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
50c7210d6b fix: resolve signup watchlist review issues and auto-unlock on SIGNUP entry removal (#27923)
* fix: resolve signup watchlist review issues with deleteEntry, email verification ordering, and unlock flow

Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>

* fix: scope sendEmailVerification to non-invite signups only

Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>

* feat: auto-unlock users when SIGNUP-source watchlist entries are removed

Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>

* fix: remove PII from error logging in unlockSignupUser

Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-02-13 10:13:33 +00:00

135 lines
4.2 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
const mockSendEmailVerification = vi.fn().mockResolvedValue({ ok: true, skipped: false });
const mockFindBlockedEmail = vi.fn();
const mockDeleteEntry = vi.fn();
vi.mock("@calcom/features/auth/lib/verifyEmail", () => ({
sendEmailVerification: (...args: unknown[]) => mockSendEmailVerification(...args),
}));
vi.mock("@calcom/features/watchlist/lib/repository/GlobalWatchlistRepository", () => ({
GlobalWatchlistRepository: class {
findBlockedEmail = mockFindBlockedEmail;
deleteEntry = mockDeleteEntry;
},
}));
vi.mock("@calcom/features/watchlist/lib/utils/normalization", () => ({
normalizeEmail: vi.fn((e: string) => e.toLowerCase()),
}));
vi.mock("@calcom/prisma", () => {
const mockPrisma = {
user: {
update: vi.fn(),
},
};
return { default: mockPrisma, prisma: mockPrisma };
});
import { prisma } from "@calcom/prisma";
import lockUserAccountHandler from "./lockUserAccount.handler";
describe("lockUserAccountHandler", () => {
const mockUser = {
id: 1,
email: "admin@example.com",
organizationId: null,
profile: null,
};
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(prisma.user.update).mockResolvedValue({
id: 42,
email: "test@example.com",
username: "testuser",
} as never);
mockFindBlockedEmail.mockResolvedValue(null);
mockDeleteEntry.mockResolvedValue(undefined);
mockSendEmailVerification.mockResolvedValue({ ok: true, skipped: false });
});
describe("unlocking a user", () => {
it("should remove watchlist entry when unlocking a user", async () => {
const watchlistEntry = { id: "watchlist-entry-123" };
mockFindBlockedEmail.mockResolvedValue(watchlistEntry);
await lockUserAccountHandler({
ctx: { user: mockUser as never },
input: { userId: 42, locked: false },
});
expect(mockDeleteEntry).toHaveBeenCalledWith("watchlist-entry-123");
});
it("should send verification email after unlocking and removing from watchlist", async () => {
const watchlistEntry = { id: "watchlist-entry-123" };
mockFindBlockedEmail.mockResolvedValue(watchlistEntry);
await lockUserAccountHandler({
ctx: { user: mockUser as never },
input: { userId: 42, locked: false },
});
expect(mockSendEmailVerification).toHaveBeenCalledWith({
email: "test@example.com",
username: "testuser",
});
});
it("should send verification email even when no watchlist entry exists", async () => {
mockFindBlockedEmail.mockResolvedValue(null);
await lockUserAccountHandler({
ctx: { user: mockUser as never },
input: { userId: 42, locked: false },
});
expect(mockDeleteEntry).not.toHaveBeenCalled();
expect(mockSendEmailVerification).toHaveBeenCalledWith({
email: "test@example.com",
username: "testuser",
});
});
it("should call deleteEntry before sendEmailVerification", async () => {
const callOrder: string[] = [];
mockFindBlockedEmail.mockResolvedValue({ id: "entry-1" });
mockDeleteEntry.mockImplementation(async () => {
callOrder.push("deleteEntry");
});
mockSendEmailVerification.mockImplementation(async () => {
callOrder.push("sendEmailVerification");
return { ok: true, skipped: false };
});
await lockUserAccountHandler({
ctx: { user: mockUser as never },
input: { userId: 42, locked: false },
});
expect(callOrder).toEqual(["deleteEntry", "sendEmailVerification"]);
});
});
describe("locking a user", () => {
it("should not remove watchlist entry when locking a user", async () => {
await lockUserAccountHandler({
ctx: { user: mockUser as never },
input: { userId: 42, locked: true },
});
expect(mockFindBlockedEmail).not.toHaveBeenCalled();
expect(mockDeleteEntry).not.toHaveBeenCalled();
});
it("should not send verification email when locking a user", async () => {
await lockUserAccountHandler({
ctx: { user: mockUser as never },
input: { userId: 42, locked: true },
});
expect(mockSendEmailVerification).not.toHaveBeenCalled();
});
});
});