1- import { describe , it , expect , afterAll , beforeAll } from "vitest" ;
2-
31import prisma from "@calcom/prisma" ;
4- import type { User , Team , EventType } from "@calcom/prisma/client" ;
5- import { MembershipRole } from "@calcom/prisma/enums" ;
6-
2+ import type { EventType , Team , User } from "@calcom/prisma/client" ;
3+ import { MembershipRole , SchedulingType } from "@calcom/prisma/enums" ;
4+ import { afterAll , beforeAll , describe , expect , it } from "vitest" ;
75import { listWithTeamHandler } from "./listWithTeam.handler" ;
86
97let user1 : User ;
108let user2 : User ;
9+ let user3 : User ;
1110let team1 : Team ;
1211let team2 : Team ;
12+ let team3 : Team ;
1313let eventType1 : EventType ;
1414let eventType2 : EventType ;
1515let eventType3 : EventType ;
1616let eventType4 : EventType ;
17+ let managedParent : EventType ;
18+ let managedChild : EventType ;
19+ let teamEventNoUserId : EventType ;
1720
1821describe ( "listWithTeamHandler" , ( ) => {
1922 beforeAll ( async ( ) => {
20- // Create users, teams and event types
2123 const timestamp = Date . now ( ) ;
2224 user1 = await prisma . user . create ( {
2325 data : {
@@ -33,6 +35,13 @@ describe("listWithTeamHandler", () => {
3335 name : "Test User 2" ,
3436 } ,
3537 } ) ;
38+ user3 = await prisma . user . create ( {
39+ data : {
40+ username : `testuser-lwt-3-${ timestamp } ` ,
41+ email : `testuser-lwt-3-${ timestamp } @example.com` ,
42+ name : "Test User 3" ,
43+ } ,
44+ } ) ;
3645
3746 team1 = await prisma . team . create ( {
3847 data : {
@@ -55,9 +64,23 @@ describe("listWithTeamHandler", () => {
5564 } ,
5665 } ) ;
5766
67+ team3 = await prisma . team . create ( {
68+ data : {
69+ name : "Team 3 lwt" ,
70+ slug : `team-3-lwt-${ timestamp } ` ,
71+ members : {
72+ create : {
73+ userId : user1 . id ,
74+ role : MembershipRole . ADMIN ,
75+ accepted : true ,
76+ } ,
77+ } ,
78+ } ,
79+ } ) ;
80+
5881 eventType1 = await prisma . eventType . create ( {
5982 data : {
60- title : "User 1 Event" ,
83+ title : "User 1 Individual Event" ,
6184 slug : `user1-event-lwt-${ timestamp } ` ,
6285 length : 30 ,
6386 userId : user1 . id ,
@@ -66,7 +89,7 @@ describe("listWithTeamHandler", () => {
6689
6790 eventType2 = await prisma . eventType . create ( {
6891 data : {
69- title : "Team 1 Event" ,
92+ title : "Team 1 Event (user1 owner + member) " ,
7093 slug : `team1-event-lwt-${ timestamp } ` ,
7194 length : 30 ,
7295 teamId : team1 . id ,
@@ -76,7 +99,7 @@ describe("listWithTeamHandler", () => {
7699
77100 eventType3 = await prisma . eventType . create ( {
78101 data : {
79- title : "User 2 Event" ,
102+ title : "User 2 Individual Event" ,
80103 slug : `user2-event-lwt-${ timestamp } ` ,
81104 length : 30 ,
82105 userId : user2 . id ,
@@ -85,75 +108,233 @@ describe("listWithTeamHandler", () => {
85108
86109 eventType4 = await prisma . eventType . create ( {
87110 data : {
88- title : "Team 2 Event" ,
111+ title : "Team 2 Event (user2 owner, NOT member) " ,
89112 slug : `team2-event-lwt-${ timestamp } ` ,
90113 length : 30 ,
91114 teamId : team2 . id ,
92115 userId : user2 . id ,
93116 } ,
94117 } ) ;
118+
119+ managedParent = await prisma . eventType . create ( {
120+ data : {
121+ title : "Managed Parent Event" ,
122+ slug : `managed-parent-lwt-${ timestamp } ` ,
123+ length : 45 ,
124+ teamId : team3 . id ,
125+ schedulingType : SchedulingType . MANAGED ,
126+ } ,
127+ } ) ;
128+
129+ managedChild = await prisma . eventType . create ( {
130+ data : {
131+ title : "Managed Child Event" ,
132+ slug : `managed-child-lwt-${ timestamp } ` ,
133+ length : 45 ,
134+ userId : user1 . id ,
135+ parentId : managedParent . id ,
136+ } ,
137+ } ) ;
138+
139+ teamEventNoUserId = await prisma . eventType . create ( {
140+ data : {
141+ title : "Team 3 Event (no userId)" ,
142+ slug : `team3-nouserid-lwt-${ timestamp } ` ,
143+ length : 60 ,
144+ teamId : team3 . id ,
145+ } ,
146+ } ) ;
95147 } ) ;
96148
97149 afterAll ( async ( ) => {
98150 try {
99- if ( eventType1 ?. id || eventType2 ?. id || eventType3 ?. id || eventType4 ?. id ) {
151+ const eventTypeIds = [
152+ eventType1 ?. id ,
153+ eventType2 ?. id ,
154+ eventType3 ?. id ,
155+ eventType4 ?. id ,
156+ managedChild ?. id ,
157+ managedParent ?. id ,
158+ teamEventNoUserId ?. id ,
159+ ] . filter ( Boolean ) ;
160+ if ( eventTypeIds . length > 0 ) {
100161 await prisma . eventType . deleteMany ( {
101- where : {
102- id : {
103- in : [ eventType1 ?. id , eventType2 ?. id , eventType3 ?. id , eventType4 ?. id ] . filter ( Boolean ) ,
104- } ,
105- } ,
162+ where : { id : { in : eventTypeIds } } ,
106163 } ) ;
107164 }
108- if ( team1 ?. id || team2 ?. id ) {
165+ const teamIds = [ team1 ?. id , team2 ?. id , team3 ?. id ] . filter ( Boolean ) ;
166+ if ( teamIds . length > 0 ) {
109167 await prisma . team . deleteMany ( {
110- where : {
111- id : {
112- in : [ team1 ?. id , team2 ?. id ] . filter ( Boolean ) ,
113- } ,
114- } ,
168+ where : { id : { in : teamIds } } ,
115169 } ) ;
116170 }
117- if ( user1 ?. id || user2 ?. id ) {
171+ const userIds = [ user1 ?. id , user2 ?. id , user3 ?. id ] . filter ( Boolean ) ;
172+ if ( userIds . length > 0 ) {
118173 await prisma . user . deleteMany ( {
119- where : {
120- id : {
121- in : [ user1 ?. id , user2 ?. id ] . filter ( Boolean ) ,
122- } ,
123- } ,
174+ where : { id : { in : userIds } } ,
124175 } ) ;
125176 }
126177 } catch ( error ) {
127178 console . warn ( "Test cleanup failed:" , error ) ;
128179 }
129180 } ) ;
130181
131- it ( "should return user's own event types and event types of teams they are a member of" , async ( ) => {
182+ it ( "should return user's own event types and team event types they are a member of" , async ( ) => {
132183 const result = await listWithTeamHandler ( {
133- ctx : {
134- // we only need the id from the user object
135- user : { id : user1 . id } as any ,
136- } ,
184+ ctx : { user : { id : user1 . id } } ,
137185 } ) ;
138186
139- expect ( result ) . toHaveLength ( 2 ) ;
140-
141187 const resultIds = result . map ( ( e ) => e . id ) ;
142188 expect ( resultIds ) . toContain ( eventType1 . id ) ;
143189 expect ( resultIds ) . toContain ( eventType2 . id ) ;
144190
145- const eventType1Result = result . find ( ( e ) => e . id === eventType1 . id ) ;
146- expect ( eventType1Result ) . toBeDefined ( ) ;
147- expect ( eventType1Result ?. title ) . toBe ( eventType1 . title ) ;
148- expect ( eventType1Result ?. slug ) . toBe ( eventType1 . slug ) ;
149- expect ( eventType1Result ?. team ) . toBeNull ( ) ;
150-
151- const eventType2Result = result . find ( ( e ) => e . id === eventType2 . id ) ;
152- expect ( eventType2Result ) . toBeDefined ( ) ;
153- expect ( eventType2Result ?. title ) . toBe ( eventType2 . title ) ;
154- expect ( eventType2Result ?. slug ) . toBe ( eventType2 . slug ) ;
155- expect ( eventType2Result ?. team ) . toBeDefined ( ) ;
156- expect ( eventType2Result ?. team ?. id ) . toBe ( team1 . id ) ;
157- expect ( eventType2Result ?. team ?. name ) . toBe ( team1 . name ) ;
191+ const individualResult = result . find ( ( e ) => e . id === eventType1 . id ) ;
192+ expect ( individualResult ) . toBeDefined ( ) ;
193+ expect ( individualResult ?. title ) . toBe ( eventType1 . title ) ;
194+ expect ( individualResult ?. slug ) . toBe ( eventType1 . slug ) ;
195+ expect ( individualResult ?. team ) . toBeNull ( ) ;
196+
197+ const teamResult = result . find ( ( e ) => e . id === eventType2 . id ) ;
198+ expect ( teamResult ) . toBeDefined ( ) ;
199+ expect ( teamResult ?. title ) . toBe ( eventType2 . title ) ;
200+ expect ( teamResult ?. slug ) . toBe ( eventType2 . slug ) ;
201+ expect ( teamResult ?. team ) . toBeDefined ( ) ;
202+ expect ( teamResult ?. team ?. id ) . toBe ( team1 . id ) ;
203+ expect ( teamResult ?. team ?. name ) . toBe ( team1 . name ) ;
204+ } ) ;
205+
206+ it ( "should not return event types belonging to other users" , async ( ) => {
207+ const result = await listWithTeamHandler ( {
208+ ctx : { user : { id : user1 . id } } ,
209+ } ) ;
210+
211+ const resultIds = result . map ( ( e ) => e . id ) ;
212+ expect ( resultIds ) . not . toContain ( eventType3 . id ) ;
213+ expect ( resultIds ) . not . toContain ( eventType4 . id ) ;
214+ } ) ;
215+
216+ it ( "should return team event types where userId is set but user has no membership" , async ( ) => {
217+ const result = await listWithTeamHandler ( {
218+ ctx : { user : { id : user2 . id } } ,
219+ } ) ;
220+
221+ const resultIds = result . map ( ( e ) => e . id ) ;
222+ expect ( resultIds ) . toContain ( eventType3 . id ) ;
223+ expect ( resultIds ) . toContain ( eventType4 . id ) ;
224+
225+ const teamEventResult = result . find ( ( e ) => e . id === eventType4 . id ) ;
226+ expect ( teamEventResult ) . toBeDefined ( ) ;
227+ expect ( teamEventResult ?. team ?. id ) . toBe ( team2 . id ) ;
228+ expect ( teamEventResult ?. team ?. name ) . toBe ( team2 . name ) ;
229+ } ) ;
230+
231+ it ( "should not duplicate events when user is both owner and team member" , async ( ) => {
232+ const result = await listWithTeamHandler ( {
233+ ctx : { user : { id : user1 . id } } ,
234+ } ) ;
235+
236+ const eventType2Occurrences = result . filter ( ( e ) => e . id === eventType2 . id ) ;
237+ expect ( eventType2Occurrences ) . toHaveLength ( 1 ) ;
238+ } ) ;
239+
240+ it ( "should return managed event type children with userId set" , async ( ) => {
241+ const result = await listWithTeamHandler ( {
242+ ctx : { user : { id : user1 . id } } ,
243+ } ) ;
244+
245+ const resultIds = result . map ( ( e ) => e . id ) ;
246+ expect ( resultIds ) . toContain ( managedChild . id ) ;
247+
248+ const managedChildResult = result . find ( ( e ) => e . id === managedChild . id ) ;
249+ expect ( managedChildResult ) . toBeDefined ( ) ;
250+ expect ( managedChildResult ?. title ) . toBe ( managedChild . title ) ;
251+ expect ( managedChildResult ?. team ) . toBeNull ( ) ;
252+ } ) ;
253+
254+ it ( "should return team event types without userId via membership" , async ( ) => {
255+ const result = await listWithTeamHandler ( {
256+ ctx : { user : { id : user1 . id } } ,
257+ } ) ;
258+
259+ const resultIds = result . map ( ( e ) => e . id ) ;
260+ expect ( resultIds ) . toContain ( teamEventNoUserId . id ) ;
261+
262+ const noUserIdResult = result . find ( ( e ) => e . id === teamEventNoUserId . id ) ;
263+ expect ( noUserIdResult ) . toBeDefined ( ) ;
264+ expect ( noUserIdResult ?. team ?. id ) . toBe ( team3 . id ) ;
265+ expect ( noUserIdResult ?. team ?. name ) . toBe ( team3 . name ) ;
266+ } ) ;
267+
268+ it ( "should populate username for individual event types and null for team event types" , async ( ) => {
269+ const result = await listWithTeamHandler ( {
270+ ctx : { user : { id : user1 . id } } ,
271+ } ) ;
272+
273+ const individualResult = result . find ( ( e ) => e . id === eventType1 . id ) ;
274+ expect ( individualResult ?. username ) . toBe ( user1 . username ) ;
275+
276+ const teamResult = result . find ( ( e ) => e . id === eventType2 . id ) ;
277+ expect ( teamResult ?. username ) . toBeNull ( ) ;
278+
279+ const noUserIdTeamResult = result . find ( ( e ) => e . id === teamEventNoUserId . id ) ;
280+ expect ( noUserIdTeamResult ?. username ) . toBeNull ( ) ;
281+ } ) ;
282+
283+ it ( "should return username for user2 individual events" , async ( ) => {
284+ const result = await listWithTeamHandler ( {
285+ ctx : { user : { id : user2 . id } } ,
286+ } ) ;
287+
288+ const individualResult = result . find ( ( e ) => e . id === eventType3 . id ) ;
289+ expect ( individualResult ?. username ) . toBe ( user2 . username ) ;
290+
291+ const teamEventResult = result . find ( ( e ) => e . id === eventType4 . id ) ;
292+ expect ( teamEventResult ?. username ) . toBeNull ( ) ;
293+ } ) ;
294+
295+ it ( "should return empty array for user with no event types" , async ( ) => {
296+ const result = await listWithTeamHandler ( {
297+ ctx : { user : { id : user3 . id } } ,
298+ } ) ;
299+
300+ expect ( result ) . toHaveLength ( 0 ) ;
301+ } ) ;
302+
303+ it ( "should not return team events for users with unaccepted membership" , async ( ) => {
304+ const timestamp = Date . now ( ) ;
305+ const pendingTeam = await prisma . team . create ( {
306+ data : {
307+ name : "Pending Team lwt" ,
308+ slug : `pending-team-lwt-${ timestamp } ` ,
309+ members : {
310+ create : {
311+ userId : user3 . id ,
312+ role : MembershipRole . MEMBER ,
313+ accepted : false ,
314+ } ,
315+ } ,
316+ } ,
317+ } ) ;
318+
319+ const pendingTeamEvent = await prisma . eventType . create ( {
320+ data : {
321+ title : "Pending Team Event" ,
322+ slug : `pending-team-event-lwt-${ timestamp } ` ,
323+ length : 30 ,
324+ teamId : pendingTeam . id ,
325+ } ,
326+ } ) ;
327+
328+ try {
329+ const result = await listWithTeamHandler ( {
330+ ctx : { user : { id : user3 . id } } ,
331+ } ) ;
332+
333+ const resultIds = result . map ( ( e ) => e . id ) ;
334+ expect ( resultIds ) . not . toContain ( pendingTeamEvent . id ) ;
335+ } finally {
336+ await prisma . eventType . delete ( { where : { id : pendingTeamEvent . id } } ) ;
337+ await prisma . team . delete ( { where : { id : pendingTeam . id } } ) ;
338+ }
158339 } ) ;
159340} ) ;
0 commit comments