Les établissements ont besoin de définir leur référentiel de matières pour pouvoir ensuite les associer aux enseignants et aux classes. Cette fonctionnalité permet aux administrateurs de créer, modifier et archiver les matières avec leurs propriétés (nom, code court, couleur). L'architecture suit le pattern DDD avec des Value Objects utilisant les property hooks PHP 8.5 pour garantir l'immutabilité et la validation. L'isolation multi-tenant est assurée par vérification dans les handlers.
143 lines
4.8 KiB
PHP
143 lines
4.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Unit\Administration\Application\Command\ArchiveSubject;
|
|
|
|
use App\Administration\Application\Command\ArchiveSubject\ArchiveSubjectCommand;
|
|
use App\Administration\Application\Command\ArchiveSubject\ArchiveSubjectHandler;
|
|
use App\Administration\Domain\Exception\SubjectNotFoundException;
|
|
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\SubjectColor;
|
|
use App\Administration\Domain\Model\Subject\SubjectName;
|
|
use App\Administration\Domain\Model\Subject\SubjectStatus;
|
|
use App\Administration\Infrastructure\Persistence\InMemory\InMemorySubjectRepository;
|
|
use App\Shared\Domain\Clock;
|
|
use App\Shared\Domain\Tenant\TenantId;
|
|
use DateTimeImmutable;
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
final class ArchiveSubjectHandlerTest extends TestCase
|
|
{
|
|
private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
|
|
private const string OTHER_TENANT_ID = '550e8400-e29b-41d4-a716-446655440099';
|
|
private const string SCHOOL_ID = '550e8400-e29b-41d4-a716-446655440002';
|
|
|
|
private InMemorySubjectRepository $subjectRepository;
|
|
private Clock $clock;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->subjectRepository = new InMemorySubjectRepository();
|
|
$this->clock = new class implements Clock {
|
|
public function now(): DateTimeImmutable
|
|
{
|
|
return new DateTimeImmutable('2026-02-01 10:00:00');
|
|
}
|
|
};
|
|
}
|
|
|
|
#[Test]
|
|
public function itArchivesSubjectSuccessfully(): void
|
|
{
|
|
$subject = $this->createAndSaveSubject();
|
|
$handler = new ArchiveSubjectHandler($this->subjectRepository, $this->clock);
|
|
|
|
$command = new ArchiveSubjectCommand(
|
|
subjectId: (string) $subject->id,
|
|
tenantId: self::TENANT_ID,
|
|
);
|
|
|
|
$archivedSubject = $handler($command);
|
|
|
|
self::assertSame(SubjectStatus::ARCHIVED, $archivedSubject->status);
|
|
self::assertFalse($archivedSubject->estActive());
|
|
self::assertNotNull($archivedSubject->deletedAt);
|
|
}
|
|
|
|
#[Test]
|
|
public function itPersistsArchivedSubjectInRepository(): void
|
|
{
|
|
$subject = $this->createAndSaveSubject();
|
|
$handler = new ArchiveSubjectHandler($this->subjectRepository, $this->clock);
|
|
|
|
$command = new ArchiveSubjectCommand(
|
|
subjectId: (string) $subject->id,
|
|
tenantId: self::TENANT_ID,
|
|
);
|
|
|
|
$handler($command);
|
|
|
|
// Retrieve from repository
|
|
$retrievedSubject = $this->subjectRepository->get($subject->id);
|
|
|
|
self::assertSame(SubjectStatus::ARCHIVED, $retrievedSubject->status);
|
|
}
|
|
|
|
#[Test]
|
|
public function itThrowsExceptionWhenSubjectNotFound(): void
|
|
{
|
|
$handler = new ArchiveSubjectHandler($this->subjectRepository, $this->clock);
|
|
|
|
$this->expectException(SubjectNotFoundException::class);
|
|
|
|
$command = new ArchiveSubjectCommand(
|
|
subjectId: '550e8400-e29b-41d4-a716-446655440099',
|
|
tenantId: self::TENANT_ID,
|
|
);
|
|
$handler($command);
|
|
}
|
|
|
|
#[Test]
|
|
public function itThrowsExceptionWhenSubjectBelongsToDifferentTenant(): void
|
|
{
|
|
$subject = $this->createAndSaveSubject();
|
|
$handler = new ArchiveSubjectHandler($this->subjectRepository, $this->clock);
|
|
|
|
$this->expectException(SubjectNotFoundException::class);
|
|
|
|
// Try to archive with a different tenant ID
|
|
$command = new ArchiveSubjectCommand(
|
|
subjectId: (string) $subject->id,
|
|
tenantId: self::OTHER_TENANT_ID,
|
|
);
|
|
$handler($command);
|
|
}
|
|
|
|
#[Test]
|
|
public function itIsIdempotentWhenArchivingAlreadyArchivedSubject(): void
|
|
{
|
|
$subject = $this->createAndSaveSubject();
|
|
$handler = new ArchiveSubjectHandler($this->subjectRepository, $this->clock);
|
|
|
|
$command = new ArchiveSubjectCommand(
|
|
subjectId: (string) $subject->id,
|
|
tenantId: self::TENANT_ID,
|
|
);
|
|
|
|
// Archive twice
|
|
$handler($command);
|
|
$archivedAgain = $handler($command);
|
|
|
|
self::assertSame(SubjectStatus::ARCHIVED, $archivedAgain->status);
|
|
}
|
|
|
|
private function createAndSaveSubject(): Subject
|
|
{
|
|
$subject = Subject::creer(
|
|
tenantId: TenantId::fromString(self::TENANT_ID),
|
|
schoolId: SchoolId::fromString(self::SCHOOL_ID),
|
|
name: new SubjectName('Mathématiques'),
|
|
code: new SubjectCode('MATH'),
|
|
color: new SubjectColor('#3B82F6'),
|
|
createdAt: new DateTimeImmutable('2026-01-15 10:00:00'),
|
|
);
|
|
$this->subjectRepository->save($subject);
|
|
|
|
return $subject;
|
|
}
|
|
}
|