mirror of
https://github.com/hanzoai/calendar.git
synced 2026-10-30 14:46:43 +00:00
* 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>
277 lines
7.6 KiB
TypeScript
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();
|
|
});
|
|
});
|