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,135 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Administration\Application\Command\ArchiveClass;
|
||||
|
||||
use App\Administration\Application\Command\ArchiveClass\ArchiveClassCommand;
|
||||
use App\Administration\Application\Command\ArchiveClass\ArchiveClassHandler;
|
||||
use App\Administration\Application\Query\HasStudentsInClass\HasStudentsInClassQuery;
|
||||
use App\Administration\Domain\Exception\ClasseNonSupprimableException;
|
||||
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\Administration\Infrastructure\Persistence\InMemory\InMemoryClassRepository;
|
||||
use App\Shared\Domain\Clock;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Messenger\Envelope;
|
||||
use Symfony\Component\Messenger\MessageBusInterface;
|
||||
use Symfony\Component\Messenger\Stamp\HandledStamp;
|
||||
|
||||
final class ArchiveClassHandlerTest 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';
|
||||
|
||||
private InMemoryClassRepository $classRepository;
|
||||
private Clock $clock;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->classRepository = new InMemoryClassRepository();
|
||||
$this->clock = new class implements Clock {
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
return new DateTimeImmutable('2026-02-01 10:00:00');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itArchivesEmptyClass(): void
|
||||
{
|
||||
$class = $this->createAndSaveClass();
|
||||
$queryBus = $this->createQueryBusReturning(0);
|
||||
$handler = new ArchiveClassHandler($this->classRepository, $queryBus, $this->clock);
|
||||
|
||||
$command = new ArchiveClassCommand(classId: (string) $class->id);
|
||||
|
||||
$handler($command);
|
||||
|
||||
$archivedClass = $this->classRepository->get($class->id);
|
||||
self::assertSame(ClassStatus::ARCHIVED, $archivedClass->status);
|
||||
self::assertNotNull($archivedClass->deletedAt);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itThrowsExceptionWhenStudentsAreAffected(): void
|
||||
{
|
||||
$class = $this->createAndSaveClass();
|
||||
$queryBus = $this->createQueryBusReturning(5);
|
||||
$handler = new ArchiveClassHandler($this->classRepository, $queryBus, $this->clock);
|
||||
|
||||
$command = new ArchiveClassCommand(classId: (string) $class->id);
|
||||
|
||||
$this->expectException(ClasseNonSupprimableException::class);
|
||||
$this->expectExceptionMessage('5 élève(s)');
|
||||
|
||||
$handler($command);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itDoesNotModifyStatusWhenAlreadyArchived(): void
|
||||
{
|
||||
$class = $this->createAndSaveClass();
|
||||
$archiveTime = new DateTimeImmutable('2026-01-20 10:00:00');
|
||||
$class->archiver($archiveTime);
|
||||
$this->classRepository->save($class);
|
||||
|
||||
$queryBus = $this->createQueryBusReturning(0);
|
||||
$handler = new ArchiveClassHandler($this->classRepository, $queryBus, $this->clock);
|
||||
|
||||
$command = new ArchiveClassCommand(classId: (string) $class->id);
|
||||
|
||||
$handler($command);
|
||||
|
||||
$archivedClass = $this->classRepository->get($class->id);
|
||||
// deletedAt should not have changed
|
||||
self::assertEquals($archiveTime, $archivedClass->deletedAt);
|
||||
}
|
||||
|
||||
private function createAndSaveClass(): SchoolClass
|
||||
{
|
||||
$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('6ème A'),
|
||||
level: SchoolLevel::SIXIEME,
|
||||
capacity: 30,
|
||||
createdAt: new DateTimeImmutable('2026-01-15 10:00:00'),
|
||||
);
|
||||
|
||||
$this->classRepository->save($class);
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
private function createQueryBusReturning(int $studentCount): MessageBusInterface
|
||||
{
|
||||
return new class($studentCount) implements MessageBusInterface {
|
||||
public function __construct(private readonly int $studentCount)
|
||||
{
|
||||
}
|
||||
|
||||
public function dispatch(object $message, array $stamps = []): Envelope
|
||||
{
|
||||
if (!$message instanceof HasStudentsInClassQuery) {
|
||||
throw new RuntimeException('Unexpected message type');
|
||||
}
|
||||
|
||||
$envelope = new Envelope($message);
|
||||
|
||||
return $envelope->with(new HandledStamp($this->studentCount, 'handler'));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Administration\Application\Command\CreateClass;
|
||||
|
||||
use App\Administration\Application\Command\CreateClass\CreateClassCommand;
|
||||
use App\Administration\Application\Command\CreateClass\CreateClassHandler;
|
||||
use App\Administration\Domain\Exception\ClasseDejaExistanteException;
|
||||
use App\Administration\Domain\Model\SchoolClass\ClassId;
|
||||
use App\Administration\Domain\Model\SchoolClass\ClassStatus;
|
||||
use App\Administration\Domain\Model\SchoolClass\SchoolLevel;
|
||||
use App\Administration\Infrastructure\Persistence\InMemory\InMemoryClassRepository;
|
||||
use App\Shared\Domain\Clock;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class CreateClassHandlerTest 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';
|
||||
|
||||
private InMemoryClassRepository $classRepository;
|
||||
private Clock $clock;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->classRepository = new InMemoryClassRepository();
|
||||
$this->clock = new class implements Clock {
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
return new DateTimeImmutable('2026-01-31 10:00:00');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itCreatesClassSuccessfully(): void
|
||||
{
|
||||
$handler = new CreateClassHandler($this->classRepository, $this->clock);
|
||||
$command = new CreateClassCommand(
|
||||
tenantId: self::TENANT_ID,
|
||||
schoolId: self::SCHOOL_ID,
|
||||
academicYearId: self::ACADEMIC_YEAR_ID,
|
||||
name: '6ème A',
|
||||
level: SchoolLevel::SIXIEME->value,
|
||||
capacity: 30,
|
||||
);
|
||||
|
||||
$class = $handler($command);
|
||||
|
||||
self::assertNotEmpty((string) $class->id);
|
||||
self::assertSame('6ème A', (string) $class->name);
|
||||
self::assertSame(SchoolLevel::SIXIEME, $class->level);
|
||||
self::assertSame(30, $class->capacity);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itCreatesClassWithNullLevelAndCapacity(): void
|
||||
{
|
||||
$handler = new CreateClassHandler($this->classRepository, $this->clock);
|
||||
$command = new CreateClassCommand(
|
||||
tenantId: self::TENANT_ID,
|
||||
schoolId: self::SCHOOL_ID,
|
||||
academicYearId: self::ACADEMIC_YEAR_ID,
|
||||
name: 'Classe spéciale',
|
||||
level: null,
|
||||
capacity: null,
|
||||
);
|
||||
|
||||
$class = $handler($command);
|
||||
|
||||
self::assertNotEmpty((string) $class->id);
|
||||
self::assertSame('Classe spéciale', (string) $class->name);
|
||||
self::assertNull($class->level);
|
||||
self::assertNull($class->capacity);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itPersistsClassInRepository(): void
|
||||
{
|
||||
$handler = new CreateClassHandler($this->classRepository, $this->clock);
|
||||
$command = new CreateClassCommand(
|
||||
tenantId: self::TENANT_ID,
|
||||
schoolId: self::SCHOOL_ID,
|
||||
academicYearId: self::ACADEMIC_YEAR_ID,
|
||||
name: '6ème A',
|
||||
level: SchoolLevel::SIXIEME->value,
|
||||
capacity: 30,
|
||||
);
|
||||
|
||||
$createdClass = $handler($command);
|
||||
|
||||
$class = $this->classRepository->get(
|
||||
ClassId::fromString((string) $createdClass->id),
|
||||
);
|
||||
|
||||
self::assertSame('6ème A', (string) $class->name);
|
||||
self::assertSame(ClassStatus::ACTIVE, $class->status);
|
||||
self::assertSame(SchoolLevel::SIXIEME, $class->level);
|
||||
self::assertSame(30, $class->capacity);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itThrowsExceptionWhenClassNameAlreadyExists(): void
|
||||
{
|
||||
$handler = new CreateClassHandler($this->classRepository, $this->clock);
|
||||
$command = new CreateClassCommand(
|
||||
tenantId: self::TENANT_ID,
|
||||
schoolId: self::SCHOOL_ID,
|
||||
academicYearId: self::ACADEMIC_YEAR_ID,
|
||||
name: '6ème A',
|
||||
level: SchoolLevel::SIXIEME->value,
|
||||
capacity: 30,
|
||||
);
|
||||
|
||||
// First creation should succeed
|
||||
$handler($command);
|
||||
|
||||
// Second creation with same name should throw
|
||||
$this->expectException(ClasseDejaExistanteException::class);
|
||||
$this->expectExceptionMessage('Une classe avec le nom "6ème A" existe déjà pour cette année scolaire.');
|
||||
|
||||
$handler($command);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itAllowsSameNameInDifferentTenant(): void
|
||||
{
|
||||
$handler = new CreateClassHandler($this->classRepository, $this->clock);
|
||||
|
||||
// Create in tenant 1
|
||||
$command1 = new CreateClassCommand(
|
||||
tenantId: self::TENANT_ID,
|
||||
schoolId: self::SCHOOL_ID,
|
||||
academicYearId: self::ACADEMIC_YEAR_ID,
|
||||
name: '6ème A',
|
||||
level: SchoolLevel::SIXIEME->value,
|
||||
capacity: 30,
|
||||
);
|
||||
$class1 = $handler($command1);
|
||||
|
||||
// Create same name in tenant 2 should succeed
|
||||
$command2 = new CreateClassCommand(
|
||||
tenantId: '550e8400-e29b-41d4-a716-446655440099',
|
||||
schoolId: self::SCHOOL_ID,
|
||||
academicYearId: self::ACADEMIC_YEAR_ID,
|
||||
name: '6ème A',
|
||||
level: SchoolLevel::SIXIEME->value,
|
||||
capacity: 30,
|
||||
);
|
||||
$class2 = $handler($command2);
|
||||
|
||||
self::assertFalse($class1->id->equals($class2->id));
|
||||
self::assertSame('6ème A', (string) $class1->name);
|
||||
self::assertSame('6ème A', (string) $class2->name);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itAllowsSameNameInDifferentAcademicYear(): void
|
||||
{
|
||||
$handler = new CreateClassHandler($this->classRepository, $this->clock);
|
||||
|
||||
// Create in year 1
|
||||
$command1 = new CreateClassCommand(
|
||||
tenantId: self::TENANT_ID,
|
||||
schoolId: self::SCHOOL_ID,
|
||||
academicYearId: self::ACADEMIC_YEAR_ID,
|
||||
name: '6ème A',
|
||||
level: SchoolLevel::SIXIEME->value,
|
||||
capacity: 30,
|
||||
);
|
||||
$class1 = $handler($command1);
|
||||
|
||||
// Create same name in year 2 should succeed
|
||||
$command2 = new CreateClassCommand(
|
||||
tenantId: self::TENANT_ID,
|
||||
schoolId: self::SCHOOL_ID,
|
||||
academicYearId: '550e8400-e29b-41d4-a716-446655440099',
|
||||
name: '6ème A',
|
||||
level: SchoolLevel::SIXIEME->value,
|
||||
capacity: 30,
|
||||
);
|
||||
$class2 = $handler($command2);
|
||||
|
||||
self::assertFalse($class1->id->equals($class2->id));
|
||||
self::assertSame('6ème A', (string) $class1->name);
|
||||
self::assertSame('6ème A', (string) $class2->name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Administration\Application\Command\UpdateClass;
|
||||
|
||||
use App\Administration\Application\Command\UpdateClass\UpdateClassCommand;
|
||||
use App\Administration\Application\Command\UpdateClass\UpdateClassHandler;
|
||||
use App\Administration\Domain\Exception\ClasseDejaExistanteException;
|
||||
use App\Administration\Domain\Model\SchoolClass\AcademicYearId;
|
||||
use App\Administration\Domain\Model\SchoolClass\ClassName;
|
||||
use App\Administration\Domain\Model\SchoolClass\SchoolClass;
|
||||
use App\Administration\Domain\Model\SchoolClass\SchoolId;
|
||||
use App\Administration\Domain\Model\SchoolClass\SchoolLevel;
|
||||
use App\Administration\Infrastructure\Persistence\InMemory\InMemoryClassRepository;
|
||||
use App\Shared\Domain\Clock;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class UpdateClassHandlerTest 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';
|
||||
|
||||
private InMemoryClassRepository $classRepository;
|
||||
private Clock $clock;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->classRepository = new InMemoryClassRepository();
|
||||
$this->clock = new class implements Clock {
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
return new DateTimeImmutable('2026-02-01 10:00:00');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itUpdatesClassName(): void
|
||||
{
|
||||
$class = $this->createAndSaveClass();
|
||||
$handler = new UpdateClassHandler($this->classRepository, $this->clock);
|
||||
|
||||
$command = new UpdateClassCommand(
|
||||
classId: (string) $class->id,
|
||||
name: '6ème B',
|
||||
);
|
||||
|
||||
$handler($command);
|
||||
|
||||
$updatedClass = $this->classRepository->get($class->id);
|
||||
self::assertSame('6ème B', (string) $updatedClass->name);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itUpdatesClassLevel(): void
|
||||
{
|
||||
$class = $this->createAndSaveClass();
|
||||
$handler = new UpdateClassHandler($this->classRepository, $this->clock);
|
||||
|
||||
$command = new UpdateClassCommand(
|
||||
classId: (string) $class->id,
|
||||
level: SchoolLevel::CINQUIEME->value,
|
||||
);
|
||||
|
||||
$handler($command);
|
||||
|
||||
$updatedClass = $this->classRepository->get($class->id);
|
||||
self::assertSame(SchoolLevel::CINQUIEME, $updatedClass->level);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itClearsClassLevel(): void
|
||||
{
|
||||
$class = $this->createAndSaveClass();
|
||||
$handler = new UpdateClassHandler($this->classRepository, $this->clock);
|
||||
|
||||
$command = new UpdateClassCommand(
|
||||
classId: (string) $class->id,
|
||||
clearLevel: true,
|
||||
);
|
||||
|
||||
$handler($command);
|
||||
|
||||
$updatedClass = $this->classRepository->get($class->id);
|
||||
self::assertNull($updatedClass->level);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itUpdatesClassCapacity(): void
|
||||
{
|
||||
$class = $this->createAndSaveClass();
|
||||
$handler = new UpdateClassHandler($this->classRepository, $this->clock);
|
||||
|
||||
$command = new UpdateClassCommand(
|
||||
classId: (string) $class->id,
|
||||
capacity: 35,
|
||||
);
|
||||
|
||||
$handler($command);
|
||||
|
||||
$updatedClass = $this->classRepository->get($class->id);
|
||||
self::assertSame(35, $updatedClass->capacity);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itUpdatesClassDescription(): void
|
||||
{
|
||||
$class = $this->createAndSaveClass();
|
||||
$handler = new UpdateClassHandler($this->classRepository, $this->clock);
|
||||
|
||||
$command = new UpdateClassCommand(
|
||||
classId: (string) $class->id,
|
||||
description: 'Classe option musique',
|
||||
);
|
||||
|
||||
$handler($command);
|
||||
|
||||
$updatedClass = $this->classRepository->get($class->id);
|
||||
self::assertSame('Classe option musique', $updatedClass->description);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itUpdatesMultipleFields(): void
|
||||
{
|
||||
$class = $this->createAndSaveClass();
|
||||
$handler = new UpdateClassHandler($this->classRepository, $this->clock);
|
||||
|
||||
$command = new UpdateClassCommand(
|
||||
classId: (string) $class->id,
|
||||
name: '5ème C',
|
||||
level: SchoolLevel::CINQUIEME->value,
|
||||
capacity: 28,
|
||||
description: 'Section européenne',
|
||||
);
|
||||
|
||||
$handler($command);
|
||||
|
||||
$updatedClass = $this->classRepository->get($class->id);
|
||||
self::assertSame('5ème C', (string) $updatedClass->name);
|
||||
self::assertSame(SchoolLevel::CINQUIEME, $updatedClass->level);
|
||||
self::assertSame(28, $updatedClass->capacity);
|
||||
self::assertSame('Section européenne', $updatedClass->description);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itThrowsExceptionWhenRenamingToExistingName(): void
|
||||
{
|
||||
// Create first class
|
||||
$class1 = $this->createAndSaveClass();
|
||||
|
||||
// Create second class with different name
|
||||
$class2 = 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 B'),
|
||||
level: SchoolLevel::SIXIEME,
|
||||
capacity: 30,
|
||||
createdAt: new DateTimeImmutable('2026-01-15 10:00:00'),
|
||||
);
|
||||
$this->classRepository->save($class2);
|
||||
|
||||
$handler = new UpdateClassHandler($this->classRepository, $this->clock);
|
||||
|
||||
// Try to rename class2 to class1's name
|
||||
$command = new UpdateClassCommand(
|
||||
classId: (string) $class2->id,
|
||||
name: '6ème A',
|
||||
);
|
||||
|
||||
$this->expectException(ClasseDejaExistanteException::class);
|
||||
$this->expectExceptionMessage('Une classe avec le nom "6ème A" existe déjà pour cette année scolaire.');
|
||||
|
||||
$handler($command);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itAllowsRenamingToSameName(): void
|
||||
{
|
||||
$class = $this->createAndSaveClass();
|
||||
$handler = new UpdateClassHandler($this->classRepository, $this->clock);
|
||||
|
||||
// Renaming to the same name should work
|
||||
$command = new UpdateClassCommand(
|
||||
classId: (string) $class->id,
|
||||
name: '6ème A',
|
||||
);
|
||||
|
||||
$handler($command);
|
||||
|
||||
$updatedClass = $this->classRepository->get($class->id);
|
||||
self::assertSame('6ème A', (string) $updatedClass->name);
|
||||
}
|
||||
|
||||
private function createAndSaveClass(): SchoolClass
|
||||
{
|
||||
$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('6ème A'),
|
||||
level: SchoolLevel::SIXIEME,
|
||||
capacity: 30,
|
||||
createdAt: new DateTimeImmutable('2026-01-15 10:00:00'),
|
||||
);
|
||||
|
||||
$this->classRepository->save($class);
|
||||
|
||||
return $class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Administration\Domain\Model\SchoolClass;
|
||||
|
||||
use App\Administration\Domain\Exception\ClassNameInvalideException;
|
||||
use App\Administration\Domain\Model\SchoolClass\ClassName;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class ClassNameTest extends TestCase
|
||||
{
|
||||
#[Test]
|
||||
public function constructWithValidName(): void
|
||||
{
|
||||
$name = new ClassName('6ème A');
|
||||
|
||||
self::assertSame('6ème A', $name->value);
|
||||
self::assertSame('6ème A', (string) $name);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function constructTrimsWhitespace(): void
|
||||
{
|
||||
$name = new ClassName(' 6ème A ');
|
||||
|
||||
self::assertSame('6ème A', $name->value);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
#[DataProvider('validNamesProvider')]
|
||||
public function constructAcceptsValidNames(string $value): void
|
||||
{
|
||||
$name = new ClassName($value);
|
||||
|
||||
self::assertNotEmpty($name->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array{string}>
|
||||
*/
|
||||
public static function validNamesProvider(): array
|
||||
{
|
||||
return [
|
||||
'minimum length' => ['AB'],
|
||||
'typical class name' => ['6ème A'],
|
||||
'longer name' => ['Classe préparatoire aux grandes écoles'],
|
||||
'with numbers' => ['CM1-2'],
|
||||
'maximum length' => [str_repeat('A', 50)],
|
||||
];
|
||||
}
|
||||
|
||||
#[Test]
|
||||
#[DataProvider('invalidNamesProvider')]
|
||||
public function constructRejectsInvalidNames(string $value): void
|
||||
{
|
||||
$this->expectException(ClassNameInvalideException::class);
|
||||
|
||||
new ClassName($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array{string}>
|
||||
*/
|
||||
public static function invalidNamesProvider(): array
|
||||
{
|
||||
return [
|
||||
'empty string' => [''],
|
||||
'single character' => ['A'],
|
||||
'only whitespace' => [' '],
|
||||
'one char after trim' => [' A '],
|
||||
'too long' => [str_repeat('A', 51)],
|
||||
];
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function equalsReturnsTrueForSameValue(): void
|
||||
{
|
||||
$name1 = new ClassName('6ème A');
|
||||
$name2 = new ClassName('6ème A');
|
||||
|
||||
self::assertTrue($name1->equals($name2));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function equalsReturnsFalseForDifferentValue(): void
|
||||
{
|
||||
$name1 = new ClassName('6ème A');
|
||||
$name2 = new ClassName('6ème B');
|
||||
|
||||
self::assertFalse($name1->equals($name2));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function equalsIsCaseSensitive(): void
|
||||
{
|
||||
$name1 = new ClassName('Classe A');
|
||||
$name2 = new ClassName('classe A');
|
||||
|
||||
self::assertFalse($name1->equals($name2));
|
||||
}
|
||||
}
|
||||
@@ -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'),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Administration\Domain\Model\SchoolClass;
|
||||
|
||||
use App\Administration\Domain\Model\SchoolClass\SchoolLevel;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class SchoolLevelTest extends TestCase
|
||||
{
|
||||
#[Test]
|
||||
#[DataProvider('primaryLevelsProvider')]
|
||||
public function estPrimaireReturnsTrueForPrimaryLevels(SchoolLevel $level): void
|
||||
{
|
||||
self::assertTrue($level->estPrimaire());
|
||||
self::assertFalse($level->estCollege());
|
||||
self::assertFalse($level->estLycee());
|
||||
self::assertSame('Primaire', $level->cycle());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array{SchoolLevel}>
|
||||
*/
|
||||
public static function primaryLevelsProvider(): array
|
||||
{
|
||||
return [
|
||||
'CP' => [SchoolLevel::CP],
|
||||
'CE1' => [SchoolLevel::CE1],
|
||||
'CE2' => [SchoolLevel::CE2],
|
||||
'CM1' => [SchoolLevel::CM1],
|
||||
'CM2' => [SchoolLevel::CM2],
|
||||
];
|
||||
}
|
||||
|
||||
#[Test]
|
||||
#[DataProvider('collegeLevelsProvider')]
|
||||
public function estCollegeReturnsTrueForCollegeLevels(SchoolLevel $level): void
|
||||
{
|
||||
self::assertFalse($level->estPrimaire());
|
||||
self::assertTrue($level->estCollege());
|
||||
self::assertFalse($level->estLycee());
|
||||
self::assertSame('Collège', $level->cycle());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array{SchoolLevel}>
|
||||
*/
|
||||
public static function collegeLevelsProvider(): array
|
||||
{
|
||||
return [
|
||||
'6ème' => [SchoolLevel::SIXIEME],
|
||||
'5ème' => [SchoolLevel::CINQUIEME],
|
||||
'4ème' => [SchoolLevel::QUATRIEME],
|
||||
'3ème' => [SchoolLevel::TROISIEME],
|
||||
];
|
||||
}
|
||||
|
||||
#[Test]
|
||||
#[DataProvider('lyceeLevelsProvider')]
|
||||
public function estLyceeReturnsTrueForLyceeLevels(SchoolLevel $level): void
|
||||
{
|
||||
self::assertFalse($level->estPrimaire());
|
||||
self::assertFalse($level->estCollege());
|
||||
self::assertTrue($level->estLycee());
|
||||
self::assertSame('Lycée', $level->cycle());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array{SchoolLevel}>
|
||||
*/
|
||||
public static function lyceeLevelsProvider(): array
|
||||
{
|
||||
return [
|
||||
'2nde' => [SchoolLevel::SECONDE],
|
||||
'1ère' => [SchoolLevel::PREMIERE],
|
||||
'Terminale' => [SchoolLevel::TERMINALE],
|
||||
];
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function labelReturnsValue(): void
|
||||
{
|
||||
self::assertSame('6ème', SchoolLevel::SIXIEME->label());
|
||||
self::assertSame('CM2', SchoolLevel::CM2->label());
|
||||
self::assertSame('Terminale', SchoolLevel::TERMINALE->label());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function valueMatchesExpectedString(): void
|
||||
{
|
||||
self::assertSame('CP', SchoolLevel::CP->value);
|
||||
self::assertSame('6ème', SchoolLevel::SIXIEME->value);
|
||||
self::assertSame('2nde', SchoolLevel::SECONDE->value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Administration\Infrastructure\Persistence\InMemory;
|
||||
|
||||
use App\Administration\Domain\Exception\ClasseNotFoundException;
|
||||
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\SchoolClass;
|
||||
use App\Administration\Domain\Model\SchoolClass\SchoolId;
|
||||
use App\Administration\Domain\Model\SchoolClass\SchoolLevel;
|
||||
use App\Administration\Infrastructure\Persistence\InMemory\InMemoryClassRepository;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class InMemoryClassRepositoryTest 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';
|
||||
|
||||
private InMemoryClassRepository $repository;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->repository = new InMemoryClassRepository();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function saveAndGet(): void
|
||||
{
|
||||
$class = $this->createClass('6ème A');
|
||||
|
||||
$this->repository->save($class);
|
||||
|
||||
$retrieved = $this->repository->get($class->id);
|
||||
self::assertTrue($class->id->equals($retrieved->id));
|
||||
self::assertTrue($class->name->equals($retrieved->name));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function getThrowsExceptionForUnknownId(): void
|
||||
{
|
||||
$this->expectException(ClasseNotFoundException::class);
|
||||
|
||||
$this->repository->get(ClassId::generate());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function findByIdReturnsNullForUnknownId(): void
|
||||
{
|
||||
$result = $this->repository->findById(ClassId::generate());
|
||||
|
||||
self::assertNull($result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function findByName(): void
|
||||
{
|
||||
$class = $this->createClass('6ème A');
|
||||
$this->repository->save($class);
|
||||
|
||||
$found = $this->repository->findByName(
|
||||
new ClassName('6ème A'),
|
||||
TenantId::fromString(self::TENANT_ID),
|
||||
AcademicYearId::fromString(self::ACADEMIC_YEAR_ID),
|
||||
);
|
||||
|
||||
self::assertNotNull($found);
|
||||
self::assertTrue($class->id->equals($found->id));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function findByNameReturnsNullForUnknownName(): void
|
||||
{
|
||||
$class = $this->createClass('6ème A');
|
||||
$this->repository->save($class);
|
||||
|
||||
$found = $this->repository->findByName(
|
||||
new ClassName('6ème B'),
|
||||
TenantId::fromString(self::TENANT_ID),
|
||||
AcademicYearId::fromString(self::ACADEMIC_YEAR_ID),
|
||||
);
|
||||
|
||||
self::assertNull($found);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function findByNameIsCaseInsensitive(): void
|
||||
{
|
||||
$class = $this->createClass('6ème A');
|
||||
$this->repository->save($class);
|
||||
|
||||
$found = $this->repository->findByName(
|
||||
new ClassName('6ÈME A'),
|
||||
TenantId::fromString(self::TENANT_ID),
|
||||
AcademicYearId::fromString(self::ACADEMIC_YEAR_ID),
|
||||
);
|
||||
|
||||
self::assertNotNull($found);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function findActiveByTenantAndYear(): void
|
||||
{
|
||||
$class1 = $this->createClass('6ème A');
|
||||
$class2 = $this->createClass('6ème B');
|
||||
$class3 = $this->createClass('6ème C');
|
||||
$class3->archiver(new DateTimeImmutable());
|
||||
|
||||
$this->repository->save($class1);
|
||||
$this->repository->save($class2);
|
||||
$this->repository->save($class3);
|
||||
|
||||
$activeClasses = $this->repository->findActiveByTenantAndYear(
|
||||
TenantId::fromString(self::TENANT_ID),
|
||||
AcademicYearId::fromString(self::ACADEMIC_YEAR_ID),
|
||||
);
|
||||
|
||||
self::assertCount(2, $activeClasses);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function findActiveByTenantAndYearReturnsEmptyArrayForDifferentTenant(): void
|
||||
{
|
||||
$class = $this->createClass('6ème A');
|
||||
$this->repository->save($class);
|
||||
|
||||
$activeClasses = $this->repository->findActiveByTenantAndYear(
|
||||
TenantId::fromString('550e8400-e29b-41d4-a716-446655440099'),
|
||||
AcademicYearId::fromString(self::ACADEMIC_YEAR_ID),
|
||||
);
|
||||
|
||||
self::assertEmpty($activeClasses);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function delete(): void
|
||||
{
|
||||
$class = $this->createClass('6ème A');
|
||||
$this->repository->save($class);
|
||||
|
||||
$this->repository->delete($class->id);
|
||||
|
||||
self::assertNull($this->repository->findById($class->id));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function deleteNonExistentClassDoesNotThrow(): void
|
||||
{
|
||||
// Should not throw
|
||||
$this->repository->delete(ClassId::generate());
|
||||
|
||||
$this->expectNotToPerformAssertions();
|
||||
}
|
||||
|
||||
private function createClass(string $name): 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($name),
|
||||
level: SchoolLevel::SIXIEME,
|
||||
capacity: 30,
|
||||
createdAt: new DateTimeImmutable('2026-01-15 10:00:00'),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user