Files
Classeo/backend/tests/Unit/Scolarite/Application/Query/GetStudentSchedule/GetStudentScheduleHandlerTest.php
Mathias STRASSER 81e97c4f3b
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: Afficher la couleur des matières dans l'emploi du temps élève et parent
L'admin pouvait attribuer une couleur à chaque matière, mais cette
couleur n'était utilisée que dans la vue admin de l'emploi du temps.
Les APIs élève et parent ne renvoyaient pas cette information, ce qui
donnait un affichage générique (gris/bleu) pour tous les créneaux.

L'API renvoie désormais subjectColor dans chaque créneau, et les vues
jour/semaine/widget/détails affichent la bordure colorée correspondante.
Le marqueur "Prochain cours" conserve sa priorité visuelle via une
surcharge CSS variable.
2026-03-09 15:57:14 +01:00

237 lines
8.4 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests\Unit\Scolarite\Application\Query\GetStudentSchedule;
use App\Administration\Domain\Model\SchoolCalendar\SchoolCalendar;
use App\Administration\Domain\Model\SchoolClass\AcademicYearId;
use App\Administration\Domain\Model\SchoolClass\ClassId;
use App\Administration\Domain\Model\Subject\SubjectId;
use App\Administration\Domain\Model\User\UserId;
use App\Scolarite\Application\Port\CurrentCalendarProvider;
use App\Scolarite\Application\Port\ScheduleDisplayReader;
use App\Scolarite\Application\Port\StudentClassReader;
use App\Scolarite\Application\Query\GetStudentSchedule\GetStudentScheduleHandler;
use App\Scolarite\Application\Query\GetStudentSchedule\GetStudentScheduleQuery;
use App\Scolarite\Application\Query\GetStudentSchedule\StudentScheduleSlotDto;
use App\Scolarite\Application\Service\ScheduleResolver;
use App\Scolarite\Domain\Model\Schedule\DayOfWeek;
use App\Scolarite\Domain\Model\Schedule\ScheduleSlot;
use App\Scolarite\Domain\Model\Schedule\TimeSlot;
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryScheduleExceptionRepository;
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryScheduleSlotRepository;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
final class GetStudentScheduleHandlerTest extends TestCase
{
private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
private const string STUDENT_ID = '550e8400-e29b-41d4-a716-446655440050';
private const string CLASS_ID = '550e8400-e29b-41d4-a716-446655440020';
private const string SUBJECT_ID = '550e8400-e29b-41d4-a716-446655440030';
private const string TEACHER_ID = '550e8400-e29b-41d4-a716-446655440010';
private InMemoryScheduleSlotRepository $slotRepository;
private InMemoryScheduleExceptionRepository $exceptionRepository;
protected function setUp(): void
{
$this->slotRepository = new InMemoryScheduleSlotRepository();
$this->exceptionRepository = new InMemoryScheduleExceptionRepository();
}
#[Test]
public function returnsEmptyWhenStudentHasNoClass(): void
{
$handler = $this->createHandler(classId: null);
$result = $handler(new GetStudentScheduleQuery(
studentId: self::STUDENT_ID,
tenantId: self::TENANT_ID,
date: '2026-03-02',
));
self::assertSame([], $result);
}
#[Test]
public function returnsScheduleForStudentClass(): void
{
$this->saveRecurringSlot(DayOfWeek::MONDAY, '08:00', '09:00');
$this->saveRecurringSlot(DayOfWeek::TUESDAY, '10:00', '11:00');
$handler = $this->createHandler(classId: self::CLASS_ID);
$result = $handler(new GetStudentScheduleQuery(
studentId: self::STUDENT_ID,
tenantId: self::TENANT_ID,
date: '2026-03-02', // Monday
));
self::assertCount(2, $result);
self::assertContainsOnlyInstancesOf(StudentScheduleSlotDto::class, $result);
}
#[Test]
public function enrichesSlotsWithSubjectAndTeacherNames(): void
{
$this->saveRecurringSlot(DayOfWeek::MONDAY, '08:00', '09:00');
$handler = $this->createHandler(
classId: self::CLASS_ID,
subjectNames: [self::SUBJECT_ID => 'Mathématiques'],
teacherNames: [self::TEACHER_ID => 'Jean Dupont'],
);
$result = $handler(new GetStudentScheduleQuery(
studentId: self::STUDENT_ID,
tenantId: self::TENANT_ID,
date: '2026-03-02',
));
self::assertCount(1, $result);
self::assertSame('Mathématiques', $result[0]->subjectName);
self::assertSame('Jean Dupont', $result[0]->teacherName);
}
#[Test]
public function computesMondayFromAnyDayOfWeek(): void
{
$this->saveRecurringSlot(DayOfWeek::MONDAY, '08:00', '09:00');
$handler = $this->createHandler(classId: self::CLASS_ID);
// Wednesday of the same week → should still return Monday's slot
$result = $handler(new GetStudentScheduleQuery(
studentId: self::STUDENT_ID,
tenantId: self::TENANT_ID,
date: '2026-03-04', // Wednesday
));
self::assertCount(1, $result);
self::assertSame('2026-03-02', $result[0]->date);
}
#[Test]
public function returnsCorrectDtoFields(): void
{
$this->saveRecurringSlot(DayOfWeek::MONDAY, '08:00', '09:00', 'Salle 101');
$handler = $this->createHandler(
classId: self::CLASS_ID,
subjectNames: [self::SUBJECT_ID => 'Français'],
teacherNames: [self::TEACHER_ID => 'Marie Martin'],
);
$result = $handler(new GetStudentScheduleQuery(
studentId: self::STUDENT_ID,
tenantId: self::TENANT_ID,
date: '2026-03-02',
));
self::assertCount(1, $result);
$dto = $result[0];
self::assertSame('2026-03-02', $dto->date);
self::assertSame(1, $dto->dayOfWeek);
self::assertSame('08:00', $dto->startTime);
self::assertSame('09:00', $dto->endTime);
self::assertSame(self::SUBJECT_ID, $dto->subjectId);
self::assertSame('Français', $dto->subjectName);
self::assertSame(self::TEACHER_ID, $dto->teacherId);
self::assertSame('Marie Martin', $dto->teacherName);
self::assertSame('Salle 101', $dto->room);
self::assertFalse($dto->isModified);
self::assertNull($dto->exceptionId);
}
private function saveRecurringSlot(
DayOfWeek $day,
string $start,
string $end,
?string $room = null,
): void {
$slot = ScheduleSlot::creer(
tenantId: TenantId::fromString(self::TENANT_ID),
classId: ClassId::fromString(self::CLASS_ID),
subjectId: SubjectId::fromString(self::SUBJECT_ID),
teacherId: UserId::fromString(self::TEACHER_ID),
dayOfWeek: $day,
timeSlot: new TimeSlot($start, $end),
room: $room,
isRecurring: true,
now: new DateTimeImmutable('2026-01-01'),
recurrenceStart: new DateTimeImmutable('2026-01-01'),
);
$this->slotRepository->save($slot);
}
/**
* @param array<string, string> $subjectNames
* @param array<string, string> $teacherNames
*/
private function createHandler(
?string $classId = null,
array $subjectNames = [],
array $teacherNames = [],
): GetStudentScheduleHandler {
$tenantId = TenantId::fromString(self::TENANT_ID);
$studentClassReader = new class($classId) implements StudentClassReader {
public function __construct(private ?string $classId)
{
}
public function currentClassId(string $studentId, TenantId $tenantId): ?string
{
return $this->classId;
}
};
$calendarProvider = new class($tenantId) implements CurrentCalendarProvider {
public function __construct(private TenantId $tenantId)
{
}
public function forCurrentYear(TenantId $tenantId): SchoolCalendar
{
return SchoolCalendar::initialiser($this->tenantId, AcademicYearId::generate());
}
};
$displayReader = new class($subjectNames, $teacherNames) implements ScheduleDisplayReader {
/** @param array<string, string> $subjects @param array<string, string> $teachers */
public function __construct(
private array $subjects,
private array $teachers,
) {
}
public function subjectDisplay(string $tenantId, string ...$subjectIds): array
{
$display = [];
foreach ($this->subjects as $id => $name) {
$display[$id] = ['name' => $name, 'color' => null];
}
return $display;
}
public function teacherNames(string $tenantId, string ...$teacherIds): array
{
return $this->teachers;
}
};
return new GetStudentScheduleHandler(
$studentClassReader,
new ScheduleResolver($this->slotRepository, $this->exceptionRepository),
$calendarProvider,
$displayReader,
);
}
}