Files
Classeo/backend/tests/Unit/Scolarite/Application/Query/GetBlockedDates/GetBlockedDatesHandlerTest.php
Mathias STRASSER dc2be898d5
Some checks failed
CI / Backend Tests (push) Has been cancelled
CI / Frontend Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Naming Conventions (push) Has been cancelled
CI / Build Check (push) Has been cancelled
feat: Provisionner automatiquement un nouvel établissement
Lorsqu'un super-admin crée un établissement via l'interface, le système
doit automatiquement créer la base tenant, exécuter les migrations,
créer le premier utilisateur admin et envoyer l'invitation — le tout
de manière asynchrone pour ne pas bloquer la réponse HTTP.

Ce mécanisme rend chaque établissement opérationnel dès sa création
sans intervention manuelle sur l'infrastructure.
2026-04-16 09:27:25 +02:00

262 lines
9.1 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests\Unit\Scolarite\Application\Query\GetBlockedDates;
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\Scolarite\Application\Port\HomeworkRulesChecker;
use App\Scolarite\Application\Port\HomeworkRulesCheckResult;
use App\Scolarite\Application\Query\GetBlockedDates\GetBlockedDatesHandler;
use App\Scolarite\Application\Query\GetBlockedDates\GetBlockedDatesQuery;
use App\Shared\Domain\Clock;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
final class GetBlockedDatesHandlerTest extends TestCase
{
private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
private const string ACADEMIC_YEAR_ID = '550e8400-e29b-41d4-a716-446655440050';
private InMemorySchoolCalendarRepository $calendarRepository;
private GetBlockedDatesHandler $handler;
protected function setUp(): void
{
$this->calendarRepository = new InMemorySchoolCalendarRepository();
$rulesChecker = new class implements HomeworkRulesChecker {
public function verifier(
TenantId $tenantId,
DateTimeImmutable $dueDate,
DateTimeImmutable $creationDate,
): HomeworkRulesCheckResult {
return HomeworkRulesCheckResult::ok();
}
};
$clock = new class implements Clock {
public function now(): DateTimeImmutable
{
return new DateTimeImmutable('2026-03-01 10:00:00');
}
};
$this->handler = new GetBlockedDatesHandler(
$this->calendarRepository,
$rulesChecker,
$clock,
);
}
#[Test]
public function returnsWeekendsAsBlocked(): void
{
// 2026-03-02 est un lundi, 2026-03-08 est un dimanche
$result = ($this->handler)(new GetBlockedDatesQuery(
tenantId: self::TENANT_ID,
academicYearId: self::ACADEMIC_YEAR_ID,
startDate: '2026-03-02',
endDate: '2026-03-08',
));
$weekendDates = array_filter($result, static fn ($d) => $d->type === 'weekend');
self::assertCount(2, $weekendDates);
$dates = array_map(static fn ($d) => $d->date, array_values($weekendDates));
self::assertContains('2026-03-07', $dates);
self::assertContains('2026-03-08', $dates);
}
#[Test]
public function returnsHolidaysAsBlocked(): void
{
$calendar = $this->createCalendarWithHoliday(
new DateTimeImmutable('2026-03-04'),
'Jour de test',
);
$this->calendarRepository->save($calendar);
$result = ($this->handler)(new GetBlockedDatesQuery(
tenantId: self::TENANT_ID,
academicYearId: self::ACADEMIC_YEAR_ID,
startDate: '2026-03-02',
endDate: '2026-03-06',
));
$holidays = array_filter($result, static fn ($d) => $d->type === CalendarEntryType::HOLIDAY->value);
self::assertCount(1, $holidays);
$holiday = array_values($holidays)[0];
self::assertSame('2026-03-04', $holiday->date);
self::assertSame('Jour de test', $holiday->reason);
}
#[Test]
public function returnsEmptyForWeekWithNoBlockedDates(): void
{
// Lundi à vendredi sans calendrier configuré
$result = ($this->handler)(new GetBlockedDatesQuery(
tenantId: self::TENANT_ID,
academicYearId: self::ACADEMIC_YEAR_ID,
startDate: '2026-03-02',
endDate: '2026-03-06',
));
self::assertEmpty($result);
}
#[Test]
public function returnsVacationsAsBlocked(): void
{
$calendar = $this->createCalendarWithVacation(
new DateTimeImmutable('2026-03-02'),
new DateTimeImmutable('2026-03-06'),
'Vacances de printemps',
);
$this->calendarRepository->save($calendar);
$result = ($this->handler)(new GetBlockedDatesQuery(
tenantId: self::TENANT_ID,
academicYearId: self::ACADEMIC_YEAR_ID,
startDate: '2026-03-02',
endDate: '2026-03-06',
));
$vacations = array_filter($result, static fn ($d) => $d->type === CalendarEntryType::VACATION->value);
self::assertCount(5, $vacations);
}
#[Test]
public function returnsRuleHardBlockedDates(): void
{
$rulesChecker = new class implements HomeworkRulesChecker {
public function verifier(
TenantId $tenantId,
DateTimeImmutable $dueDate,
DateTimeImmutable $creationDate,
): HomeworkRulesCheckResult {
// Block Tuesday March 3
if ($dueDate->format('Y-m-d') === '2026-03-03') {
return new HomeworkRulesCheckResult(
warnings: [new \App\Scolarite\Application\Port\RuleWarning('minimum_delay', 'Délai minimum non respecté')],
bloquant: true,
);
}
return HomeworkRulesCheckResult::ok();
}
};
$clock = new class implements Clock {
public function now(): DateTimeImmutable
{
return new DateTimeImmutable('2026-03-01 10:00:00');
}
};
$handler = new GetBlockedDatesHandler($this->calendarRepository, $rulesChecker, $clock);
$result = ($handler)(new GetBlockedDatesQuery(
tenantId: self::TENANT_ID,
academicYearId: self::ACADEMIC_YEAR_ID,
startDate: '2026-03-02',
endDate: '2026-03-06',
));
$ruleBlocked = array_filter($result, static fn ($d) => $d->type === 'rule_hard');
self::assertCount(1, $ruleBlocked);
$blocked = array_values($ruleBlocked)[0];
self::assertSame('2026-03-03', $blocked->date);
self::assertSame('Délai minimum non respecté', $blocked->reason);
}
#[Test]
public function returnsRuleSoftWarningDates(): void
{
$rulesChecker = new class implements HomeworkRulesChecker {
public function verifier(
TenantId $tenantId,
DateTimeImmutable $dueDate,
DateTimeImmutable $creationDate,
): HomeworkRulesCheckResult {
if ($dueDate->format('Y-m-d') === '2026-03-04') {
return new HomeworkRulesCheckResult(
warnings: [new \App\Scolarite\Application\Port\RuleWarning('no_monday_after', 'Devoirs pour lundi déconseillés')],
bloquant: false,
);
}
return HomeworkRulesCheckResult::ok();
}
};
$clock = new class implements Clock {
public function now(): DateTimeImmutable
{
return new DateTimeImmutable('2026-03-01 10:00:00');
}
};
$handler = new GetBlockedDatesHandler($this->calendarRepository, $rulesChecker, $clock);
$result = ($handler)(new GetBlockedDatesQuery(
tenantId: self::TENANT_ID,
academicYearId: self::ACADEMIC_YEAR_ID,
startDate: '2026-03-02',
endDate: '2026-03-06',
));
$ruleSoft = array_filter($result, static fn ($d) => $d->type === 'rule_soft');
self::assertCount(1, $ruleSoft);
$soft = array_values($ruleSoft)[0];
self::assertSame('2026-03-04', $soft->date);
self::assertSame('rule_soft', $soft->type);
}
private function createCalendarWithHoliday(DateTimeImmutable $date, string $label): SchoolCalendar
{
$tenantId = TenantId::fromString(self::TENANT_ID);
$academicYearId = AcademicYearId::fromString(self::ACADEMIC_YEAR_ID);
$calendar = SchoolCalendar::initialiser($tenantId, $academicYearId);
$calendar->ajouterEntree(new CalendarEntry(
id: CalendarEntryId::generate(),
type: CalendarEntryType::HOLIDAY,
startDate: $date,
endDate: $date,
label: $label,
));
return $calendar;
}
private function createCalendarWithVacation(
DateTimeImmutable $startDate,
DateTimeImmutable $endDate,
string $label,
): SchoolCalendar {
$tenantId = TenantId::fromString(self::TENANT_ID);
$academicYearId = AcademicYearId::fromString(self::ACADEMIC_YEAR_ID);
$calendar = SchoolCalendar::initialiser($tenantId, $academicYearId);
$calendar->ajouterEntree(new CalendarEntry(
id: CalendarEntryId::generate(),
type: CalendarEntryType::VACATION,
startDate: $startDate,
endDate: $endDate,
label: $label,
));
return $calendar;
}
}