@@ -15,11 +15,11 @@ import { NestExpressApplication } from "@nestjs/platform-express";
1515import { Test } from "@nestjs/testing" ;
1616import { PlatformOAuthClient , Team , User } from "@prisma/client" ;
1717import * as request from "supertest" ;
18+ import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture" ;
1819import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture" ;
1920import { MembershipRepositoryFixture } from "test/fixtures/repository/membership.repository.fixture" ;
2021import { OAuthClientRepositoryFixture } from "test/fixtures/repository/oauth-client.repository.fixture" ;
2122import { ProfileRepositoryFixture } from "test/fixtures/repository/profiles.repository.fixture" ;
22- import { SchedulesRepositoryFixture } from "test/fixtures/repository/schedules.repository.fixture" ;
2323import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture" ;
2424import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture" ;
2525import { randomString } from "test/utils/randomString" ;
@@ -36,6 +36,7 @@ import {
3636 CreateBookingInput_2024_08_13 ,
3737 CreateEventTypeInput_2024_06_14 ,
3838 EventTypeOutput_2024_06_14 ,
39+ GetBookingOutput_2024_08_13 ,
3940 GetBookingsOutput_2024_08_13 ,
4041} from "@calcom/platform-types" ;
4142
@@ -50,9 +51,9 @@ describe("Managed user bookings 2024-08-13", () => {
5051 let oauthClientRepositoryFixture : OAuthClientRepositoryFixture ;
5152 let teamRepositoryFixture : TeamRepositoryFixture ;
5253 let eventTypesRepositoryFixture : EventTypesRepositoryFixture ;
53- let schedulesRepositoryFixture : SchedulesRepositoryFixture ;
5454 let profilesRepositoryFixture : ProfileRepositoryFixture ;
5555 let membershipsRepositoryFixture : MembershipRepositoryFixture ;
56+ let bookingsRepositoryFixture : BookingsRepositoryFixture ;
5657
5758 const platformAdminEmail = `managed-users-bookings-2024-08-13-admin-${ randomString ( ) } @api.com` ;
5859 let platformAdmin : User ;
@@ -67,6 +68,10 @@ describe("Managed user bookings 2024-08-13", () => {
6768 let thirdManagedUser : CreateManagedUserData ;
6869
6970 let firstManagedUserEventTypeId : number ;
71+ let eventTypeRequiresConfirmationId : number ;
72+
73+ const orgAdminManagedUserEmail = `managed-user-bookings-2024-08-13-org-admin-${ randomString ( ) } @api.com` ;
74+ let orgAdminManagedUser : CreateManagedUserData ;
7075
7176 let firstManagedUserBookingsCount = 0 ;
7277 let secondManagedUserBookingsCount = 0 ;
@@ -85,16 +90,23 @@ describe("Managed user bookings 2024-08-13", () => {
8590 userRepositoryFixture = new UserRepositoryFixture ( moduleRef ) ;
8691 teamRepositoryFixture = new TeamRepositoryFixture ( moduleRef ) ;
8792 eventTypesRepositoryFixture = new EventTypesRepositoryFixture ( moduleRef ) ;
88- schedulesRepositoryFixture = new SchedulesRepositoryFixture ( moduleRef ) ;
8993 profilesRepositoryFixture = new ProfileRepositoryFixture ( moduleRef ) ;
9094 membershipsRepositoryFixture = new MembershipRepositoryFixture ( moduleRef ) ;
95+ bookingsRepositoryFixture = new BookingsRepositoryFixture ( moduleRef ) ;
9196
9297 platformAdmin = await userRepositoryFixture . create ( { email : platformAdminEmail } ) ;
9398
9499 organization = await teamRepositoryFixture . create ( {
95100 name : `oauth-client-users-organization-${ randomString ( ) } ` ,
96101 isPlatform : true ,
97102 isOrganization : true ,
103+ platformBilling : {
104+ create : {
105+ customerId : "cus_999" ,
106+ plan : "SCALE" ,
107+ subscriptionId : "sub_999" ,
108+ } ,
109+ } ,
98110 } ) ;
99111 oAuthClient = await createOAuthClient ( organization . id ) ;
100112
@@ -243,6 +255,59 @@ describe("Managed user bookings 2024-08-13", () => {
243255 thirdManagedUser = responseBody . data ;
244256 } ) ;
245257
258+ it ( `should create org admin managed user` , async ( ) => {
259+ const requestBody : CreateManagedUserInput = {
260+ email : orgAdminManagedUserEmail ,
261+ timeZone : managedUsersTimeZone ,
262+ weekStart : "Monday" ,
263+ timeFormat : 24 ,
264+ locale : Locales . FR ,
265+ name : "Org Admin Smith" ,
266+ avatarUrl : "https://cal.com/api/avatar/2b735186-b01b-46d3-87da-019b8f61776b.png" ,
267+ } ;
268+
269+ const response = await request ( app . getHttpServer ( ) )
270+ . post ( `/api/v2/oauth-clients/${ oAuthClient . id } /users` )
271+ . set ( "x-cal-secret-key" , oAuthClient . secret )
272+ . send ( requestBody )
273+ . expect ( 201 ) ;
274+
275+ const responseBody : CreateManagedUserOutput = response . body ;
276+ expect ( responseBody . status ) . toEqual ( SUCCESS_STATUS ) ;
277+ expect ( responseBody . data ) . toBeDefined ( ) ;
278+ expect ( responseBody . data . user . email ) . toEqual (
279+ OAuthClientUsersService . getOAuthUserEmail ( oAuthClient . id , requestBody . email )
280+ ) ;
281+ expect ( responseBody . data . accessToken ) . toBeDefined ( ) ;
282+ expect ( responseBody . data . refreshToken ) . toBeDefined ( ) ;
283+
284+ orgAdminManagedUser = responseBody . data ;
285+
286+ await request ( app . getHttpServer ( ) )
287+ . post ( `/v2/organizations/${ organization . id } /memberships` )
288+ . set ( "x-cal-client-id" , oAuthClient . id )
289+ . set ( "x-cal-secret-key" , oAuthClient . secret )
290+ . send ( {
291+ userId : orgAdminManagedUser . user . id ,
292+ role : "ADMIN" ,
293+ accepted : true ,
294+ } )
295+ . expect ( 201 ) ;
296+ } ) ;
297+
298+ it ( "should create an event type requiring confirmation for first managed user" , async ( ) => {
299+ const eventTypeRequiresConfirmation = await eventTypesRepositoryFixture . create (
300+ {
301+ title : `managed-user-bookings-event-type-requires-confirmation-${ randomString ( ) } ` ,
302+ slug : `managed-user-bookings-event-type-requires-confirmation-${ randomString ( ) } ` ,
303+ length : 60 ,
304+ requiresConfirmation : true ,
305+ } ,
306+ firstManagedUser . user . id
307+ ) ;
308+ eventTypeRequiresConfirmationId = eventTypeRequiresConfirmation . id ;
309+ } ) ;
310+
246311 describe ( "bookings using original emails" , ( ) => {
247312 it ( "managed user should be booked by managed user attendee and booking shows up in both users' bookings" , async ( ) => {
248313 const body : CreateBookingInput_2024_08_13 = {
@@ -543,10 +608,180 @@ describe("Managed user bookings 2024-08-13", () => {
543608 } ) ;
544609 } ) ;
545610
611+ describe ( "booking confirmation by org admin" , ( ) => {
612+ it ( "should allow org admin managed user to confirm booking using access token" , async ( ) => {
613+ const bookingRequiringConfirmation = await bookingsRepositoryFixture . create ( {
614+ user : {
615+ connect : {
616+ id : firstManagedUser . user . id ,
617+ } ,
618+ } ,
619+ startTime : new Date ( Date . UTC ( 2030 , 0 , 9 , 13 , 0 , 0 ) ) ,
620+ endTime : new Date ( Date . UTC ( 2030 , 0 , 9 , 14 , 0 , 0 ) ) ,
621+ title : "Booking requiring confirmation" ,
622+ uid : `booking-requiring-confirmation-${ randomString ( ) } ` ,
623+ eventType : {
624+ connect : {
625+ id : eventTypeRequiresConfirmationId ,
626+ } ,
627+ } ,
628+ location : "https://meet.google.com/abc-def-ghi" ,
629+ customInputs : { } ,
630+ metadata : { } ,
631+ status : "PENDING" ,
632+ responses : {
633+ name : secondManagedUser . user . name ,
634+ email : secondManagedUserEmail ,
635+ } ,
636+ attendees : {
637+ create : {
638+ email : secondManagedUserEmail ,
639+ name : secondManagedUser . user . name ! ,
640+ locale : secondManagedUser . user . locale ,
641+ timeZone : secondManagedUser . user . timeZone ,
642+ } ,
643+ } ,
644+ } ) ;
645+
646+ const response = await request ( app . getHttpServer ( ) )
647+ . post ( `/v2/bookings/${ bookingRequiringConfirmation . uid } /confirm` )
648+ . set ( CAL_API_VERSION_HEADER , VERSION_2024_08_13 )
649+ . set ( "Authorization" , `Bearer ${ orgAdminManagedUser . accessToken } ` )
650+ . expect ( 200 ) ;
651+
652+ const responseBody : GetBookingOutput_2024_08_13 = response . body ;
653+ expect ( responseBody . status ) . toEqual ( SUCCESS_STATUS ) ;
654+ expect ( responseBody . data ) . toBeDefined ( ) ;
655+
656+ const bookingData = responseBody . data as BookingOutput_2024_08_13 ;
657+ expect ( bookingData . status ) . toEqual ( "accepted" ) ;
658+ expect ( bookingData . uid ) . toEqual ( bookingRequiringConfirmation . uid ) ;
659+
660+ const confirmedBooking = await bookingsRepositoryFixture . getByUid ( bookingRequiringConfirmation . uid ) ;
661+ expect ( confirmedBooking ?. status ) . toEqual ( "ACCEPTED" ) ;
662+ } ) ;
663+
664+ it ( "should allow org admin managed user to reject booking using access token" , async ( ) => {
665+ const bookingRequiringConfirmation = await bookingsRepositoryFixture . create ( {
666+ user : {
667+ connect : {
668+ id : firstManagedUser . user . id ,
669+ } ,
670+ } ,
671+ startTime : new Date ( Date . UTC ( 2030 , 0 , 9 , 15 , 0 , 0 ) ) ,
672+ endTime : new Date ( Date . UTC ( 2030 , 0 , 9 , 16 , 0 , 0 ) ) ,
673+ title : "Booking requiring rejection" ,
674+ uid : `booking-requiring-rejection-${ randomString ( ) } ` ,
675+ eventType : {
676+ connect : {
677+ id : eventTypeRequiresConfirmationId ,
678+ } ,
679+ } ,
680+ location : "https://meet.google.com/abc-def-ghi" ,
681+ customInputs : { } ,
682+ metadata : { } ,
683+ status : "PENDING" ,
684+ responses : {
685+ name : secondManagedUser . user . name ,
686+ email : secondManagedUserEmail ,
687+ } ,
688+ attendees : {
689+ create : {
690+ email : secondManagedUserEmail ,
691+ name : secondManagedUser . user . name ! ,
692+ locale : secondManagedUser . user . locale ,
693+ timeZone : secondManagedUser . user . timeZone ,
694+ } ,
695+ } ,
696+ } ) ;
697+
698+ const response = await request ( app . getHttpServer ( ) )
699+ . post ( `/v2/bookings/${ bookingRequiringConfirmation . uid } /decline` )
700+ . set ( CAL_API_VERSION_HEADER , VERSION_2024_08_13 )
701+ . set ( "Authorization" , `Bearer ${ orgAdminManagedUser . accessToken } ` )
702+ . expect ( 200 ) ;
703+
704+ const responseBody : GetBookingOutput_2024_08_13 = response . body ;
705+ expect ( responseBody . status ) . toEqual ( SUCCESS_STATUS ) ;
706+ expect ( responseBody . data ) . toBeDefined ( ) ;
707+
708+ const bookingData = responseBody . data as BookingOutput_2024_08_13 ;
709+ expect ( bookingData . status ) . toEqual ( "rejected" ) ;
710+ expect ( bookingData . uid ) . toEqual ( bookingRequiringConfirmation . uid ) ;
711+
712+ const rejectedBooking = await bookingsRepositoryFixture . getByUid ( bookingRequiringConfirmation . uid ) ;
713+ expect ( rejectedBooking ?. status ) . toEqual ( "REJECTED" ) ;
714+ } ) ;
715+
716+ it ( "should return unauthorized when org admin tries to confirm regular user's booking" , async ( ) => {
717+ // Create regular user (not managed)
718+ const regularUser = await userRepositoryFixture . create ( {
719+ email : `managed-user-bookings-2024-08-13-regular-user-${ randomString ( ) } @api.com` ,
720+ name : "Regular User" ,
721+ } ) ;
722+
723+ // Create event type requiring confirmation for regular user
724+ const regularUserEventType = await eventTypesRepositoryFixture . create (
725+ {
726+ title : `regular-user-event-type-requires-confirmation-${ randomString ( ) } ` ,
727+ slug : `regular-user-event-type-requires-confirmation-${ randomString ( ) } ` ,
728+ length : 60 ,
729+ requiresConfirmation : true ,
730+ } ,
731+ regularUser . id
732+ ) ;
733+
734+ // Create booking for regular user
735+ const regularUserBooking = await bookingsRepositoryFixture . create ( {
736+ user : {
737+ connect : {
738+ id : regularUser . id ,
739+ } ,
740+ } ,
741+ startTime : new Date ( Date . UTC ( 2030 , 0 , 9 , 17 , 0 , 0 ) ) ,
742+ endTime : new Date ( Date . UTC ( 2030 , 0 , 9 , 18 , 0 , 0 ) ) ,
743+ title : "Regular user booking requiring confirmation" ,
744+ uid : `regular-user-booking-${ randomString ( ) } ` ,
745+ eventType : {
746+ connect : {
747+ id : regularUserEventType . id ,
748+ } ,
749+ } ,
750+ location : "https://meet.google.com/abc-def-ghi" ,
751+ customInputs : { } ,
752+ metadata : { } ,
753+ status : "PENDING" ,
754+ responses : {
755+ name : "External Attendee" ,
756+ email : "external@example.com" ,
757+ } ,
758+ attendees : {
759+ create : {
760+ email : "external@example.com" ,
761+ name : "External Attendee" ,
762+ locale : "en" ,
763+ timeZone : "Europe/Rome" ,
764+ } ,
765+ } ,
766+ } ) ;
767+
768+ // Org admin should not be able to confirm regular user's booking
769+ await request ( app . getHttpServer ( ) )
770+ . post ( `/v2/bookings/${ regularUserBooking . uid } /confirm` )
771+ . set ( CAL_API_VERSION_HEADER , VERSION_2024_08_13 )
772+ . set ( "Authorization" , `Bearer ${ orgAdminManagedUser . accessToken } ` )
773+ . expect ( 401 ) ;
774+
775+ // Clean up
776+ await userRepositoryFixture . delete ( regularUser . id ) ;
777+ } ) ;
778+ } ) ;
779+
546780 afterAll ( async ( ) => {
547781 await userRepositoryFixture . delete ( firstManagedUser . user . id ) ;
548782 await userRepositoryFixture . delete ( secondManagedUser . user . id ) ;
549783 await userRepositoryFixture . delete ( thirdManagedUser . user . id ) ;
784+ await userRepositoryFixture . delete ( orgAdminManagedUser . user . id ) ;
550785 await userRepositoryFixture . delete ( platformAdmin . id ) ;
551786 await oauthClientRepositoryFixture . delete ( oAuthClient . id ) ;
552787 await teamRepositoryFixture . delete ( organization . id ) ;
0 commit comments