Skip to content

Commit 68a8fad

Browse files
authored
add tests (calcom#25566)
1 parent d819938 commit 68a8fad

2 files changed

Lines changed: 355 additions & 117 deletions

File tree

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
import type { User } from "next-auth";
2+
import { describe, expect, it, vi, beforeEach } from "vitest";
3+
4+
import { IdentityProvider, UserPermissionRole } from "@calcom/prisma/enums";
5+
6+
import { ErrorCode } from "./ErrorCode";
7+
8+
// Mock dependencies
9+
vi.mock("@calcom/prisma", () => ({
10+
prisma: {
11+
user: {
12+
update: vi.fn(),
13+
},
14+
},
15+
default: {
16+
user: {
17+
update: vi.fn(),
18+
},
19+
},
20+
}));
21+
22+
const mockFindByEmailAndIncludeProfilesAndPassword = vi.fn();
23+
24+
vi.mock("@calcom/features/users/repositories/UserRepository", () => {
25+
return {
26+
UserRepository: vi.fn().mockImplementation(() => ({
27+
findByEmailAndIncludeProfilesAndPassword: mockFindByEmailAndIncludeProfilesAndPassword,
28+
})),
29+
};
30+
});
31+
32+
vi.mock("./verifyPassword", () => ({
33+
verifyPassword: vi.fn(),
34+
}));
35+
36+
vi.mock("@calcom/lib/checkRateLimitAndThrowError", () => ({
37+
checkRateLimitAndThrowError: vi.fn().mockResolvedValue(undefined),
38+
}));
39+
40+
vi.mock("@calcom/lib/server/PiiHasher", () => ({
41+
hashEmail: vi.fn((email: string) => `hashed_${email}`),
42+
}));
43+
44+
vi.mock("@calcom/lib/totp", () => ({
45+
totpAuthenticatorCheck: vi.fn(),
46+
}));
47+
48+
vi.mock("@calcom/lib/crypto", () => ({
49+
symmetricDecrypt: vi.fn(),
50+
symmetricEncrypt: vi.fn(),
51+
}));
52+
53+
vi.mock("@calcom/lib/auth/isPasswordValid", () => ({
54+
isPasswordValid: vi.fn(),
55+
}));
56+
57+
vi.mock("@calcom/lib/env", () => ({
58+
isENVDev: false,
59+
}));
60+
61+
vi.mock("@calcom/lib/constants", async (importOriginal) => {
62+
const actual = await importOriginal<typeof import("@calcom/lib/constants")>();
63+
return {
64+
...actual,
65+
IS_TEAM_BILLING_ENABLED: false,
66+
ENABLE_PROFILE_SWITCHER: false,
67+
WEBAPP_URL: "http://localhost:3000",
68+
};
69+
});
70+
71+
vi.mock("@calcom/lib/logger", () => ({
72+
default: {
73+
getSubLogger: vi.fn(() => ({
74+
debug: vi.fn(),
75+
error: vi.fn(),
76+
info: vi.fn(),
77+
})),
78+
},
79+
}));
80+
81+
vi.mock("@calcom/lib/safeStringify", () => ({
82+
safeStringify: vi.fn((obj) => JSON.stringify(obj)),
83+
}));
84+
85+
vi.mock("./next-auth-custom-adapter", () => ({
86+
default: vi.fn(() => ({
87+
linkAccount: vi.fn(),
88+
})),
89+
}));
90+
91+
vi.mock("@calcom/features/profile/repositories/ProfileRepository", () => ({
92+
ProfileRepository: {
93+
findAllProfilesForUserIncludingMovedUser: vi.fn(),
94+
findByUpIdWithAuth: vi.fn(),
95+
},
96+
}));
97+
98+
describe("CredentialsProvider authorize", () => {
99+
let authorizeCredentials: typeof import("./next-auth-options").authorizeCredentials;
100+
let verifyPassword: any;
101+
102+
beforeEach(async () => {
103+
vi.clearAllMocks();
104+
mockFindByEmailAndIncludeProfilesAndPassword.mockReset();
105+
106+
const verifyPasswordModule = await import("./verifyPassword");
107+
verifyPassword = verifyPasswordModule.verifyPassword;
108+
109+
// Import the exported authorize function directly
110+
const authModule = await import("./next-auth-options");
111+
authorizeCredentials = authModule.authorizeCredentials;
112+
});
113+
114+
const createMockUser = (overrides: Partial<any> = {}) => ({
115+
id: 1,
116+
email: "test@example.com",
117+
name: "Test User",
118+
username: "testuser",
119+
role: UserPermissionRole.USER,
120+
locked: false,
121+
identityProvider: IdentityProvider.CAL,
122+
twoFactorEnabled: false,
123+
twoFactorSecret: null,
124+
backupCodes: null,
125+
password: {
126+
hash: "$2a$10$hashedpassword",
127+
},
128+
allProfiles: [
129+
{
130+
id: 1,
131+
upId: "usr_123",
132+
username: "testuser",
133+
},
134+
],
135+
teams: [],
136+
...overrides,
137+
});
138+
139+
describe("Password validation", () => {
140+
it("should throw error when user has no password hash with CAL identity provider", async () => {
141+
const mockUser = createMockUser({
142+
password: null,
143+
identityProvider: IdentityProvider.CAL,
144+
});
145+
mockFindByEmailAndIncludeProfilesAndPassword.mockResolvedValue(mockUser);
146+
147+
await expect(
148+
authorizeCredentials({
149+
email: "test@example.com",
150+
password: "password123",
151+
} as any)
152+
).rejects.toThrow(ErrorCode.IncorrectEmailPassword);
153+
});
154+
155+
it("should throw error when user has no password hash with Google identity provider", async () => {
156+
const mockUser = createMockUser({
157+
password: null,
158+
identityProvider: IdentityProvider.GOOGLE,
159+
});
160+
mockFindByEmailAndIncludeProfilesAndPassword.mockResolvedValue(mockUser);
161+
162+
await expect(
163+
authorizeCredentials({
164+
email: "test@example.com",
165+
password: "password123",
166+
} as any)
167+
).rejects.toThrow(ErrorCode.IncorrectEmailPassword);
168+
});
169+
170+
it("should throw error when user has no password hash with SAML identity provider", async () => {
171+
const mockUser = createMockUser({
172+
password: null,
173+
identityProvider: IdentityProvider.SAML,
174+
});
175+
mockFindByEmailAndIncludeProfilesAndPassword.mockResolvedValue(mockUser);
176+
177+
await expect(
178+
authorizeCredentials({
179+
email: "test@example.com",
180+
password: "password123",
181+
} as any)
182+
).rejects.toThrow(ErrorCode.IncorrectEmailPassword);
183+
});
184+
185+
it("should throw error when user has no password hash even with TOTP code provided", async () => {
186+
const mockUser = createMockUser({
187+
password: null,
188+
identityProvider: IdentityProvider.CAL,
189+
});
190+
mockFindByEmailAndIncludeProfilesAndPassword.mockResolvedValue(mockUser);
191+
192+
await expect(
193+
authorizeCredentials({
194+
email: "test@example.com",
195+
password: "password123",
196+
totpCode: "123456",
197+
} as any)
198+
).rejects.toThrow(ErrorCode.IncorrectEmailPassword);
199+
});
200+
201+
it("should throw error when user has no password hash with Google identity provider and TOTP code", async () => {
202+
const mockUser = createMockUser({
203+
password: null,
204+
identityProvider: IdentityProvider.GOOGLE,
205+
});
206+
mockFindByEmailAndIncludeProfilesAndPassword.mockResolvedValue(mockUser);
207+
208+
await expect(
209+
authorizeCredentials({
210+
email: "test@example.com",
211+
password: "password123",
212+
totpCode: "123456",
213+
} as any)
214+
).rejects.toThrow(ErrorCode.IncorrectEmailPassword);
215+
});
216+
217+
it("should throw error when user has no password hash with SAML identity provider and TOTP code", async () => {
218+
const mockUser = createMockUser({
219+
password: null,
220+
identityProvider: IdentityProvider.SAML,
221+
});
222+
mockFindByEmailAndIncludeProfilesAndPassword.mockResolvedValue(mockUser);
223+
224+
await expect(
225+
authorizeCredentials({
226+
email: "test@example.com",
227+
password: "password123",
228+
totpCode: "123456",
229+
} as any)
230+
).rejects.toThrow(ErrorCode.IncorrectEmailPassword);
231+
});
232+
});
233+
});

0 commit comments

Comments
 (0)