Co-authored-by: zomars@cal.com <zomars@me.com> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
453 lines
13 KiB
TypeScript
453 lines
13 KiB
TypeScript
import type { DestinationCalendar } from "@prisma/client";
|
|
import { describe, expect, it, vi, beforeEach } from "vitest";
|
|
|
|
import { symmetricDecrypt } from "@calcom/lib/crypto";
|
|
import type { CredentialForCalendarService } from "@calcom/types/Credential";
|
|
|
|
import EventManager from "./EventManager";
|
|
|
|
vi.mock("@calcom/lib/crypto", () => ({
|
|
symmetricDecrypt: vi.fn(),
|
|
}));
|
|
|
|
const mockedSymmetricDecrypt = vi.mocked(symmetricDecrypt);
|
|
|
|
function buildCalDAVCredential(data: {
|
|
id: number;
|
|
key: string;
|
|
userId?: number;
|
|
}): CredentialForCalendarService {
|
|
return {
|
|
id: data.id,
|
|
type: "caldav_calendar",
|
|
key: data.key,
|
|
userId: data.userId || 1,
|
|
user: { email: "test@example.com" },
|
|
teamId: null,
|
|
appId: "caldav-calendar",
|
|
invalid: false,
|
|
delegatedTo: null,
|
|
delegationCredentialId: null,
|
|
};
|
|
}
|
|
|
|
function buildDestinationCalendar(data: {
|
|
id: number;
|
|
integration: string;
|
|
externalId: string | null;
|
|
}): DestinationCalendar {
|
|
return {
|
|
id: data.id,
|
|
integration: data.integration,
|
|
externalId: data.externalId || "",
|
|
userId: 1,
|
|
eventTypeId: null,
|
|
credentialId: null,
|
|
primaryEmail: null,
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
delegationCredentialId: null,
|
|
domainWideDelegationCredentialId: null,
|
|
};
|
|
}
|
|
|
|
describe("EventManager CalDAV credential validation", () => {
|
|
let eventManager: EventManager;
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
eventManager = new EventManager({
|
|
credentials: [],
|
|
destinationCalendar: null,
|
|
});
|
|
});
|
|
|
|
describe("extractServerUrlFromCredential", () => {
|
|
it("should extract server URL from valid CalDAV credential", () => {
|
|
const credential = buildCalDAVCredential({
|
|
id: 1,
|
|
key: "encrypted_key",
|
|
});
|
|
|
|
mockedSymmetricDecrypt.mockReturnValue(
|
|
JSON.stringify({
|
|
username: "user",
|
|
password: "pass",
|
|
url: "https://caldav.example.com/dav/calendars/user/",
|
|
})
|
|
);
|
|
|
|
const result = (eventManager as any).extractServerUrlFromCredential(credential);
|
|
expect(result).toBe("https://caldav.example.com");
|
|
expect(mockedSymmetricDecrypt).toHaveBeenCalledWith(
|
|
"encrypted_key",
|
|
process.env.CALENDSO_ENCRYPTION_KEY || ""
|
|
);
|
|
});
|
|
|
|
it("should return null for non-CalDAV credential", () => {
|
|
const credential = {
|
|
...buildCalDAVCredential({ id: 1, key: "encrypted_key" }),
|
|
type: "google_calendar",
|
|
} as CredentialForCalendarService;
|
|
|
|
const result = (eventManager as any).extractServerUrlFromCredential(credential);
|
|
expect(result).toBeNull();
|
|
expect(mockedSymmetricDecrypt).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("should return null when decryption fails", () => {
|
|
const credential = buildCalDAVCredential({
|
|
id: 1,
|
|
key: "invalid_key",
|
|
});
|
|
|
|
mockedSymmetricDecrypt.mockImplementation(() => {
|
|
throw new Error("Decryption failed");
|
|
});
|
|
|
|
const result = (eventManager as any).extractServerUrlFromCredential(credential);
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("should return null when decrypted data has no URL", () => {
|
|
const credential = buildCalDAVCredential({
|
|
id: 1,
|
|
key: "encrypted_key",
|
|
});
|
|
|
|
mockedSymmetricDecrypt.mockReturnValue(
|
|
JSON.stringify({
|
|
username: "user",
|
|
password: "pass",
|
|
})
|
|
);
|
|
|
|
const result = (eventManager as any).extractServerUrlFromCredential(credential);
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("should return null when URL is invalid", () => {
|
|
const credential = buildCalDAVCredential({
|
|
id: 1,
|
|
key: "encrypted_key",
|
|
});
|
|
|
|
mockedSymmetricDecrypt.mockReturnValue(
|
|
JSON.stringify({
|
|
username: "user",
|
|
password: "pass",
|
|
url: "invalid-url",
|
|
})
|
|
);
|
|
|
|
const result = (eventManager as any).extractServerUrlFromCredential(credential);
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("should handle URLs with different ports", () => {
|
|
const credential = buildCalDAVCredential({
|
|
id: 1,
|
|
key: "encrypted_key",
|
|
});
|
|
|
|
mockedSymmetricDecrypt.mockReturnValue(
|
|
JSON.stringify({
|
|
username: "user",
|
|
password: "pass",
|
|
url: "https://caldav.example.com:8443/dav/calendars/user/",
|
|
})
|
|
);
|
|
|
|
const result = (eventManager as any).extractServerUrlFromCredential(credential);
|
|
expect(result).toBe("https://caldav.example.com:8443");
|
|
});
|
|
|
|
it("should handle HTTP URLs", () => {
|
|
const credential = buildCalDAVCredential({
|
|
id: 1,
|
|
key: "encrypted_key",
|
|
});
|
|
|
|
mockedSymmetricDecrypt.mockReturnValue(
|
|
JSON.stringify({
|
|
username: "user",
|
|
password: "pass",
|
|
url: "http://caldav.internal.com/dav/calendars/user/",
|
|
})
|
|
);
|
|
|
|
const result = (eventManager as any).extractServerUrlFromCredential(credential);
|
|
expect(result).toBe("http://caldav.internal.com");
|
|
});
|
|
});
|
|
|
|
describe("extractServerUrlFromDestination", () => {
|
|
it("should extract server URL from CalDAV destination calendar", () => {
|
|
const destination = buildDestinationCalendar({
|
|
id: 1,
|
|
integration: "caldav_calendar",
|
|
externalId: "https://caldav.example.com/dav/calendars/user/calendar1/",
|
|
});
|
|
|
|
const result = (eventManager as any).extractServerUrlFromDestination(destination);
|
|
expect(result).toBe("https://caldav.example.com");
|
|
});
|
|
|
|
it("should return null for non-CalDAV destination", () => {
|
|
const destination = buildDestinationCalendar({
|
|
id: 1,
|
|
integration: "google_calendar",
|
|
externalId: "calendar@gmail.com",
|
|
});
|
|
|
|
const result = (eventManager as any).extractServerUrlFromDestination(destination);
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("should return null when externalId is null", () => {
|
|
const destination = buildDestinationCalendar({
|
|
id: 1,
|
|
integration: "caldav_calendar",
|
|
externalId: null,
|
|
});
|
|
|
|
const result = (eventManager as any).extractServerUrlFromDestination(destination);
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("should return null when externalId is invalid URL", () => {
|
|
const destination = buildDestinationCalendar({
|
|
id: 1,
|
|
integration: "caldav_calendar",
|
|
externalId: "invalid-url",
|
|
});
|
|
|
|
const result = (eventManager as any).extractServerUrlFromDestination(destination);
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("should handle URLs with different ports", () => {
|
|
const destination = buildDestinationCalendar({
|
|
id: 1,
|
|
integration: "caldav_calendar",
|
|
externalId: "https://caldav.example.com:8443/dav/calendars/user/calendar1/",
|
|
});
|
|
|
|
const result = (eventManager as any).extractServerUrlFromDestination(destination);
|
|
expect(result).toBe("https://caldav.example.com:8443");
|
|
});
|
|
|
|
it("should handle HTTP URLs", () => {
|
|
const destination = buildDestinationCalendar({
|
|
id: 1,
|
|
integration: "caldav_calendar",
|
|
externalId: "http://caldav.internal.com/dav/calendars/user/calendar1/",
|
|
});
|
|
|
|
const result = (eventManager as any).extractServerUrlFromDestination(destination);
|
|
expect(result).toBe("http://caldav.internal.com");
|
|
});
|
|
});
|
|
|
|
describe("credentialMatchesDestination", () => {
|
|
it("should return true for matching CalDAV server URLs", () => {
|
|
const credential = buildCalDAVCredential({
|
|
id: 1,
|
|
key: "encrypted_key",
|
|
});
|
|
|
|
const destination = buildDestinationCalendar({
|
|
id: 1,
|
|
integration: "caldav_calendar",
|
|
externalId: "https://caldav.example.com/dav/calendars/user/calendar1/",
|
|
});
|
|
|
|
mockedSymmetricDecrypt.mockReturnValue(
|
|
JSON.stringify({
|
|
username: "user",
|
|
password: "pass",
|
|
url: "https://caldav.example.com/dav/calendars/user/",
|
|
})
|
|
);
|
|
|
|
const result = (eventManager as any).credentialMatchesDestination(credential, destination);
|
|
expect(result).toBe(true);
|
|
});
|
|
|
|
it("should return false for non-matching CalDAV server URLs", () => {
|
|
const credential = buildCalDAVCredential({
|
|
id: 1,
|
|
key: "encrypted_key",
|
|
});
|
|
|
|
const destination = buildDestinationCalendar({
|
|
id: 1,
|
|
integration: "caldav_calendar",
|
|
externalId: "https://different.example.com/dav/calendars/user/calendar1/",
|
|
});
|
|
|
|
mockedSymmetricDecrypt.mockReturnValue(
|
|
JSON.stringify({
|
|
username: "user",
|
|
password: "pass",
|
|
url: "https://caldav.example.com/dav/calendars/user/",
|
|
})
|
|
);
|
|
|
|
const result = (eventManager as any).credentialMatchesDestination(credential, destination);
|
|
expect(result).toBe(false);
|
|
});
|
|
|
|
it("should return true for non-CalDAV credentials", () => {
|
|
const credential = {
|
|
...buildCalDAVCredential({ id: 1, key: "encrypted_key" }),
|
|
type: "google_calendar",
|
|
} as CredentialForCalendarService;
|
|
|
|
const destination = buildDestinationCalendar({
|
|
id: 1,
|
|
integration: "google_calendar",
|
|
externalId: "calendar@gmail.com",
|
|
});
|
|
|
|
const result = (eventManager as any).credentialMatchesDestination(credential, destination);
|
|
expect(result).toBe(true);
|
|
expect(symmetricDecrypt).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("should return true when credential is CalDAV but destination is not", () => {
|
|
const credential = buildCalDAVCredential({
|
|
id: 1,
|
|
key: "encrypted_key",
|
|
});
|
|
|
|
const destination = buildDestinationCalendar({
|
|
id: 1,
|
|
integration: "google_calendar",
|
|
externalId: "calendar@gmail.com",
|
|
});
|
|
|
|
const result = (eventManager as any).credentialMatchesDestination(credential, destination);
|
|
expect(result).toBe(true);
|
|
expect(symmetricDecrypt).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("should return false when credential URL extraction fails", () => {
|
|
const credential = buildCalDAVCredential({
|
|
id: 1,
|
|
key: "invalid_key",
|
|
});
|
|
|
|
const destination = buildDestinationCalendar({
|
|
id: 1,
|
|
integration: "caldav_calendar",
|
|
externalId: "https://caldav.example.com/dav/calendars/user/calendar1/",
|
|
});
|
|
|
|
mockedSymmetricDecrypt.mockImplementation(() => {
|
|
throw new Error("Decryption failed");
|
|
});
|
|
|
|
const result = (eventManager as any).credentialMatchesDestination(credential, destination);
|
|
expect(result).toBe(false);
|
|
});
|
|
|
|
it("should return false when destination URL extraction fails", () => {
|
|
const credential = buildCalDAVCredential({
|
|
id: 1,
|
|
key: "encrypted_key",
|
|
});
|
|
|
|
const destination = buildDestinationCalendar({
|
|
id: 1,
|
|
integration: "caldav_calendar",
|
|
externalId: "invalid-url",
|
|
});
|
|
|
|
mockedSymmetricDecrypt.mockReturnValue(
|
|
JSON.stringify({
|
|
username: "user",
|
|
password: "pass",
|
|
url: "https://caldav.example.com/dav/calendars/user/",
|
|
})
|
|
);
|
|
|
|
const result = (eventManager as any).credentialMatchesDestination(credential, destination);
|
|
expect(result).toBe(false);
|
|
});
|
|
|
|
it("should handle URLs with different paths but same server", () => {
|
|
const credential = buildCalDAVCredential({
|
|
id: 1,
|
|
key: "encrypted_key",
|
|
});
|
|
|
|
const destination = buildDestinationCalendar({
|
|
id: 1,
|
|
integration: "caldav_calendar",
|
|
externalId: "https://caldav.example.com/different/path/calendar1/",
|
|
});
|
|
|
|
mockedSymmetricDecrypt.mockReturnValue(
|
|
JSON.stringify({
|
|
username: "user",
|
|
password: "pass",
|
|
url: "https://caldav.example.com/dav/calendars/user/",
|
|
})
|
|
);
|
|
|
|
const result = (eventManager as any).credentialMatchesDestination(credential, destination);
|
|
expect(result).toBe(true);
|
|
});
|
|
|
|
it("should handle URLs with different ports as non-matching", () => {
|
|
const credential = buildCalDAVCredential({
|
|
id: 1,
|
|
key: "encrypted_key",
|
|
});
|
|
|
|
const destination = buildDestinationCalendar({
|
|
id: 1,
|
|
integration: "caldav_calendar",
|
|
externalId: "https://caldav.example.com:8443/dav/calendars/user/calendar1/",
|
|
});
|
|
|
|
mockedSymmetricDecrypt.mockReturnValue(
|
|
JSON.stringify({
|
|
username: "user",
|
|
password: "pass",
|
|
url: "https://caldav.example.com:9443/dav/calendars/user/",
|
|
})
|
|
);
|
|
|
|
const result = (eventManager as any).credentialMatchesDestination(credential, destination);
|
|
expect(result).toBe(false);
|
|
});
|
|
|
|
it("should handle HTTP vs HTTPS as non-matching", () => {
|
|
const credential = buildCalDAVCredential({
|
|
id: 1,
|
|
key: "encrypted_key",
|
|
});
|
|
|
|
const destination = buildDestinationCalendar({
|
|
id: 1,
|
|
integration: "caldav_calendar",
|
|
externalId: "https://caldav.example.com/dav/calendars/user/calendar1/",
|
|
});
|
|
|
|
mockedSymmetricDecrypt.mockReturnValue(
|
|
JSON.stringify({
|
|
username: "user",
|
|
password: "pass",
|
|
url: "http://caldav.example.com/dav/calendars/user/",
|
|
})
|
|
);
|
|
|
|
const result = (eventManager as any).credentialMatchesDestination(credential, destination);
|
|
expect(result).toBe(false);
|
|
});
|
|
});
|
|
});
|