Files
calendar/apps/web/app/api/link/__tests__/route.test.ts
T
Hariom BalharaGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2f92563606 refactor: Use confirmHandler directly in link and verify-booking-token routes (#26567)
Refactors the link and verify-booking-token API routes to call confirmHandler directly instead of using the tRPC caller pattern. This simplifies the code by removing the need for session getter, legacy request building, and context creation.

Changes:
- apps/web/app/api/link/route.ts: Use confirmHandler directly with ctx and input
- apps/web/app/api/verify-booking-token/route.ts: Use confirmHandler directly with ctx and input
- Updated tests to mock confirmHandler instead of tRPC caller

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-01-08 08:23:48 +00:00

210 lines
6.8 KiB
TypeScript

import type { NextRequest } from "next/server";
import { describe, it, expect, vi, beforeEach } from "vitest";
import { confirmHandler } from "@calcom/trpc/server/routers/viewer/bookings/confirm.handler";
import type { Mock } from "vitest";
const mockConfirmHandler = confirmHandler as unknown as Mock<typeof confirmHandler>;
vi.mock("app/api/defaultResponderForAppDir", () => ({
defaultResponderForAppDir:
(handler: (req: NextRequest) => Promise<Response>) =>
(req: NextRequest, _context: { params: Promise<Record<string, string>> }) =>
handler(req),
}));
vi.mock("next/headers", () => ({
headers: vi.fn().mockResolvedValue(new Headers()),
cookies: vi.fn().mockResolvedValue({ getAll: () => [] }),
}));
vi.mock("next/server", () => ({
NextResponse: {
redirect: vi.fn((url: string | URL, init?: { status?: number }) => {
const location = typeof url === "string" ? url : url.toString();
return {
status: init?.status ?? 302,
headers: {
get: (name: string) => (name.toLowerCase() === "location" ? location : null),
},
} as unknown as Response;
}),
},
}));
vi.mock("@calcom/lib/crypto", () => ({
symmetricDecrypt: vi.fn().mockReturnValue(
JSON.stringify({
bookingUid: "test-booking-uid",
userId: 1,
})
),
}));
vi.mock("@calcom/prisma", () => {
const mockBookingFindUniqueOrThrow = vi.fn().mockResolvedValue({
id: 1,
uid: "test-booking-uid",
recurringEventId: null,
});
const mockUserFindUniqueOrThrow = vi.fn().mockResolvedValue({
id: 1,
uuid: "user-uuid",
email: "test@example.com",
username: "testuser",
role: "USER",
destinationCalendar: null,
});
const mockPrismaObj = {
booking: {
findUniqueOrThrow: mockBookingFindUniqueOrThrow,
},
user: {
findUniqueOrThrow: mockUserFindUniqueOrThrow,
},
};
return {
default: mockPrismaObj,
prisma: mockPrismaObj,
};
});
vi.mock("@calcom/trpc/server/routers/viewer/bookings/confirm.handler", () => ({
confirmHandler: vi.fn(),
}));
vi.mock("@calcom/lib/tracing/factory", () => ({
distributedTracing: {
createTrace: vi.fn().mockReturnValue({}),
},
}));
// Import after mocks are set up
import { GET } from "../route";
import prisma from "@calcom/prisma";
const createMockRequest = (url: string): NextRequest => {
const urlObj = new URL(url);
return {
method: "GET",
url,
nextUrl: {
searchParams: urlObj.searchParams,
},
} as unknown as NextRequest;
};
describe("link route", () => {
beforeEach(() => {
vi.clearAllMocks();
});
describe("GET handler - redirect URL construction", () => {
it("should redirect to booking page with the same origin as the request", async () => {
const baseUrl = "https://app.example.com/api/link?action=accept&token=encrypted-token";
const req = createMockRequest(baseUrl);
const res = await GET(req, { params: Promise.resolve({}) });
const location = res.headers.get("location");
expect(location).toBeTruthy();
const redirectUrl = new URL(location!);
expect(redirectUrl.origin).toBe("https://app.example.com");
expect(redirectUrl.pathname).toBe("/booking/test-booking-uid");
});
it("should preserve custom domain origin in redirect URL", async () => {
const baseUrl = "https://custom-domain.company.com/api/link?action=accept&token=encrypted-token";
const req = createMockRequest(baseUrl);
const res = await GET(req, { params: Promise.resolve({}) });
const location = res.headers.get("location");
expect(location).toBeTruthy();
const redirectUrl = new URL(location!);
expect(redirectUrl.origin).toBe("https://custom-domain.company.com");
expect(location).not.toContain("localhost");
});
it("should preserve self-hosted domain origin in redirect URL", async () => {
const baseUrl = "https://calcom.internal.company.net/api/link?action=reject&token=encrypted-token";
const req = createMockRequest(baseUrl);
const res = await GET(req, { params: Promise.resolve({}) });
const location = res.headers.get("location");
expect(location).toBeTruthy();
const redirectUrl = new URL(location!);
expect(redirectUrl.origin).toBe("https://calcom.internal.company.net");
expect(location).not.toContain("localhost");
});
it("should construct redirect URLs relative to the request URL for various origins", async () => {
const testOrigins = [
"https://app.cal.com",
"https://acme.cal.com",
"https://calcom.company.internal",
"http://192.168.1.100:3000",
];
for (const origin of testOrigins) {
vi.clearAllMocks();
const baseUrl = `${origin}/api/link?action=accept&token=encrypted-token`;
const req = createMockRequest(baseUrl);
const res = await GET(req, { params: Promise.resolve({}) });
const location = res.headers.get("location");
expect(location).toBeTruthy();
const redirectUrl = new URL(location!);
expect(redirectUrl.origin).toBe(origin);
expect(redirectUrl.pathname).toBe("/booking/test-booking-uid");
}
});
});
describe("GET handler - error handling", () => {
it("should redirect with error message when confirmHandler throws a TRPCError", async () => {
const { TRPCError } = await import("@trpc/server");
// Mock confirmHandler to throw a TRPCError
mockConfirmHandler.mockRejectedValueOnce(new TRPCError({ code: "BAD_REQUEST", message: "Custom error" }));
const baseUrl = "https://app.example.com/api/link?action=accept&token=encrypted-token";
const req = createMockRequest(baseUrl);
const res = await GET(req, { params: Promise.resolve({}) });
const location = res.headers.get("location");
expect(location).toBeTruthy();
const redirectUrl = new URL(location!);
expect(redirectUrl.origin).toBe("https://app.example.com");
expect(redirectUrl.pathname).toBe("/booking/test-booking-uid");
expect(redirectUrl.searchParams.get("error")).toBe("Custom error");
});
it("should preserve origin in error redirect URL", async () => {
const { TRPCError } = await import("@trpc/server");
// Mock confirmHandler to throw a TRPCError
mockConfirmHandler.mockRejectedValueOnce(new TRPCError({ code: "INTERNAL_SERVER_ERROR" }));
const baseUrl = "https://self-hosted.company.org/api/link?action=accept&token=encrypted-token";
const req = createMockRequest(baseUrl);
const res = await GET(req, { params: Promise.resolve({}) });
const location = res.headers.get("location");
expect(location).toBeTruthy();
const redirectUrl = new URL(location!);
expect(redirectUrl.origin).toBe("https://self-hosted.company.org");
expect(location).not.toContain("localhost");
});
});
});