feat: Désignation de remplaçants temporaires avec corrections sécurité
Permet aux administrateurs de désigner un enseignant remplaçant pour un autre enseignant absent, sur des classes et matières précises, pour une période donnée. Le dashboard enseignant affiche les remplacements actifs avec les noms de classes/matières au lieu des identifiants bruts. Inclut les corrections de la code review : - Requête findActiveByTenant qui excluait les remplacements en cours mais incluait les futurs (manquait start_date <= :at) - Validation tenant et rôle enseignant dans le handler de désignation pour empêcher l'affectation cross-tenant ou de non-enseignants - Validation structurée du payload classes (Assert\Collection + UUID) pour éviter les erreurs serveur sur payloads malformés - API replaced-classes enrichie avec les noms classe/matière
This commit is contained in:
@@ -0,0 +1,271 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Scolarite\Application\Command\DesignateReplacement;
|
||||
|
||||
use App\Administration\Domain\Exception\TenantMismatchException;
|
||||
use App\Administration\Domain\Exception\UserNotFoundException;
|
||||
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\Infrastructure\Persistence\InMemory\InMemoryUserRepository;
|
||||
use App\Scolarite\Application\Command\DesignateReplacement\DesignateReplacementCommand;
|
||||
use App\Scolarite\Application\Command\DesignateReplacement\DesignateReplacementHandler;
|
||||
use App\Scolarite\Domain\Exception\DatesRemplacementInvalidesException;
|
||||
use App\Scolarite\Domain\Exception\RemplacementSameTeacherException;
|
||||
use App\Scolarite\Domain\Exception\UtilisateurNonEnseignantException;
|
||||
use App\Scolarite\Domain\Model\TeacherReplacement\ReplacementStatus;
|
||||
use App\Scolarite\Domain\Model\TeacherReplacement\TeacherReplacementId;
|
||||
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryTeacherReplacementRepository;
|
||||
use App\Shared\Domain\Clock;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class DesignateReplacementHandlerTest extends TestCase
|
||||
{
|
||||
private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
|
||||
private const string REPLACED_TEACHER_ID = '550e8400-e29b-41d4-a716-446655440010';
|
||||
private const string REPLACEMENT_TEACHER_ID = '550e8400-e29b-41d4-a716-446655440011';
|
||||
private const string CLASS_ID = '550e8400-e29b-41d4-a716-446655440020';
|
||||
private const string SUBJECT_ID = '550e8400-e29b-41d4-a716-446655440030';
|
||||
private const string CREATED_BY_ID = '550e8400-e29b-41d4-a716-446655440099';
|
||||
private const string OTHER_TENANT_ID = '550e8400-e29b-41d4-a716-446655440002';
|
||||
private const string OTHER_TENANT_TEACHER_ID = '550e8400-e29b-41d4-a716-446655440012';
|
||||
private const string ADMIN_USER_ID = '550e8400-e29b-41d4-a716-446655440050';
|
||||
|
||||
private InMemoryTeacherReplacementRepository $replacementRepository;
|
||||
private InMemoryUserRepository $userRepository;
|
||||
private Clock $clock;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->replacementRepository = new InMemoryTeacherReplacementRepository();
|
||||
$this->userRepository = new InMemoryUserRepository();
|
||||
$this->clock = new class implements Clock {
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
return new DateTimeImmutable('2026-02-15 10:00:00');
|
||||
}
|
||||
};
|
||||
|
||||
$this->seedTestData();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itCreatesReplacementSuccessfully(): void
|
||||
{
|
||||
$handler = $this->createHandler();
|
||||
$command = $this->createCommand();
|
||||
|
||||
$replacement = $handler($command);
|
||||
|
||||
self::assertNotEmpty((string) $replacement->id);
|
||||
self::assertSame(ReplacementStatus::ACTIVE, $replacement->status);
|
||||
self::assertNull($replacement->endedAt);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itPersistsReplacementInRepository(): void
|
||||
{
|
||||
$handler = $this->createHandler();
|
||||
$command = $this->createCommand();
|
||||
|
||||
$created = $handler($command);
|
||||
|
||||
$replacement = $this->replacementRepository->get(
|
||||
TeacherReplacementId::fromString((string) $created->id),
|
||||
TenantId::fromString(self::TENANT_ID),
|
||||
);
|
||||
|
||||
self::assertSame(ReplacementStatus::ACTIVE, $replacement->status);
|
||||
self::assertCount(1, $replacement->classes);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itSetsAllPropertiesCorrectly(): void
|
||||
{
|
||||
$handler = $this->createHandler();
|
||||
$command = $this->createCommand();
|
||||
|
||||
$replacement = $handler($command);
|
||||
|
||||
self::assertTrue($replacement->replacedTeacherId->equals(UserId::fromString(self::REPLACED_TEACHER_ID)));
|
||||
self::assertTrue($replacement->replacementTeacherId->equals(UserId::fromString(self::REPLACEMENT_TEACHER_ID)));
|
||||
self::assertEquals(new DateTimeImmutable('2026-03-01'), $replacement->startDate);
|
||||
self::assertEquals(new DateTimeImmutable('2026-03-31'), $replacement->endDate);
|
||||
self::assertSame('Congé maladie', $replacement->reason);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itThrowsWhenReplacedTeacherDoesNotExist(): void
|
||||
{
|
||||
$handler = $this->createHandler();
|
||||
|
||||
$this->expectException(UserNotFoundException::class);
|
||||
$handler($this->createCommand(replacedTeacherId: '550e8400-e29b-41d4-a716-446655440088'));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itThrowsWhenReplacementTeacherDoesNotExist(): void
|
||||
{
|
||||
$handler = $this->createHandler();
|
||||
|
||||
$this->expectException(UserNotFoundException::class);
|
||||
$handler($this->createCommand(replacementTeacherId: '550e8400-e29b-41d4-a716-446655440088'));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itThrowsWhenSameTeacher(): void
|
||||
{
|
||||
$handler = $this->createHandler();
|
||||
|
||||
$this->expectException(RemplacementSameTeacherException::class);
|
||||
$handler($this->createCommand(replacementTeacherId: self::REPLACED_TEACHER_ID));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itThrowsWhenEndDateBeforeStartDate(): void
|
||||
{
|
||||
$handler = $this->createHandler();
|
||||
|
||||
$this->expectException(DatesRemplacementInvalidesException::class);
|
||||
$handler($this->createCommand(startDate: '2026-03-31', endDate: '2026-03-01'));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itThrowsWhenReplacedTeacherBelongsToDifferentTenant(): void
|
||||
{
|
||||
$handler = $this->createHandler();
|
||||
|
||||
$this->expectException(TenantMismatchException::class);
|
||||
$handler($this->createCommand(replacedTeacherId: self::OTHER_TENANT_TEACHER_ID));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itThrowsWhenReplacementTeacherBelongsToDifferentTenant(): void
|
||||
{
|
||||
$handler = $this->createHandler();
|
||||
|
||||
$this->expectException(TenantMismatchException::class);
|
||||
$handler($this->createCommand(replacementTeacherId: self::OTHER_TENANT_TEACHER_ID));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itThrowsWhenReplacedTeacherIsNotATeacher(): void
|
||||
{
|
||||
$handler = $this->createHandler();
|
||||
|
||||
$this->expectException(UtilisateurNonEnseignantException::class);
|
||||
$handler($this->createCommand(replacedTeacherId: self::ADMIN_USER_ID));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itThrowsWhenReplacementTeacherIsNotATeacher(): void
|
||||
{
|
||||
$handler = $this->createHandler();
|
||||
|
||||
$this->expectException(UtilisateurNonEnseignantException::class);
|
||||
$handler($this->createCommand(replacementTeacherId: self::ADMIN_USER_ID));
|
||||
}
|
||||
|
||||
private function seedTestData(): void
|
||||
{
|
||||
$tenantId = TenantId::fromString(self::TENANT_ID);
|
||||
|
||||
$replacedTeacher = User::reconstitute(
|
||||
id: UserId::fromString(self::REPLACED_TEACHER_ID),
|
||||
email: new Email('replaced@example.com'),
|
||||
roles: [Role::PROF],
|
||||
tenantId: $tenantId,
|
||||
schoolName: 'École Test',
|
||||
statut: StatutCompte::EN_ATTENTE,
|
||||
dateNaissance: null,
|
||||
createdAt: new DateTimeImmutable('2026-01-15'),
|
||||
hashedPassword: null,
|
||||
activatedAt: null,
|
||||
consentementParental: null,
|
||||
);
|
||||
$this->userRepository->save($replacedTeacher);
|
||||
|
||||
$replacementTeacher = User::reconstitute(
|
||||
id: UserId::fromString(self::REPLACEMENT_TEACHER_ID),
|
||||
email: new Email('replacement@example.com'),
|
||||
roles: [Role::PROF],
|
||||
tenantId: $tenantId,
|
||||
schoolName: 'École Test',
|
||||
statut: StatutCompte::EN_ATTENTE,
|
||||
dateNaissance: null,
|
||||
createdAt: new DateTimeImmutable('2026-01-15'),
|
||||
hashedPassword: null,
|
||||
activatedAt: null,
|
||||
consentementParental: null,
|
||||
);
|
||||
$this->userRepository->save($replacementTeacher);
|
||||
|
||||
// Enseignant d'un autre tenant
|
||||
$otherTenantTeacher = User::reconstitute(
|
||||
id: UserId::fromString(self::OTHER_TENANT_TEACHER_ID),
|
||||
email: new Email('other-tenant@example.com'),
|
||||
roles: [Role::PROF],
|
||||
tenantId: TenantId::fromString(self::OTHER_TENANT_ID),
|
||||
schoolName: 'Autre École',
|
||||
statut: StatutCompte::EN_ATTENTE,
|
||||
dateNaissance: null,
|
||||
createdAt: new DateTimeImmutable('2026-01-15'),
|
||||
hashedPassword: null,
|
||||
activatedAt: null,
|
||||
consentementParental: null,
|
||||
);
|
||||
$this->userRepository->save($otherTenantTeacher);
|
||||
|
||||
// Utilisateur admin (pas enseignant) dans le même tenant
|
||||
$adminUser = User::reconstitute(
|
||||
id: UserId::fromString(self::ADMIN_USER_ID),
|
||||
email: new Email('admin@example.com'),
|
||||
roles: [Role::ADMIN],
|
||||
tenantId: $tenantId,
|
||||
schoolName: 'École Test',
|
||||
statut: StatutCompte::EN_ATTENTE,
|
||||
dateNaissance: null,
|
||||
createdAt: new DateTimeImmutable('2026-01-15'),
|
||||
hashedPassword: null,
|
||||
activatedAt: null,
|
||||
consentementParental: null,
|
||||
);
|
||||
$this->userRepository->save($adminUser);
|
||||
}
|
||||
|
||||
private function createHandler(): DesignateReplacementHandler
|
||||
{
|
||||
return new DesignateReplacementHandler(
|
||||
$this->replacementRepository,
|
||||
$this->userRepository,
|
||||
$this->clock,
|
||||
);
|
||||
}
|
||||
|
||||
private function createCommand(
|
||||
?string $replacedTeacherId = null,
|
||||
?string $replacementTeacherId = null,
|
||||
?string $startDate = null,
|
||||
?string $endDate = null,
|
||||
): DesignateReplacementCommand {
|
||||
return new DesignateReplacementCommand(
|
||||
tenantId: self::TENANT_ID,
|
||||
replacedTeacherId: $replacedTeacherId ?? self::REPLACED_TEACHER_ID,
|
||||
replacementTeacherId: $replacementTeacherId ?? self::REPLACEMENT_TEACHER_ID,
|
||||
startDate: $startDate ?? '2026-03-01',
|
||||
endDate: $endDate ?? '2026-03-31',
|
||||
classes: [
|
||||
['classId' => self::CLASS_ID, 'subjectId' => self::SUBJECT_ID],
|
||||
],
|
||||
reason: 'Congé maladie',
|
||||
createdBy: self::CREATED_BY_ID,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Scolarite\Application\Command\EndReplacement;
|
||||
|
||||
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\Command\EndReplacement\EndReplacementCommand;
|
||||
use App\Scolarite\Application\Command\EndReplacement\EndReplacementHandler;
|
||||
use App\Scolarite\Domain\Exception\RemplacementDejaTermineException;
|
||||
use App\Scolarite\Domain\Exception\RemplacementNotFoundException;
|
||||
use App\Scolarite\Domain\Model\TeacherReplacement\ClassSubjectPair;
|
||||
use App\Scolarite\Domain\Model\TeacherReplacement\ReplacementStatus;
|
||||
use App\Scolarite\Domain\Model\TeacherReplacement\TeacherReplacement;
|
||||
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryTeacherReplacementRepository;
|
||||
use App\Shared\Domain\Clock;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class EndReplacementHandlerTest extends TestCase
|
||||
{
|
||||
private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
|
||||
private const string REPLACED_TEACHER_ID = '550e8400-e29b-41d4-a716-446655440010';
|
||||
private const string REPLACEMENT_TEACHER_ID = '550e8400-e29b-41d4-a716-446655440011';
|
||||
private const string CREATED_BY_ID = '550e8400-e29b-41d4-a716-446655440099';
|
||||
|
||||
private InMemoryTeacherReplacementRepository $replacementRepository;
|
||||
private Clock $clock;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->replacementRepository = new InMemoryTeacherReplacementRepository();
|
||||
$this->clock = new class implements Clock {
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
return new DateTimeImmutable('2026-03-15 10:00:00');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itEndsReplacementSuccessfully(): void
|
||||
{
|
||||
$replacement = $this->createAndSaveReplacement();
|
||||
$handler = $this->createHandler();
|
||||
|
||||
$handler(new EndReplacementCommand(
|
||||
replacementId: (string) $replacement->id,
|
||||
tenantId: self::TENANT_ID,
|
||||
));
|
||||
|
||||
$ended = $this->replacementRepository->get($replacement->id, TenantId::fromString(self::TENANT_ID));
|
||||
self::assertSame(ReplacementStatus::ENDED, $ended->status);
|
||||
self::assertNotNull($ended->endedAt);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itThrowsWhenReplacementNotFound(): void
|
||||
{
|
||||
$handler = $this->createHandler();
|
||||
|
||||
$this->expectException(RemplacementNotFoundException::class);
|
||||
$handler(new EndReplacementCommand(
|
||||
replacementId: '550e8400-e29b-41d4-a716-446655440088',
|
||||
tenantId: self::TENANT_ID,
|
||||
));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itThrowsWhenReplacementAlreadyEnded(): void
|
||||
{
|
||||
$replacement = $this->createAndSaveReplacement();
|
||||
$replacement->terminer(new DateTimeImmutable('2026-03-10'));
|
||||
$this->replacementRepository->save($replacement);
|
||||
|
||||
$handler = $this->createHandler();
|
||||
|
||||
$this->expectException(RemplacementDejaTermineException::class);
|
||||
$handler(new EndReplacementCommand(
|
||||
replacementId: (string) $replacement->id,
|
||||
tenantId: self::TENANT_ID,
|
||||
));
|
||||
}
|
||||
|
||||
private function createAndSaveReplacement(): TeacherReplacement
|
||||
{
|
||||
$replacement = TeacherReplacement::designer(
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
replacedTeacherId: UserId::fromString(self::REPLACED_TEACHER_ID),
|
||||
replacementTeacherId: UserId::fromString(self::REPLACEMENT_TEACHER_ID),
|
||||
startDate: new DateTimeImmutable('2026-03-01'),
|
||||
endDate: new DateTimeImmutable('2026-03-31'),
|
||||
classes: [
|
||||
new ClassSubjectPair(
|
||||
ClassId::fromString('550e8400-e29b-41d4-a716-446655440020'),
|
||||
SubjectId::fromString('550e8400-e29b-41d4-a716-446655440030'),
|
||||
),
|
||||
],
|
||||
reason: null,
|
||||
createdBy: UserId::fromString(self::CREATED_BY_ID),
|
||||
now: new DateTimeImmutable('2026-02-15 10:00:00'),
|
||||
);
|
||||
|
||||
$this->replacementRepository->save($replacement);
|
||||
|
||||
return $replacement;
|
||||
}
|
||||
|
||||
private function createHandler(): EndReplacementHandler
|
||||
{
|
||||
return new EndReplacementHandler(
|
||||
$this->replacementRepository,
|
||||
$this->clock,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Scolarite\Application\Query\GetActiveReplacements;
|
||||
|
||||
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\Query\GetActiveReplacements\GetActiveReplacementsHandler;
|
||||
use App\Scolarite\Application\Query\GetActiveReplacements\GetActiveReplacementsQuery;
|
||||
use App\Scolarite\Application\Query\GetActiveReplacements\ReplacementDto;
|
||||
use App\Scolarite\Domain\Model\TeacherReplacement\ClassSubjectPair;
|
||||
use App\Scolarite\Domain\Model\TeacherReplacement\ReplacementStatus;
|
||||
use App\Scolarite\Domain\Model\TeacherReplacement\TeacherReplacement;
|
||||
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryTeacherReplacementRepository;
|
||||
use App\Shared\Domain\Clock;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class GetActiveReplacementsHandlerTest extends TestCase
|
||||
{
|
||||
private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
|
||||
private const string OTHER_TENANT_ID = '550e8400-e29b-41d4-a716-446655440002';
|
||||
private const string REPLACED_TEACHER_ID = '550e8400-e29b-41d4-a716-446655440010';
|
||||
private const string REPLACEMENT_TEACHER_ID = '550e8400-e29b-41d4-a716-446655440011';
|
||||
private const string CLASS_ID = '550e8400-e29b-41d4-a716-446655440020';
|
||||
private const string SUBJECT_ID = '550e8400-e29b-41d4-a716-446655440030';
|
||||
private const string CREATED_BY_ID = '550e8400-e29b-41d4-a716-446655440099';
|
||||
|
||||
private InMemoryTeacherReplacementRepository $repository;
|
||||
private DateTimeImmutable $now;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->repository = new InMemoryTeacherReplacementRepository();
|
||||
$this->now = new DateTimeImmutable('2026-03-15 10:00:00');
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itReturnsActiveReplacementsForTenant(): void
|
||||
{
|
||||
$this->saveActiveReplacement();
|
||||
|
||||
$handler = $this->createHandler();
|
||||
$result = $handler(new GetActiveReplacementsQuery(self::TENANT_ID));
|
||||
|
||||
self::assertCount(1, $result);
|
||||
self::assertContainsOnlyInstancesOf(ReplacementDto::class, $result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itReturnsEmptyArrayWhenNoneExist(): void
|
||||
{
|
||||
$handler = $this->createHandler();
|
||||
$result = $handler(new GetActiveReplacementsQuery(self::TENANT_ID));
|
||||
|
||||
self::assertSame([], $result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itMapsCorrectlyToDtos(): void
|
||||
{
|
||||
$replacement = $this->saveActiveReplacement();
|
||||
|
||||
$handler = $this->createHandler();
|
||||
$result = $handler(new GetActiveReplacementsQuery(self::TENANT_ID));
|
||||
|
||||
self::assertCount(1, $result);
|
||||
|
||||
$dto = $result[0];
|
||||
self::assertSame((string) $replacement->id, $dto->id);
|
||||
self::assertSame(self::REPLACED_TEACHER_ID, $dto->replacedTeacherId);
|
||||
self::assertSame(self::REPLACEMENT_TEACHER_ID, $dto->replacementTeacherId);
|
||||
self::assertEquals(new DateTimeImmutable('2026-03-01'), $dto->startDate);
|
||||
self::assertEquals(new DateTimeImmutable('2026-03-31'), $dto->endDate);
|
||||
self::assertSame(ReplacementStatus::ACTIVE->value, $dto->status);
|
||||
self::assertSame('Congé maladie', $dto->reason);
|
||||
self::assertCount(1, $dto->classes);
|
||||
self::assertSame(self::CLASS_ID, $dto->classes[0]['classId']);
|
||||
self::assertSame(self::SUBJECT_ID, $dto->classes[0]['subjectId']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itFiltersOutExpiredReplacements(): void
|
||||
{
|
||||
$this->saveReplacement(
|
||||
startDate: new DateTimeImmutable('2026-01-01'),
|
||||
endDate: new DateTimeImmutable('2026-02-01'),
|
||||
);
|
||||
|
||||
$handler = $this->createHandler();
|
||||
$result = $handler(new GetActiveReplacementsQuery(self::TENANT_ID));
|
||||
|
||||
self::assertSame([], $result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itFiltersOutEndedReplacements(): void
|
||||
{
|
||||
$replacement = $this->saveActiveReplacement();
|
||||
$replacement->terminer(new DateTimeImmutable('2026-03-10'));
|
||||
$this->repository->save($replacement);
|
||||
|
||||
$handler = $this->createHandler();
|
||||
$result = $handler(new GetActiveReplacementsQuery(self::TENANT_ID));
|
||||
|
||||
self::assertSame([], $result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itDoesNotReturnReplacementsFromOtherTenants(): void
|
||||
{
|
||||
$this->saveReplacement(tenantId: self::OTHER_TENANT_ID);
|
||||
|
||||
$handler = $this->createHandler();
|
||||
$result = $handler(new GetActiveReplacementsQuery(self::TENANT_ID));
|
||||
|
||||
self::assertSame([], $result);
|
||||
}
|
||||
|
||||
private function saveActiveReplacement(): TeacherReplacement
|
||||
{
|
||||
return $this->saveReplacement();
|
||||
}
|
||||
|
||||
private function saveReplacement(
|
||||
?string $tenantId = null,
|
||||
?DateTimeImmutable $startDate = null,
|
||||
?DateTimeImmutable $endDate = null,
|
||||
): TeacherReplacement {
|
||||
$replacement = TeacherReplacement::designer(
|
||||
tenantId: TenantId::fromString($tenantId ?? self::TENANT_ID),
|
||||
replacedTeacherId: UserId::fromString(self::REPLACED_TEACHER_ID),
|
||||
replacementTeacherId: UserId::fromString(self::REPLACEMENT_TEACHER_ID),
|
||||
startDate: $startDate ?? new DateTimeImmutable('2026-03-01'),
|
||||
endDate: $endDate ?? new DateTimeImmutable('2026-03-31'),
|
||||
classes: [
|
||||
new ClassSubjectPair(
|
||||
ClassId::fromString(self::CLASS_ID),
|
||||
SubjectId::fromString(self::SUBJECT_ID),
|
||||
),
|
||||
],
|
||||
reason: 'Congé maladie',
|
||||
createdBy: UserId::fromString(self::CREATED_BY_ID),
|
||||
now: new DateTimeImmutable('2026-02-15 10:00:00'),
|
||||
);
|
||||
|
||||
$this->repository->save($replacement);
|
||||
|
||||
return $replacement;
|
||||
}
|
||||
|
||||
private function createHandler(): GetActiveReplacementsHandler
|
||||
{
|
||||
$now = $this->now;
|
||||
$clock = new class($now) implements Clock {
|
||||
public function __construct(private readonly DateTimeImmutable $now)
|
||||
{
|
||||
}
|
||||
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
return $this->now;
|
||||
}
|
||||
};
|
||||
|
||||
return new GetActiveReplacementsHandler($this->repository, $clock);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Scolarite\Application\Query\GetActiveReplacements;
|
||||
|
||||
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\Query\GetActiveReplacements\ReplacementDto;
|
||||
use App\Scolarite\Domain\Model\TeacherReplacement\ClassSubjectPair;
|
||||
use App\Scolarite\Domain\Model\TeacherReplacement\ReplacementStatus;
|
||||
use App\Scolarite\Domain\Model\TeacherReplacement\TeacherReplacement;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class ReplacementDtoTest extends TestCase
|
||||
{
|
||||
private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
|
||||
private const string REPLACED_TEACHER_ID = '550e8400-e29b-41d4-a716-446655440010';
|
||||
private const string REPLACEMENT_TEACHER_ID = '550e8400-e29b-41d4-a716-446655440011';
|
||||
private const string CLASS_ID = '550e8400-e29b-41d4-a716-446655440020';
|
||||
private const string SUBJECT_ID = '550e8400-e29b-41d4-a716-446655440030';
|
||||
private const string CREATED_BY_ID = '550e8400-e29b-41d4-a716-446655440099';
|
||||
|
||||
#[Test]
|
||||
public function fromDomainCreatesCorrectDto(): void
|
||||
{
|
||||
$replacement = $this->createReplacement();
|
||||
|
||||
$dto = ReplacementDto::fromDomain($replacement);
|
||||
|
||||
self::assertSame((string) $replacement->id, $dto->id);
|
||||
self::assertSame(self::REPLACED_TEACHER_ID, $dto->replacedTeacherId);
|
||||
self::assertSame(self::REPLACEMENT_TEACHER_ID, $dto->replacementTeacherId);
|
||||
self::assertEquals(new DateTimeImmutable('2026-03-01'), $dto->startDate);
|
||||
self::assertEquals(new DateTimeImmutable('2026-03-31'), $dto->endDate);
|
||||
self::assertSame(ReplacementStatus::ACTIVE->value, $dto->status);
|
||||
self::assertSame('Congé maladie', $dto->reason);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function fromDomainPreservesAllClassSubjectPairs(): void
|
||||
{
|
||||
$replacement = $this->createReplacementWithMultipleClasses();
|
||||
|
||||
$dto = ReplacementDto::fromDomain($replacement);
|
||||
|
||||
self::assertCount(2, $dto->classes);
|
||||
self::assertSame(self::CLASS_ID, $dto->classes[0]['classId']);
|
||||
self::assertSame(self::SUBJECT_ID, $dto->classes[0]['subjectId']);
|
||||
self::assertSame('550e8400-e29b-41d4-a716-446655440021', $dto->classes[1]['classId']);
|
||||
self::assertSame('550e8400-e29b-41d4-a716-446655440031', $dto->classes[1]['subjectId']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function fromDomainPreservesNullReason(): void
|
||||
{
|
||||
$replacement = TeacherReplacement::designer(
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
replacedTeacherId: UserId::fromString(self::REPLACED_TEACHER_ID),
|
||||
replacementTeacherId: UserId::fromString(self::REPLACEMENT_TEACHER_ID),
|
||||
startDate: new DateTimeImmutable('2026-03-01'),
|
||||
endDate: new DateTimeImmutable('2026-03-31'),
|
||||
classes: [
|
||||
new ClassSubjectPair(
|
||||
ClassId::fromString(self::CLASS_ID),
|
||||
SubjectId::fromString(self::SUBJECT_ID),
|
||||
),
|
||||
],
|
||||
reason: null,
|
||||
createdBy: UserId::fromString(self::CREATED_BY_ID),
|
||||
now: new DateTimeImmutable('2026-02-15 10:00:00'),
|
||||
);
|
||||
|
||||
$dto = ReplacementDto::fromDomain($replacement);
|
||||
|
||||
self::assertNull($dto->reason);
|
||||
}
|
||||
|
||||
private function createReplacement(): TeacherReplacement
|
||||
{
|
||||
return TeacherReplacement::designer(
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
replacedTeacherId: UserId::fromString(self::REPLACED_TEACHER_ID),
|
||||
replacementTeacherId: UserId::fromString(self::REPLACEMENT_TEACHER_ID),
|
||||
startDate: new DateTimeImmutable('2026-03-01'),
|
||||
endDate: new DateTimeImmutable('2026-03-31'),
|
||||
classes: [
|
||||
new ClassSubjectPair(
|
||||
ClassId::fromString(self::CLASS_ID),
|
||||
SubjectId::fromString(self::SUBJECT_ID),
|
||||
),
|
||||
],
|
||||
reason: 'Congé maladie',
|
||||
createdBy: UserId::fromString(self::CREATED_BY_ID),
|
||||
now: new DateTimeImmutable('2026-02-15 10:00:00'),
|
||||
);
|
||||
}
|
||||
|
||||
private function createReplacementWithMultipleClasses(): TeacherReplacement
|
||||
{
|
||||
return TeacherReplacement::designer(
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
replacedTeacherId: UserId::fromString(self::REPLACED_TEACHER_ID),
|
||||
replacementTeacherId: UserId::fromString(self::REPLACEMENT_TEACHER_ID),
|
||||
startDate: new DateTimeImmutable('2026-03-01'),
|
||||
endDate: new DateTimeImmutable('2026-03-31'),
|
||||
classes: [
|
||||
new ClassSubjectPair(
|
||||
ClassId::fromString(self::CLASS_ID),
|
||||
SubjectId::fromString(self::SUBJECT_ID),
|
||||
),
|
||||
new ClassSubjectPair(
|
||||
ClassId::fromString('550e8400-e29b-41d4-a716-446655440021'),
|
||||
SubjectId::fromString('550e8400-e29b-41d4-a716-446655440031'),
|
||||
),
|
||||
],
|
||||
reason: 'Congé maladie',
|
||||
createdBy: UserId::fromString(self::CREATED_BY_ID),
|
||||
now: new DateTimeImmutable('2026-02-15 10:00:00'),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Scolarite\Application\Query\GetReplacedClassesForTeacher;
|
||||
|
||||
use App\Administration\Domain\Model\SchoolClass\AcademicYearId;
|
||||
use App\Administration\Domain\Model\SchoolClass\ClassId;
|
||||
use App\Administration\Domain\Model\SchoolClass\ClassName;
|
||||
use App\Administration\Domain\Model\SchoolClass\ClassStatus;
|
||||
use App\Administration\Domain\Model\SchoolClass\SchoolClass;
|
||||
use App\Administration\Domain\Model\SchoolClass\SchoolId;
|
||||
use App\Administration\Domain\Model\Subject\Subject;
|
||||
use App\Administration\Domain\Model\Subject\SubjectCode;
|
||||
use App\Administration\Domain\Model\Subject\SubjectId;
|
||||
use App\Administration\Domain\Model\Subject\SubjectName;
|
||||
use App\Administration\Domain\Model\Subject\SubjectStatus;
|
||||
use App\Administration\Domain\Model\User\UserId;
|
||||
use App\Administration\Infrastructure\Persistence\InMemory\InMemoryClassRepository;
|
||||
use App\Administration\Infrastructure\Persistence\InMemory\InMemorySubjectRepository;
|
||||
use App\Scolarite\Application\Query\GetReplacedClassesForTeacher\GetReplacedClassesForTeacherHandler;
|
||||
use App\Scolarite\Application\Query\GetReplacedClassesForTeacher\GetReplacedClassesForTeacherQuery;
|
||||
use App\Scolarite\Application\Query\GetReplacedClassesForTeacher\ReplacedClassDto;
|
||||
use App\Scolarite\Domain\Model\TeacherReplacement\ClassSubjectPair;
|
||||
use App\Scolarite\Domain\Model\TeacherReplacement\TeacherReplacement;
|
||||
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryTeacherReplacementRepository;
|
||||
use App\Shared\Domain\Clock;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class GetReplacedClassesForTeacherHandlerTest extends TestCase
|
||||
{
|
||||
private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
|
||||
private const string REPLACED_TEACHER_ID = '550e8400-e29b-41d4-a716-446655440010';
|
||||
private const string REPLACEMENT_TEACHER_ID = '550e8400-e29b-41d4-a716-446655440011';
|
||||
private const string OTHER_TEACHER_ID = '550e8400-e29b-41d4-a716-446655440012';
|
||||
private const string CLASS_ID_1 = '550e8400-e29b-41d4-a716-446655440020';
|
||||
private const string SUBJECT_ID_1 = '550e8400-e29b-41d4-a716-446655440030';
|
||||
private const string CLASS_ID_2 = '550e8400-e29b-41d4-a716-446655440021';
|
||||
private const string SUBJECT_ID_2 = '550e8400-e29b-41d4-a716-446655440031';
|
||||
private const string CREATED_BY_ID = '550e8400-e29b-41d4-a716-446655440099';
|
||||
private const string SCHOOL_ID = '550e8400-e29b-41d4-a716-446655440040';
|
||||
private const string ACADEMIC_YEAR_ID = '550e8400-e29b-41d4-a716-446655440041';
|
||||
|
||||
private InMemoryTeacherReplacementRepository $repository;
|
||||
private InMemoryClassRepository $classRepository;
|
||||
private InMemorySubjectRepository $subjectRepository;
|
||||
private DateTimeImmutable $now;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->repository = new InMemoryTeacherReplacementRepository();
|
||||
$this->classRepository = new InMemoryClassRepository();
|
||||
$this->subjectRepository = new InMemorySubjectRepository();
|
||||
$this->now = new DateTimeImmutable('2026-03-15 10:00:00');
|
||||
|
||||
$this->seedClassesAndSubjects();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itReturnsReplacedClassesForTeacher(): void
|
||||
{
|
||||
$this->saveReplacement();
|
||||
|
||||
$handler = $this->createHandler();
|
||||
$result = $handler(new GetReplacedClassesForTeacherQuery(
|
||||
replacementTeacherId: self::REPLACEMENT_TEACHER_ID,
|
||||
tenantId: self::TENANT_ID,
|
||||
));
|
||||
|
||||
self::assertCount(1, $result);
|
||||
self::assertContainsOnlyInstancesOf(ReplacedClassDto::class, $result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itReturnsEmptyWhenNoReplacements(): void
|
||||
{
|
||||
$handler = $this->createHandler();
|
||||
$result = $handler(new GetReplacedClassesForTeacherQuery(
|
||||
replacementTeacherId: self::REPLACEMENT_TEACHER_ID,
|
||||
tenantId: self::TENANT_ID,
|
||||
));
|
||||
|
||||
self::assertSame([], $result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itFlattensClassSubjectPairs(): void
|
||||
{
|
||||
$this->saveReplacementWithMultipleClasses();
|
||||
|
||||
$handler = $this->createHandler();
|
||||
$result = $handler(new GetReplacedClassesForTeacherQuery(
|
||||
replacementTeacherId: self::REPLACEMENT_TEACHER_ID,
|
||||
tenantId: self::TENANT_ID,
|
||||
));
|
||||
|
||||
self::assertCount(2, $result);
|
||||
|
||||
$classIds = array_map(static fn (ReplacedClassDto $dto) => $dto->classId, $result);
|
||||
self::assertContains(self::CLASS_ID_1, $classIds);
|
||||
self::assertContains(self::CLASS_ID_2, $classIds);
|
||||
|
||||
$subjectIds = array_map(static fn (ReplacedClassDto $dto) => $dto->subjectId, $result);
|
||||
self::assertContains(self::SUBJECT_ID_1, $subjectIds);
|
||||
self::assertContains(self::SUBJECT_ID_2, $subjectIds);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itOnlyReturnsActiveReplacements(): void
|
||||
{
|
||||
$replacement = $this->saveReplacement();
|
||||
$replacement->terminer(new DateTimeImmutable('2026-03-10'));
|
||||
$this->repository->save($replacement);
|
||||
|
||||
$handler = $this->createHandler();
|
||||
$result = $handler(new GetReplacedClassesForTeacherQuery(
|
||||
replacementTeacherId: self::REPLACEMENT_TEACHER_ID,
|
||||
tenantId: self::TENANT_ID,
|
||||
));
|
||||
|
||||
self::assertSame([], $result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itDoesNotReturnReplacementsForOtherTeachers(): void
|
||||
{
|
||||
$this->saveReplacement();
|
||||
|
||||
$handler = $this->createHandler();
|
||||
$result = $handler(new GetReplacedClassesForTeacherQuery(
|
||||
replacementTeacherId: self::OTHER_TEACHER_ID,
|
||||
tenantId: self::TENANT_ID,
|
||||
));
|
||||
|
||||
self::assertSame([], $result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itMapsDtoFieldsCorrectly(): void
|
||||
{
|
||||
$replacement = $this->saveReplacement();
|
||||
|
||||
$handler = $this->createHandler();
|
||||
$result = $handler(new GetReplacedClassesForTeacherQuery(
|
||||
replacementTeacherId: self::REPLACEMENT_TEACHER_ID,
|
||||
tenantId: self::TENANT_ID,
|
||||
));
|
||||
|
||||
self::assertCount(1, $result);
|
||||
|
||||
$dto = $result[0];
|
||||
self::assertSame((string) $replacement->id, $dto->replacementId);
|
||||
self::assertSame(self::REPLACED_TEACHER_ID, $dto->replacedTeacherId);
|
||||
self::assertSame(self::CLASS_ID_1, $dto->classId);
|
||||
self::assertSame(self::SUBJECT_ID_1, $dto->subjectId);
|
||||
self::assertSame('6ème A', $dto->className);
|
||||
self::assertSame('Mathématiques', $dto->subjectName);
|
||||
self::assertEquals(new DateTimeImmutable('2026-03-01'), $dto->startDate);
|
||||
self::assertEquals(new DateTimeImmutable('2026-03-31'), $dto->endDate);
|
||||
}
|
||||
|
||||
private function saveReplacement(): TeacherReplacement
|
||||
{
|
||||
$replacement = TeacherReplacement::designer(
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
replacedTeacherId: UserId::fromString(self::REPLACED_TEACHER_ID),
|
||||
replacementTeacherId: UserId::fromString(self::REPLACEMENT_TEACHER_ID),
|
||||
startDate: new DateTimeImmutable('2026-03-01'),
|
||||
endDate: new DateTimeImmutable('2026-03-31'),
|
||||
classes: [
|
||||
new ClassSubjectPair(
|
||||
ClassId::fromString(self::CLASS_ID_1),
|
||||
SubjectId::fromString(self::SUBJECT_ID_1),
|
||||
),
|
||||
],
|
||||
reason: null,
|
||||
createdBy: UserId::fromString(self::CREATED_BY_ID),
|
||||
now: new DateTimeImmutable('2026-02-15 10:00:00'),
|
||||
);
|
||||
|
||||
$this->repository->save($replacement);
|
||||
|
||||
return $replacement;
|
||||
}
|
||||
|
||||
private function saveReplacementWithMultipleClasses(): TeacherReplacement
|
||||
{
|
||||
$replacement = TeacherReplacement::designer(
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
replacedTeacherId: UserId::fromString(self::REPLACED_TEACHER_ID),
|
||||
replacementTeacherId: UserId::fromString(self::REPLACEMENT_TEACHER_ID),
|
||||
startDate: new DateTimeImmutable('2026-03-01'),
|
||||
endDate: new DateTimeImmutable('2026-03-31'),
|
||||
classes: [
|
||||
new ClassSubjectPair(
|
||||
ClassId::fromString(self::CLASS_ID_1),
|
||||
SubjectId::fromString(self::SUBJECT_ID_1),
|
||||
),
|
||||
new ClassSubjectPair(
|
||||
ClassId::fromString(self::CLASS_ID_2),
|
||||
SubjectId::fromString(self::SUBJECT_ID_2),
|
||||
),
|
||||
],
|
||||
reason: null,
|
||||
createdBy: UserId::fromString(self::CREATED_BY_ID),
|
||||
now: new DateTimeImmutable('2026-02-15 10:00:00'),
|
||||
);
|
||||
|
||||
$this->repository->save($replacement);
|
||||
|
||||
return $replacement;
|
||||
}
|
||||
|
||||
private function seedClassesAndSubjects(): void
|
||||
{
|
||||
$tenantId = TenantId::fromString(self::TENANT_ID);
|
||||
$schoolId = SchoolId::fromString(self::SCHOOL_ID);
|
||||
$academicYearId = AcademicYearId::fromString(self::ACADEMIC_YEAR_ID);
|
||||
$now = new DateTimeImmutable('2026-01-01');
|
||||
|
||||
$class1 = SchoolClass::reconstitute(
|
||||
ClassId::fromString(self::CLASS_ID_1), $tenantId, $schoolId, $academicYearId,
|
||||
new ClassName('6ème A'), null, null, ClassStatus::ACTIVE, null, $now, $now, null,
|
||||
);
|
||||
$this->classRepository->save($class1);
|
||||
|
||||
$class2 = SchoolClass::reconstitute(
|
||||
ClassId::fromString(self::CLASS_ID_2), $tenantId, $schoolId, $academicYearId,
|
||||
new ClassName('5ème B'), null, null, ClassStatus::ACTIVE, null, $now, $now, null,
|
||||
);
|
||||
$this->classRepository->save($class2);
|
||||
|
||||
$subject1 = Subject::reconstitute(
|
||||
SubjectId::fromString(self::SUBJECT_ID_1), $tenantId, $schoolId,
|
||||
new SubjectName('Mathématiques'), new SubjectCode('MATH'), null,
|
||||
SubjectStatus::ACTIVE,
|
||||
null, $now, $now, null,
|
||||
);
|
||||
$this->subjectRepository->save($subject1);
|
||||
|
||||
$subject2 = Subject::reconstitute(
|
||||
SubjectId::fromString(self::SUBJECT_ID_2), $tenantId, $schoolId,
|
||||
new SubjectName('Français'), new SubjectCode('FR'), null,
|
||||
SubjectStatus::ACTIVE,
|
||||
null, $now, $now, null,
|
||||
);
|
||||
$this->subjectRepository->save($subject2);
|
||||
}
|
||||
|
||||
private function createHandler(): GetReplacedClassesForTeacherHandler
|
||||
{
|
||||
$now = $this->now;
|
||||
$clock = new class($now) implements Clock {
|
||||
public function __construct(private readonly DateTimeImmutable $now)
|
||||
{
|
||||
}
|
||||
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
return $this->now;
|
||||
}
|
||||
};
|
||||
|
||||
return new GetReplacedClassesForTeacherHandler(
|
||||
$this->repository,
|
||||
$this->classRepository,
|
||||
$this->subjectRepository,
|
||||
$clock,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Scolarite\Application\Query\GetReplacedClassesForTeacher;
|
||||
|
||||
use App\Scolarite\Application\Query\GetReplacedClassesForTeacher\ReplacedClassDto;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class ReplacedClassDtoTest extends TestCase
|
||||
{
|
||||
private const string REPLACEMENT_ID = '550e8400-e29b-41d4-a716-446655440001';
|
||||
private const string REPLACED_TEACHER_ID = '550e8400-e29b-41d4-a716-446655440010';
|
||||
private const string CLASS_ID = '550e8400-e29b-41d4-a716-446655440020';
|
||||
private const string SUBJECT_ID = '550e8400-e29b-41d4-a716-446655440030';
|
||||
|
||||
#[Test]
|
||||
public function constructorSetsAllPropertiesCorrectly(): void
|
||||
{
|
||||
$startDate = new DateTimeImmutable('2026-03-01');
|
||||
$endDate = new DateTimeImmutable('2026-03-31');
|
||||
|
||||
$dto = new ReplacedClassDto(
|
||||
replacementId: self::REPLACEMENT_ID,
|
||||
replacedTeacherId: self::REPLACED_TEACHER_ID,
|
||||
classId: self::CLASS_ID,
|
||||
subjectId: self::SUBJECT_ID,
|
||||
className: '6ème A',
|
||||
subjectName: 'Mathématiques',
|
||||
startDate: $startDate,
|
||||
endDate: $endDate,
|
||||
);
|
||||
|
||||
self::assertSame(self::REPLACEMENT_ID, $dto->replacementId);
|
||||
self::assertSame(self::REPLACED_TEACHER_ID, $dto->replacedTeacherId);
|
||||
self::assertSame(self::CLASS_ID, $dto->classId);
|
||||
self::assertSame(self::SUBJECT_ID, $dto->subjectId);
|
||||
self::assertSame('6ème A', $dto->className);
|
||||
self::assertSame('Mathématiques', $dto->subjectName);
|
||||
self::assertEquals($startDate, $dto->startDate);
|
||||
self::assertEquals($endDate, $dto->endDate);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function constructorPreservesExactDateValues(): void
|
||||
{
|
||||
$startDate = new DateTimeImmutable('2026-03-01 08:00:00');
|
||||
$endDate = new DateTimeImmutable('2026-03-31 23:59:59');
|
||||
|
||||
$dto = new ReplacedClassDto(
|
||||
replacementId: self::REPLACEMENT_ID,
|
||||
replacedTeacherId: self::REPLACED_TEACHER_ID,
|
||||
classId: self::CLASS_ID,
|
||||
subjectId: self::SUBJECT_ID,
|
||||
className: '6ème A',
|
||||
subjectName: 'Mathématiques',
|
||||
startDate: $startDate,
|
||||
endDate: $endDate,
|
||||
);
|
||||
|
||||
self::assertSame($startDate, $dto->startDate);
|
||||
self::assertSame($endDate, $dto->endDate);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user