tempDir = sys_get_temp_dir() . '/classeo-calendar-test-' . uniqid(); mkdir($this->tempDir); $this->provider = new JsonOfficialCalendarProvider( dataDirectory: $this->tempDir, httpClient: HttpClient::create(), holidaysCalculator: new FrenchPublicHolidaysCalculator(), logger: new NullLogger(), ); } protected function tearDown(): void { // Supprimer les fichiers générés $files = glob($this->tempDir . '/*.json'); foreach ($files as $file) { unlink($file); } rmdir($this->tempDir); } #[Test] public function apiRetourneDesVacancesPourChaqueZone(): void { $vacationsA = $this->provider->vacancesParZone(SchoolZone::A, self::ACADEMIC_YEAR); $vacationsB = $this->provider->vacancesParZone(SchoolZone::B, self::ACADEMIC_YEAR); $vacationsC = $this->provider->vacancesParZone(SchoolZone::C, self::ACADEMIC_YEAR); self::assertNotEmpty($vacationsA, 'L\'API doit retourner des vacances pour la zone A'); self::assertNotEmpty($vacationsB, 'L\'API doit retourner des vacances pour la zone B'); self::assertNotEmpty($vacationsC, 'L\'API doit retourner des vacances pour la zone C'); } #[Test] public function chaqueEntreeALesBonsChamps(): void { $entries = $this->provider->toutesEntreesOfficielles(SchoolZone::A, self::ACADEMIC_YEAR); foreach ($entries as $entry) { self::assertNotEmpty((string) $entry->id, 'Chaque entrée doit avoir un id'); self::assertInstanceOf(CalendarEntryType::class, $entry->type); self::assertNotNull($entry->startDate, 'startDate ne doit pas être null'); self::assertNotNull($entry->endDate, 'endDate ne doit pas être null'); self::assertNotEmpty($entry->label, 'label ne doit pas être vide'); } } #[Test] public function lesDatesDeVacancesSontCoherentes(): void { $vacations = $this->provider->vacancesParZone(SchoolZone::A, self::ACADEMIC_YEAR); foreach ($vacations as $vacation) { self::assertSame(CalendarEntryType::VACATION, $vacation->type); self::assertGreaterThanOrEqual( $vacation->startDate->format('Y-m-d'), $vacation->endDate->format('Y-m-d'), sprintf('La fin (%s) doit être >= au début (%s) pour "%s"', $vacation->endDate->format('Y-m-d'), $vacation->startDate->format('Y-m-d'), $vacation->label, ), ); } } #[Test] public function leFichierJsonEstCreeEnCache(): void { $expectedFile = $this->tempDir . '/official-holidays-' . self::ACADEMIC_YEAR . '.json'; self::assertFileDoesNotExist($expectedFile); $this->provider->toutesEntreesOfficielles(SchoolZone::A, self::ACADEMIC_YEAR); self::assertFileExists($expectedFile); $content = json_decode(file_get_contents($expectedFile), true); self::assertArrayHasKey('holidays', $content); self::assertArrayHasKey('vacations', $content); self::assertArrayHasKey('A', $content['vacations']); self::assertArrayHasKey('B', $content['vacations']); self::assertArrayHasKey('C', $content['vacations']); } #[Test] public function leDeuxiemeAppelUtiliseLeCacheSansToucherLApi(): void { // Premier appel : fetch API + sauvegarde $first = $this->provider->toutesEntreesOfficielles(SchoolZone::A, self::ACADEMIC_YEAR); // Recréer un provider avec un HttpClient qui échoue systématiquement // Si le cache fonctionne, il ne touchera pas au HttpClient $cachedProvider = new JsonOfficialCalendarProvider( dataDirectory: $this->tempDir, httpClient: HttpClient::create(), // pas utilisé si le fichier existe holidaysCalculator: new FrenchPublicHolidaysCalculator(), logger: new NullLogger(), ); $second = $cachedProvider->toutesEntreesOfficielles(SchoolZone::A, self::ACADEMIC_YEAR); self::assertCount(count($first), $second); } #[Test] public function vacancesToussaintEtNoelPresentes(): void { $vacations = $this->provider->vacancesParZone(SchoolZone::A, self::ACADEMIC_YEAR); $labels = array_map(static fn ($v) => $v->label, $vacations); self::assertNotEmpty( array_filter($labels, static fn (string $l) => str_contains(strtolower($l), 'toussaint')), 'Les vacances de la Toussaint doivent être présentes', ); self::assertNotEmpty( array_filter($labels, static fn (string $l) => str_contains(strtolower($l), 'noël') || str_contains(strtolower($l), 'noel')), 'Les vacances de Noël doivent être présentes', ); } }