From 9b88ea32d2e2f4da596cef17b3239b8bbcc10632 Mon Sep 17 00:00:00 2001 From: SebastianKrupinski Date: Sat, 16 Nov 2024 14:24:36 -0500 Subject: [PATCH] fix: appointment slots start and end time Signed-off-by: SebastianKrupinski --- lib/Controller/BookingController.php | 18 +-- .../unit/Controller/BookingControllerTest.php | 139 +++++++++++++++--- 2 files changed, 129 insertions(+), 28 deletions(-) diff --git a/lib/Controller/BookingController.php b/lib/Controller/BookingController.php index d28561dfa5..b23a4d405b 100644 --- a/lib/Controller/BookingController.php +++ b/lib/Controller/BookingController.php @@ -7,6 +7,7 @@ */ namespace OCA\Calendar\Controller; +use DateTime; use DateTimeImmutable; use DateTimeZone; use InvalidArgumentException; @@ -89,22 +90,21 @@ public function __construct(string $appName, public function getBookableSlots(int $appointmentConfigId, int $startTime, string $timeZone): JsonResponse { - // Convert the timestamps to the beginning and end of the respective day in the specified timezone try { $tz = new DateTimeZone($timeZone); } catch (Exception $e) { $this->logger->error('Timezone invalid', ['exception' => $e]); return JsonResponse::fail('Invalid time zone', Http::STATUS_UNPROCESSABLE_ENTITY); } - $startTimeInTz = (new DateTimeImmutable()) - ->setTimestamp($startTime) - ->setTimezone($tz) - ->setTime(0, 0) + // UI sends epoch start of day adjusted for system users calendar + // E.g "Mon, 18 Nov 2024 05:00:00 +0000" (America/Toronto) + $startDate = (new DateTimeImmutable("@$startTime")); + // Convert start date to requesters selected timezone adjusted start and end of day in epoch + // E.g "Mon, 18 Nov 2024 06:00:00 +0000" (America/Mexico_City) + $startTimeInTz = (new DateTime($startDate->format('Y-m-d'), $tz)) ->getTimestamp(); - $endTimeInTz = (new DateTimeImmutable()) - ->setTimestamp($startTime) - ->setTimezone($tz) - ->setTime(23, 59, 59) + $endTimeInTz = (new DateTime($startDate->format('Y-m-d'), $tz)) + ->modify('+1 day') ->getTimestamp(); if ($startTimeInTz > $endTimeInTz) { diff --git a/tests/php/unit/Controller/BookingControllerTest.php b/tests/php/unit/Controller/BookingControllerTest.php index 0ec33ab960..07f7349574 100644 --- a/tests/php/unit/Controller/BookingControllerTest.php +++ b/tests/php/unit/Controller/BookingControllerTest.php @@ -8,6 +8,7 @@ namespace OCA\Calendar\Controller; use ChristophWurst\Nextcloud\Testing\TestCase; +use DateTime; use DateTimeImmutable; use DateTimeZone; use Exception; @@ -108,15 +109,11 @@ protected function setUp():void { public function testGetBookableSlots(): void { $start = time(); $tz = new DateTimeZone('Europe/Berlin'); - $sDT = (new DateTimeImmutable()) - ->setTimestamp($start) - ->setTimezone($tz) - ->setTime(0, 0) + $startDate = (new DateTimeImmutable("@$start")); + $sDT = (new DateTime($startDate->format('Y-m-d'), $tz)) ->getTimestamp(); - $eDT = (new DateTimeImmutable()) - ->setTimestamp($start) - ->setTimezone($tz) - ->setTime(23, 59, 59) + $eDT = (new DateTime($startDate->format('Y-m-d'), $tz)) + ->modify('+1 day') ->getTimestamp(); $apptConfg = new AppointmentConfig(); @@ -135,6 +132,25 @@ public function testGetBookableSlots(): void { $this->controller->getBookableSlots($apptConfg->getId(), $start, 'Europe/Berlin'); } + public function testGetBookableSlotsDatesInPast(): void { + $start = time(); + $fakeFutureTimestamp = time() + (100 * 24 * 60 * 60); + $apptConfg = new AppointmentConfig(); + $apptConfg->setId(1); + $this->time->expects(self::once()) + ->method('getTime') + ->willReturn($fakeFutureTimestamp); + $this->apptService->expects(self::never()) + ->method('findById') + ->with(1); + $this->bookingService->expects(self::never()) + ->method('getAvailableSlots'); + $this->logger->expects(self::once()) + ->method('warning'); + + $this->controller->getBookableSlots($apptConfg->getId(), $start, 'Europe/Berlin'); + } + public function testGetBookableSlotsInvalidTimezone(): void { $start = time(); $apptConfg = new AppointmentConfig(); @@ -151,23 +167,108 @@ public function testGetBookableSlotsInvalidTimezone(): void { $this->controller->getBookableSlots($apptConfg->getId(), $start, 'Hook/Neverland'); } - public function testGetBookableSlotsDatesInPast(): void { - $start = time(); - $fakeFutureTimestamp = time() + (100 * 24 * 60 * 60); + public function testGetBookableSlotsTimezoneIdentical(): void { + + $now = (new DateTime('2024-6-30 8:00:00'))->getTimestamp(); + $start = (new DateTime('2024-7-1 04:00:00'))->getTimestamp(); // Start date with America/Toronto offset + $timezone = 'America/Toronto'; + $sDT = (new DateTime('2024-7-1 04:00:00'))->getTimestamp(); + ; + $eDT = (new DateTime('2024-7-2 04:00:00'))->getTimestamp(); + ; + $apptConfg = new AppointmentConfig(); $apptConfg->setId(1); $this->time->expects(self::once()) ->method('getTime') - ->willReturn($fakeFutureTimestamp); - $this->apptService->expects(self::never()) + ->willReturn($now); + $this->apptService->expects(self::once()) ->method('findById') - ->with(1); - $this->bookingService->expects(self::never()) - ->method('getAvailableSlots'); - $this->logger->expects(self::once()) - ->method('warning'); + ->with(1) + ->willReturn($apptConfg); + $this->bookingService->expects(self::once()) + ->method('getAvailableSlots') + ->with($apptConfg, $sDT, $eDT); - $this->controller->getBookableSlots($apptConfg->getId(), $start, 'Europe/Berlin'); + $this->controller->getBookableSlots($apptConfg->getId(), $start, $timezone); + } + + public function testGetBookableSlotsTimezoneMinus10(): void { + + $now = (new DateTime('2024-6-30 8:00:00'))->getTimestamp(); + $start = (new DateTime('2024-7-1 4:00:00'))->getTimestamp(); // Start date with America/Toronto offset + $timezone = 'Pacific/Pago_Pago'; + $sDT = (new DateTime('2024-7-1 11:00:00'))->getTimestamp(); + ; + $eDT = (new DateTime('2024-7-2 11:00:00'))->getTimestamp(); + ; + + $apptConfg = new AppointmentConfig(); + $apptConfg->setId(1); + $this->time->expects(self::once()) + ->method('getTime') + ->willReturn($now); + $this->apptService->expects(self::once()) + ->method('findById') + ->with(1) + ->willReturn($apptConfg); + $this->bookingService->expects(self::once()) + ->method('getAvailableSlots') + ->with($apptConfg, $sDT, $eDT); + + $this->controller->getBookableSlots($apptConfg->getId(), $start, $timezone); + } + + public function testGetBookableSlotsTimezonePlus10(): void { + + $now = (new DateTime('2024-6-30 8:00:00'))->getTimestamp(); + $start = (new DateTime('2024-7-1 4:00:00'))->getTimestamp(); // Start date with America/Toronto offset + $timezone = 'Australia/Sydney'; + $sDT = (new DateTime('2024-6-30 14:00:00'))->getTimestamp(); + ; + $eDT = (new DateTime('2024-7-1 14:00:00'))->getTimestamp(); + ; + + $apptConfg = new AppointmentConfig(); + $apptConfg->setId(1); + $this->time->expects(self::once()) + ->method('getTime') + ->willReturn($now); + $this->apptService->expects(self::once()) + ->method('findById') + ->with(1) + ->willReturn($apptConfg); + $this->bookingService->expects(self::once()) + ->method('getAvailableSlots') + ->with($apptConfg, $sDT, $eDT); + + $this->controller->getBookableSlots($apptConfg->getId(), $start, $timezone); + } + + public function testGetBookableSlotsTimezonePlus14(): void { + + $now = (new DateTime('2024-6-30 8:00:00'))->getTimestamp(); + $start = (new DateTime('2024-7-1 4:00:00'))->getTimestamp(); // Start date with America/Toronto offset + $timezone = 'Pacific/Kiritimati'; + $sDT = (new DateTime('2024-6-30 10:00:00'))->getTimestamp(); + ; + $eDT = (new DateTime('2024-7-1 10:00:00'))->getTimestamp(); + ; + + $apptConfg = new AppointmentConfig(); + $apptConfg->setId(1); + $this->time->expects(self::once()) + ->method('getTime') + ->willReturn($now); + $this->apptService->expects(self::once()) + ->method('findById') + ->with(1) + ->willReturn($apptConfg); + $this->bookingService->expects(self::once()) + ->method('getAvailableSlots') + ->with($apptConfg, $sDT, $eDT); + + $this->controller->getBookableSlots($apptConfg->getId(), $start, $timezone); } public function testBook(): void {