mirror of
https://github.com/hanzoai/calendar.git
synced 2026-09-24 22:42:24 +00:00
* refactor: apply biome formatting to packages/features (batch 1 - small subdirs) Format small subdirectories in packages/features: di, flags, holidays, oauth, settings, users, assignment-reason, selectedCalendar, hashedLink, host, form, form-builder, availability, data-table, pbac, schedules, troubleshooter, eventtypes, calendar-subscription, and root-level files. Also includes straggler apps/web BookEventForm.tsx. Co-Authored-By: Claude Opus 4.6 <[email protected]> * refactor: apply biome formatting to packages/features (batch 2 - medium subdirs) Format medium subdirectories in packages/features: auth, credentials, calendars, routing-forms, routing-trace, attributes, watchlist, calAIPhone, tasker, and webhooks. Co-Authored-By: Claude Opus 4.6 <[email protected]> * refactor: apply biome formatting to packages/features (batch 3 - bookings + insights) Format bookings and insights subdirectories in packages/features. Co-Authored-By: Claude Opus 4.6 <[email protected]> * refactor: apply biome formatting to packages/features (batch 4 - ee) Format packages/features/ee subdirectory covering billing, workflows, organizations, teams, managed-event-types, round-robin, dsync, integration-attribute-sync, and payments. Co-Authored-By: Claude Opus 4.6 <[email protected]> * refactor: apply biome formatting to packages/features (batch 5 - booking-audit part 1) Format booking-audit di, actions, common, dto, repository, and types subdirectories in packages/features/booking-audit. Co-Authored-By: Claude Opus 4.6 <[email protected]> * refactor: apply biome formatting to packages/features (batch 6 - booking-audit part 2) Format booking-audit service subdirectory in packages/features/booking-audit. Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]>
389 lines
14 KiB
TypeScript
389 lines
14 KiB
TypeScript
import { describe, test, expect, vi, beforeEach } from "vitest";
|
|
|
|
import {
|
|
getBlockedUsersMap,
|
|
isUserBlocked,
|
|
checkWatchlistBlocking,
|
|
type BlockableUser,
|
|
type BlockingInfo,
|
|
} from "./check-user-blocking";
|
|
|
|
vi.mock("@calcom/features/di/watchlist/containers/watchlist", () => ({
|
|
getWatchlistFeature: vi.fn(),
|
|
}));
|
|
|
|
const mockGlobalBlocking: { areBlocked: ReturnType<typeof vi.fn> } = {
|
|
areBlocked: vi.fn(),
|
|
};
|
|
|
|
const mockOrgBlocking: { areBlocked: ReturnType<typeof vi.fn> } = {
|
|
areBlocked: vi.fn(),
|
|
};
|
|
|
|
const mockWatchlistFeature: {
|
|
globalBlocking: typeof mockGlobalBlocking;
|
|
orgBlocking: typeof mockOrgBlocking;
|
|
watchlist: Record<string, unknown>;
|
|
audit: Record<string, unknown>;
|
|
} = {
|
|
globalBlocking: mockGlobalBlocking,
|
|
orgBlocking: mockOrgBlocking,
|
|
watchlist: {},
|
|
audit: {},
|
|
};
|
|
|
|
describe("check-user-blocking", () => {
|
|
beforeEach(async () => {
|
|
vi.clearAllMocks();
|
|
const { getWatchlistFeature } = await import("@calcom/features/di/watchlist/containers/watchlist");
|
|
vi.mocked(getWatchlistFeature).mockResolvedValue(mockWatchlistFeature as never);
|
|
});
|
|
|
|
describe("getBlockedUsersMap", () => {
|
|
describe("Empty input handling", () => {
|
|
test("should return empty result for empty users array", async () => {
|
|
const result = await getBlockedUsersMap([]);
|
|
|
|
expect(result).toEqual({
|
|
blockingMap: new Map(),
|
|
blockedCount: 0,
|
|
lockedCount: 0,
|
|
watchlistBlockedCount: 0,
|
|
});
|
|
expect(mockGlobalBlocking.areBlocked).not.toHaveBeenCalled();
|
|
});
|
|
|
|
test("should skip users with empty emails", async () => {
|
|
mockGlobalBlocking.areBlocked.mockResolvedValue(
|
|
new Map([["[email protected]", { isBlocked: false }]])
|
|
);
|
|
|
|
const users: BlockableUser[] = [
|
|
{ email: "", locked: false },
|
|
{ email: " ", locked: false },
|
|
{ email: "[email protected]", locked: false },
|
|
];
|
|
|
|
const result = await getBlockedUsersMap(users);
|
|
|
|
expect(result.blockedCount).toBe(0);
|
|
expect(mockGlobalBlocking.areBlocked).toHaveBeenCalledWith(["[email protected]"]);
|
|
});
|
|
|
|
test("should return empty result if all users have empty emails", async () => {
|
|
const users: BlockableUser[] = [
|
|
{ email: "", locked: false },
|
|
{ email: " ", locked: false },
|
|
];
|
|
|
|
const result = await getBlockedUsersMap(users);
|
|
|
|
expect(result).toEqual({
|
|
blockingMap: new Map(),
|
|
blockedCount: 0,
|
|
lockedCount: 0,
|
|
watchlistBlockedCount: 0,
|
|
});
|
|
expect(mockGlobalBlocking.areBlocked).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("Locked user blocking", () => {
|
|
test("should block locked users without calling watchlist", async () => {
|
|
const users: BlockableUser[] = [
|
|
{ email: "[email protected]", locked: true },
|
|
{ email: "[email protected]", locked: false },
|
|
];
|
|
|
|
mockGlobalBlocking.areBlocked.mockResolvedValue(
|
|
new Map([["[email protected]", { isBlocked: false }]])
|
|
);
|
|
|
|
const result = await getBlockedUsersMap(users);
|
|
|
|
expect(result.blockedCount).toBe(1);
|
|
expect(result.lockedCount).toBe(1);
|
|
expect(result.watchlistBlockedCount).toBe(0);
|
|
|
|
const lockedInfo = result.blockingMap.get("[email protected]");
|
|
expect(lockedInfo).toEqual({ isBlocked: true, reason: "locked" });
|
|
|
|
// Should only check unlocked user in watchlist
|
|
expect(mockGlobalBlocking.areBlocked).toHaveBeenCalledWith(["[email protected]"]);
|
|
});
|
|
|
|
test("should not call watchlist if all users are locked", async () => {
|
|
const users: BlockableUser[] = [
|
|
{ email: "[email protected]", locked: true },
|
|
{ email: "[email protected]", locked: true },
|
|
];
|
|
|
|
const result = await getBlockedUsersMap(users);
|
|
|
|
expect(result.blockedCount).toBe(2);
|
|
expect(result.lockedCount).toBe(2);
|
|
expect(result.watchlistBlockedCount).toBe(0);
|
|
expect(mockGlobalBlocking.areBlocked).not.toHaveBeenCalled();
|
|
});
|
|
|
|
test("should correctly count multiple locked users", async () => {
|
|
const users: BlockableUser[] = [
|
|
{ email: "[email protected]", locked: true },
|
|
{ email: "[email protected]", locked: true },
|
|
{ email: "[email protected]", locked: true },
|
|
];
|
|
|
|
const result = await getBlockedUsersMap(users);
|
|
|
|
expect(result.lockedCount).toBe(3);
|
|
expect(result.blockedCount).toBe(3);
|
|
});
|
|
});
|
|
|
|
describe("Watchlist blocking", () => {
|
|
test("should block users on global watchlist", async () => {
|
|
const users: BlockableUser[] = [
|
|
{ email: "[email protected]", locked: false },
|
|
{ email: "[email protected]", locked: false },
|
|
];
|
|
|
|
mockGlobalBlocking.areBlocked.mockResolvedValue(
|
|
new Map([
|
|
["[email protected]", { isBlocked: true }],
|
|
["[email protected]", { isBlocked: false }],
|
|
])
|
|
);
|
|
|
|
const result = await getBlockedUsersMap(users);
|
|
|
|
expect(result.blockedCount).toBe(1);
|
|
expect(result.lockedCount).toBe(0);
|
|
expect(result.watchlistBlockedCount).toBe(1);
|
|
|
|
const blockedInfo = result.blockingMap.get("[email protected]");
|
|
expect(blockedInfo).toEqual({ isBlocked: true, reason: "watchlist" });
|
|
|
|
const cleanInfo = result.blockingMap.get("[email protected]");
|
|
expect(cleanInfo).toEqual({ isBlocked: false });
|
|
});
|
|
|
|
test("should check org-level blocking when organizationId is provided", async () => {
|
|
const users: BlockableUser[] = [{ email: "[email protected]", locked: false }];
|
|
|
|
mockGlobalBlocking.areBlocked.mockResolvedValue(
|
|
new Map([["[email protected]", { isBlocked: false }]])
|
|
);
|
|
mockOrgBlocking.areBlocked.mockResolvedValue(new Map([["[email protected]", { isBlocked: true }]]));
|
|
|
|
const result = await getBlockedUsersMap(users, 123);
|
|
|
|
expect(result.blockedCount).toBe(1);
|
|
expect(result.watchlistBlockedCount).toBe(1);
|
|
expect(mockOrgBlocking.areBlocked).toHaveBeenCalledWith(["[email protected]"], 123);
|
|
});
|
|
|
|
test("should not check org-level blocking when organizationId is not provided", async () => {
|
|
const users: BlockableUser[] = [{ email: "[email protected]", locked: false }];
|
|
|
|
mockGlobalBlocking.areBlocked.mockResolvedValue(
|
|
new Map([["[email protected]", { isBlocked: false }]])
|
|
);
|
|
|
|
await getBlockedUsersMap(users);
|
|
|
|
expect(mockOrgBlocking.areBlocked).not.toHaveBeenCalled();
|
|
});
|
|
|
|
test("should not check org-level blocking when organizationId is null", async () => {
|
|
const users: BlockableUser[] = [{ email: "[email protected]", locked: false }];
|
|
|
|
mockGlobalBlocking.areBlocked.mockResolvedValue(
|
|
new Map([["[email protected]", { isBlocked: false }]])
|
|
);
|
|
|
|
await getBlockedUsersMap(users, null);
|
|
|
|
expect(mockOrgBlocking.areBlocked).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("Combined blocking (locked + watchlist)", () => {
|
|
test("should correctly count both locked and watchlist-blocked users", async () => {
|
|
const users: BlockableUser[] = [
|
|
{ email: "[email protected]", locked: true },
|
|
{ email: "[email protected]", locked: false },
|
|
{ email: "[email protected]", locked: false },
|
|
];
|
|
|
|
mockGlobalBlocking.areBlocked.mockResolvedValue(
|
|
new Map([
|
|
["[email protected]", { isBlocked: true }],
|
|
["[email protected]", { isBlocked: false }],
|
|
])
|
|
);
|
|
|
|
const result = await getBlockedUsersMap(users);
|
|
|
|
expect(result.blockedCount).toBe(2);
|
|
expect(result.lockedCount).toBe(1);
|
|
expect(result.watchlistBlockedCount).toBe(1);
|
|
});
|
|
|
|
test("should prioritize locked status over watchlist (locked users not checked in watchlist)", async () => {
|
|
const users: BlockableUser[] = [
|
|
{ email: "[email protected]", locked: true },
|
|
{ email: "[email protected]", locked: false },
|
|
];
|
|
|
|
mockGlobalBlocking.areBlocked.mockResolvedValue(
|
|
new Map([["[email protected]", { isBlocked: false }]])
|
|
);
|
|
|
|
const result = await getBlockedUsersMap(users);
|
|
|
|
// The locked user's email should NOT be sent to watchlist check
|
|
expect(mockGlobalBlocking.areBlocked).toHaveBeenCalledWith(["[email protected]"]);
|
|
expect(mockGlobalBlocking.areBlocked).not.toHaveBeenCalledWith(
|
|
expect.arrayContaining(["[email protected]"])
|
|
);
|
|
|
|
// But the locked user should still be marked as blocked
|
|
const lockedInfo = result.blockingMap.get("[email protected]");
|
|
expect(lockedInfo).toEqual({ isBlocked: true, reason: "locked" });
|
|
});
|
|
});
|
|
|
|
describe("Email normalization", () => {
|
|
test("should normalize emails to lowercase", async () => {
|
|
const users: BlockableUser[] = [{ email: "[email protected]", locked: false }];
|
|
|
|
mockGlobalBlocking.areBlocked.mockResolvedValue(
|
|
new Map([["[email protected]", { isBlocked: false }]])
|
|
);
|
|
|
|
const result = await getBlockedUsersMap(users);
|
|
|
|
expect(result.blockingMap.has("[email protected]")).toBe(true);
|
|
expect(result.blockingMap.has("[email protected]")).toBe(false);
|
|
});
|
|
|
|
test("should trim whitespace from emails", async () => {
|
|
const users: BlockableUser[] = [{ email: " [email protected] ", locked: false }];
|
|
|
|
mockGlobalBlocking.areBlocked.mockResolvedValue(
|
|
new Map([["[email protected]", { isBlocked: false }]])
|
|
);
|
|
|
|
const result = await getBlockedUsersMap(users);
|
|
|
|
expect(result.blockingMap.has("[email protected]")).toBe(true);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("isUserBlocked", () => {
|
|
test("should return true for blocked user", () => {
|
|
const blockingMap = new Map<string, BlockingInfo>([
|
|
["[email protected]", { isBlocked: true, reason: "locked" }],
|
|
]);
|
|
|
|
expect(isUserBlocked("[email protected]", blockingMap)).toBe(true);
|
|
});
|
|
|
|
test("should return false for non-blocked user", () => {
|
|
const blockingMap = new Map<string, BlockingInfo>([["[email protected]", { isBlocked: false }]]);
|
|
|
|
expect(isUserBlocked("[email protected]", blockingMap)).toBe(false);
|
|
});
|
|
|
|
test("should return false for user not in map", () => {
|
|
const blockingMap = new Map<string, BlockingInfo>();
|
|
|
|
expect(isUserBlocked("[email protected]", blockingMap)).toBe(false);
|
|
});
|
|
|
|
test("should return false for empty email", () => {
|
|
const blockingMap = new Map<string, BlockingInfo>([
|
|
["[email protected]", { isBlocked: true, reason: "locked" }],
|
|
]);
|
|
|
|
expect(isUserBlocked("", blockingMap)).toBe(false);
|
|
expect(isUserBlocked(" ", blockingMap)).toBe(false);
|
|
});
|
|
|
|
test("should normalize email before lookup", () => {
|
|
const blockingMap = new Map<string, BlockingInfo>([
|
|
["[email protected]", { isBlocked: true, reason: "locked" }],
|
|
]);
|
|
|
|
expect(isUserBlocked("[email protected]", blockingMap)).toBe(true);
|
|
expect(isUserBlocked(" [email protected] ", blockingMap)).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("checkWatchlistBlocking", () => {
|
|
test("should batch check all emails against global watchlist", async () => {
|
|
const emails = ["[email protected]", "[email protected]"];
|
|
|
|
mockGlobalBlocking.areBlocked.mockResolvedValue(
|
|
new Map([
|
|
["[email protected]", { isBlocked: false }],
|
|
["[email protected]", { isBlocked: true }],
|
|
])
|
|
);
|
|
|
|
const result = await checkWatchlistBlocking(emails);
|
|
|
|
expect(mockGlobalBlocking.areBlocked).toHaveBeenCalledWith(emails);
|
|
expect(result.get("[email protected]")).toBe(false);
|
|
expect(result.get("[email protected]")).toBe(true);
|
|
});
|
|
|
|
test("should combine global and org blocking results", async () => {
|
|
const emails = ["[email protected]"];
|
|
|
|
mockGlobalBlocking.areBlocked.mockResolvedValue(new Map([["[email protected]", { isBlocked: false }]]));
|
|
mockOrgBlocking.areBlocked.mockResolvedValue(new Map([["[email protected]", { isBlocked: true }]]));
|
|
|
|
const result = await checkWatchlistBlocking(emails, 123);
|
|
|
|
expect(result.get("[email protected]")).toBe(true);
|
|
});
|
|
|
|
test("should block if either global or org blocks", async () => {
|
|
const emails = ["[email protected]", "[email protected]", "[email protected]"];
|
|
|
|
mockGlobalBlocking.areBlocked.mockResolvedValue(
|
|
new Map([
|
|
["[email protected]", { isBlocked: true }],
|
|
["[email protected]", { isBlocked: false }],
|
|
["[email protected]", { isBlocked: false }],
|
|
])
|
|
);
|
|
mockOrgBlocking.areBlocked.mockResolvedValue(
|
|
new Map([
|
|
["[email protected]", { isBlocked: false }],
|
|
["[email protected]", { isBlocked: true }],
|
|
["[email protected]", { isBlocked: false }],
|
|
])
|
|
);
|
|
|
|
const result = await checkWatchlistBlocking(emails, 123);
|
|
|
|
expect(result.get("[email protected]")).toBe(true);
|
|
expect(result.get("[email protected]")).toBe(true);
|
|
expect(result.get("[email protected]")).toBe(false);
|
|
});
|
|
|
|
test("should normalize emails in result map", async () => {
|
|
const emails = ["[email protected]"];
|
|
|
|
mockGlobalBlocking.areBlocked.mockResolvedValue(new Map([["[email protected]", { isBlocked: true }]]));
|
|
|
|
const result = await checkWatchlistBlocking(emails);
|
|
|
|
expect(result.get("[email protected]")).toBe(true);
|
|
});
|
|
});
|
|
});
|