Files
Classeo/backend/tests/Unit/Administration/Application/Query/IsSchoolDay/IsSchoolDayHandlerTest.php
Mathias STRASSER e06fd5424d feat: Configurer les jours fériés et vacances du calendrier scolaire
Les administrateurs d'établissement avaient besoin de gérer le calendrier
scolaire (FR80) pour que l'EDT et les devoirs respectent automatiquement
les jours non travaillés. Sans cette configuration centralisée, chaque
module devait gérer indépendamment les contraintes de dates.

Le calendrier s'appuie sur l'API data.education.gouv.fr pour importer
les vacances officielles par zone (A/B/C) et calcule les 11 jours fériés
français (dont les fêtes mobiles liées à Pâques). Les enseignants sont
notifiés par email lors de l'ajout d'une journée pédagogique. Un query
IsSchoolDay et une validation des dates d'échéance de devoirs permettent
aux autres modules de s'intégrer sans couplage direct.
2026-02-18 12:09:19 +01:00

177 lines
5.6 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests\Unit\Administration\Application\Query\IsSchoolDay;
use App\Administration\Application\Query\IsSchoolDay\IsSchoolDayHandler;
use App\Administration\Application\Query\IsSchoolDay\IsSchoolDayQuery;
use App\Administration\Domain\Model\SchoolCalendar\CalendarEntry;
use App\Administration\Domain\Model\SchoolCalendar\CalendarEntryId;
use App\Administration\Domain\Model\SchoolCalendar\CalendarEntryType;
use App\Administration\Domain\Model\SchoolCalendar\SchoolCalendar;
use App\Administration\Domain\Model\SchoolClass\AcademicYearId;
use App\Administration\Infrastructure\Persistence\InMemory\InMemorySchoolCalendarRepository;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use InvalidArgumentException;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
final class IsSchoolDayHandlerTest extends TestCase
{
private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
private const string ACADEMIC_YEAR_ID = '550e8400-e29b-41d4-a716-446655440010';
private InMemorySchoolCalendarRepository $repository;
private IsSchoolDayHandler $handler;
protected function setUp(): void
{
$this->repository = new InMemorySchoolCalendarRepository();
$this->handler = new IsSchoolDayHandler(
calendarRepository: $this->repository,
);
}
#[Test]
public function weekdayWithNoCalendarIsSchoolDay(): void
{
$query = new IsSchoolDayQuery(
tenantId: self::TENANT_ID,
academicYearId: self::ACADEMIC_YEAR_ID,
date: '2025-03-10', // Lundi
);
self::assertTrue(($this->handler)($query));
}
#[Test]
public function saturdayIsNotSchoolDay(): void
{
$query = new IsSchoolDayQuery(
tenantId: self::TENANT_ID,
academicYearId: self::ACADEMIC_YEAR_ID,
date: '2025-03-15', // Samedi
);
self::assertFalse(($this->handler)($query));
}
#[Test]
public function sundayIsNotSchoolDay(): void
{
$query = new IsSchoolDayQuery(
tenantId: self::TENANT_ID,
academicYearId: self::ACADEMIC_YEAR_ID,
date: '2025-03-16', // Dimanche
);
self::assertFalse(($this->handler)($query));
}
#[Test]
public function holidayIsNotSchoolDay(): void
{
$this->seedCalendarWithHoliday('2025-05-01', 'Fête du travail');
$query = new IsSchoolDayQuery(
tenantId: self::TENANT_ID,
academicYearId: self::ACADEMIC_YEAR_ID,
date: '2025-05-01', // Jeudi férié
);
self::assertFalse(($this->handler)($query));
}
#[Test]
public function vacationDayIsNotSchoolDay(): void
{
$this->seedCalendarWithVacation('2025-02-08', '2025-02-23', 'Vacances hiver');
$query = new IsSchoolDayQuery(
tenantId: self::TENANT_ID,
academicYearId: self::ACADEMIC_YEAR_ID,
date: '2025-02-10', // Lundi en vacances
);
self::assertFalse(($this->handler)($query));
}
#[Test]
public function regularWeekdayWithCalendarIsSchoolDay(): void
{
$this->seedCalendarWithHoliday('2025-05-01', 'Fête du travail');
$query = new IsSchoolDayQuery(
tenantId: self::TENANT_ID,
academicYearId: self::ACADEMIC_YEAR_ID,
date: '2025-05-02', // Vendredi normal
);
self::assertTrue(($this->handler)($query));
}
#[Test]
public function itRejectsMalformedDate(): void
{
$query = new IsSchoolDayQuery(
tenantId: self::TENANT_ID,
academicYearId: self::ACADEMIC_YEAR_ID,
date: 'invalid-date',
);
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('La date doit être au format YYYY-MM-DD.');
($this->handler)($query);
}
#[Test]
public function itRejectsImpossibleCalendarDate(): void
{
$query = new IsSchoolDayQuery(
tenantId: self::TENANT_ID,
academicYearId: self::ACADEMIC_YEAR_ID,
date: '2025-02-30',
);
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('La date n\'existe pas dans le calendrier.');
($this->handler)($query);
}
private function seedCalendarWithHoliday(string $date, string $label): void
{
$calendar = SchoolCalendar::initialiser(
tenantId: TenantId::fromString(self::TENANT_ID),
academicYearId: AcademicYearId::fromString(self::ACADEMIC_YEAR_ID),
);
$calendar->ajouterEntree(new CalendarEntry(
id: CalendarEntryId::generate(),
type: CalendarEntryType::HOLIDAY,
startDate: new DateTimeImmutable($date),
endDate: new DateTimeImmutable($date),
label: $label,
));
$this->repository->save($calendar);
}
private function seedCalendarWithVacation(string $start, string $end, string $label): void
{
$calendar = SchoolCalendar::initialiser(
tenantId: TenantId::fromString(self::TENANT_ID),
academicYearId: AcademicYearId::fromString(self::ACADEMIC_YEAR_ID),
);
$calendar->ajouterEntree(new CalendarEntry(
id: CalendarEntryId::generate(),
type: CalendarEntryType::VACATION,
startDate: new DateTimeImmutable($start),
endDate: new DateTimeImmutable($end),
label: $label,
));
$this->repository->save($calendar);
}
}