feat: Gestion des classes scolaires
Permet aux administrateurs de créer, modifier et supprimer des classes pour organiser les élèves par niveau. L'archivage soft-delete préserve l'historique tout en masquant les classes obsolètes. Inclut la validation des noms (2-50 caractères), les niveaux scolaires du CP à la Terminale, et les contrôles d'accès par rôle.
This commit is contained in:
@@ -0,0 +1,285 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Administration\Domain\Model\SchoolClass;
|
||||
|
||||
use App\Administration\Domain\Event\ClasseArchivee;
|
||||
use App\Administration\Domain\Event\ClasseCreee;
|
||||
use App\Administration\Domain\Event\ClasseModifiee;
|
||||
use App\Administration\Domain\Model\SchoolClass\AcademicYearId;
|
||||
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\SchoolClass\SchoolLevel;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class SchoolClassTest extends TestCase
|
||||
{
|
||||
private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
|
||||
private const string SCHOOL_ID = '550e8400-e29b-41d4-a716-446655440002';
|
||||
private const string ACADEMIC_YEAR_ID = '550e8400-e29b-41d4-a716-446655440003';
|
||||
|
||||
#[Test]
|
||||
public function creerCreatesClassWithActiveStatus(): void
|
||||
{
|
||||
$class = $this->createClass();
|
||||
|
||||
self::assertSame(ClassStatus::ACTIVE, $class->status);
|
||||
self::assertTrue($class->estActive());
|
||||
self::assertTrue($class->peutRecevoirEleves());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function creerRecordsClasseCreeeEvent(): void
|
||||
{
|
||||
$class = $this->createClass();
|
||||
|
||||
$events = $class->pullDomainEvents();
|
||||
|
||||
self::assertCount(1, $events);
|
||||
self::assertInstanceOf(ClasseCreee::class, $events[0]);
|
||||
self::assertSame($class->id, $events[0]->classId);
|
||||
self::assertSame($class->tenantId, $events[0]->tenantId);
|
||||
self::assertSame($class->name, $events[0]->name);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function creerSetsAllProperties(): void
|
||||
{
|
||||
$tenantId = TenantId::fromString(self::TENANT_ID);
|
||||
$schoolId = SchoolId::fromString(self::SCHOOL_ID);
|
||||
$academicYearId = AcademicYearId::fromString(self::ACADEMIC_YEAR_ID);
|
||||
$name = new ClassName('6ème A');
|
||||
$level = SchoolLevel::SIXIEME;
|
||||
$capacity = 30;
|
||||
$createdAt = new DateTimeImmutable('2026-01-15 10:00:00');
|
||||
|
||||
$class = SchoolClass::creer(
|
||||
tenantId: $tenantId,
|
||||
schoolId: $schoolId,
|
||||
academicYearId: $academicYearId,
|
||||
name: $name,
|
||||
level: $level,
|
||||
capacity: $capacity,
|
||||
createdAt: $createdAt,
|
||||
);
|
||||
|
||||
self::assertTrue($class->tenantId->equals($tenantId));
|
||||
self::assertTrue($class->schoolId->equals($schoolId));
|
||||
self::assertTrue($class->academicYearId->equals($academicYearId));
|
||||
self::assertTrue($class->name->equals($name));
|
||||
self::assertSame($level, $class->level);
|
||||
self::assertSame($capacity, $class->capacity);
|
||||
self::assertEquals($createdAt, $class->createdAt);
|
||||
self::assertEquals($createdAt, $class->updatedAt);
|
||||
self::assertNull($class->deletedAt);
|
||||
self::assertNull($class->description);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function creerWithNullLevelAndCapacity(): void
|
||||
{
|
||||
$class = SchoolClass::creer(
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
schoolId: SchoolId::fromString(self::SCHOOL_ID),
|
||||
academicYearId: AcademicYearId::fromString(self::ACADEMIC_YEAR_ID),
|
||||
name: new ClassName('Classe spéciale'),
|
||||
level: null,
|
||||
capacity: null,
|
||||
createdAt: new DateTimeImmutable(),
|
||||
);
|
||||
|
||||
self::assertNull($class->level);
|
||||
self::assertNull($class->capacity);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function renommerChangesNameAndRecordsEvent(): void
|
||||
{
|
||||
$class = $this->createClass();
|
||||
$class->pullDomainEvents();
|
||||
$ancienNom = $class->name;
|
||||
$nouveauNom = new ClassName('6ème B');
|
||||
$at = new DateTimeImmutable('2026-02-01 10:00:00');
|
||||
|
||||
$class->renommer($nouveauNom, $at);
|
||||
|
||||
self::assertTrue($class->name->equals($nouveauNom));
|
||||
self::assertEquals($at, $class->updatedAt);
|
||||
|
||||
$events = $class->pullDomainEvents();
|
||||
self::assertCount(1, $events);
|
||||
self::assertInstanceOf(ClasseModifiee::class, $events[0]);
|
||||
self::assertTrue($events[0]->ancienNom->equals($ancienNom));
|
||||
self::assertTrue($events[0]->nouveauNom->equals($nouveauNom));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function renommerWithSameNameDoesNothing(): void
|
||||
{
|
||||
$class = $this->createClass();
|
||||
$class->pullDomainEvents();
|
||||
$originalUpdatedAt = $class->updatedAt;
|
||||
|
||||
$class->renommer(new ClassName('6ème A'), new DateTimeImmutable('2026-02-01 10:00:00'));
|
||||
|
||||
self::assertEquals($originalUpdatedAt, $class->updatedAt);
|
||||
self::assertEmpty($class->pullDomainEvents());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function changerNiveauUpdatesLevel(): void
|
||||
{
|
||||
$class = $this->createClass();
|
||||
$at = new DateTimeImmutable('2026-02-01 10:00:00');
|
||||
|
||||
$class->changerNiveau(SchoolLevel::CINQUIEME, $at);
|
||||
|
||||
self::assertSame(SchoolLevel::CINQUIEME, $class->level);
|
||||
self::assertEquals($at, $class->updatedAt);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function changerNiveauWithSameLevelDoesNothing(): void
|
||||
{
|
||||
$class = $this->createClass();
|
||||
$originalUpdatedAt = $class->updatedAt;
|
||||
|
||||
$class->changerNiveau(SchoolLevel::SIXIEME, new DateTimeImmutable('2026-02-01 10:00:00'));
|
||||
|
||||
self::assertEquals($originalUpdatedAt, $class->updatedAt);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function changerCapaciteUpdatesCapacity(): void
|
||||
{
|
||||
$class = $this->createClass();
|
||||
$at = new DateTimeImmutable('2026-02-01 10:00:00');
|
||||
|
||||
$class->changerCapacite(35, $at);
|
||||
|
||||
self::assertSame(35, $class->capacity);
|
||||
self::assertEquals($at, $class->updatedAt);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function changerCapaciteWithSameValueDoesNothing(): void
|
||||
{
|
||||
$class = $this->createClass();
|
||||
$originalUpdatedAt = $class->updatedAt;
|
||||
|
||||
$class->changerCapacite(30, new DateTimeImmutable('2026-02-01 10:00:00'));
|
||||
|
||||
self::assertEquals($originalUpdatedAt, $class->updatedAt);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function decrireUpdatesDescription(): void
|
||||
{
|
||||
$class = $this->createClass();
|
||||
$at = new DateTimeImmutable('2026-02-01 10:00:00');
|
||||
|
||||
$class->decrire('Classe option musique', $at);
|
||||
|
||||
self::assertSame('Classe option musique', $class->description);
|
||||
self::assertEquals($at, $class->updatedAt);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function archiverChangesStatusAndRecordsEvent(): void
|
||||
{
|
||||
$class = $this->createClass();
|
||||
$class->pullDomainEvents();
|
||||
$at = new DateTimeImmutable('2026-02-01 10:00:00');
|
||||
|
||||
$class->archiver($at);
|
||||
|
||||
self::assertSame(ClassStatus::ARCHIVED, $class->status);
|
||||
self::assertFalse($class->estActive());
|
||||
self::assertFalse($class->peutRecevoirEleves());
|
||||
self::assertEquals($at, $class->deletedAt);
|
||||
self::assertEquals($at, $class->updatedAt);
|
||||
|
||||
$events = $class->pullDomainEvents();
|
||||
self::assertCount(1, $events);
|
||||
self::assertInstanceOf(ClasseArchivee::class, $events[0]);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function archiverAlreadyArchivedClassDoesNothing(): void
|
||||
{
|
||||
$class = $this->createClass();
|
||||
$class->archiver(new DateTimeImmutable('2026-02-01 10:00:00'));
|
||||
$class->pullDomainEvents();
|
||||
$originalDeletedAt = $class->deletedAt;
|
||||
|
||||
$class->archiver(new DateTimeImmutable('2026-02-02 10:00:00'));
|
||||
|
||||
self::assertEquals($originalDeletedAt, $class->deletedAt);
|
||||
self::assertEmpty($class->pullDomainEvents());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function reconstituteRestoresAllProperties(): void
|
||||
{
|
||||
$id = \App\Administration\Domain\Model\SchoolClass\ClassId::generate();
|
||||
$tenantId = TenantId::fromString(self::TENANT_ID);
|
||||
$schoolId = SchoolId::fromString(self::SCHOOL_ID);
|
||||
$academicYearId = AcademicYearId::fromString(self::ACADEMIC_YEAR_ID);
|
||||
$name = new ClassName('6ème A');
|
||||
$level = SchoolLevel::SIXIEME;
|
||||
$capacity = 30;
|
||||
$status = ClassStatus::ARCHIVED;
|
||||
$description = 'Test description';
|
||||
$createdAt = new DateTimeImmutable('2026-01-15 10:00:00');
|
||||
$updatedAt = new DateTimeImmutable('2026-02-01 10:00:00');
|
||||
$deletedAt = new DateTimeImmutable('2026-02-01 10:00:00');
|
||||
|
||||
$class = SchoolClass::reconstitute(
|
||||
id: $id,
|
||||
tenantId: $tenantId,
|
||||
schoolId: $schoolId,
|
||||
academicYearId: $academicYearId,
|
||||
name: $name,
|
||||
level: $level,
|
||||
capacity: $capacity,
|
||||
status: $status,
|
||||
description: $description,
|
||||
createdAt: $createdAt,
|
||||
updatedAt: $updatedAt,
|
||||
deletedAt: $deletedAt,
|
||||
);
|
||||
|
||||
self::assertTrue($class->id->equals($id));
|
||||
self::assertTrue($class->tenantId->equals($tenantId));
|
||||
self::assertTrue($class->schoolId->equals($schoolId));
|
||||
self::assertTrue($class->academicYearId->equals($academicYearId));
|
||||
self::assertTrue($class->name->equals($name));
|
||||
self::assertSame($level, $class->level);
|
||||
self::assertSame($capacity, $class->capacity);
|
||||
self::assertSame($status, $class->status);
|
||||
self::assertSame($description, $class->description);
|
||||
self::assertEquals($createdAt, $class->createdAt);
|
||||
self::assertEquals($updatedAt, $class->updatedAt);
|
||||
self::assertEquals($deletedAt, $class->deletedAt);
|
||||
self::assertEmpty($class->pullDomainEvents());
|
||||
}
|
||||
|
||||
private function createClass(): SchoolClass
|
||||
{
|
||||
return SchoolClass::creer(
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
schoolId: SchoolId::fromString(self::SCHOOL_ID),
|
||||
academicYearId: AcademicYearId::fromString(self::ACADEMIC_YEAR_ID),
|
||||
name: new ClassName('6ème A'),
|
||||
level: SchoolLevel::SIXIEME,
|
||||
capacity: 30,
|
||||
createdAt: new DateTimeImmutable('2026-01-15 10:00:00'),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user