slotRepository = new InMemoryScheduleSlotRepository(); $clock = new class implements Clock { public function now(): DateTimeImmutable { return new DateTimeImmutable('2026-10-01 10:00:00'); } }; $this->handler = new TruncateSlotRecurrenceHandler( $this->slotRepository, $clock, ); } #[Test] public function truncatesRecurrenceOneDayBeforeGivenDate(): void { $slot = $this->createAndSaveSlot( recurrenceStart: new DateTimeImmutable('2026-09-01'), recurrenceEnd: new DateTimeImmutable('2027-07-04'), ); $command = new TruncateSlotRecurrenceCommand( tenantId: self::TENANT_ID, slotId: (string) $slot->id, fromDate: '2026-10-05', updatedBy: self::UPDATER_ID, ); ($this->handler)($command); $tenantId = TenantId::fromString(self::TENANT_ID); $updated = $this->slotRepository->get($slot->id, $tenantId); self::assertSame('2026-10-04', $updated->recurrenceEnd->format('Y-m-d')); } #[Test] public function throwsWhenDateIsNotActiveForSlot(): void { $slot = $this->createAndSaveSlot( recurrenceStart: new DateTimeImmutable('2026-09-01'), recurrenceEnd: new DateTimeImmutable('2026-09-28'), ); $command = new TruncateSlotRecurrenceCommand( tenantId: self::TENANT_ID, slotId: (string) $slot->id, fromDate: '2026-10-05', updatedBy: self::UPDATER_ID, ); $this->expectException(DateExceptionInvalideException::class); ($this->handler)($command); } private function createAndSaveSlot( ?DateTimeImmutable $recurrenceStart = null, ?DateTimeImmutable $recurrenceEnd = null, ): ScheduleSlot { $slot = ScheduleSlot::creer( tenantId: TenantId::fromString(self::TENANT_ID), classId: ClassId::fromString(self::CLASS_ID), subjectId: SubjectId::fromString(self::SUBJECT_ID), teacherId: UserId::fromString(self::TEACHER_ID), dayOfWeek: DayOfWeek::MONDAY, timeSlot: new TimeSlot('08:00', '09:00'), room: null, isRecurring: true, now: new DateTimeImmutable('2026-09-01 10:00:00'), recurrenceStart: $recurrenceStart, recurrenceEnd: $recurrenceEnd, ); $this->slotRepository->save($slot); return $slot; } }