chore: convert booking_denormalized e2e test to integration test (#21148)

This commit is contained in:
Eunjae Lee authored and GitHub committed 2025-05-07 14:23:25 +02:00
1 parent 2bce38fd2a
commit ac821f6b13
2 files changed
+265 -201

No files matched your search

@@ -1,201 +0,0 @@
import { expect } from "@playwright/test";
import type { User, EventType } from "@calcom/prisma/client";
import { BookingStatus } from "@calcom/prisma/enums";
import { test } from "./lib/fixtures";
test.describe.configure({ mode: "parallel" });
test.describe("BookingDenormalized", () => {
let userId: number;
let eventTypeId: number;
let bookingId: number;
let user: User;
let eventType: EventType;
test.beforeEach(async ({ prisma }) => {
// Create test user with random email to avoid conflicts
const randomId = Math.floor(Math.random() * 1000000);
user = await prisma.user.create({
data: {
email: `booking-denorm-${randomId}@example.com`,
username: `booking-denorm-testuser-${randomId}`,
name: "Test User",
},
});
userId = user.id;
// Create test event type
eventType = await prisma.eventType.create({
data: {
title: "Test Event",
slug: "test-event",
length: 60,
userId: user.id,
},
});
eventTypeId = eventType.id;
// Create test booking with PENDING status (no idempotencyKey needed)
const booking = await prisma.booking.create({
data: {
uid: `booking-denorm-${randomId}`,
title: "Test Booking",
startTime: new Date(),
endTime: new Date(Date.now() + 60 * 60 * 1000), // 1 hour later
userId: user.id,
eventTypeId: eventType.id,
status: BookingStatus.PENDING,
},
});
bookingId = booking.id;
});
test.afterEach(async ({ prisma }) => {
// Clean up test data in reverse order of creation to avoid foreign key constraints
if (bookingId) {
await prisma.booking.deleteMany({
where: { id: bookingId },
});
}
if (eventTypeId) {
await prisma.eventType.deleteMany({
where: { id: eventTypeId },
});
}
if (userId) {
await prisma.user.deleteMany({
where: { id: userId },
});
}
});
test("should create denormalized entry when booking is created", async ({ prisma }) => {
const denormalizedBooking = await prisma.bookingDenormalized.findUniqueOrThrow({
where: { id: bookingId },
});
expect(denormalizedBooking.eventTypeId).toBe(eventTypeId);
expect(denormalizedBooking.userId).toBe(userId);
expect(denormalizedBooking.title).toBe("Test Booking");
expect(denormalizedBooking.eventLength).toBe(60);
expect(denormalizedBooking.userName).toBe("Test User");
});
test("should update denormalized entry when booking is updated", async ({ prisma }) => {
await prisma.booking.update({
where: { id: bookingId },
data: {
title: "Updated Booking",
status: BookingStatus.CANCELLED,
},
});
const updatedDenormalizedBooking = await prisma.bookingDenormalized.findUniqueOrThrow({
where: { id: bookingId },
});
expect(updatedDenormalizedBooking.title).toBe("Updated Booking");
expect(updatedDenormalizedBooking.status).toBe(BookingStatus.CANCELLED);
});
test("should update denormalized entry when user is updated", async ({ prisma }) => {
await prisma.user.update({
where: { id: userId },
data: {
name: "Updated User",
email: "updated@example.com",
},
});
const userUpdatedDenormalizedBooking = await prisma.bookingDenormalized.findUniqueOrThrow({
where: { id: bookingId },
});
expect(userUpdatedDenormalizedBooking.userName).toBe("Updated User");
expect(userUpdatedDenormalizedBooking.userEmail).toBe("updated@example.com");
});
test("should update denormalized entry when event type is updated", async ({ prisma }) => {
await prisma.eventType.update({
where: { id: eventTypeId },
data: {
length: 30,
},
});
const eventTypeUpdatedDenormalizedBooking = await prisma.bookingDenormalized.findUniqueOrThrow({
where: { id: bookingId },
});
expect(eventTypeUpdatedDenormalizedBooking.eventLength).toBe(30);
});
test("should delete denormalized entry when booking is deleted", async ({ prisma }) => {
await prisma.booking.delete({
where: { id: bookingId },
});
const deletedDenormalizedBooking = await prisma.bookingDenormalized.findUnique({
where: { id: bookingId },
});
expect(deletedDenormalizedBooking).toBeNull();
});
test("should update denormalized entries when event type is deleted", async ({ prisma }) => {
// Create an additional booking for this test
const booking2 = await prisma.booking.create({
data: {
uid: `test-booking-${Math.floor(Math.random() * 1000000)}`,
title: "Test Booking 2",
startTime: new Date(),
endTime: new Date(Date.now() + 60 * 60 * 1000),
userId: userId,
eventTypeId: eventTypeId,
status: BookingStatus.PENDING,
},
});
await prisma.eventType.delete({
where: { id: eventTypeId },
});
const eventTypeDeletedDenormalizedBooking = await prisma.bookingDenormalized.findUniqueOrThrow({
where: { id: booking2.id },
});
expect(eventTypeDeletedDenormalizedBooking.eventTypeId).toBeNull();
expect(eventTypeDeletedDenormalizedBooking.eventLength).toBeNull();
});
test("should delete denormalized entries when user is deleted", async ({ prisma }) => {
// Create an additional booking for this test
const booking2 = await prisma.booking.create({
data: {
uid: `test-booking-${Math.floor(Math.random() * 1000000)}`,
title: "Test Booking 2",
startTime: new Date(),
endTime: new Date(Date.now() + 60 * 60 * 1000),
userId: userId,
eventTypeId: eventTypeId,
status: BookingStatus.PENDING,
},
});
await prisma.user.delete({
where: { id: userId },
});
const userDeletedDenormalizedBooking1 = await prisma.bookingDenormalized.findUnique({
where: { id: bookingId },
});
const userDeletedDenormalizedBooking2 = await prisma.bookingDenormalized.findUnique({
where: { id: booking2.id },
});
expect(userDeletedDenormalizedBooking1).toBeNull();
expect(userDeletedDenormalizedBooking2).toBeNull();
});
});
@@ -0,0 +1,265 @@
import { describe, expect, beforeEach, afterEach, it } from "vitest";
import prisma from "@calcom/prisma";
import { BookingStatus, SchedulingType } from "@calcom/prisma/enums";
describe("BookingDenormalized", () => {
let userId: number;
let eventTypeId: number;
let bookingId: number;
const randomId = Math.floor(Math.random() * 1000000);
const email = `booking-denorm-${randomId}@example.com`;
beforeEach(async () => {
// Create test user
const user = await prisma.user.create({
data: {
email,
username: `booking-denorm-testuser-${randomId}`,
name: "Test User",
},
});
userId = user.id;
// Create test event type
const eventType = await prisma.eventType.create({
data: {
title: "Test Event",
slug: "test-event",
length: 60,
userId: user.id,
},
});
eventTypeId = eventType.id;
// Create test booking
const booking = await prisma.booking.create({
data: {
uid: `booking-denorm-${randomId}`,
title: "Test Booking",
startTime: new Date(),
endTime: new Date(Date.now() + 60 * 60 * 1000),
userId: user.id,
eventTypeId: eventType.id,
status: BookingStatus.PENDING,
},
});
bookingId = booking.id;
});
afterEach(async () => {
// Clean up test data in reverse order of creation
if (bookingId) {
await prisma.booking.deleteMany({
where: { id: bookingId },
});
}
if (eventTypeId) {
await prisma.eventType.deleteMany({
where: { id: eventTypeId },
});
}
if (userId) {
await prisma.user.deleteMany({
where: { id: userId },
});
}
});
describe("Creation and Updates", () => {
it("should create denormalized entry when booking is created", async () => {
const denormalizedBooking = await prisma.bookingDenormalized.findUniqueOrThrow({
where: { id: bookingId },
});
expect(denormalizedBooking.eventTypeId).toBe(eventTypeId);
expect(denormalizedBooking.userId).toBe(userId);
expect(denormalizedBooking.title).toBe("Test Booking");
expect(denormalizedBooking.eventLength).toBe(60);
expect(denormalizedBooking.userName).toBe("Test User");
});
it("should update denormalized entry when booking is updated", async () => {
await prisma.booking.update({
where: { id: bookingId },
data: {
title: "Updated Booking",
status: BookingStatus.CANCELLED,
},
});
const updatedDenormalizedBooking = await prisma.bookingDenormalized.findUniqueOrThrow({
where: { id: bookingId },
});
expect(updatedDenormalizedBooking.title).toBe("Updated Booking");
expect(updatedDenormalizedBooking.status).toBe(BookingStatus.CANCELLED);
});
it("should update denormalized entry when user is updated", async () => {
await prisma.user.update({
where: { id: userId },
data: {
name: "Updated User",
email: "updated@example.com",
},
});
const userUpdatedDenormalizedBooking = await prisma.bookingDenormalized.findUniqueOrThrow({
where: { id: bookingId },
});
expect(userUpdatedDenormalizedBooking.userName).toBe("Updated User");
expect(userUpdatedDenormalizedBooking.userEmail).toBe("updated@example.com");
});
it("should update denormalized entry when event type is updated", async () => {
await prisma.eventType.update({
where: { id: eventTypeId },
data: {
length: 30,
schedulingType: SchedulingType.ROUND_ROBIN,
},
});
const eventTypeUpdatedDenormalizedBooking = await prisma.bookingDenormalized.findUniqueOrThrow({
where: { id: bookingId },
});
expect(eventTypeUpdatedDenormalizedBooking.eventLength).toBe(30);
});
});
describe("Deletion Scenarios", () => {
it("should delete denormalized entry when booking is deleted", async () => {
await prisma.booking.delete({
where: { id: bookingId },
});
const deletedDenormalizedBooking = await prisma.bookingDenormalized.findUnique({
where: { id: bookingId },
});
expect(deletedDenormalizedBooking).toBeNull();
});
it("should update denormalized entries when event type is deleted", async () => {
// Create an additional booking for this test
const booking2 = await prisma.booking.create({
data: {
uid: `test-booking-${Math.floor(Math.random() * 1000000)}`,
title: "Test Booking 2",
startTime: new Date(),
endTime: new Date(Date.now() + 60 * 60 * 1000),
userId: userId,
eventTypeId: eventTypeId,
status: BookingStatus.PENDING,
},
});
await prisma.eventType.delete({
where: { id: eventTypeId },
});
const eventTypeDeletedDenormalizedBooking = await prisma.bookingDenormalized.findUniqueOrThrow({
where: { id: booking2.id },
});
expect(eventTypeDeletedDenormalizedBooking.eventTypeId).toBeNull();
expect(eventTypeDeletedDenormalizedBooking.eventLength).toBeNull();
// Clean up the additional booking
await prisma.booking.delete({
where: { id: booking2.id },
});
});
it("should delete denormalized entries when user is deleted", async () => {
// Create an additional booking for this test
const booking2 = await prisma.booking.create({
data: {
uid: `test-booking-${Math.floor(Math.random() * 1000000)}`,
title: "Test Booking 2",
startTime: new Date(),
endTime: new Date(Date.now() + 60 * 60 * 1000),
userId: userId,
eventTypeId: eventTypeId,
status: BookingStatus.PENDING,
},
});
await prisma.user.delete({
where: { id: userId },
});
const userDeletedDenormalizedBooking1 = await prisma.bookingDenormalized.findUnique({
where: { id: bookingId },
});
const userDeletedDenormalizedBooking2 = await prisma.bookingDenormalized.findUnique({
where: { id: booking2.id },
});
expect(userDeletedDenormalizedBooking1).toBeNull();
expect(userDeletedDenormalizedBooking2).toBeNull();
});
});
describe("Data Integrity", () => {
it("should maintain correct relationships across all tables", async () => {
const denormalizedBooking = await prisma.bookingDenormalized.findUniqueOrThrow({
where: { id: bookingId },
});
const originalBooking = await prisma.booking.findUniqueOrThrow({
where: { id: bookingId },
});
// Verify relationships and data consistency
expect(denormalizedBooking.uid).toBe(originalBooking.uid);
expect(denormalizedBooking.eventTypeId).toBe(originalBooking.eventTypeId);
expect(denormalizedBooking.userId).toBe(originalBooking.userId);
expect(denormalizedBooking.status).toBe(originalBooking.status);
expect(denormalizedBooking.startTime.getTime()).toBe(originalBooking.startTime.getTime());
expect(denormalizedBooking.endTime.getTime()).toBe(originalBooking.endTime.getTime());
});
});
describe("Edge Cases", () => {
it("should handle special characters in text fields", async () => {
const specialChars = "Test 'quotes' and \"double quotes\" and \\ backslashes";
await prisma.booking.update({
where: { id: bookingId },
data: {
title: specialChars,
description: specialChars,
},
});
const denormalizedBooking = await prisma.bookingDenormalized.findUniqueOrThrow({
where: { id: bookingId },
});
expect(denormalizedBooking.title).toBe(specialChars);
expect(denormalizedBooking.description).toBe(specialChars);
});
it("should handle null values in optional fields", async () => {
await prisma.booking.update({
where: { id: bookingId },
data: {
description: null,
location: null,
eventTypeId: null,
},
});
const denormalizedBooking = await prisma.bookingDenormalized.findUniqueOrThrow({
where: { id: bookingId },
});
expect(denormalizedBooking.description).toBeNull();
expect(denormalizedBooking.location).toBeNull();
expect(denormalizedBooking.eventTypeId).toBeNull();
});
});
});