repository = new InMemorySchoolCalendarRepository(); $clock = new class implements Clock { public function now(): DateTimeImmutable { return new DateTimeImmutable('2026-02-17 10:00:00'); } }; $this->handler = new AddPedagogicalDayHandler( calendarRepository: $this->repository, clock: $clock, ); } #[Test] public function itAddsPedagogicalDayToExistingCalendar(): void { $this->seedCalendar(); $command = new AddPedagogicalDayCommand( tenantId: self::TENANT_ID, academicYearId: self::ACADEMIC_YEAR_ID, date: '2025-03-14', label: 'Formation enseignants', ); $calendar = ($this->handler)($command); $entries = $calendar->entries(); $pedagogicalDays = array_filter( $entries, static fn ($e) => $e->type === CalendarEntryType::PEDAGOGICAL_DAY, ); self::assertCount(1, $pedagogicalDays); $day = array_values($pedagogicalDays)[0]; self::assertSame('Formation enseignants', $day->label); self::assertSame('2025-03-14', $day->startDate->format('Y-m-d')); } #[Test] public function itCreatesNewCalendarIfNoneExists(): void { $command = new AddPedagogicalDayCommand( tenantId: self::TENANT_ID, academicYearId: self::ACADEMIC_YEAR_ID, date: '2025-03-14', label: 'Formation enseignants', ); $calendar = ($this->handler)($command); self::assertCount(1, $calendar->entries()); } #[Test] public function itRecordsJourneePedagogiqueAjouteeEvent(): void { $command = new AddPedagogicalDayCommand( tenantId: self::TENANT_ID, academicYearId: self::ACADEMIC_YEAR_ID, date: '2025-03-14', label: 'Formation enseignants', ); $calendar = ($this->handler)($command); $events = $calendar->pullDomainEvents(); self::assertCount(1, $events); self::assertInstanceOf(JourneePedagogiqueAjoutee::class, $events[0]); self::assertSame('Formation enseignants', $events[0]->label); } #[Test] public function itSavesCalendarWithPedagogicalDay(): void { $command = new AddPedagogicalDayCommand( tenantId: self::TENANT_ID, academicYearId: self::ACADEMIC_YEAR_ID, date: '2025-03-14', label: 'Formation', description: 'Journée de formation continue', ); ($this->handler)($command); $saved = $this->repository->getByTenantAndYear( TenantId::fromString(self::TENANT_ID), AcademicYearId::fromString(self::ACADEMIC_YEAR_ID), ); self::assertCount(1, $saved->entries()); self::assertSame('Journée de formation continue', $saved->entries()[0]->description); } #[Test] public function itRejectsMalformedDate(): void { $command = new AddPedagogicalDayCommand( tenantId: self::TENANT_ID, academicYearId: self::ACADEMIC_YEAR_ID, date: 'not-a-date', label: 'Formation', ); $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('La date doit être au format YYYY-MM-DD.'); ($this->handler)($command); } #[Test] public function itRejectsImpossibleCalendarDate(): void { $command = new AddPedagogicalDayCommand( tenantId: self::TENANT_ID, academicYearId: self::ACADEMIC_YEAR_ID, date: '2025-06-31', label: 'Formation', ); $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('La date n\'existe pas dans le calendrier.'); ($this->handler)($command); } private function seedCalendar(): void { $calendar = SchoolCalendar::initialiser( tenantId: TenantId::fromString(self::TENANT_ID), academicYearId: AcademicYearId::fromString(self::ACADEMIC_YEAR_ID), ); $this->repository->save($calendar); } }