\r\n\r\n\r\n\r\n\r\n
\r\n
\r\n
________________________________________________________________________________\r\n
\r\n
\r\n
Réunion Microsoft Teams\r\n
\r\n
\r\n
Rejoindre sur votre ordinateur ou application mobile\r\n
\r\nCliquez\r\n ici pour participer à la réunion
\r\n
Pour en savoir\r\n plus | \r\nOptions de réunion
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
________________________________________________________________________________\r\n
\r\n\r\n\r\n" }, "start": { "dateTime": "2022-06-11T12:30:00.0000000", "timeZone": "UTC" }, "end": { "dateTime": "2022-06-11T13:30:00.0000000", "timeZone": "UTC" }, "location": { "displayName": "", "locationType": "default", "uniqueIdType": "unknown", "address": {}, "coordinates": {} }, "locations": [], "recurrence": null, "attendees": [ { "type": "required", "status": { "response": "accepted", "time": "2022-06-08T16:22:15.1392583Z" }, "emailAddress": { "name": "Alex Wilber", "address": "AlexW@2zy74l.onmicrosoft.com" } }, { "type": "required", "status": { "response": "declined", "time": "2022-06-08T16:22:15.1392583Z" }, "emailAddress": { "name": "Alfred Nobel", "address": "alfredN@2zy74l.onmicrosoft.com" } } ], "organizer": { "emailAddress": { "name": "Diego Siciliani", "address": "DiegoS@2zy74l.onmicrosoft.com" } }, "onlineMeeting": { "joinUrl": "https://teams.microsoft.com/l/meetup-join/19%3ameeting_NTE3ODUxY2ItNGJhNi00Y2UwLTljN2QtMmQ3YjAxNWY1Nzk2%40thread.v2/0?context=%7b%22Tid%22%3a%22421bf216-3f48-47bd-a7cf-8b1995cb24bd%22%2c%22Oid%22%3a%224feb0ae3-7ffb-48dd-891e-c86b2cdeefd4%22%7d" } } JSON; private const REMOTE_CALENDAR_WITH_ATTENDEES = <<<'JSON' { "@odata.etag": "W/\"B3bmsWoxX06b9JHIZPptRQAAJcrv7A==\"", "id": "AAMkADM1MTdlMGIzLTZhZWUtNDQ0ZC05Y2M4LWViMjhmOWJlMDhhMQBGAAAAAAA5e3965gkBSLcU1p00sMSyBwAHduaxajFfTpv0kchk_m1FAAAAAAENAAAHduaxajFfTpv0kchk_m1FAAAl1BuqAAA=", "createdDateTime": "2022-06-08T16:19:18.997293Z", "lastModifiedDateTime": "2022-06-08T16:22:18.7276485Z", "changeKey": "B3bmsWoxX06b9JHIZPptRQAAJcrv7A==", "categories": [], "transactionId": "42ac0b77-313e-20ca-2cac-1a3f58b3452d", "originalStartTimeZone": "Romance Standard Time", "originalEndTimeZone": "Romance Standard Time", "iCalUId": "040000008200E00074C5B7101A82E00800000000A8955A81537BD801000000000000000010000000007EB443987CD641B6794C4BA48EE356", "reminderMinutesBeforeStart": 15, "isReminderOn": true, "hasAttachments": false, "subject": "test 2", "bodyPreview": "________________________________________________________________________________\r\nRéunion Microsoft Teams\r\nRejoindre sur votre ordinateur ou application mobile\r\nCliquez ici pour participer à la réunion\r\nPour en savoir plus | Options de réunion\r\n________", "importance": "normal", "sensitivity": "normal", "isAllDay": false, "isCancelled": false, "isOrganizer": true, "responseRequested": true, "seriesMasterId": null, "showAs": "busy", "type": "singleInstance", "webLink": "https://outlook.office365.com/owa/?itemid=AAMkADM1MTdlMGIzLTZhZWUtNDQ0ZC05Y2M4LWViMjhmOWJlMDhhMQBGAAAAAAA5e3965gkBSLcU1p00sMSyBwAHduaxajFfTpv0kchk%2Bm1FAAAAAAENAAAHduaxajFfTpv0kchk%2Bm1FAAAl1BuqAAA%3D&exvsurl=1&path=/calendar/item", "onlineMeetingUrl": null, "isOnlineMeeting": true, "onlineMeetingProvider": "teamsForBusiness", "allowNewTimeProposals": true, "occurrenceId": null, "isDraft": false, "hideAttendees": false, "responseStatus": { "response": "organizer", "time": "0001-01-01T00:00:00Z" }, "body": { "contentType": "html", "content": "\r\n\r\n\r\n\r\n\r\n
\r\n
\r\n
________________________________________________________________________________\r\n
\r\n
\r\n
Réunion Microsoft Teams\r\n
\r\n
\r\n
Rejoindre sur votre ordinateur ou application mobile\r\n
\r\nCliquez\r\n ici pour participer à la réunion
\r\n
Pour en savoir\r\n plus | \r\nOptions de réunion
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
________________________________________________________________________________\r\n
\r\n\r\n\r\n" }, "start": { "dateTime": "2022-06-11T12:30:00.0000000", "timeZone": "UTC" }, "end": { "dateTime": "2022-06-11T13:30:00.0000000", "timeZone": "UTC" }, "location": { "displayName": "", "locationType": "default", "uniqueIdType": "unknown", "address": {}, "coordinates": {} }, "locations": [], "recurrence": null, "attendees": [ { "type": "required", "status": { "response": "accepted", "time": "2022-06-08T16:22:15.1392583Z" }, "emailAddress": { "name": "Alex Wilber", "address": "AlexW@2zy74l.onmicrosoft.com" } }, { "type": "required", "status": { "response": "accepted", "time": "2022-06-08T16:22:15.1392583Z" }, "emailAddress": { "name": "External User", "address": "external@example.com" } }, { "type": "required", "status": { "response": "declined", "time": "2022-06-08T16:22:15.1392583Z" }, "emailAddress": { "name": "Alfred Nobel", "address": "alfredN@2zy74l.onmicrosoft.com" } } ], "organizer": { "emailAddress": { "name": "Diego Siciliani", "address": "DiegoS@2zy74l.onmicrosoft.com" } }, "onlineMeeting": { "joinUrl": "https://teams.microsoft.com/l/meetup-join/19%3ameeting_NTE3ODUxY2ItNGJhNi00Y2UwLTljN2QtMmQ3YjAxNWY1Nzk2%40thread.v2/0?context=%7b%22Tid%22%3a%22421bf216-3f48-47bd-a7cf-8b1995cb24bd%22%2c%22Oid%22%3a%224feb0ae3-7ffb-48dd-891e-c86b2cdeefd4%22%7d" } } JSON; public function testHandleAttendeesConfirmingCalendar(): void { $machineHttpClient = new MockHttpClient([ new MockResponse(self::REMOTE_CALENDAR_WITH_ATTENDEES, ['http_code' => 200]), ]); $userA = (new User())->setEmail('alexw@2zy74l.onmicrosoft.com') ->setEmailCanonical('alexw@2zy74l.onmicrosoft.com'); $userB = (new User())->setEmail('zzzzz@2zy74l.onmicrosoft.com') ->setEmailCanonical('zzzzz@2zy74l.onmicrosoft.com'); $userC = (new User())->setEmail('alfredN@2zy74l.onmicrosoft.com') ->setEmailCanonical('alfredn@2zy74l.onmicrosoft.com'); $userRepository = $this->prophesize(UserRepositoryInterface::class); $userRepository->findOneByUsernameOrEmail(Argument::exact('AlexW@2zy74l.onmicrosoft.com')) ->willReturn($userA); $userRepository->findOneByUsernameOrEmail(Argument::exact('zzzzz@2zy74l.onmicrosoft.com')) ->willReturn($userB); $userRepository->findOneByUsernameOrEmail(Argument::exact('alfredN@2zy74l.onmicrosoft.com')) ->willReturn($userC); $userRepository->findOneByUsernameOrEmail(Argument::exact('external@example.com')) ->willReturn(null); $calendarSyncer = new CalendarSyncer( new NullLogger(), $machineHttpClient, $userRepository->reveal() ); $calendar = new Calendar(); $calendar ->setMainUser($user = new User()) ->setStartDate(new \DateTimeImmutable('2022-06-11 14:30:00')) ->setEndDate(new \DateTimeImmutable('2022-06-11 15:30:00')) ->addUser($userA) ->addUser($userB) ->setCalendarRange(new CalendarRange()) ->addRemoteAttributes([ 'lastModifiedDateTime' => 0, 'changeKey' => 'abcd', ]); $notification = json_decode(self::NOTIF_UPDATE, true); $calendarSyncer->handleCalendarSync( $calendar, $notification['value'][0], $user ); $this->assertTrue($calendar->preventEnqueueChanges); $this->assertEquals(Calendar::STATUS_VALID, $calendar->getStatus()); // user A is invited, and accepted $this->assertTrue($calendar->isInvited($userA)); $this->assertEquals(Invite::ACCEPTED, $calendar->getInviteForUser($userA)->getStatus()); $this->assertFalse($calendar->getInviteForUser($userA)->preventEnqueueChanges); // user B is no more invited $this->assertFalse($calendar->isInvited($userB)); // user C is invited, but declined $this->assertFalse($calendar->getInviteForUser($userC)->preventEnqueueChanges); $this->assertTrue($calendar->isInvited($userC)); $this->assertEquals(Invite::DECLINED, $calendar->getInviteForUser($userC)->getStatus()); } public function testHandleDeleteCalendar(): void { $machineHttpClient = new MockHttpClient([]); $userRepository = $this->prophesize(UserRepositoryInterface::class); $calendarSyncer = new CalendarSyncer( new NullLogger(), $machineHttpClient, $userRepository->reveal() ); $calendar = new Calendar(); $calendar ->setMainUser($user = new User()) ->setCalendarRange($calendarRange = new CalendarRange()); $notification = json_decode(self::NOTIF_DELETE, true); $calendarSyncer->handleCalendarSync( $calendar, $notification['value'][0], $user ); $this->assertEquals(Calendar::STATUS_CANCELED, $calendar->getStatus()); $this->assertNull($calendar->getCalendarRange()); $this->assertTrue($calendar->preventEnqueueChanges); } public function testHandleMoveCalendar(): void { $machineHttpClient = new MockHttpClient([ new MockResponse(self::REMOTE_CALENDAR_NO_ATTENDEES, ['http_code' => 200]), ]); $userRepository = $this->prophesize(UserRepositoryInterface::class); $calendarSyncer = new CalendarSyncer( new NullLogger(), $machineHttpClient, $userRepository->reveal() ); $calendar = new Calendar(); $calendar ->setMainUser($user = new User()) ->setStartDate(new \DateTimeImmutable('2020-01-01 10:00:00')) ->setEndDate(new \DateTimeImmutable('2020-01-01 12:00:00')) ->setCalendarRange(new CalendarRange()) ->addRemoteAttributes([ 'lastModifiedDateTime' => 0, 'changeKey' => 'abcd', ]); $previousVersion = $calendar->getDateTimeVersion(); $notification = json_decode(self::NOTIF_UPDATE, true); $calendarSyncer->handleCalendarSync( $calendar, $notification['value'][0], $user ); $this->assertStringContainsString( '2022-06-10T15:30:00', $calendar->getStartDate()->format(\DateTimeImmutable::ATOM) ); $this->assertStringContainsString( '2022-06-10T17:30:00', $calendar->getEndDate()->format(\DateTimeImmutable::ATOM) ); $this->assertTrue($calendar->preventEnqueueChanges); $this->assertGreaterThan($previousVersion, $calendar->getDateTimeVersion()); } public function testHandleNotMovedCalendar(): void { $machineHttpClient = new MockHttpClient([ new MockResponse(self::REMOTE_CALENDAR_NO_ATTENDEES, ['http_code' => 200]), ]); $userRepository = $this->prophesize(UserRepositoryInterface::class); $calendarSyncer = new CalendarSyncer( new NullLogger(), $machineHttpClient, $userRepository->reveal() ); $calendar = new Calendar(); $calendar ->setMainUser($user = new User()) ->setStartDate(new \DateTimeImmutable('2022-06-10 15:30:00')) ->setEndDate(new \DateTimeImmutable('2022-06-10 17:30:00')) ->setCalendarRange(new CalendarRange()) ->addRemoteAttributes([ 'lastModifiedDateTime' => 0, 'changeKey' => 'abcd', ]); $notification = json_decode(self::NOTIF_UPDATE, true); $calendarSyncer->handleCalendarSync( $calendar, $notification['value'][0], $user ); $this->assertStringContainsString( '2022-06-10T15:30:00', $calendar->getStartDate()->format(\DateTimeImmutable::ATOM) ); $this->assertStringContainsString( '2022-06-10T17:30:00', $calendar->getEndDate()->format(\DateTimeImmutable::ATOM) ); $this->assertTrue($calendar->preventEnqueueChanges); $this->assertEquals(Calendar::STATUS_VALID, $calendar->getStatus()); } public function testHandleNotOrganizer(): void { // when isOrganiser === false, nothing should happens $machineHttpClient = new MockHttpClient([ new MockResponse(self::REMOTE_CALENDAR_NOT_ORGANIZER, ['http_code' => 200]), ]); $userRepository = $this->prophesize(UserRepositoryInterface::class); $calendarSyncer = new CalendarSyncer( new NullLogger(), $machineHttpClient, $userRepository->reveal() ); $calendar = new Calendar(); $calendar ->setMainUser($user = new User()) ->setStartDate(new \DateTimeImmutable('2020-01-01 10:00:00')) ->setEndDate(new \DateTimeImmutable('2020-01-01 12:00:00')) ->setCalendarRange(new CalendarRange()) ->addRemoteAttributes([ 'lastModifiedDateTime' => 0, 'changeKey' => 'abcd', ]); $notification = json_decode(self::NOTIF_UPDATE, true); $calendarSyncer->handleCalendarSync( $calendar, $notification['value'][0], $user ); $this->assertStringContainsString( '2020-01-01T10:00:00', $calendar->getStartDate()->format(\DateTimeImmutable::ATOM) ); $this->assertStringContainsString( '2020-01-01T12:00:00', $calendar->getEndDate()->format(\DateTimeImmutable::ATOM) ); $this->assertEquals(Calendar::STATUS_VALID, $calendar->getStatus()); } }