@@ -9,6 +9,18 @@ import type { EventType, User, WorkflowsOnEventTypes } from "@calcom/prisma/clie
99import type { Prisma } from "@calcom/prisma/client" ;
1010import { SchedulingType } from "@calcom/prisma/enums" ;
1111
12+ // Helper to setup transaction mock that executes the callback with the prisma mock
13+ const setupTransactionMock = ( ) => {
14+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
15+ // @ts -ignore
16+ prismaMock . $transaction . mockImplementation ( async ( callback ) => {
17+ if ( typeof callback === "function" ) {
18+ return await callback ( prismaMock ) ;
19+ }
20+ return Promise . all ( callback ) ;
21+ } ) ;
22+ } ;
23+
1224// create input does not allow ID
1325const mockFindFirstEventType = (
1426 data ?: Partial < EventType > & { workflows ?: WorkflowsOnEventTypes [ ] } & { users ?: User [ ] }
@@ -132,6 +144,13 @@ describe("handleChildrenEventTypes", () => {
132144 metadata : { managedEventConfig : { } } ,
133145 locations : [ ] ,
134146 } ) ;
147+
148+ // Setup transaction mock to execute the callback
149+ setupTransactionMock ( ) ;
150+
151+ // Mock findMany to return empty array (no workflows to link)
152+ prismaMock . eventType . findMany . mockResolvedValue ( [ ] ) ;
153+
135154 const result = await updateChildrenEventTypes ( {
136155 eventTypeId : 1 ,
137156 oldEventType : { children : [ ] , team : { name : "" } } ,
@@ -143,25 +162,27 @@ describe("handleChildrenEventTypes", () => {
143162 updatedValues : { } ,
144163 } ) ;
145164 const { createdAt, updatedAt, ...expectedEvType } = evType ;
146- expect ( prismaMock . eventType . create ) . toHaveBeenCalledWith ( {
147- data : {
148- ...expectedEvType ,
149- parentId : 1 ,
150- users : { connect : [ { id : 4 } ] } ,
151- lockTimeZoneToggleOnBookingPage : false ,
152- requiresBookerEmailVerification : false ,
153- bookingLimits : undefined ,
154- durationLimits : undefined ,
155- recurringEvent : undefined ,
156- eventTypeColor : undefined ,
157- customReplyToEmail : null ,
158- userId : 4 ,
159- rrSegmentQueryValue : undefined ,
160- assignRRMembersUsingSegment : false ,
161- useBookerTimezone : false ,
162- restrictionScheduleId : null ,
163- allowReschedulingCancelledBookings : false ,
164- } ,
165+ expect ( prismaMock . eventType . createMany ) . toHaveBeenCalledWith ( {
166+ data : [
167+ {
168+ ...expectedEvType ,
169+ parentId : 1 ,
170+ lockTimeZoneToggleOnBookingPage : false ,
171+ requiresBookerEmailVerification : false ,
172+ bookingLimits : undefined ,
173+ durationLimits : undefined ,
174+ recurringEvent : undefined ,
175+ eventTypeColor : undefined ,
176+ customReplyToEmail : null ,
177+ userId : 4 ,
178+ rrSegmentQueryValue : undefined ,
179+ assignRRMembersUsingSegment : false ,
180+ useBookerTimezone : false ,
181+ restrictionScheduleId : null ,
182+ allowReschedulingCancelledBookings : false ,
183+ } ,
184+ ] ,
185+ skipDuplicates : true ,
165186 } ) ;
166187 expect ( result . newUserIds ) . toEqual ( [ 4 ] ) ;
167188 expect ( result . oldUserIds ) . toEqual ( [ ] ) ;
@@ -304,6 +325,13 @@ describe("handleChildrenEventTypes", () => {
304325 metadata : { managedEventConfig : { } } ,
305326 locations : [ ] ,
306327 } ) ;
328+
329+ // Setup transaction mock to execute the callback
330+ setupTransactionMock ( ) ;
331+
332+ // Mock findMany to return empty array (no workflows to link)
333+ prismaMock . eventType . findMany . mockResolvedValue ( [ ] ) ;
334+
307335 prismaMock . eventType . deleteMany . mockResolvedValue ( [ 123 ] as unknown as Prisma . BatchPayload ) ;
308336 const result = await updateChildrenEventTypes ( {
309337 eventTypeId : 1 ,
@@ -316,27 +344,28 @@ describe("handleChildrenEventTypes", () => {
316344 updatedValues : { } ,
317345 } ) ;
318346 const { createdAt, updatedAt, ...expectedEvType } = evType ;
319- expect ( prismaMock . eventType . create ) . toHaveBeenCalledWith ( {
320- data : {
321- ...expectedEvType ,
322- parentId : 1 ,
323- users : { connect : [ { id : 4 } ] } ,
324- bookingLimits : undefined ,
325- durationLimits : undefined ,
326- recurringEvent : undefined ,
327- eventTypeColor : undefined ,
328- customReplyToEmail : null ,
329- instantMeetingScheduleId : undefined ,
330- lockTimeZoneToggleOnBookingPage : false ,
331- requiresBookerEmailVerification : false ,
332- userId : 4 ,
333- workflows : undefined ,
334- rrSegmentQueryValue : undefined ,
335- assignRRMembersUsingSegment : false ,
336- useBookerTimezone : false ,
337- restrictionScheduleId : null ,
338- allowReschedulingCancelledBookings : false ,
339- } ,
347+ expect ( prismaMock . eventType . createMany ) . toHaveBeenCalledWith ( {
348+ data : [
349+ {
350+ ...expectedEvType ,
351+ parentId : 1 ,
352+ bookingLimits : undefined ,
353+ durationLimits : undefined ,
354+ recurringEvent : undefined ,
355+ eventTypeColor : undefined ,
356+ customReplyToEmail : null ,
357+ instantMeetingScheduleId : undefined ,
358+ lockTimeZoneToggleOnBookingPage : false ,
359+ requiresBookerEmailVerification : false ,
360+ userId : 4 ,
361+ rrSegmentQueryValue : undefined ,
362+ assignRRMembersUsingSegment : false ,
363+ useBookerTimezone : false ,
364+ restrictionScheduleId : null ,
365+ allowReschedulingCancelledBookings : false ,
366+ } ,
367+ ] ,
368+ skipDuplicates : true ,
340369 } ) ;
341370 expect ( result . newUserIds ) . toEqual ( [ 4 ] ) ;
342371 expect ( result . oldUserIds ) . toEqual ( [ ] ) ;
@@ -439,6 +468,96 @@ describe("handleChildrenEventTypes", () => {
439468 ] ,
440469 } ) ;
441470
471+ // Setup transaction mock to execute the callback
472+ setupTransactionMock ( ) ;
473+
474+ // Mock findMany to return the newly created event type for workflow linking
475+ // This simulates the event type created for user 5
476+ prismaMock . eventType . findMany . mockResolvedValue ( [
477+ {
478+ id : 3 ,
479+ title : "test" ,
480+ slug : "test" ,
481+ description : null ,
482+ position : 0 ,
483+ locations : [ ] ,
484+ length : 30 ,
485+ offsetStart : 0 ,
486+ hidden : false ,
487+ userId : 5 ,
488+ profileId : null ,
489+ teamId : null ,
490+ eventName : null ,
491+ parentId : 1 ,
492+ bookingFields : null ,
493+ timeZone : null ,
494+ periodType : "UNLIMITED" ,
495+ periodStartDate : null ,
496+ periodEndDate : null ,
497+ periodDays : null ,
498+ periodCountCalendarDays : null ,
499+ lockTimeZoneToggleOnBookingPage : false ,
500+ lockedTimeZone : null ,
501+ requiresConfirmation : false ,
502+ requiresConfirmationWillBlockSlot : false ,
503+ requiresConfirmationForFreeEmail : false ,
504+ requiresBookerEmailVerification : false ,
505+ canSendCalVideoTranscriptionEmails : true ,
506+ autoTranslateDescriptionEnabled : false ,
507+ recurringEvent : null ,
508+ disableGuests : false ,
509+ hideCalendarNotes : false ,
510+ hideCalendarEventDetails : false ,
511+ minimumBookingNotice : 120 ,
512+ beforeEventBuffer : 0 ,
513+ afterEventBuffer : 0 ,
514+ seatsPerTimeSlot : null ,
515+ onlyShowFirstAvailableSlot : false ,
516+ showOptimizedSlots : false ,
517+ disableCancelling : false ,
518+ disableRescheduling : false ,
519+ seatsShowAttendees : false ,
520+ seatsShowAvailabilityCount : true ,
521+ schedulingType : null ,
522+ scheduleId : null ,
523+ price : 0 ,
524+ currency : "usd" ,
525+ slotInterval : null ,
526+ metadata : { } ,
527+ successRedirectUrl : null ,
528+ forwardParamsSuccessRedirect : true ,
529+ bookingLimits : null ,
530+ durationLimits : null ,
531+ isInstantEvent : false ,
532+ instantMeetingExpiryTimeOffsetInSeconds : 90 ,
533+ instantMeetingScheduleId : null ,
534+ assignAllTeamMembers : false ,
535+ useEventTypeDestinationCalendarEmail : false ,
536+ secondaryEmailId : null ,
537+ eventTypeColor : null ,
538+ rescheduleWithSameRoundRobinHost : false ,
539+ rrSegmentQueryValue : null ,
540+ assignRRMembersUsingSegment : false ,
541+ useEventLevelSelectedCalendars : false ,
542+ restrictionScheduleId : null ,
543+ useBookerTimezone : false ,
544+ allowReschedulingCancelledBookings : false ,
545+ includeNoShowInRRCalculation : false ,
546+ interfaceLanguage : null ,
547+ customReplyToEmail : null ,
548+ createdAt : new Date ( ) ,
549+ updatedAt : new Date ( ) ,
550+ instantMeetingParameters : [ ] ,
551+ isRRWeightsEnabled : false ,
552+ maxLeadThreshold : null ,
553+ allowReschedulingPastBookings : false ,
554+ hideOrganizerEmail : false ,
555+ maxActiveBookingsPerBooker : null ,
556+ maxActiveBookingPerBookerOfferReschedule : false ,
557+ bookingRequiresAuthentication : false ,
558+ } ,
559+ ] ) ;
560+
442561 // Mock the event type that will be returned for existing users
443562 const mockUpdatedEventType = {
444563 id : 2 ,
@@ -481,33 +600,41 @@ describe("handleChildrenEventTypes", () => {
481600 } ) ;
482601
483602 const { createdAt, updatedAt, ...expectedEvType } = evType ;
484- expect ( prismaMock . eventType . create ) . toHaveBeenCalledWith ( {
485- data : {
486- ...expectedEvType ,
487- bookingLimits : undefined ,
488- durationLimits : undefined ,
489- recurringEvent : undefined ,
490- eventTypeColor : undefined ,
491- customReplyToEmail : null ,
492- locations : [ ] ,
493- lockTimeZoneToggleOnBookingPage : false ,
494- requiresBookerEmailVerification : false ,
495- useBookerTimezone : false ,
496- restrictionScheduleId : null ,
497- parentId : 1 ,
498- userId : 5 ,
499- users : {
500- connect : [ { id : 5 } ] ,
501- } ,
502- workflows : {
503- create : [ { workflowId : 11 } ] ,
603+ if ( "workflows" in expectedEvType ) delete expectedEvType . workflows ;
604+ // Verify createMany was called for new users (user 5)
605+ // Note: createMany doesn't support nested relations like workflows, so they're handled separately
606+ expect ( prismaMock . eventType . createMany ) . toHaveBeenCalledWith ( {
607+ data : [
608+ {
609+ ...expectedEvType ,
610+ bookingLimits : undefined ,
611+ durationLimits : undefined ,
612+ recurringEvent : undefined ,
613+ eventTypeColor : undefined ,
614+ customReplyToEmail : null ,
615+ locations : [ ] ,
616+ lockTimeZoneToggleOnBookingPage : false ,
617+ requiresBookerEmailVerification : false ,
618+ useBookerTimezone : false ,
619+ restrictionScheduleId : null ,
620+ instantMeetingScheduleId : undefined ,
621+ parentId : 1 ,
622+ userId : 5 ,
623+ rrSegmentQueryValue : undefined ,
624+ assignRRMembersUsingSegment : false ,
625+ useEventLevelSelectedCalendars : false ,
626+ allowReschedulingCancelledBookings : false ,
504627 } ,
505- rrSegmentQueryValue : undefined ,
506- assignRRMembersUsingSegment : false ,
507- useEventLevelSelectedCalendars : false ,
508- allowReschedulingCancelledBookings : false ,
509- } ,
628+ ] ,
629+ skipDuplicates : true ,
510630 } ) ;
631+
632+ // Verify workflowsOnEventTypes.createMany was called for new users' workflows
633+ expect ( prismaMock . workflowsOnEventTypes . createMany ) . toHaveBeenCalledWith ( {
634+ data : [ { eventTypeId : 3 , workflowId : 11 } ] ,
635+ skipDuplicates : true ,
636+ } ) ;
637+
511638 const { profileId, rrSegmentQueryValue, createdAt : _ , updatedAt : __ , ...rest } = evType ;
512639 if ( "workflows" in rest ) delete rest . workflows ;
513640 expect ( prismaMock . eventType . update ) . toHaveBeenCalledWith ( {
@@ -531,6 +658,7 @@ describe("handleChildrenEventTypes", () => {
531658 } ,
532659 } ,
533660 } ) ;
661+ // Verify workflowsOnEventTypes.upsert was called for existing users' workflows
534662 expect ( prismaMock . workflowsOnEventTypes . upsert ) . toHaveBeenCalledWith ( {
535663 create : {
536664 eventTypeId : 2 ,
0 commit comments