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.
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Administration\Infrastructure\Messaging;
|
||||
|
||||
use App\Administration\Domain\Event\JourneePedagogiqueAjoutee;
|
||||
use App\Administration\Domain\Model\SchoolCalendar\CalendarEntryId;
|
||||
use App\Administration\Domain\Model\SchoolClass\AcademicYearId;
|
||||
use App\Administration\Domain\Model\User\Email;
|
||||
use App\Administration\Domain\Model\User\Role;
|
||||
use App\Administration\Domain\Model\User\StatutCompte;
|
||||
use App\Administration\Domain\Model\User\User;
|
||||
use App\Administration\Domain\Model\User\UserId;
|
||||
use App\Administration\Domain\Repository\UserRepository;
|
||||
use App\Administration\Infrastructure\Messaging\NotifyTeachersPedagogicalDayHandler;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\NullLogger;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Mailer\MailerInterface;
|
||||
use Twig\Environment;
|
||||
|
||||
final class NotifyTeachersPedagogicalDayHandlerTest extends TestCase
|
||||
{
|
||||
private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
|
||||
private const string ACADEMIC_YEAR_ID = '550e8400-e29b-41d4-a716-446655440010';
|
||||
|
||||
#[Test]
|
||||
public function itSendsEmailToAllTeachersInTenant(): void
|
||||
{
|
||||
$mailer = $this->createMock(MailerInterface::class);
|
||||
$twig = $this->createMock(Environment::class);
|
||||
$userRepository = $this->createMock(UserRepository::class);
|
||||
|
||||
$twig->method('render')->willReturn('<html>notification</html>');
|
||||
|
||||
$userRepository->method('findAllByTenant')->willReturn([
|
||||
$this->createUser('teacher1@school.fr', [Role::PROF]),
|
||||
$this->createUser('teacher2@school.fr', [Role::PROF]),
|
||||
$this->createUser('parent@school.fr', [Role::PARENT]),
|
||||
]);
|
||||
|
||||
$mailer->expects(self::exactly(2))->method('send');
|
||||
|
||||
$handler = new NotifyTeachersPedagogicalDayHandler($mailer, $twig, $userRepository, new NullLogger());
|
||||
($handler)($this->createEvent());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itSkipsWhenNoTeachersInTenant(): void
|
||||
{
|
||||
$mailer = $this->createMock(MailerInterface::class);
|
||||
$twig = $this->createMock(Environment::class);
|
||||
$userRepository = $this->createMock(UserRepository::class);
|
||||
|
||||
$userRepository->method('findAllByTenant')->willReturn([
|
||||
$this->createUser('parent@school.fr', [Role::PARENT]),
|
||||
]);
|
||||
|
||||
$mailer->expects(self::never())->method('send');
|
||||
$twig->expects(self::never())->method('render');
|
||||
|
||||
$handler = new NotifyTeachersPedagogicalDayHandler($mailer, $twig, $userRepository, new NullLogger());
|
||||
($handler)($this->createEvent());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itHandlesMailerFailureGracefully(): void
|
||||
{
|
||||
$mailer = $this->createMock(MailerInterface::class);
|
||||
$twig = $this->createMock(Environment::class);
|
||||
$userRepository = $this->createMock(UserRepository::class);
|
||||
|
||||
$twig->method('render')->willReturn('<html>notification</html>');
|
||||
|
||||
$userRepository->method('findAllByTenant')->willReturn([
|
||||
$this->createUser('teacher@school.fr', [Role::PROF]),
|
||||
]);
|
||||
|
||||
$mailer->method('send')->willThrowException(new RuntimeException('SMTP error'));
|
||||
|
||||
$handler = new NotifyTeachersPedagogicalDayHandler($mailer, $twig, $userRepository, new NullLogger());
|
||||
($handler)($this->createEvent());
|
||||
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
|
||||
private function createEvent(): JourneePedagogiqueAjoutee
|
||||
{
|
||||
return new JourneePedagogiqueAjoutee(
|
||||
entryId: CalendarEntryId::generate(),
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
academicYearId: AcademicYearId::fromString(self::ACADEMIC_YEAR_ID),
|
||||
date: new DateTimeImmutable('2025-03-14'),
|
||||
label: 'Formation enseignants',
|
||||
occurredOn: new DateTimeImmutable('2026-02-18 10:00:00'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Role[] $roles
|
||||
*/
|
||||
private function createUser(string $email, array $roles): User
|
||||
{
|
||||
return User::reconstitute(
|
||||
id: UserId::generate(),
|
||||
email: new Email($email),
|
||||
roles: $roles,
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
schoolName: 'École Test',
|
||||
statut: StatutCompte::ACTIF,
|
||||
dateNaissance: null,
|
||||
createdAt: new DateTimeImmutable('2026-01-01'),
|
||||
hashedPassword: 'hashed',
|
||||
activatedAt: new DateTimeImmutable('2026-01-02'),
|
||||
consentementParental: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user