* feat: calendar cache and sync - wip * Add env.example * refactor on CalendarCacheEventService * remove test console.log * Fix type checks errors * chore: remove pt comment * add route.ts * chore: fix tests * Improve cache impl * chore: update recurring event id * chore: small improvements * calendar cache improvements * Fix remove dynamic imports * Add cleanup stale cache * Fix tests * add event update * type fixes * feat: add comprehensive tests for new calendar subscription API routes - Add tests for /api/cron/calendar-subscriptions-cleanup route (9 tests) - Add tests for /api/cron/calendar-subscriptions route (10 tests) - Add tests for /api/webhooks/calendar-subscription/[provider] route (11 tests) - Add missing feature flags for calendar-subscription-cache and calendar-subscription-sync - All 30 tests pass with comprehensive coverage of authentication, feature flags, error handling, and service instantiation Tests cover: - Authentication scenarios (API key validation, Bearer tokens, query parameters) - Feature flag combinations (cache/sync enabled/disabled states) - Success and error handling (including non-Error exceptions) - Service instantiation with proper dependency injection - Provider validation for webhook endpoints Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com> * feat: add comprehensive tests for calendar subscription services, repositories, and adapters - Add unit tests for CalendarSubscriptionService with subscription, webhook, and event processing - Add unit tests for CalendarCacheEventService with cache operations and cleanup - Add unit tests for CalendarSyncService with Cal.com event filtering and booking operations - Add unit tests for CalendarCacheEventRepository with CRUD operations - Add unit tests for SelectedCalendarRepository with calendar selection management - Add unit tests for GoogleCalendarSubscriptionAdapter with subscription and event fetching - Add unit tests for Office365CalendarSubscriptionAdapter with placeholder implementation - Add unit tests for AdaptersFactory with provider management and adapter creation - Fix lint issues by removing explicit 'any' type casting and unused variables - All tests follow Cal.com conventions using Vitest framework with proper mocking Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com> * fix: improve calendar-subscriptions-cleanup test performance by adding missing mocks - Add comprehensive mocks for defaultResponderForAppDir, logger, performance monitoring, and Sentry - Fix slow test execution (933ms -> <100ms) caused by missing dependency mocks - Ensure consistent test performance across different environments Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com> * Fix tests * Fix tests * type fix * Fix coderabbit comments * Fix types * Fix test * Update apps/web/app/api/cron/calendar-subscriptions/route.ts Co-authored-by: Alex van Andel <me@alexvanandel.com> * Fixes by first review * feat: add database migrations for calendar cache and sync fields - Add CalendarCacheEventStatus enum with confirmed, tentative, cancelled values - Add new fields to SelectedCalendar: channelId, channelKind, channelResourceId, channelResourceUri, channelExpiration, syncSubscribedAt, syncToken, syncedAt, syncErrorAt, syncErrorCount - Create CalendarCacheEvent table with foreign key to SelectedCalendar - Add necessary indexes and constraints for performance and data integrity Fixes database schema issues causing e2e test failures with 'column does not exist' errors. Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com> * only google-calendar for now * docs: add Calendar Cache and Sync feature documentation - Add comprehensive feature overview and motivation - Document feature flags with SQL examples - Include SQL examples for enabling features for users and teams - Reference technical documentation files Addresses PR #23876 documentation requirements Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com> * docs: update calendar subscription README with comprehensive documentation - Undo incorrect changes to main README.md - Update packages/features/calendar-subscription/README.md with: - Feature overview and motivation - Environment variables section - Complete feature flags documentation with SQL examples - SQL examples for enabling features for users and teams - Detailed architecture documentation Addresses PR #23876 documentation requirements Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com> * fix docs * Fix test to available calendars * Fix test to available calendars * add migration and sync boilerplate * fix typo * remove double log * sync boilerplate --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Alex van Andel <me@alexvanandel.com> Co-authored-by: Keith Williams <keithwillcode@gmail.com>
175 lines
5.2 KiB
TypeScript
175 lines
5.2 KiB
TypeScript
import prismaMock from "../../../../../../tests/libs/__mocks__/prismaMock";
|
|
|
|
import type { Request, Response } from "express";
|
|
import type { NextApiRequest, NextApiResponse } from "next";
|
|
import { createMocks } from "node-mocks-http";
|
|
import { describe, expect, test } from "vitest";
|
|
|
|
import { HttpError } from "@calcom/lib/http-error";
|
|
import type { User } from "@calcom/prisma/client";
|
|
|
|
import handler from "../../../pages/api/selected-calendars/_post";
|
|
|
|
type CustomNextApiRequest = NextApiRequest & Request;
|
|
type CustomNextApiResponse = NextApiResponse & Response;
|
|
|
|
describe("POST /api/selected-calendars", () => {
|
|
describe("Errors", () => {
|
|
test("Returns 403 if non-admin user tries to set userId in body", async () => {
|
|
const { req, res } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
|
|
method: "POST",
|
|
body: {
|
|
integration: "google",
|
|
externalId: "ext123",
|
|
userId: 444444,
|
|
},
|
|
});
|
|
|
|
req.userId = 333333;
|
|
|
|
try {
|
|
await handler(req, res);
|
|
} catch (e) {
|
|
expect(e).toBeInstanceOf(HttpError);
|
|
expect((e as HttpError).statusCode).toBe(403);
|
|
expect((e as HttpError).message).toBe("ADMIN required for userId");
|
|
}
|
|
});
|
|
|
|
test("Returns 400 if request body is invalid", async () => {
|
|
const { req, res } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
|
|
method: "POST",
|
|
body: {
|
|
integration: "google",
|
|
},
|
|
});
|
|
|
|
req.userId = 333333;
|
|
req.isSystemWideAdmin = true;
|
|
|
|
await handler(req, res);
|
|
|
|
expect(res.statusCode).toBe(400);
|
|
expect(JSON.parse(res._getData()).message).toBe("invalid_type in 'externalId': Required");
|
|
});
|
|
});
|
|
|
|
describe("Success", () => {
|
|
test("Creates selected calendar if user is admin and sets bodyUserId", async () => {
|
|
const { req, res } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
|
|
method: "POST",
|
|
query: {
|
|
apiKey: "validApiKey",
|
|
},
|
|
body: {
|
|
integration: "google",
|
|
externalId: "ext123",
|
|
userId: 444444,
|
|
},
|
|
});
|
|
|
|
req.userId = 333333;
|
|
req.isSystemWideAdmin = true;
|
|
|
|
prismaMock.user.findFirstOrThrow.mockResolvedValue({
|
|
id: 444444,
|
|
} as User);
|
|
|
|
prismaMock.selectedCalendar.create.mockResolvedValue({
|
|
credentialId: 1,
|
|
integration: "google",
|
|
externalId: "ext123",
|
|
userId: 444444,
|
|
id: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
eventTypeId: null,
|
|
delegationCredentialId: null,
|
|
domainWideDelegationCredentialId: null,
|
|
googleChannelId: null,
|
|
googleChannelKind: null,
|
|
googleChannelResourceId: null,
|
|
googleChannelResourceUri: null,
|
|
googleChannelExpiration: null,
|
|
error: null,
|
|
lastErrorAt: null,
|
|
watchAttempts: 0,
|
|
maxAttempts: 3,
|
|
unwatchAttempts: 0,
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
channelId: null,
|
|
channelKind: null,
|
|
channelResourceId: null,
|
|
channelResourceUri: null,
|
|
channelExpiration: null,
|
|
syncSubscribedAt: null,
|
|
syncToken: null,
|
|
syncedAt: null,
|
|
syncErrorAt: null,
|
|
syncErrorCount: null,
|
|
});
|
|
|
|
await handler(req, res);
|
|
|
|
expect(res.statusCode).toBe(200);
|
|
const responseData = JSON.parse(res._getData());
|
|
expect(responseData.selected_calendar.credentialId).toBe(1);
|
|
expect(responseData.message).toBe("Selected Calendar created successfully");
|
|
});
|
|
|
|
test("Creates selected calendar if user is non-admin and does not set bodyUserId", async () => {
|
|
const { req, res } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
|
|
method: "POST",
|
|
query: {
|
|
apiKey: "validApiKey",
|
|
},
|
|
body: {
|
|
integration: "google",
|
|
externalId: "ext123",
|
|
},
|
|
});
|
|
|
|
req.userId = 333333;
|
|
|
|
prismaMock.selectedCalendar.create.mockResolvedValue({
|
|
id: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
credentialId: 1,
|
|
integration: "google",
|
|
externalId: "ext123",
|
|
userId: 333333,
|
|
googleChannelId: null,
|
|
googleChannelKind: null,
|
|
googleChannelResourceId: null,
|
|
googleChannelResourceUri: null,
|
|
googleChannelExpiration: null,
|
|
delegationCredentialId: null,
|
|
domainWideDelegationCredentialId: null,
|
|
eventTypeId: null,
|
|
error: null,
|
|
lastErrorAt: null,
|
|
watchAttempts: 0,
|
|
maxAttempts: 3,
|
|
unwatchAttempts: 0,
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
channelId: null,
|
|
channelKind: null,
|
|
channelResourceId: null,
|
|
channelResourceUri: null,
|
|
channelExpiration: null,
|
|
syncSubscribedAt: null,
|
|
syncToken: null,
|
|
syncedAt: null,
|
|
syncErrorAt: null,
|
|
syncErrorCount: null,
|
|
});
|
|
|
|
await handler(req, res);
|
|
|
|
expect(res.statusCode).toBe(200);
|
|
const responseData = JSON.parse(res._getData());
|
|
expect(responseData.selected_calendar.credentialId).toBe(1);
|
|
expect(responseData.message).toBe("Selected Calendar created successfully");
|
|
});
|
|
});
|
|
});
|