tempDir = sys_get_temp_dir() . '/classeo-handler-test-' . uniqid(); $this->repository = new InMemorySchoolCalendarRepository(); $clock = new class implements Clock { public function now(): DateTimeImmutable { return new DateTimeImmutable('2026-02-17 10:00:00'); } }; $this->handler = new ConfigureCalendarHandler( calendarRepository: $this->repository, calendarProvider: new JsonOfficialCalendarProvider( dataDirectory: $this->tempDir, httpClient: new MockHttpClient($this->mockApiResponse()), holidaysCalculator: new FrenchPublicHolidaysCalculator(), logger: new NullLogger(), ), clock: $clock, ); } protected function tearDown(): void { $files = glob($this->tempDir . '/*'); if ($files !== false) { foreach ($files as $file) { unlink($file); } } if (is_dir($this->tempDir)) { rmdir($this->tempDir); } } #[Test] public function itConfiguresCalendarWithZoneA(): void { $command = new ConfigureCalendarCommand( tenantId: self::TENANT_ID, academicYearId: self::ACADEMIC_YEAR_ID, zone: 'A', academicYear: '2024-2025', ); $calendar = ($this->handler)($command); self::assertSame(SchoolZone::A, $calendar->zone); self::assertNotEmpty($calendar->entries()); } #[Test] public function itImportsHolidaysAndVacations(): void { $command = new ConfigureCalendarCommand( tenantId: self::TENANT_ID, academicYearId: self::ACADEMIC_YEAR_ID, zone: 'B', academicYear: '2024-2025', ); $calendar = ($this->handler)($command); $holidays = array_filter( $calendar->entries(), static fn ($e) => $e->type === CalendarEntryType::HOLIDAY, ); $vacations = array_filter( $calendar->entries(), static fn ($e) => $e->type === CalendarEntryType::VACATION, ); self::assertNotEmpty($holidays); self::assertNotEmpty($vacations); } #[Test] public function itSavesCalendarToRepository(): void { $command = new ConfigureCalendarCommand( tenantId: self::TENANT_ID, academicYearId: self::ACADEMIC_YEAR_ID, zone: 'C', academicYear: '2024-2025', ); ($this->handler)($command); $saved = $this->repository->findByTenantAndYear( TenantId::fromString(self::TENANT_ID), AcademicYearId::fromString(self::ACADEMIC_YEAR_ID), ); self::assertNotNull($saved); self::assertSame(SchoolZone::C, $saved->zone); } #[Test] public function itReconfiguresExistingCalendar(): void { $command = new ConfigureCalendarCommand( tenantId: self::TENANT_ID, academicYearId: self::ACADEMIC_YEAR_ID, zone: 'A', academicYear: '2024-2025', ); ($this->handler)($command); $commandB = new ConfigureCalendarCommand( tenantId: self::TENANT_ID, academicYearId: self::ACADEMIC_YEAR_ID, zone: 'B', academicYear: '2024-2025', ); $calendar = ($this->handler)($commandB); self::assertSame(SchoolZone::B, $calendar->zone); } #[Test] public function itRecordsCalendrierConfigureEvent(): void { $command = new ConfigureCalendarCommand( tenantId: self::TENANT_ID, academicYearId: self::ACADEMIC_YEAR_ID, zone: 'A', academicYear: '2024-2025', ); $calendar = ($this->handler)($command); $events = $calendar->pullDomainEvents(); self::assertNotEmpty($events); } private function mockApiResponse(): MockResponse { $records = [ ['description' => 'Vacances de la Toussaint', 'start_date' => '2024-10-18T23:00:00+00:00', 'end_date' => '2024-11-03T23:00:00+00:00', 'zones' => 'Zone A'], ['description' => 'Vacances de Noël', 'start_date' => '2024-12-20T23:00:00+00:00', 'end_date' => '2025-01-05T23:00:00+00:00', 'zones' => 'Zone A'], ['description' => 'Vacances d\'hiver', 'start_date' => '2025-02-21T23:00:00+00:00', 'end_date' => '2025-03-09T23:00:00+00:00', 'zones' => 'Zone A'], ['description' => 'Vacances de printemps', 'start_date' => '2025-04-18T22:00:00+00:00', 'end_date' => '2025-05-04T22:00:00+00:00', 'zones' => 'Zone A'], ['description' => 'Vacances d\'été', 'start_date' => '2025-07-04T22:00:00+00:00', 'end_date' => '2025-08-31T22:00:00+00:00', 'zones' => 'Zone A'], ['description' => 'Vacances de la Toussaint', 'start_date' => '2024-10-18T23:00:00+00:00', 'end_date' => '2024-11-03T23:00:00+00:00', 'zones' => 'Zone B'], ['description' => 'Vacances de Noël', 'start_date' => '2024-12-20T23:00:00+00:00', 'end_date' => '2025-01-05T23:00:00+00:00', 'zones' => 'Zone B'], ['description' => 'Vacances d\'hiver', 'start_date' => '2025-02-07T23:00:00+00:00', 'end_date' => '2025-02-23T23:00:00+00:00', 'zones' => 'Zone B'], ['description' => 'Vacances de printemps', 'start_date' => '2025-04-04T22:00:00+00:00', 'end_date' => '2025-04-20T22:00:00+00:00', 'zones' => 'Zone B'], ['description' => 'Vacances d\'été', 'start_date' => '2025-07-04T22:00:00+00:00', 'end_date' => '2025-08-31T22:00:00+00:00', 'zones' => 'Zone B'], ['description' => 'Vacances de la Toussaint', 'start_date' => '2024-10-18T23:00:00+00:00', 'end_date' => '2024-11-03T23:00:00+00:00', 'zones' => 'Zone C'], ['description' => 'Vacances de Noël', 'start_date' => '2024-12-20T23:00:00+00:00', 'end_date' => '2025-01-05T23:00:00+00:00', 'zones' => 'Zone C'], ['description' => 'Vacances d\'hiver', 'start_date' => '2025-02-14T23:00:00+00:00', 'end_date' => '2025-03-02T23:00:00+00:00', 'zones' => 'Zone C'], ['description' => 'Vacances de printemps', 'start_date' => '2025-04-11T22:00:00+00:00', 'end_date' => '2025-04-27T22:00:00+00:00', 'zones' => 'Zone C'], ['description' => 'Vacances d\'été', 'start_date' => '2025-07-04T22:00:00+00:00', 'end_date' => '2025-08-31T22:00:00+00:00', 'zones' => 'Zone C'], ]; return new MockResponse(json_encode(['results' => $records], JSON_THROW_ON_ERROR), ['http_code' => 200]); } }