@@ -14,29 +14,27 @@ import { Test } from "@nestjs/testing";
1414import { User } from "@prisma/client" ;
1515import { DateTime } from "luxon" ;
1616import * as request from "supertest" ;
17+ import { ApiKeysRepositoryFixture } from "test/fixtures/repository/api-keys.repository.fixture" ;
18+ import { BookingSeatRepositoryFixture } from "test/fixtures/repository/booking-seat.repository.fixture" ;
1719import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture" ;
1820import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture" ;
1921import { OAuthClientRepositoryFixture } from "test/fixtures/repository/oauth-client.repository.fixture" ;
2022import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture" ;
2123import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture" ;
2224import { randomString } from "test/utils/randomString" ;
23- import { withApiAuth } from "test/utils/withApiAuth" ;
2425
2526import { CAL_API_VERSION_HEADER , SUCCESS_STATUS , VERSION_2024_08_13 } from "@calcom/platform-constants" ;
2627import {
28+ CancelBookingInput_2024_08_13 ,
2729 CancelSeatedBookingInput_2024_08_13 ,
2830 CreateRecurringSeatedBookingOutput_2024_08_13 ,
2931 CreateSeatedBookingOutput_2024_08_13 ,
3032 GetBookingOutput_2024_08_13 ,
3133 GetBookingsOutput_2024_08_13 ,
32- GetRecurringSeatedBookingOutput_2024_08_13 ,
3334 GetSeatedBookingOutput_2024_08_13 ,
3435 RescheduleSeatedBookingInput_2024_08_13 ,
3536} from "@calcom/platform-types" ;
36- import {
37- CreateBookingInput_2024_08_13 ,
38- CreateRecurringBookingInput_2024_08_13 ,
39- } from "@calcom/platform-types" ;
37+ import { CreateBookingInput_2024_08_13 } from "@calcom/platform-types" ;
4038import { PlatformOAuthClient , Team } from "@calcom/prisma/client" ;
4139
4240describe ( "Bookings Endpoints 2024-08-13" , ( ) => {
@@ -51,31 +49,31 @@ describe("Bookings Endpoints 2024-08-13", () => {
5149 let schedulesService : SchedulesService_2024_04_15 ;
5250 let eventTypesRepositoryFixture : EventTypesRepositoryFixture ;
5351 let oauthClientRepositoryFixture : OAuthClientRepositoryFixture ;
54- let oAuthClient : PlatformOAuthClient ;
5552 let teamRepositoryFixture : TeamRepositoryFixture ;
53+ let apiKeysRepositoryFixture : ApiKeysRepositoryFixture ;
54+ let bookingSeatRepositoryFixture : BookingSeatRepositoryFixture ;
5655
5756 const userEmail = `seated-bookings-user-${ randomString ( ) } @api.com` ;
5857 let user : User ;
58+ let apiKeyString : string ;
5959
6060 let seatedEventTypeId : number ;
6161 const maxRecurrenceCount = 3 ;
6262
6363 const seatedEventSlug = `seated-bookings-event-type-${ randomString ( ) } ` ;
6464
6565 let createdSeatedBooking : CreateSeatedBookingOutput_2024_08_13 ;
66+ let createdSeatedBooking2 : CreateSeatedBookingOutput_2024_08_13 ;
6667
6768 const emailAttendeeOne = `seated-bookings-attendee1-${ randomString ( ) } @api.com` ;
6869 const nameAttendeeOne = `Attendee One ${ randomString ( ) } ` ;
6970 const emailAttendeeTwo = `seated-bookings-attendee2-${ randomString ( ) } @api.com` ;
7071 const nameAttendeeTwo = `Attendee Two ${ randomString ( ) } ` ;
7172
7273 beforeAll ( async ( ) => {
73- const moduleRef = await withApiAuth (
74- userEmail ,
75- Test . createTestingModule ( {
76- imports : [ AppModule , PrismaModule , UsersModule , SchedulesModule_2024_04_15 ] ,
77- } )
78- )
74+ const moduleRef = await Test . createTestingModule ( {
75+ imports : [ AppModule , PrismaModule , UsersModule , SchedulesModule_2024_04_15 ] ,
76+ } )
7977 . overrideGuard ( PermissionsGuard )
8078 . useValue ( {
8179 canActivate : ( ) => true ,
@@ -87,22 +85,21 @@ describe("Bookings Endpoints 2024-08-13", () => {
8785 eventTypesRepositoryFixture = new EventTypesRepositoryFixture ( moduleRef ) ;
8886 oauthClientRepositoryFixture = new OAuthClientRepositoryFixture ( moduleRef ) ;
8987 teamRepositoryFixture = new TeamRepositoryFixture ( moduleRef ) ;
88+ apiKeysRepositoryFixture = new ApiKeysRepositoryFixture ( moduleRef ) ;
9089 schedulesService = moduleRef . get < SchedulesService_2024_04_15 > ( SchedulesService_2024_04_15 ) ;
90+ bookingSeatRepositoryFixture = new BookingSeatRepositoryFixture ( moduleRef ) ;
9191
9292 organization = await teamRepositoryFixture . create ( {
9393 name : `seated-bookings-organization-${ randomString ( ) } ` ,
9494 } ) ;
95- oAuthClient = await createOAuthClient ( organization . id ) ;
9695
9796 user = await userRepositoryFixture . create ( {
9897 email : userEmail ,
99- platformOAuthClients : {
100- connect : {
101- id : oAuthClient . id ,
102- } ,
103- } ,
10498 } ) ;
10599
100+ const { keyString } = await apiKeysRepositoryFixture . createApiKey ( user . id , null ) ;
101+ apiKeyString = `cal_test_${ keyString } ` ;
102+
106103 const userSchedule : CreateScheduleInput_2024_04_15 = {
107104 name : `seated-bookings-2024-08-13-schedule-${ randomString ( ) } ` ,
108105 timeZone : "Europe/Rome" ,
@@ -118,6 +115,14 @@ describe("Bookings Endpoints 2024-08-13", () => {
118115 seatsShowAttendees : true ,
119116 seatsShowAvailabilityCount : true ,
120117 locations : [ { type : "inPerson" , address : "via 10, rome, italy" } ] ,
118+ metadata : {
119+ disableStandardEmails : {
120+ all : {
121+ attendee : true ,
122+ host : true ,
123+ } ,
124+ } ,
125+ } ,
121126 } ,
122127 user . id
123128 ) ;
@@ -129,19 +134,6 @@ describe("Bookings Endpoints 2024-08-13", () => {
129134 await app . init ( ) ;
130135 } ) ;
131136
132- async function createOAuthClient ( organizationId : number ) {
133- const data = {
134- logo : "logo-url" ,
135- name : "name" ,
136- redirectUris : [ "http://localhost:5555" ] ,
137- permissions : 32 ,
138- } ;
139- const secret = "secret" ;
140-
141- const client = await oauthClientRepositoryFixture . create ( organizationId , data , secret ) ;
142- return client ;
143- }
144-
145137 it ( "should book an event type with seats for the first time" , async ( ) => {
146138 const body : CreateBookingInput_2024_08_13 = {
147139 start : new Date ( Date . UTC ( 2030 , 0 , 8 , 13 , 0 , 0 ) ) . toISOString ( ) ,
@@ -333,6 +325,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
333325 return request ( app . getHttpServer ( ) )
334326 . get ( "/v2/bookings?sortCreated=asc" )
335327 . set ( CAL_API_VERSION_HEADER , VERSION_2024_08_13 )
328+ . set ( "Authorization" , `Bearer ${ apiKeyString } ` )
336329 . expect ( 200 )
337330 . then ( async ( response ) => {
338331 const responseBody : GetBookingsOutput_2024_08_13 = response . body ;
@@ -409,67 +402,186 @@ describe("Bookings Endpoints 2024-08-13", () => {
409402 } ) ;
410403 } ) ;
411404
412- it ( "should cancel seated booking" , async ( ) => {
413- const body : CancelSeatedBookingInput_2024_08_13 = {
414- seatUid : createdSeatedBooking . seatUid ,
415- } ;
405+ describe ( "cancel seated booking" , ( ) => {
406+ describe ( "cancel seated booking as attendee" , ( ) => {
407+ it ( "should cancel seated booking" , async ( ) => {
408+ const body : CancelSeatedBookingInput_2024_08_13 = {
409+ seatUid : createdSeatedBooking . seatUid ,
410+ } ;
411+
412+ return request ( app . getHttpServer ( ) )
413+ . post ( `/v2/bookings/${ createdSeatedBooking . uid } /cancel` )
414+ . send ( body )
415+ . set ( CAL_API_VERSION_HEADER , VERSION_2024_08_13 )
416+ . expect ( 200 )
417+ . then ( async ( response ) => {
418+ const responseBody : RescheduleBookingOutput_2024_08_13 = response . body ;
419+ expect ( responseBody . status ) . toEqual ( SUCCESS_STATUS ) ;
420+ expect ( responseBody . data ) . toBeDefined ( ) ;
421+ expect ( responseDataIsGetSeatedBooking ( responseBody . data ) ) . toBe ( true ) ;
422+
423+ if ( responseDataIsGetSeatedBooking ( responseBody . data ) ) {
424+ const data : GetSeatedBookingOutput_2024_08_13 = responseBody . data ;
425+ expect ( data . id ) . toBeDefined ( ) ;
426+ expect ( data . uid ) . toBeDefined ( ) ;
427+ expect ( data . hosts [ 0 ] . id ) . toEqual ( user . id ) ;
428+ expect ( data . status ) . toEqual ( "cancelled" ) ;
429+ expect ( data . start ) . toEqual ( createdSeatedBooking . start ) ;
430+ expect ( data . end ) . toEqual ( createdSeatedBooking . end ) ;
431+ expect ( data . duration ) . toEqual ( 60 ) ;
432+ expect ( data . eventTypeId ) . toEqual ( seatedEventTypeId ) ;
433+ expect ( data . eventType ) . toEqual ( {
434+ id : seatedEventTypeId ,
435+ slug : seatedEventSlug ,
436+ } ) ;
437+ expect ( data . attendees . length ) . toEqual ( 0 ) ;
438+ expect ( data . location ) . toBeDefined ( ) ;
439+ expect ( data . absentHost ) . toEqual ( false ) ;
440+ } else {
441+ throw new Error ( "Invalid response data - expected booking but received array response" ) ;
442+ }
443+ } ) ;
444+ } ) ;
445+ } ) ;
416446
417- return request ( app . getHttpServer ( ) )
418- . post ( `/v2/bookings/${ createdSeatedBooking . uid } /cancel` )
419- . send ( body )
420- . set ( CAL_API_VERSION_HEADER , VERSION_2024_08_13 )
421- . expect ( 200 )
422- . then ( async ( response ) => {
423- const responseBody : RescheduleBookingOutput_2024_08_13 = response . body ;
424- expect ( responseBody . status ) . toEqual ( SUCCESS_STATUS ) ;
425- expect ( responseBody . data ) . toBeDefined ( ) ;
426- expect ( responseDataIsGetSeatedBooking ( responseBody . data ) ) . toBe ( true ) ;
447+ describe ( "cancel seated booking as host" , ( ) => {
448+ it ( "should book an event type with seats" , async ( ) => {
449+ const body : CreateBookingInput_2024_08_13 = {
450+ start : new Date ( Date . UTC ( 2030 , 0 , 8 , 14 , 0 , 0 ) ) . toISOString ( ) ,
451+ eventTypeId : seatedEventTypeId ,
452+ attendee : {
453+ name : nameAttendeeOne ,
454+ email : emailAttendeeOne ,
455+ timeZone : "Europe/Rome" ,
456+ language : "it" ,
457+ } ,
458+ bookingFieldsResponses : {
459+ codingLanguage : "TypeScript" ,
460+ } ,
461+ metadata : {
462+ userId : "100" ,
463+ } ,
464+ } ;
465+
466+ return request ( app . getHttpServer ( ) )
467+ . post ( "/v2/bookings" )
468+ . send ( body )
469+ . set ( CAL_API_VERSION_HEADER , VERSION_2024_08_13 )
470+ . expect ( 201 )
471+ . then ( async ( response ) => {
472+ const responseBody : CreateBookingOutput_2024_08_13 = response . body ;
473+ expect ( responseBody . status ) . toEqual ( SUCCESS_STATUS ) ;
474+ expect ( responseBody . data ) . toBeDefined ( ) ;
475+ expect ( responseDataIsCreateSeatedBooking ( responseBody . data ) ) . toBe ( true ) ;
476+
477+ if ( responseDataIsCreateSeatedBooking ( responseBody . data ) ) {
478+ const data : CreateSeatedBookingOutput_2024_08_13 = responseBody . data ;
479+ expect ( data . seatUid ) . toBeDefined ( ) ;
480+ const seatUid = data . seatUid ;
481+ expect ( data . id ) . toBeDefined ( ) ;
482+ expect ( data . uid ) . toBeDefined ( ) ;
483+ expect ( data . hosts [ 0 ] . id ) . toEqual ( user . id ) ;
484+ expect ( data . status ) . toEqual ( "accepted" ) ;
485+ expect ( data . start ) . toEqual ( body . start ) ;
486+ expect ( data . end ) . toEqual (
487+ DateTime . fromISO ( body . start , { zone : "utc" } ) . plus ( { hours : 1 } ) . toISO ( )
488+ ) ;
489+ expect ( data . duration ) . toEqual ( 60 ) ;
490+ expect ( data . eventTypeId ) . toEqual ( seatedEventTypeId ) ;
491+ expect ( data . eventType ) . toEqual ( {
492+ id : seatedEventTypeId ,
493+ slug : seatedEventSlug ,
494+ } ) ;
495+ expect ( data . attendees . length ) . toEqual ( 1 ) ;
496+ expect ( data . attendees [ 0 ] ) . toEqual ( {
497+ name : body . attendee . name ,
498+ email : body . attendee . email ,
499+ timeZone : body . attendee . timeZone ,
500+ language : body . attendee . language ,
501+ absent : false ,
502+ seatUid,
503+ bookingFieldsResponses : {
504+ name : body . attendee . name ,
505+ ...body . bookingFieldsResponses ,
506+ } ,
507+ metadata : body . metadata ,
508+ } ) ;
509+ expect ( data . location ) . toBeDefined ( ) ;
510+ expect ( data . absentHost ) . toEqual ( false ) ;
511+ createdSeatedBooking2 = data ;
512+ } else {
513+ throw new Error (
514+ "Invalid response data - expected recurring booking but received non array response"
515+ ) ;
516+ }
517+ } ) ;
518+ } ) ;
427519
428- if ( responseDataIsGetSeatedBooking ( responseBody . data ) ) {
429- const data : GetSeatedBookingOutput_2024_08_13 = responseBody . data ;
430- expect ( data . id ) . toBeDefined ( ) ;
431- expect ( data . uid ) . toBeDefined ( ) ;
432- expect ( data . hosts [ 0 ] . id ) . toEqual ( user . id ) ;
433- expect ( data . status ) . toEqual ( "cancelled" ) ;
434- expect ( data . start ) . toEqual ( createdSeatedBooking . start ) ;
435- expect ( data . end ) . toEqual ( createdSeatedBooking . end ) ;
436- expect ( data . duration ) . toEqual ( 60 ) ;
437- expect ( data . eventTypeId ) . toEqual ( seatedEventTypeId ) ;
438- expect ( data . eventType ) . toEqual ( {
439- id : seatedEventTypeId ,
440- slug : seatedEventSlug ,
520+ it ( "should not be able to cancel without cancellation reason" , async ( ) => {
521+ const response = await request ( app . getHttpServer ( ) )
522+ . post ( `/v2/bookings/${ createdSeatedBooking2 . uid } /cancel` )
523+ . set ( CAL_API_VERSION_HEADER , VERSION_2024_08_13 )
524+ . set ( "Authorization" , `Bearer ${ apiKeyString } ` )
525+ . expect ( 400 ) ;
526+
527+ expect ( response . body . message ) . toEqual ( "Cancellation reason is required when you are the host" ) ;
528+ } ) ;
529+
530+ it ( "should cancel seated booking" , async ( ) => {
531+ const body : CancelBookingInput_2024_08_13 = {
532+ cancellationReason : "I will be travelling without internet" ,
533+ } ;
534+
535+ return request ( app . getHttpServer ( ) )
536+ . post ( `/v2/bookings/${ createdSeatedBooking2 . uid } /cancel` )
537+ . send ( body )
538+ . set ( CAL_API_VERSION_HEADER , VERSION_2024_08_13 )
539+ . set ( "Authorization" , `Bearer ${ apiKeyString } ` )
540+ . expect ( 200 )
541+ . then ( async ( response ) => {
542+ const responseBody : RescheduleBookingOutput_2024_08_13 = response . body ;
543+ expect ( responseBody . status ) . toEqual ( SUCCESS_STATUS ) ;
544+ expect ( responseBody . data ) . toBeDefined ( ) ;
545+ expect ( responseDataIsGetSeatedBooking ( responseBody . data ) ) . toBe ( true ) ;
546+
547+ if ( responseDataIsGetSeatedBooking ( responseBody . data ) ) {
548+ const data : GetSeatedBookingOutput_2024_08_13 = responseBody . data ;
549+ expect ( data . id ) . toBeDefined ( ) ;
550+ expect ( data . uid ) . toBeDefined ( ) ;
551+ expect ( data . hosts [ 0 ] . id ) . toEqual ( user . id ) ;
552+ expect ( data . status ) . toEqual ( "cancelled" ) ;
553+ expect ( data . start ) . toEqual ( createdSeatedBooking2 . start ) ;
554+ expect ( data . end ) . toEqual ( createdSeatedBooking2 . end ) ;
555+ expect ( data . duration ) . toEqual ( 60 ) ;
556+ expect ( data . eventTypeId ) . toEqual ( seatedEventTypeId ) ;
557+ expect ( data . eventType ) . toEqual ( {
558+ id : seatedEventTypeId ,
559+ slug : seatedEventSlug ,
560+ } ) ;
561+ expect ( data . attendees . length ) . toEqual ( 0 ) ;
562+ expect ( data . location ) . toBeDefined ( ) ;
563+ expect ( data . absentHost ) . toEqual ( false ) ;
564+ expect ( data . cancellationReason ) . toEqual ( "I will be travelling without internet" ) ;
565+
566+ const seats = await bookingSeatRepositoryFixture . findAllByBookingId ( data . id ) ;
567+ expect ( seats . length ) . toEqual ( 0 ) ;
568+ } else {
569+ throw new Error ( "Invalid response data - expected booking but received array response" ) ;
570+ }
441571 } ) ;
442- expect ( data . attendees . length ) . toEqual ( 0 ) ;
443- expect ( data . location ) . toBeDefined ( ) ;
444- expect ( data . absentHost ) . toEqual ( false ) ;
445- } else {
446- throw new Error ( "Invalid response data - expected booking but received array response" ) ;
447- }
448572 } ) ;
573+ } ) ;
449574 } ) ;
450575
451576 function responseDataIsCreateSeatedBooking ( data : any ) : data is CreateSeatedBookingOutput_2024_08_13 {
452577 return data . hasOwnProperty ( "seatUid" ) ;
453578 }
454579
455- function responseDataIsCreateRecurringSeatedBooking (
456- data : any
457- ) : data is CreateRecurringSeatedBookingOutput_2024_08_13 [ ] {
458- return Array . isArray ( data ) ;
459- }
460-
461580 function responseDataIsGetSeatedBooking ( data : any ) : data is GetSeatedBookingOutput_2024_08_13 {
462581 return data ?. attendees ?. every ( ( attendee : any ) => attendee ?. hasOwnProperty ( "seatUid" ) ) ;
463582 }
464583
465- function responseDataIsGetRecurringSeatedBooking (
466- data : any
467- ) : data is GetRecurringSeatedBookingOutput_2024_08_13 [ ] {
468- return Array . isArray ( data ) ;
469- }
470-
471584 afterAll ( async ( ) => {
472- await oauthClientRepositoryFixture . delete ( oAuthClient . id ) ;
473585 await teamRepositoryFixture . delete ( organization . id ) ;
474586 await userRepositoryFixture . deleteByEmail ( user . email ) ;
475587 await bookingsRepositoryFixture . deleteAllBookings ( user . id , user . email ) ;
0 commit comments