Story 1-7 avait posé les fondations d'audit trail mais laissé en dehors du
périmètre initial les événements notes/évaluations, qui étaient alors non
couverts par les domaines. Avec la clôture des epics notation, ces actions
sensibles (création/modification/suppression d'évaluation, saisie/modification
de note, publication) doivent maintenant être tracées pour répondre aux
exigences RGPD et faciliter la résolution des litiges parent/enseignant.
Les événements de domaine existants ne transportaient pas tous les champs
nécessaires à l'audit (ancien/nouveau titre, description, barème, coefficient,
date, studentId). L'enrichissement de leur payload permet aux handlers d'audit
de journaliser les diffs complets via AuditLogger, sans que les autres
consommateurs (recalcul de moyennes) n'aient besoin de changer leur logique.
Au passage, le test E2E student-grades AC5 ("Nouveau" badge) visait
séquentiellement '.grade-card' puis '.badge-new' : la fenêtre de 3 s avant
markGradesSeen pouvait se refermer entre les deux attentes sur Firefox CI.
Un seul expect combiné '.grade-card .badge-new' élimine cette course.
189 lines
6.4 KiB
PHP
189 lines
6.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Unit\Scolarite\Infrastructure\EventHandler;
|
|
|
|
use App\Scolarite\Domain\Event\NoteModifiee;
|
|
use App\Scolarite\Domain\Event\NoteSaisie;
|
|
use App\Scolarite\Domain\Model\Grade\GradeId;
|
|
use App\Scolarite\Infrastructure\EventHandler\AuditGradeEventsHandler;
|
|
use App\Shared\Application\Port\AuditLogger;
|
|
|
|
use function array_key_exists;
|
|
|
|
use DateTimeImmutable;
|
|
use PHPUnit\Framework\MockObject\MockObject;
|
|
use PHPUnit\Framework\TestCase;
|
|
use Ramsey\Uuid\Uuid;
|
|
|
|
final class AuditGradeEventsHandlerTest extends TestCase
|
|
{
|
|
private AuditLogger&MockObject $auditLogger;
|
|
private AuditGradeEventsHandler $handler;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->auditLogger = $this->createMock(AuditLogger::class);
|
|
$this->handler = new AuditGradeEventsHandler($this->auditLogger);
|
|
}
|
|
|
|
public function testHandleNoteSaisieLogsAuditEntryWithCreationPayload(): void
|
|
{
|
|
$gradeId = GradeId::generate();
|
|
$evaluationId = Uuid::uuid4()->toString();
|
|
$studentId = Uuid::uuid4()->toString();
|
|
$createdBy = Uuid::uuid4()->toString();
|
|
|
|
$event = new NoteSaisie(
|
|
gradeId: $gradeId,
|
|
evaluationId: $evaluationId,
|
|
studentId: $studentId,
|
|
value: 15.5,
|
|
status: 'draft',
|
|
createdBy: $createdBy,
|
|
occurredOn: new DateTimeImmutable(),
|
|
);
|
|
|
|
$this->auditLogger->expects($this->once())
|
|
->method('logDataChange')
|
|
->with(
|
|
$this->equalTo('Grade'),
|
|
$this->callback(static fn ($uuid) => $uuid->toString() === $gradeId->value->toString()),
|
|
$this->equalTo('NoteSaisie'),
|
|
$this->equalTo([]),
|
|
$this->callback(static fn ($new) => $new['evaluation_id'] === $evaluationId
|
|
&& $new['student_id'] === $studentId
|
|
&& $new['value'] === 15.5
|
|
&& $new['status'] === 'draft'
|
|
&& $new['created_by'] === $createdBy
|
|
),
|
|
);
|
|
|
|
$this->handler->handleNoteSaisie($event);
|
|
}
|
|
|
|
public function testHandleNoteSaisieSupportsNullValue(): void
|
|
{
|
|
$event = new NoteSaisie(
|
|
gradeId: GradeId::generate(),
|
|
evaluationId: Uuid::uuid4()->toString(),
|
|
studentId: Uuid::uuid4()->toString(),
|
|
value: null,
|
|
status: 'absent',
|
|
createdBy: Uuid::uuid4()->toString(),
|
|
occurredOn: new DateTimeImmutable(),
|
|
);
|
|
|
|
$this->auditLogger->expects($this->once())
|
|
->method('logDataChange')
|
|
->with(
|
|
$this->equalTo('Grade'),
|
|
$this->anything(),
|
|
$this->equalTo('NoteSaisie'),
|
|
$this->equalTo([]),
|
|
$this->callback(static fn ($new) => $new['value'] === null
|
|
&& $new['status'] === 'absent'
|
|
),
|
|
);
|
|
|
|
$this->handler->handleNoteSaisie($event);
|
|
}
|
|
|
|
public function testHandleNoteModifieeLogsAuditEntryWithDiff(): void
|
|
{
|
|
$gradeId = GradeId::generate();
|
|
$evaluationId = Uuid::uuid4()->toString();
|
|
$studentId = Uuid::uuid4()->toString();
|
|
$modifiedBy = Uuid::uuid4()->toString();
|
|
|
|
$event = new NoteModifiee(
|
|
gradeId: $gradeId,
|
|
evaluationId: $evaluationId,
|
|
studentId: $studentId,
|
|
oldValue: 12.0,
|
|
newValue: 14.5,
|
|
oldStatus: 'draft',
|
|
newStatus: 'published',
|
|
modifiedBy: $modifiedBy,
|
|
occurredOn: new DateTimeImmutable(),
|
|
);
|
|
|
|
$this->auditLogger->expects($this->once())
|
|
->method('logDataChange')
|
|
->with(
|
|
$this->equalTo('Grade'),
|
|
$this->callback(static fn ($uuid) => $uuid->toString() === $gradeId->value->toString()),
|
|
$this->equalTo('NoteModifiee'),
|
|
$this->callback(static fn ($old) => $old['value'] === 12.0
|
|
&& $old['status'] === 'draft'
|
|
),
|
|
$this->callback(static fn ($new) => $new['value'] === 14.5
|
|
&& $new['status'] === 'published'
|
|
&& $new['modified_by'] === $modifiedBy
|
|
&& $new['evaluation_id'] === $evaluationId
|
|
&& $new['student_id'] === $studentId
|
|
),
|
|
);
|
|
|
|
$this->handler->handleNoteModifiee($event);
|
|
}
|
|
|
|
public function testHandleNoteModifieeSupportsNullValues(): void
|
|
{
|
|
$event = new NoteModifiee(
|
|
gradeId: GradeId::generate(),
|
|
evaluationId: Uuid::uuid4()->toString(),
|
|
studentId: Uuid::uuid4()->toString(),
|
|
oldValue: 10.0,
|
|
newValue: null,
|
|
oldStatus: 'published',
|
|
newStatus: 'absent',
|
|
modifiedBy: Uuid::uuid4()->toString(),
|
|
occurredOn: new DateTimeImmutable(),
|
|
);
|
|
|
|
$this->auditLogger->expects($this->once())
|
|
->method('logDataChange')
|
|
->with(
|
|
$this->equalTo('Grade'),
|
|
$this->anything(),
|
|
$this->equalTo('NoteModifiee'),
|
|
$this->callback(static fn ($old) => $old['value'] === 10.0),
|
|
$this->callback(static fn ($new) => $new['value'] === null
|
|
&& $new['status'] === 'absent'
|
|
),
|
|
);
|
|
|
|
$this->handler->handleNoteModifiee($event);
|
|
}
|
|
|
|
public function testHandleNoteSaisieSupportsZeroValue(): void
|
|
{
|
|
$event = new NoteSaisie(
|
|
gradeId: GradeId::generate(),
|
|
evaluationId: Uuid::uuid4()->toString(),
|
|
studentId: Uuid::uuid4()->toString(),
|
|
value: 0.0,
|
|
status: 'published',
|
|
createdBy: Uuid::uuid4()->toString(),
|
|
occurredOn: new DateTimeImmutable(),
|
|
);
|
|
|
|
$this->auditLogger->expects($this->once())
|
|
->method('logDataChange')
|
|
->with(
|
|
$this->equalTo('Grade'),
|
|
$this->anything(),
|
|
$this->equalTo('NoteSaisie'),
|
|
$this->equalTo([]),
|
|
$this->callback(static fn ($new) => array_key_exists('value', $new)
|
|
&& $new['value'] === 0.0
|
|
&& $new['status'] === 'published'
|
|
),
|
|
);
|
|
|
|
$this->handler->handleNoteSaisie($event);
|
|
}
|
|
}
|