Les enseignants avaient besoin de définir les critères de notation (barème, coefficient) avant de pouvoir saisir des notes. Sans cette brique, le module Notes & Évaluations (Epic 6) ne pouvait pas démarrer. L'évaluation est un agrégat du bounded context Scolarité avec deux Value Objects (GradeScale 1-100, Coefficient 0.1-10). Le barème est verrouillé dès qu'une note existe pour éviter les incohérences. Un port EvaluationGradesChecker (stub pour l'instant) sera branché sur le repository de notes dans la story 6.2.
289 lines
11 KiB
PHP
289 lines
11 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Unit\Scolarite\Domain\Model\Evaluation;
|
|
|
|
use App\Administration\Domain\Model\SchoolClass\ClassId;
|
|
use App\Administration\Domain\Model\Subject\SubjectId;
|
|
use App\Administration\Domain\Model\User\UserId;
|
|
use App\Scolarite\Domain\Event\EvaluationCreee;
|
|
use App\Scolarite\Domain\Event\EvaluationModifiee;
|
|
use App\Scolarite\Domain\Event\EvaluationSupprimee;
|
|
use App\Scolarite\Domain\Exception\BaremeNonModifiableException;
|
|
use App\Scolarite\Domain\Exception\EvaluationDejaSupprimeeException;
|
|
use App\Scolarite\Domain\Model\Evaluation\Coefficient;
|
|
use App\Scolarite\Domain\Model\Evaluation\Evaluation;
|
|
use App\Scolarite\Domain\Model\Evaluation\EvaluationId;
|
|
use App\Scolarite\Domain\Model\Evaluation\EvaluationStatus;
|
|
use App\Scolarite\Domain\Model\Evaluation\GradeScale;
|
|
use App\Shared\Domain\Tenant\TenantId;
|
|
use DateTimeImmutable;
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
final class EvaluationTest extends TestCase
|
|
{
|
|
private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
|
|
private const string CLASS_ID = '550e8400-e29b-41d4-a716-446655440020';
|
|
private const string SUBJECT_ID = '550e8400-e29b-41d4-a716-446655440030';
|
|
private const string TEACHER_ID = '550e8400-e29b-41d4-a716-446655440010';
|
|
|
|
#[Test]
|
|
public function creerCreatesPublishedEvaluation(): void
|
|
{
|
|
$evaluation = $this->createEvaluation();
|
|
|
|
self::assertSame(EvaluationStatus::PUBLISHED, $evaluation->status);
|
|
}
|
|
|
|
#[Test]
|
|
public function creerRecordsEvaluationCreeeEvent(): void
|
|
{
|
|
$evaluation = $this->createEvaluation();
|
|
|
|
$events = $evaluation->pullDomainEvents();
|
|
|
|
self::assertCount(1, $events);
|
|
self::assertInstanceOf(EvaluationCreee::class, $events[0]);
|
|
self::assertSame($evaluation->id, $events[0]->evaluationId);
|
|
}
|
|
|
|
#[Test]
|
|
public function creerSetsAllProperties(): void
|
|
{
|
|
$tenantId = TenantId::fromString(self::TENANT_ID);
|
|
$classId = ClassId::fromString(self::CLASS_ID);
|
|
$subjectId = SubjectId::fromString(self::SUBJECT_ID);
|
|
$teacherId = UserId::fromString(self::TEACHER_ID);
|
|
$evaluationDate = new DateTimeImmutable('2026-04-15');
|
|
$now = new DateTimeImmutable('2026-03-12 10:00:00');
|
|
$gradeScale = new GradeScale(20);
|
|
$coefficient = new Coefficient(1.5);
|
|
|
|
$evaluation = Evaluation::creer(
|
|
tenantId: $tenantId,
|
|
classId: $classId,
|
|
subjectId: $subjectId,
|
|
teacherId: $teacherId,
|
|
title: 'Contrôle chapitre 5',
|
|
description: 'Évaluation sur les fonctions',
|
|
evaluationDate: $evaluationDate,
|
|
gradeScale: $gradeScale,
|
|
coefficient: $coefficient,
|
|
now: $now,
|
|
);
|
|
|
|
self::assertTrue($evaluation->tenantId->equals($tenantId));
|
|
self::assertTrue($evaluation->classId->equals($classId));
|
|
self::assertTrue($evaluation->subjectId->equals($subjectId));
|
|
self::assertTrue($evaluation->teacherId->equals($teacherId));
|
|
self::assertSame('Contrôle chapitre 5', $evaluation->title);
|
|
self::assertSame('Évaluation sur les fonctions', $evaluation->description);
|
|
self::assertEquals($evaluationDate, $evaluation->evaluationDate);
|
|
self::assertSame(20, $evaluation->gradeScale->maxValue);
|
|
self::assertSame(1.5, $evaluation->coefficient->value);
|
|
self::assertSame(EvaluationStatus::PUBLISHED, $evaluation->status);
|
|
self::assertEquals($now, $evaluation->createdAt);
|
|
self::assertEquals($now, $evaluation->updatedAt);
|
|
}
|
|
|
|
#[Test]
|
|
public function creerAllowsNullDescription(): void
|
|
{
|
|
$evaluation = Evaluation::creer(
|
|
tenantId: TenantId::fromString(self::TENANT_ID),
|
|
classId: ClassId::fromString(self::CLASS_ID),
|
|
subjectId: SubjectId::fromString(self::SUBJECT_ID),
|
|
teacherId: UserId::fromString(self::TEACHER_ID),
|
|
title: 'Évaluation sans description',
|
|
description: null,
|
|
evaluationDate: new DateTimeImmutable('2026-04-15'),
|
|
gradeScale: new GradeScale(20),
|
|
coefficient: new Coefficient(1.0),
|
|
now: new DateTimeImmutable('2026-03-12 10:00:00'),
|
|
);
|
|
|
|
self::assertNull($evaluation->description);
|
|
}
|
|
|
|
#[Test]
|
|
public function modifierUpdatesFieldsAndRecordsEvent(): void
|
|
{
|
|
$evaluation = $this->createEvaluation();
|
|
$evaluation->pullDomainEvents();
|
|
$modifiedAt = new DateTimeImmutable('2026-03-13 14:00:00');
|
|
$newDate = new DateTimeImmutable('2026-04-20');
|
|
$newCoefficient = new Coefficient(2.0);
|
|
|
|
$evaluation->modifier(
|
|
title: 'Titre modifié',
|
|
description: 'Nouvelle description',
|
|
coefficient: $newCoefficient,
|
|
evaluationDate: $newDate,
|
|
gradeScale: null,
|
|
hasGrades: false,
|
|
now: $modifiedAt,
|
|
);
|
|
|
|
self::assertSame('Titre modifié', $evaluation->title);
|
|
self::assertSame('Nouvelle description', $evaluation->description);
|
|
self::assertSame(2.0, $evaluation->coefficient->value);
|
|
self::assertEquals($newDate, $evaluation->evaluationDate);
|
|
self::assertEquals($modifiedAt, $evaluation->updatedAt);
|
|
|
|
$events = $evaluation->pullDomainEvents();
|
|
self::assertCount(1, $events);
|
|
self::assertInstanceOf(EvaluationModifiee::class, $events[0]);
|
|
self::assertSame($evaluation->id, $events[0]->evaluationId);
|
|
}
|
|
|
|
#[Test]
|
|
public function modifierAllowsGradeScaleChangeWhenNoGrades(): void
|
|
{
|
|
$evaluation = $this->createEvaluation();
|
|
$evaluation->pullDomainEvents();
|
|
$newGradeScale = new GradeScale(10);
|
|
|
|
$evaluation->modifier(
|
|
title: $evaluation->title,
|
|
description: $evaluation->description,
|
|
coefficient: $evaluation->coefficient,
|
|
evaluationDate: $evaluation->evaluationDate,
|
|
gradeScale: $newGradeScale,
|
|
hasGrades: false,
|
|
now: new DateTimeImmutable('2026-03-13 14:00:00'),
|
|
);
|
|
|
|
self::assertSame(10, $evaluation->gradeScale->maxValue);
|
|
}
|
|
|
|
#[Test]
|
|
public function modifierBlocksGradeScaleChangeWhenGradesExist(): void
|
|
{
|
|
$evaluation = $this->createEvaluation();
|
|
|
|
$this->expectException(BaremeNonModifiableException::class);
|
|
|
|
$evaluation->modifier(
|
|
title: $evaluation->title,
|
|
description: $evaluation->description,
|
|
coefficient: $evaluation->coefficient,
|
|
evaluationDate: $evaluation->evaluationDate,
|
|
gradeScale: new GradeScale(10),
|
|
hasGrades: true,
|
|
now: new DateTimeImmutable('2026-03-13 14:00:00'),
|
|
);
|
|
}
|
|
|
|
#[Test]
|
|
public function modifierThrowsWhenDeleted(): void
|
|
{
|
|
$evaluation = $this->createEvaluation();
|
|
$evaluation->supprimer(new DateTimeImmutable('2026-03-13'));
|
|
|
|
$this->expectException(EvaluationDejaSupprimeeException::class);
|
|
|
|
$evaluation->modifier(
|
|
title: 'Titre',
|
|
description: null,
|
|
coefficient: new Coefficient(1.0),
|
|
evaluationDate: new DateTimeImmutable('2026-04-20'),
|
|
gradeScale: null,
|
|
hasGrades: false,
|
|
now: new DateTimeImmutable('2026-03-14'),
|
|
);
|
|
}
|
|
|
|
#[Test]
|
|
public function supprimerChangesStatusAndRecordsEvent(): void
|
|
{
|
|
$evaluation = $this->createEvaluation();
|
|
$evaluation->pullDomainEvents();
|
|
$deletedAt = new DateTimeImmutable('2026-03-14 08:00:00');
|
|
|
|
$evaluation->supprimer($deletedAt);
|
|
|
|
self::assertSame(EvaluationStatus::DELETED, $evaluation->status);
|
|
self::assertEquals($deletedAt, $evaluation->updatedAt);
|
|
|
|
$events = $evaluation->pullDomainEvents();
|
|
self::assertCount(1, $events);
|
|
self::assertInstanceOf(EvaluationSupprimee::class, $events[0]);
|
|
self::assertSame($evaluation->id, $events[0]->evaluationId);
|
|
}
|
|
|
|
#[Test]
|
|
public function supprimerThrowsWhenAlreadyDeleted(): void
|
|
{
|
|
$evaluation = $this->createEvaluation();
|
|
$evaluation->supprimer(new DateTimeImmutable('2026-03-14'));
|
|
|
|
$this->expectException(EvaluationDejaSupprimeeException::class);
|
|
|
|
$evaluation->supprimer(new DateTimeImmutable('2026-03-15'));
|
|
}
|
|
|
|
#[Test]
|
|
public function reconstituteRestoresAllPropertiesWithoutEvents(): void
|
|
{
|
|
$id = EvaluationId::generate();
|
|
$tenantId = TenantId::fromString(self::TENANT_ID);
|
|
$classId = ClassId::fromString(self::CLASS_ID);
|
|
$subjectId = SubjectId::fromString(self::SUBJECT_ID);
|
|
$teacherId = UserId::fromString(self::TEACHER_ID);
|
|
$evaluationDate = new DateTimeImmutable('2026-04-15');
|
|
$createdAt = new DateTimeImmutable('2026-03-12 10:00:00');
|
|
$updatedAt = new DateTimeImmutable('2026-03-13 14:00:00');
|
|
$gradeScale = new GradeScale(20);
|
|
$coefficient = new Coefficient(1.5);
|
|
|
|
$evaluation = Evaluation::reconstitute(
|
|
id: $id,
|
|
tenantId: $tenantId,
|
|
classId: $classId,
|
|
subjectId: $subjectId,
|
|
teacherId: $teacherId,
|
|
title: 'Contrôle chapitre 5',
|
|
description: 'Évaluation sur les fonctions',
|
|
evaluationDate: $evaluationDate,
|
|
gradeScale: $gradeScale,
|
|
coefficient: $coefficient,
|
|
status: EvaluationStatus::PUBLISHED,
|
|
createdAt: $createdAt,
|
|
updatedAt: $updatedAt,
|
|
);
|
|
|
|
self::assertTrue($evaluation->id->equals($id));
|
|
self::assertTrue($evaluation->tenantId->equals($tenantId));
|
|
self::assertTrue($evaluation->classId->equals($classId));
|
|
self::assertTrue($evaluation->subjectId->equals($subjectId));
|
|
self::assertTrue($evaluation->teacherId->equals($teacherId));
|
|
self::assertSame('Contrôle chapitre 5', $evaluation->title);
|
|
self::assertSame('Évaluation sur les fonctions', $evaluation->description);
|
|
self::assertEquals($evaluationDate, $evaluation->evaluationDate);
|
|
self::assertSame(20, $evaluation->gradeScale->maxValue);
|
|
self::assertSame(1.5, $evaluation->coefficient->value);
|
|
self::assertSame(EvaluationStatus::PUBLISHED, $evaluation->status);
|
|
self::assertEquals($createdAt, $evaluation->createdAt);
|
|
self::assertEquals($updatedAt, $evaluation->updatedAt);
|
|
self::assertEmpty($evaluation->pullDomainEvents());
|
|
}
|
|
|
|
private function createEvaluation(): Evaluation
|
|
{
|
|
return Evaluation::creer(
|
|
tenantId: TenantId::fromString(self::TENANT_ID),
|
|
classId: ClassId::fromString(self::CLASS_ID),
|
|
subjectId: SubjectId::fromString(self::SUBJECT_ID),
|
|
teacherId: UserId::fromString(self::TEACHER_ID),
|
|
title: 'Contrôle chapitre 5',
|
|
description: 'Évaluation sur les fonctions',
|
|
evaluationDate: new DateTimeImmutable('2026-04-15'),
|
|
gradeScale: new GradeScale(20),
|
|
coefficient: new Coefficient(1.0),
|
|
now: new DateTimeImmutable('2026-03-12 10:00:00'),
|
|
);
|
|
}
|
|
}
|