Files
Benny JooGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
ab21c7f805 refactor: Cal.diy (#28903)
* feat: Cal.diy — community-driven MIT-licensed fork of Cal.com

This squashed commit contains all Cal.diy changes applied on top of calcom/cal.com main:

- Rebrand Cal.com to Cal.diy across the entire codebase
- Remove Enterprise Edition (EE) features, license checks, and AGPL restrictions
- Switch license from AGPL-3.0 to MIT
- Remove docs/ directory (migrated to Nextra at cal.diy)
- Remove dead code: org tests, EE tips, platform nav, premium username, SAML/SSO, etc.
- Clean up .env.example for self-hosted Cal.diy
- Update Docker image references to calcom/cal.diy
- Update README, CONTRIBUTING.md, and issue templates for Cal.diy community fork
- Add PR welcome bot for Cal.diy contributors
- Fix API v2 breaking changes oasdiff ignore entries
- Replace Blacksmith CI runners with default GitHub Actions

3893 files changed, 20789 insertions(+), 411020 deletions(-)

Co-Authored-By: [email protected] <[email protected]>

* refactor: remove org-specific /organizations/:orgId endpoints from API v2 atoms controllers (#1701)

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix: revert Cal.diy Inc to Cal.com, Inc. in license files, copyright notices, and package metadata (#1702)

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* rip out org related comments in api v2

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-04-15 09:52:36 -03:00

277 lines
7.6 KiB
TypeScript

import getAllUserBookings from "@calcom/features/bookings/lib/getAllUserBookings";
import type { DB } from "@calcom/kysely";
import type { PrismaClient } from "@calcom/prisma";
import type { Kysely } from "kysely";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { getBookings, getHandler } from "./get.handler";
vi.mock("@calcom/features/bookings/lib/getAllUserBookings");
vi.mock("@calcom/kysely", () => ({
default: {
selectFrom: vi.fn(),
executeQuery: vi.fn(),
},
}));
vi.mock("@calcom/lib/logger", () => ({
default: {
getSubLogger: () => ({
debug: vi.fn(),
info: vi.fn(),
error: vi.fn(),
}),
},
}));
describe("getHandler", () => {
const mockUser = {
id: 1,
email: "[email protected]",
name: "Test User",
profile: {
organizationId: null,
},
};
const mockPrisma = {} as unknown as PrismaClient;
beforeEach(() => {
vi.clearAllMocks();
});
it("should return bookings successfully", async () => {
const mockBookings = [
{
id: 1,
uid: "booking-1",
startTime: new Date().toISOString(),
endTime: new Date().toISOString(),
rescheduler: null,
eventType: {
recurringEvent: null,
eventTypeColor: null,
price: 0,
currency: "usd",
metadata: {},
},
},
] as any;
vi.mocked(getAllUserBookings).mockResolvedValue({
bookings: mockBookings,
recurringInfo: [],
totalCount: 1,
});
const result = await getHandler({
ctx: {
user: mockUser as any,
prisma: mockPrisma,
},
input: {
filters: {},
limit: 10,
offset: 0,
},
});
expect(result.bookings).toEqual(mockBookings);
expect(result.totalCount).toBe(1);
expect(getAllUserBookings).toHaveBeenCalledWith(
expect.objectContaining({
ctx: expect.objectContaining({
user: expect.objectContaining({
id: mockUser.id,
email: mockUser.email,
orgId: null,
}),
}),
filters: {},
take: 10,
skip: 0,
bookingListingByStatus: ["upcoming"],
})
);
});
});
describe("getBookings - stub PermissionCheckService behavior", () => {
const mockUser = {
id: 1,
email: "[email protected]",
orgId: null,
};
const mockPrisma = {
membership: {
findMany: vi.fn(),
},
user: {
findMany: vi.fn(),
},
eventType: {
findMany: vi.fn(),
},
booking: {
findUnique: vi.fn(),
groupBy: vi.fn(),
},
$queryRaw: vi.fn().mockResolvedValue([]),
} as unknown as PrismaClient;
const createMockKysely = () => {
const mockQueryBuilder = {
select: vi.fn((arg?: unknown) => {
if (typeof arg === "function") {
return mockQueryBuilder;
}
return mockQueryBuilder;
}),
selectAll: vi.fn(() => mockQueryBuilder),
where: vi.fn(() => mockQueryBuilder),
innerJoin: vi.fn(() => mockQueryBuilder),
union: vi.fn(() => mockQueryBuilder),
unionAll: vi.fn(() => mockQueryBuilder),
distinct: vi.fn(() => mockQueryBuilder),
as: vi.fn(() => mockQueryBuilder),
$if: vi.fn(() => mockQueryBuilder),
orderBy: vi.fn(() => mockQueryBuilder),
limit: vi.fn(() => mockQueryBuilder),
offset: vi.fn(() => mockQueryBuilder),
compile: vi.fn(() => ({ sql: "SELECT * FROM bookings" })),
executeTakeFirst: vi.fn().mockResolvedValue({ bookingCount: 0 }),
execute: vi.fn().mockResolvedValue([]),
};
return {
selectFrom: vi.fn(() => mockQueryBuilder),
executeQuery: vi.fn().mockResolvedValue({ rows: [] }),
_mockQueryBuilder: mockQueryBuilder,
} as unknown as Kysely<DB> & { _mockQueryBuilder: typeof mockQueryBuilder };
};
let mockKysely: ReturnType<typeof createMockKysely>;
beforeEach(() => {
vi.clearAllMocks();
mockKysely = createMockKysely();
});
it("should allow access when filtering by own userId", async () => {
mockPrisma.user.findMany = vi.fn((args: { where?: { id?: { in?: number[] } } }) => {
if (args?.where?.id?.in?.includes(1)) {
return Promise.resolve([{ id: 1, email: "[email protected]" }]) as ReturnType<typeof mockPrisma.user.findMany>;
}
return Promise.resolve([]) as ReturnType<typeof mockPrisma.user.findMany>;
});
mockPrisma.eventType.findMany = vi.fn().mockResolvedValue([]);
mockPrisma.booking.groupBy = vi.fn().mockResolvedValue([]);
await expect(
getBookings({
user: mockUser,
prisma: mockPrisma,
kysely: mockKysely as unknown as Kysely<DB>,
bookingListingByStatus: ["upcoming"],
filters: {
userIds: [1],
},
take: 10,
skip: 0,
})
).resolves.not.toThrow();
});
it("should throw BAD_REQUEST when filtering by non-existent userIds", async () => {
mockPrisma.user.findMany = vi.fn().mockResolvedValue([]);
mockPrisma.eventType.findMany = vi.fn().mockResolvedValue([]);
mockPrisma.booking.groupBy = vi.fn().mockResolvedValue([]);
await expect(
getBookings({
user: mockUser,
prisma: mockPrisma,
kysely: mockKysely as unknown as Kysely<DB>,
bookingListingByStatus: ["upcoming"],
filters: {
userIds: [4],
},
take: 10,
skip: 0,
})
).rejects.toMatchObject({
code: "BAD_REQUEST",
message: "The requested users do not exist.",
});
});
it("should execute query via kysely when no userIds filter is provided", async () => {
mockPrisma.user.findMany = vi.fn().mockResolvedValue([]);
mockPrisma.eventType.findMany = vi.fn().mockResolvedValue([]);
mockPrisma.booking.groupBy = vi.fn().mockResolvedValue([]);
await getBookings({
user: mockUser,
prisma: mockPrisma,
kysely: mockKysely as unknown as Kysely<DB>,
bookingListingByStatus: ["upcoming"],
filters: {},
take: 10,
skip: 0,
});
expect((mockKysely as unknown as { executeQuery: ReturnType<typeof vi.fn> }).executeQuery).toHaveBeenCalled();
});
it("should NOT fetch user IDs when no userIds filter is provided", async () => {
mockPrisma.user.findMany = vi.fn().mockResolvedValue([]);
mockPrisma.eventType.findMany = vi.fn().mockResolvedValue([]);
mockPrisma.booking.groupBy = vi.fn().mockResolvedValue([]);
await getBookings({
user: mockUser,
prisma: mockPrisma,
kysely: mockKysely as unknown as Kysely<DB>,
bookingListingByStatus: ["upcoming"],
filters: {},
take: 10,
skip: 0,
});
expect(mockPrisma.user.findMany).not.toHaveBeenCalled();
});
it("should use unionAll for combining booking queries", async () => {
mockPrisma.user.findMany = vi.fn().mockResolvedValue([]);
mockPrisma.booking.groupBy = vi.fn().mockResolvedValue([]);
await getBookings({
user: mockUser,
prisma: mockPrisma,
kysely: mockKysely as unknown as Kysely<DB>,
bookingListingByStatus: ["upcoming"],
filters: {},
take: 10,
skip: 0,
});
expect(mockKysely._mockQueryBuilder.unionAll).toHaveBeenCalled();
});
it("should apply DISTINCT on the outer select", async () => {
mockPrisma.user.findMany = vi.fn().mockResolvedValue([]);
mockPrisma.booking.groupBy = vi.fn().mockResolvedValue([]);
await getBookings({
user: mockUser,
prisma: mockPrisma,
kysely: mockKysely as unknown as Kysely<DB>,
bookingListingByStatus: ["upcoming"],
filters: {},
take: 10,
skip: 0,
});
expect(mockKysely._mockQueryBuilder.distinct).toHaveBeenCalled();
});
});