feat: add integration test for RoutingFormResponseField triggers (#21586)

* feat: add integration test for RoutingFormResponseField triggers

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* refactor: remove changing field types test case

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: eunjae@cal.com <hey@eunjae.dev>
This commit is contained in:
devin-ai-integration[bot]
2025-06-23 10:11:20 +00:00
committed by GitHub
co-authored by Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> eunjae@cal.com <hey@eunjae.dev>
parent a745d8a60a
commit 07d5594085
@@ -0,0 +1,451 @@
import { Prisma } from "@prisma/client";
import { describe, expect, beforeEach, afterEach, it } from "vitest";
import prisma from "@calcom/prisma";
import { BookingStatus } from "@calcom/prisma/enums";
describe("RoutingFormResponseField", () => {
let userId: number;
let formId: string;
let eventTypeId: number;
let bookingId: number;
let responseId: number;
const randomId = Math.floor(Math.random() * 1000000);
const email = `routing-form-response-field-${randomId}@example.com`;
beforeEach(async () => {
const user = await prisma.user.create({
data: {
email,
username: `routing-form-response-field-testuser-${randomId}`,
name: "Test User",
},
});
userId = user.id;
const eventType = await prisma.eventType.create({
data: {
title: "Test Event",
slug: "test-event",
length: 60,
userId: user.id,
},
});
eventTypeId = eventType.id;
const form = await prisma.app_RoutingForms_Form.create({
data: {
name: "Test Form",
userId: user.id,
fields: [
{
id: "text-field-id",
type: "text",
label: "Name",
required: true,
},
{
id: "number-field-id",
type: "number",
label: "Rating",
required: false,
},
{
id: "multiselect-field-id",
type: "multiselect",
label: "Skills",
required: false,
options: [
{
id: "skill-1",
label: "JavaScript",
},
{
id: "skill-2",
label: "TypeScript",
},
{
id: "skill-3",
label: "React",
},
],
},
{
id: "email-field-id",
type: "email",
label: "Email",
required: true,
},
],
},
});
formId = form.id;
const booking = await prisma.booking.create({
data: {
uid: `routing-form-response-field-${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 () => {
if (responseId) {
await prisma.routingFormResponseField.deleteMany({
where: { responseId },
});
await prisma.routingFormResponseDenormalized.deleteMany({
where: { id: responseId },
});
await prisma.app_RoutingForms_FormResponse.deleteMany({
where: { id: responseId },
});
}
if (bookingId) {
await prisma.booking.deleteMany({
where: { id: bookingId },
});
}
if (formId) {
await prisma.app_RoutingForms_Form.deleteMany({
where: { id: formId },
});
}
if (eventTypeId) {
await prisma.eventType.deleteMany({
where: { id: eventTypeId },
});
}
if (userId) {
await prisma.user.deleteMany({
where: { id: userId },
});
}
});
describe("INSERT Trigger Tests", () => {
it("should create field entries when form response is created", async () => {
const formResponse = await prisma.app_RoutingForms_FormResponse.create({
data: {
formFillerId: "test-filler-all-fields",
formId: formId,
response: {
"text-field-id": {
label: "Name",
value: "John Doe",
},
"number-field-id": {
label: "Rating",
value: 5,
},
"multiselect-field-id": {
label: "Skills",
value: ["skill-1", "skill-3"],
},
"email-field-id": {
label: "Email",
value: "john.doe@example.com",
},
},
routedToBookingUid: null,
},
});
responseId = formResponse.id;
const denormalizedResponse = await prisma.routingFormResponseDenormalized.findUnique({
where: { id: responseId },
include: { fields: true },
});
expect(denormalizedResponse).not.toBeNull();
expect(denormalizedResponse?.fields).toBeDefined();
expect(denormalizedResponse?.fields).toHaveLength(4);
const textField = denormalizedResponse?.fields.find((field) => field.fieldId === "text-field-id");
expect(textField).toBeDefined();
expect(textField?.valueString).toBe("John Doe");
expect(textField?.valueNumber).toBeNull();
expect(textField?.valueStringArray).toEqual([]);
const numberField = denormalizedResponse?.fields.find((field) => field.fieldId === "number-field-id");
expect(numberField).toBeDefined();
expect(numberField?.valueString).toBeNull();
expect(numberField?.valueNumber?.equals(new Prisma.Decimal(5))).toBe(true);
expect(numberField?.valueStringArray).toEqual([]);
const multiselectField = denormalizedResponse?.fields.find(
(field) => field.fieldId === "multiselect-field-id"
);
expect(multiselectField).toBeDefined();
expect(multiselectField?.valueString).toBeNull();
expect(multiselectField?.valueNumber).toBeNull();
expect(multiselectField?.valueStringArray).toContain("skill-1");
expect(multiselectField?.valueStringArray).toContain("skill-3");
expect(multiselectField?.valueStringArray).toHaveLength(2);
const emailField = denormalizedResponse?.fields.find((field) => field.fieldId === "email-field-id");
expect(emailField).toBeDefined();
expect(emailField?.valueString).toBe("john.doe@example.com");
expect(emailField?.valueNumber).toBeNull();
expect(emailField?.valueStringArray).toEqual([]);
});
it("should handle null and undefined field values", async () => {
const formResponse = await prisma.app_RoutingForms_FormResponse.create({
data: {
formFillerId: "test-filler-null-values",
formId: formId,
response: {
"text-field-id": {
label: "Name",
value: null,
},
"number-field-id": {
label: "Rating",
value: undefined,
},
"multiselect-field-id": {
label: "Skills",
value: [],
},
"email-field-id": {
label: "Email",
value: "",
},
},
routedToBookingUid: null,
},
});
responseId = formResponse.id;
const denormalizedResponse = await prisma.routingFormResponseDenormalized.findUnique({
where: { id: responseId },
include: { fields: true },
});
expect(denormalizedResponse).not.toBeNull();
expect(denormalizedResponse?.fields).toBeDefined();
expect(denormalizedResponse?.fields.length).toBeLessThanOrEqual(2);
const emailField = denormalizedResponse?.fields.find((field) => field.fieldId === "email-field-id");
if (emailField) {
expect(emailField.valueString).toBe("");
}
const multiselectField = denormalizedResponse?.fields.find(
(field) => field.fieldId === "multiselect-field-id"
);
if (multiselectField) {
expect(multiselectField.valueStringArray).toEqual([]);
}
});
it("should handle invalid data types", async () => {
const formResponse = await prisma.app_RoutingForms_FormResponse.create({
data: {
formFillerId: "test-filler-invalid-types",
formId: formId,
response: {
"text-field-id": {
label: "Name",
value: 123, // Number instead of string
},
"number-field-id": {
label: "Rating",
value: "five", // String instead of number
},
"multiselect-field-id": {
label: "Skills",
value: "skill-1", // String instead of array
},
},
routedToBookingUid: null,
},
});
responseId = formResponse.id;
const denormalizedResponse = await prisma.routingFormResponseDenormalized.findUnique({
where: { id: responseId },
include: { fields: true },
});
expect(denormalizedResponse).not.toBeNull();
expect(denormalizedResponse?.fields).toBeDefined();
});
});
describe("UPDATE Trigger Tests", () => {
it("should update field entries when form response is updated", async () => {
const formResponse = await prisma.app_RoutingForms_FormResponse.create({
data: {
formFillerId: "test-filler-update",
formId: formId,
response: {
"text-field-id": {
label: "Name",
value: "Original Name",
},
"number-field-id": {
label: "Rating",
value: 3,
},
"multiselect-field-id": {
label: "Skills",
value: ["skill-1"],
},
},
routedToBookingUid: null,
},
});
responseId = formResponse.id;
const initialDenormalizedResponse = await prisma.routingFormResponseDenormalized.findUnique({
where: { id: responseId },
include: { fields: true },
});
expect(initialDenormalizedResponse).not.toBeNull();
expect(initialDenormalizedResponse?.fields).toBeDefined();
expect(initialDenormalizedResponse?.fields).toHaveLength(3);
const initialTextField = initialDenormalizedResponse?.fields.find(
(field) => field.fieldId === "text-field-id"
);
expect(initialTextField?.valueString).toBe("Original Name");
await prisma.app_RoutingForms_FormResponse.update({
where: { id: responseId },
data: {
response: {
"text-field-id": {
label: "Name",
value: "Updated Name",
},
"number-field-id": {
label: "Rating",
value: 5,
},
"multiselect-field-id": {
label: "Skills",
value: ["skill-1", "skill-2"],
},
"email-field-id": {
label: "Email",
value: "new@example.com",
},
},
},
});
const updatedDenormalizedResponse = await prisma.routingFormResponseDenormalized.findUnique({
where: { id: responseId },
include: { fields: true },
});
expect(updatedDenormalizedResponse).not.toBeNull();
expect(updatedDenormalizedResponse?.fields).toBeDefined();
expect(updatedDenormalizedResponse?.fields).toHaveLength(4); // Now we have 4 fields
const updatedTextField = updatedDenormalizedResponse?.fields.find(
(field) => field.fieldId === "text-field-id"
);
expect(updatedTextField).toBeDefined();
expect(updatedTextField?.valueString).toBe("Updated Name");
const updatedNumberField = updatedDenormalizedResponse?.fields.find(
(field) => field.fieldId === "number-field-id"
);
expect(updatedNumberField).toBeDefined();
expect(updatedNumberField?.valueNumber?.equals(new Prisma.Decimal(5))).toBe(true);
const updatedMultiselectField = updatedDenormalizedResponse?.fields.find(
(field) => field.fieldId === "multiselect-field-id"
);
expect(updatedMultiselectField).toBeDefined();
expect(updatedMultiselectField?.valueStringArray).toContain("skill-1");
expect(updatedMultiselectField?.valueStringArray).toContain("skill-2");
expect(updatedMultiselectField?.valueStringArray).toHaveLength(2);
const newEmailField = updatedDenormalizedResponse?.fields.find(
(field) => field.fieldId === "email-field-id"
);
expect(newEmailField).toBeDefined();
expect(newEmailField?.valueString).toBe("new@example.com");
});
it("should remove field entries when fields are removed from response", async () => {
const formResponse = await prisma.app_RoutingForms_FormResponse.create({
data: {
formFillerId: "test-filler-remove-fields",
formId: formId,
response: {
"text-field-id": {
label: "Name",
value: "Test Name",
},
"number-field-id": {
label: "Rating",
value: 4,
},
"multiselect-field-id": {
label: "Skills",
value: ["skill-1", "skill-2"],
},
"email-field-id": {
label: "Email",
value: "test@example.com",
},
},
routedToBookingUid: null,
},
});
responseId = formResponse.id;
const initialDenormalizedResponse = await prisma.routingFormResponseDenormalized.findUnique({
where: { id: responseId },
include: { fields: true },
});
expect(initialDenormalizedResponse).not.toBeNull();
expect(initialDenormalizedResponse?.fields).toBeDefined();
expect(initialDenormalizedResponse?.fields).toHaveLength(4);
await prisma.app_RoutingForms_FormResponse.update({
where: { id: responseId },
data: {
response: {
"text-field-id": {
label: "Name",
value: "Test Name",
},
"email-field-id": {
label: "Email",
value: "test@example.com",
},
},
},
});
const updatedDenormalizedResponse = await prisma.routingFormResponseDenormalized.findUnique({
where: { id: responseId },
include: { fields: true },
});
expect(updatedDenormalizedResponse).not.toBeNull();
expect(updatedDenormalizedResponse?.fields).toBeDefined();
expect(updatedDenormalizedResponse?.fields).toHaveLength(2); // Now we have only 2 fields
const fieldIds = updatedDenormalizedResponse?.fields.map((field) => field.fieldId);
expect(fieldIds).toContain("text-field-id");
expect(fieldIds).toContain("email-field-id");
expect(fieldIds).not.toContain("number-field-id");
expect(fieldIds).not.toContain("multiselect-field-id");
});
});
});