Un enseignant qui donne le même travail à plusieurs classes devait jusqu'ici recréer manuellement chaque devoir. La duplication permet de sélectionner les classes cibles, d'ajuster les dates d'échéance par classe, et de créer tous les devoirs en une seule opération atomique (transaction). La validation s'effectue par classe (affectation enseignant, date d'échéance) avec un rapport d'erreurs détaillé. L'infrastructure de warnings est prête pour les règles de timing de la Story 5.3. Le filtrage par classe dans la liste des devoirs passe côté serveur pour rester compatible avec la pagination.
158 lines
6.0 KiB
PHP
158 lines
6.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Unit\Scolarite\Domain\Service;
|
|
|
|
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\Model\Homework\Homework;
|
|
use App\Scolarite\Domain\Service\HomeworkDuplicator;
|
|
use App\Shared\Domain\Tenant\TenantId;
|
|
use DateTimeImmutable;
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
final class HomeworkDuplicatorTest 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';
|
|
private const string TARGET_CLASS_1 = '550e8400-e29b-41d4-a716-446655440021';
|
|
private const string TARGET_CLASS_2 = '550e8400-e29b-41d4-a716-446655440022';
|
|
|
|
private HomeworkDuplicator $duplicator;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->duplicator = new HomeworkDuplicator();
|
|
}
|
|
|
|
#[Test]
|
|
public function itDuplicatesHomeworkForOneTargetClass(): void
|
|
{
|
|
$source = $this->createSourceHomework();
|
|
$now = new DateTimeImmutable('2026-03-14 10:00:00');
|
|
$targetClassId = ClassId::fromString(self::TARGET_CLASS_1);
|
|
$dueDate = new DateTimeImmutable('2026-04-20');
|
|
|
|
$duplicates = $this->duplicator->dupliquer($source, [$targetClassId], [$dueDate], $now);
|
|
|
|
self::assertCount(1, $duplicates);
|
|
$duplicate = $duplicates[0];
|
|
self::assertSame($source->title, $duplicate->title);
|
|
self::assertSame($source->description, $duplicate->description);
|
|
self::assertTrue($duplicate->classId->equals($targetClassId));
|
|
self::assertTrue($duplicate->subjectId->equals($source->subjectId));
|
|
self::assertTrue($duplicate->teacherId->equals($source->teacherId));
|
|
self::assertTrue($duplicate->tenantId->equals($source->tenantId));
|
|
self::assertSame('2026-04-20', $duplicate->dueDate->format('Y-m-d'));
|
|
}
|
|
|
|
#[Test]
|
|
public function itDuplicatesHomeworkForMultipleTargetClasses(): void
|
|
{
|
|
$source = $this->createSourceHomework();
|
|
$now = new DateTimeImmutable('2026-03-14 10:00:00');
|
|
$targetClasses = [
|
|
ClassId::fromString(self::TARGET_CLASS_1),
|
|
ClassId::fromString(self::TARGET_CLASS_2),
|
|
];
|
|
$dueDates = [
|
|
new DateTimeImmutable('2026-04-20'),
|
|
new DateTimeImmutable('2026-04-21'),
|
|
];
|
|
|
|
$duplicates = $this->duplicator->dupliquer($source, $targetClasses, $dueDates, $now);
|
|
|
|
self::assertCount(2, $duplicates);
|
|
self::assertTrue($duplicates[0]->classId->equals($targetClasses[0]));
|
|
self::assertTrue($duplicates[1]->classId->equals($targetClasses[1]));
|
|
self::assertSame('2026-04-20', $duplicates[0]->dueDate->format('Y-m-d'));
|
|
self::assertSame('2026-04-21', $duplicates[1]->dueDate->format('Y-m-d'));
|
|
}
|
|
|
|
#[Test]
|
|
public function itCreatesIndependentCopiesWithUniqueIds(): void
|
|
{
|
|
$source = $this->createSourceHomework();
|
|
$now = new DateTimeImmutable('2026-03-14 10:00:00');
|
|
$targetClasses = [
|
|
ClassId::fromString(self::TARGET_CLASS_1),
|
|
ClassId::fromString(self::TARGET_CLASS_2),
|
|
];
|
|
$dueDates = [
|
|
new DateTimeImmutable('2026-04-20'),
|
|
new DateTimeImmutable('2026-04-20'),
|
|
];
|
|
|
|
$duplicates = $this->duplicator->dupliquer($source, $targetClasses, $dueDates, $now);
|
|
|
|
self::assertFalse($duplicates[0]->id->equals($source->id));
|
|
self::assertFalse($duplicates[1]->id->equals($source->id));
|
|
self::assertFalse($duplicates[0]->id->equals($duplicates[1]->id));
|
|
}
|
|
|
|
#[Test]
|
|
public function itRecordsDevoirCreeEventForEachDuplicate(): void
|
|
{
|
|
$source = $this->createSourceHomework();
|
|
$now = new DateTimeImmutable('2026-03-14 10:00:00');
|
|
$targetClasses = [
|
|
ClassId::fromString(self::TARGET_CLASS_1),
|
|
ClassId::fromString(self::TARGET_CLASS_2),
|
|
];
|
|
$dueDates = [
|
|
new DateTimeImmutable('2026-04-20'),
|
|
new DateTimeImmutable('2026-04-20'),
|
|
];
|
|
|
|
$duplicates = $this->duplicator->dupliquer($source, $targetClasses, $dueDates, $now);
|
|
|
|
foreach ($duplicates as $duplicate) {
|
|
$events = $duplicate->pullDomainEvents();
|
|
self::assertCount(1, $events);
|
|
}
|
|
}
|
|
|
|
#[Test]
|
|
public function itUsesSourceDueDateWhenNoneProvided(): void
|
|
{
|
|
$source = $this->createSourceHomework();
|
|
$now = new DateTimeImmutable('2026-03-14 10:00:00');
|
|
$targetClassId = ClassId::fromString(self::TARGET_CLASS_1);
|
|
|
|
$duplicates = $this->duplicator->dupliquer($source, [$targetClassId], [], $now);
|
|
|
|
self::assertSame($source->dueDate->format('Y-m-d'), $duplicates[0]->dueDate->format('Y-m-d'));
|
|
}
|
|
|
|
#[Test]
|
|
public function itPreservesNullDescription(): void
|
|
{
|
|
$source = $this->createSourceHomework(description: null);
|
|
$now = new DateTimeImmutable('2026-03-14 10:00:00');
|
|
$targetClassId = ClassId::fromString(self::TARGET_CLASS_1);
|
|
|
|
$duplicates = $this->duplicator->dupliquer($source, [$targetClassId], [], $now);
|
|
|
|
self::assertNull($duplicates[0]->description);
|
|
}
|
|
|
|
private function createSourceHomework(?string $description = 'Faire les exercices 1 à 10'): Homework
|
|
{
|
|
return Homework::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: 'Exercices chapitre 5',
|
|
description: $description,
|
|
dueDate: new DateTimeImmutable('2026-04-15'),
|
|
now: new DateTimeImmutable('2026-03-12 10:00:00'),
|
|
);
|
|
}
|
|
}
|