feat: Permettre à l'élève de rendre un devoir avec réponse texte et pièces jointes
L'élève peut désormais répondre à un devoir via un éditeur WYSIWYG, joindre des fichiers (PDF, JPEG, PNG, DOCX), sauvegarder un brouillon et soumettre définitivement son rendu. Le système détecte automatiquement les soumissions en retard par rapport à la date d'échéance. Côté enseignant, une page dédiée affiche la liste complète des élèves avec leur statut (soumis, en retard, brouillon, non rendu), le détail de chaque rendu avec ses pièces jointes téléchargeables, et les statistiques de rendus par classe.
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Scolarite\Application\Command\SaveDraftSubmission;
|
||||
|
||||
use App\Administration\Domain\Model\SchoolClass\ClassId;
|
||||
use App\Administration\Domain\Model\User\UserId;
|
||||
use App\Scolarite\Application\Command\SaveDraftSubmission\SaveDraftSubmissionCommand;
|
||||
use App\Scolarite\Application\Command\SaveDraftSubmission\SaveDraftSubmissionHandler;
|
||||
use App\Scolarite\Application\Port\HtmlSanitizer;
|
||||
use App\Scolarite\Application\Port\StudentClassReader;
|
||||
use App\Scolarite\Domain\Exception\EleveNonAffecteAuDevoirException;
|
||||
use App\Scolarite\Domain\Exception\RenduDejaSoumisException;
|
||||
use App\Scolarite\Domain\Model\Homework\Homework;
|
||||
use App\Scolarite\Domain\Model\Homework\HomeworkId;
|
||||
use App\Scolarite\Domain\Model\HomeworkSubmission\SubmissionStatus;
|
||||
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryHomeworkRepository;
|
||||
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryHomeworkSubmissionRepository;
|
||||
use App\Shared\Domain\Clock;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class SaveDraftSubmissionHandlerTest 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 STUDENT_ID = '550e8400-e29b-41d4-a716-446655440060';
|
||||
|
||||
private InMemoryHomeworkRepository $homeworkRepository;
|
||||
private InMemoryHomeworkSubmissionRepository $submissionRepository;
|
||||
private Clock $clock;
|
||||
private Homework $homework;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->homeworkRepository = new InMemoryHomeworkRepository();
|
||||
$this->submissionRepository = new InMemoryHomeworkSubmissionRepository();
|
||||
$this->clock = new class implements Clock {
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
return new DateTimeImmutable('2026-03-24 10:00:00');
|
||||
}
|
||||
};
|
||||
|
||||
$this->homework = Homework::creer(
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
classId: ClassId::fromString(self::CLASS_ID),
|
||||
subjectId: \App\Administration\Domain\Model\Subject\SubjectId::fromString(self::SUBJECT_ID),
|
||||
teacherId: UserId::fromString(self::TEACHER_ID),
|
||||
title: 'Exercices chapitre 5',
|
||||
description: null,
|
||||
dueDate: new DateTimeImmutable('2026-04-15'),
|
||||
now: new DateTimeImmutable('2026-03-12 10:00:00'),
|
||||
);
|
||||
$this->homeworkRepository->save($this->homework);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itCreatesNewDraftSubmission(): void
|
||||
{
|
||||
$handler = $this->createHandler();
|
||||
$command = $this->createCommand();
|
||||
|
||||
$submission = $handler($command);
|
||||
|
||||
self::assertSame(SubmissionStatus::DRAFT, $submission->status);
|
||||
self::assertSame('<p>Ma réponse</p>', $submission->responseHtml);
|
||||
self::assertNull($submission->submittedAt);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itPersistsDraftInRepository(): void
|
||||
{
|
||||
$handler = $this->createHandler();
|
||||
$command = $this->createCommand();
|
||||
|
||||
$created = $handler($command);
|
||||
|
||||
$found = $this->submissionRepository->findByHomeworkAndStudent(
|
||||
HomeworkId::fromString((string) $this->homework->id),
|
||||
UserId::fromString(self::STUDENT_ID),
|
||||
TenantId::fromString(self::TENANT_ID),
|
||||
);
|
||||
|
||||
self::assertNotNull($found);
|
||||
self::assertTrue($found->id->equals($created->id));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itUpdatesExistingDraft(): void
|
||||
{
|
||||
$handler = $this->createHandler();
|
||||
|
||||
$handler($this->createCommand(responseHtml: '<p>Première version</p>'));
|
||||
$updated = $handler($this->createCommand(responseHtml: '<p>Version modifiée</p>'));
|
||||
|
||||
self::assertSame('<p>Version modifiée</p>', $updated->responseHtml);
|
||||
self::assertSame(SubmissionStatus::DRAFT, $updated->status);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itThrowsWhenStudentNotInClass(): void
|
||||
{
|
||||
$handler = $this->createHandler(studentClassId: null);
|
||||
|
||||
$this->expectException(EleveNonAffecteAuDevoirException::class);
|
||||
|
||||
$handler($this->createCommand());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itThrowsWhenStudentInDifferentClass(): void
|
||||
{
|
||||
$handler = $this->createHandler(studentClassId: '550e8400-e29b-41d4-a716-446655440099');
|
||||
|
||||
$this->expectException(EleveNonAffecteAuDevoirException::class);
|
||||
|
||||
$handler($this->createCommand());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itThrowsWhenUpdatingSubmittedSubmission(): void
|
||||
{
|
||||
$handler = $this->createHandler();
|
||||
|
||||
$handler($this->createCommand());
|
||||
|
||||
// Soumettre le rendu
|
||||
$submission = $this->submissionRepository->findByHomeworkAndStudent(
|
||||
HomeworkId::fromString((string) $this->homework->id),
|
||||
UserId::fromString(self::STUDENT_ID),
|
||||
TenantId::fromString(self::TENANT_ID),
|
||||
);
|
||||
self::assertNotNull($submission);
|
||||
$submission->soumettre(
|
||||
dueDate: new DateTimeImmutable('2026-04-15'),
|
||||
now: new DateTimeImmutable('2026-03-24 11:00:00'),
|
||||
);
|
||||
$this->submissionRepository->save($submission);
|
||||
|
||||
$this->expectException(RenduDejaSoumisException::class);
|
||||
|
||||
$handler($this->createCommand(responseHtml: '<p>Tentative de modification</p>'));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itSanitizesHtmlResponse(): void
|
||||
{
|
||||
$sanitizer = new class implements HtmlSanitizer {
|
||||
public function sanitize(string $html): string
|
||||
{
|
||||
return strip_tags($html, '<p><strong><em>');
|
||||
}
|
||||
};
|
||||
|
||||
$handler = $this->createHandler(htmlSanitizer: $sanitizer);
|
||||
$command = $this->createCommand(responseHtml: '<p>Texte</p><script>alert("xss")</script>');
|
||||
|
||||
$submission = $handler($command);
|
||||
|
||||
self::assertSame('<p>Texte</p>alert("xss")', $submission->responseHtml);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itAllowsNullResponse(): void
|
||||
{
|
||||
$handler = $this->createHandler();
|
||||
$command = $this->createCommand(responseHtml: null);
|
||||
|
||||
$submission = $handler($command);
|
||||
|
||||
self::assertNull($submission->responseHtml);
|
||||
}
|
||||
|
||||
private function createHandler(
|
||||
?string $studentClassId = self::CLASS_ID,
|
||||
?HtmlSanitizer $htmlSanitizer = null,
|
||||
): SaveDraftSubmissionHandler {
|
||||
$studentClassReader = new class($studentClassId) implements StudentClassReader {
|
||||
public function __construct(private readonly ?string $classId)
|
||||
{
|
||||
}
|
||||
|
||||
public function currentClassId(string $studentId, TenantId $tenantId): ?string
|
||||
{
|
||||
return $this->classId;
|
||||
}
|
||||
};
|
||||
|
||||
$sanitizer = $htmlSanitizer ?? new class implements HtmlSanitizer {
|
||||
public function sanitize(string $html): string
|
||||
{
|
||||
return $html;
|
||||
}
|
||||
};
|
||||
|
||||
return new SaveDraftSubmissionHandler(
|
||||
$this->homeworkRepository,
|
||||
$this->submissionRepository,
|
||||
$studentClassReader,
|
||||
$sanitizer,
|
||||
$this->clock,
|
||||
);
|
||||
}
|
||||
|
||||
private function createCommand(?string $responseHtml = '<p>Ma réponse</p>'): SaveDraftSubmissionCommand
|
||||
{
|
||||
return new SaveDraftSubmissionCommand(
|
||||
tenantId: self::TENANT_ID,
|
||||
homeworkId: (string) $this->homework->id,
|
||||
studentId: self::STUDENT_ID,
|
||||
responseHtml: $responseHtml,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Scolarite\Application\Command\SubmitHomework;
|
||||
|
||||
use App\Administration\Domain\Model\SchoolClass\ClassId;
|
||||
use App\Administration\Domain\Model\Subject\SubjectId;
|
||||
use App\Administration\Domain\Model\User\UserId;
|
||||
use App\Scolarite\Application\Command\SubmitHomework\SubmitHomeworkCommand;
|
||||
use App\Scolarite\Application\Command\SubmitHomework\SubmitHomeworkHandler;
|
||||
use App\Scolarite\Application\Port\StudentClassReader;
|
||||
use App\Scolarite\Domain\Exception\EleveNonAffecteAuDevoirException;
|
||||
use App\Scolarite\Domain\Exception\RenduDejaSoumisException;
|
||||
use App\Scolarite\Domain\Exception\RenduNonTrouveException;
|
||||
use App\Scolarite\Domain\Model\Homework\Homework;
|
||||
use App\Scolarite\Domain\Model\HomeworkSubmission\HomeworkSubmission;
|
||||
use App\Scolarite\Domain\Model\HomeworkSubmission\SubmissionStatus;
|
||||
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryHomeworkRepository;
|
||||
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryHomeworkSubmissionRepository;
|
||||
use App\Shared\Domain\Clock;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class SubmitHomeworkHandlerTest 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 STUDENT_ID = '550e8400-e29b-41d4-a716-446655440060';
|
||||
|
||||
private InMemoryHomeworkRepository $homeworkRepository;
|
||||
private InMemoryHomeworkSubmissionRepository $submissionRepository;
|
||||
private Homework $homework;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->homeworkRepository = new InMemoryHomeworkRepository();
|
||||
$this->submissionRepository = new InMemoryHomeworkSubmissionRepository();
|
||||
|
||||
$this->homework = 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: null,
|
||||
dueDate: new DateTimeImmutable('2026-04-15'),
|
||||
now: new DateTimeImmutable('2026-03-12 10:00:00'),
|
||||
);
|
||||
$this->homeworkRepository->save($this->homework);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itSubmitsOnTimeSuccessfully(): void
|
||||
{
|
||||
$this->createDraft();
|
||||
|
||||
$handler = $this->createHandler(now: '2026-04-10 14:00:00');
|
||||
$command = $this->createCommand();
|
||||
|
||||
$submission = $handler($command);
|
||||
|
||||
self::assertSame(SubmissionStatus::SUBMITTED, $submission->status);
|
||||
self::assertNotNull($submission->submittedAt);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itSubmitsLateWhenAfterDueDate(): void
|
||||
{
|
||||
$this->createDraft();
|
||||
|
||||
$handler = $this->createHandler(now: '2026-04-20 14:00:00');
|
||||
$command = $this->createCommand();
|
||||
|
||||
$submission = $handler($command);
|
||||
|
||||
self::assertSame(SubmissionStatus::LATE, $submission->status);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itThrowsWhenNoDraftExists(): void
|
||||
{
|
||||
$handler = $this->createHandler(now: '2026-04-10 14:00:00');
|
||||
|
||||
$this->expectException(RenduNonTrouveException::class);
|
||||
|
||||
$handler($this->createCommand());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itThrowsWhenAlreadySubmitted(): void
|
||||
{
|
||||
$this->createDraft();
|
||||
|
||||
$handler = $this->createHandler(now: '2026-04-10 14:00:00');
|
||||
$handler($this->createCommand());
|
||||
|
||||
$this->expectException(RenduDejaSoumisException::class);
|
||||
|
||||
$handler($this->createCommand());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itThrowsWhenStudentNotInClass(): void
|
||||
{
|
||||
$this->createDraft();
|
||||
|
||||
$handler = $this->createHandler(now: '2026-04-10 14:00:00', studentClassId: null);
|
||||
|
||||
$this->expectException(EleveNonAffecteAuDevoirException::class);
|
||||
|
||||
$handler($this->createCommand());
|
||||
}
|
||||
|
||||
private function createDraft(): void
|
||||
{
|
||||
$submission = HomeworkSubmission::creerBrouillon(
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
homeworkId: $this->homework->id,
|
||||
studentId: UserId::fromString(self::STUDENT_ID),
|
||||
responseHtml: '<p>Ma réponse</p>',
|
||||
now: new DateTimeImmutable('2026-03-24 10:00:00'),
|
||||
);
|
||||
$this->submissionRepository->save($submission);
|
||||
}
|
||||
|
||||
private function createHandler(string $now, ?string $studentClassId = self::CLASS_ID): SubmitHomeworkHandler
|
||||
{
|
||||
$clock = new class($now) implements Clock {
|
||||
public function __construct(private readonly string $time)
|
||||
{
|
||||
}
|
||||
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
return new DateTimeImmutable($this->time);
|
||||
}
|
||||
};
|
||||
|
||||
$studentClassReader = new class($studentClassId) implements StudentClassReader {
|
||||
public function __construct(private readonly ?string $classId)
|
||||
{
|
||||
}
|
||||
|
||||
public function currentClassId(string $studentId, TenantId $tenantId): ?string
|
||||
{
|
||||
return $this->classId;
|
||||
}
|
||||
};
|
||||
|
||||
return new SubmitHomeworkHandler(
|
||||
$this->homeworkRepository,
|
||||
$this->submissionRepository,
|
||||
$studentClassReader,
|
||||
$clock,
|
||||
);
|
||||
}
|
||||
|
||||
private function createCommand(): SubmitHomeworkCommand
|
||||
{
|
||||
return new SubmitHomeworkCommand(
|
||||
tenantId: self::TENANT_ID,
|
||||
homeworkId: (string) $this->homework->id,
|
||||
studentId: self::STUDENT_ID,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Scolarite\Application\Command\UploadSubmissionAttachment;
|
||||
|
||||
use App\Administration\Domain\Model\User\UserId;
|
||||
use App\Scolarite\Application\Command\UploadSubmissionAttachment\UploadSubmissionAttachmentCommand;
|
||||
use App\Scolarite\Application\Command\UploadSubmissionAttachment\UploadSubmissionAttachmentHandler;
|
||||
use App\Scolarite\Application\Port\FileStorage;
|
||||
use App\Scolarite\Domain\Exception\RenduDejaSoumisException;
|
||||
use App\Scolarite\Domain\Model\Homework\HomeworkId;
|
||||
use App\Scolarite\Domain\Model\HomeworkSubmission\HomeworkSubmission;
|
||||
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryHomeworkSubmissionRepository;
|
||||
use App\Shared\Domain\Clock;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class UploadSubmissionAttachmentHandlerTest extends TestCase
|
||||
{
|
||||
private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
|
||||
private const string HOMEWORK_ID = '550e8400-e29b-41d4-a716-446655440050';
|
||||
private const string STUDENT_ID = '550e8400-e29b-41d4-a716-446655440060';
|
||||
|
||||
private InMemoryHomeworkSubmissionRepository $submissionRepository;
|
||||
private string $tempFile;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->submissionRepository = new InMemoryHomeworkSubmissionRepository();
|
||||
$this->tempFile = tempnam(sys_get_temp_dir(), 'e2e_upload_');
|
||||
file_put_contents($this->tempFile, 'fake PDF content');
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
if (file_exists($this->tempFile)) {
|
||||
unlink($this->tempFile);
|
||||
}
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itUploadsAttachmentForDraftSubmission(): void
|
||||
{
|
||||
$submission = $this->createDraftSubmission();
|
||||
$this->submissionRepository->save($submission);
|
||||
|
||||
$handler = $this->createHandler();
|
||||
$command = new UploadSubmissionAttachmentCommand(
|
||||
tenantId: self::TENANT_ID,
|
||||
submissionId: (string) $submission->id,
|
||||
filename: 'devoir.pdf',
|
||||
mimeType: 'application/pdf',
|
||||
fileSize: 1024,
|
||||
tempFilePath: $this->tempFile,
|
||||
);
|
||||
|
||||
$attachment = $handler($command);
|
||||
|
||||
self::assertSame('devoir.pdf', $attachment->filename);
|
||||
self::assertSame('application/pdf', $attachment->mimeType);
|
||||
self::assertSame(1024, $attachment->fileSize);
|
||||
self::assertStringContainsString('submissions/', $attachment->filePath);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itRejectsUploadOnSubmittedSubmission(): void
|
||||
{
|
||||
$submission = $this->createDraftSubmission();
|
||||
$submission->soumettre(
|
||||
dueDate: new DateTimeImmutable('2026-04-15'),
|
||||
now: new DateTimeImmutable('2026-03-24 11:00:00'),
|
||||
);
|
||||
$this->submissionRepository->save($submission);
|
||||
|
||||
$handler = $this->createHandler();
|
||||
$command = new UploadSubmissionAttachmentCommand(
|
||||
tenantId: self::TENANT_ID,
|
||||
submissionId: (string) $submission->id,
|
||||
filename: 'devoir.pdf',
|
||||
mimeType: 'application/pdf',
|
||||
fileSize: 1024,
|
||||
tempFilePath: $this->tempFile,
|
||||
);
|
||||
|
||||
$this->expectException(RenduDejaSoumisException::class);
|
||||
|
||||
$handler($command);
|
||||
}
|
||||
|
||||
private function createDraftSubmission(): HomeworkSubmission
|
||||
{
|
||||
return HomeworkSubmission::creerBrouillon(
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
homeworkId: HomeworkId::fromString(self::HOMEWORK_ID),
|
||||
studentId: UserId::fromString(self::STUDENT_ID),
|
||||
responseHtml: '<p>Ma réponse</p>',
|
||||
now: new DateTimeImmutable('2026-03-24 10:00:00'),
|
||||
);
|
||||
}
|
||||
|
||||
private function createHandler(): UploadSubmissionAttachmentHandler
|
||||
{
|
||||
$fileStorage = new class implements FileStorage {
|
||||
public function upload(string $path, mixed $content, string $mimeType): string
|
||||
{
|
||||
return $path;
|
||||
}
|
||||
|
||||
public function delete(string $path): void
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
$clock = new class implements Clock {
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
return new DateTimeImmutable('2026-03-24 10:00:00');
|
||||
}
|
||||
};
|
||||
|
||||
return new UploadSubmissionAttachmentHandler(
|
||||
$this->submissionRepository,
|
||||
$fileStorage,
|
||||
$clock,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,10 @@ use App\Scolarite\Application\Query\GetStudentHomework\GetStudentHomeworkQuery;
|
||||
use App\Scolarite\Domain\Model\Homework\Homework;
|
||||
use App\Scolarite\Domain\Model\Homework\HomeworkAttachment;
|
||||
use App\Scolarite\Domain\Model\Homework\HomeworkAttachmentId;
|
||||
use App\Scolarite\Domain\Model\HomeworkSubmission\HomeworkSubmission;
|
||||
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryHomeworkAttachmentRepository;
|
||||
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryHomeworkRepository;
|
||||
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryHomeworkSubmissionRepository;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
@@ -32,11 +34,13 @@ final class GetStudentHomeworkHandlerTest extends TestCase
|
||||
|
||||
private InMemoryHomeworkRepository $homeworkRepository;
|
||||
private InMemoryHomeworkAttachmentRepository $attachmentRepository;
|
||||
private InMemoryHomeworkSubmissionRepository $submissionRepository;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->homeworkRepository = new InMemoryHomeworkRepository();
|
||||
$this->attachmentRepository = new InMemoryHomeworkAttachmentRepository();
|
||||
$this->submissionRepository = new InMemoryHomeworkSubmissionRepository();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
@@ -190,6 +194,73 @@ final class GetStudentHomeworkHandlerTest extends TestCase
|
||||
self::assertFalse($result[0]->hasAttachments);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itReturnsNullSubmissionStatusWhenNoSubmission(): void
|
||||
{
|
||||
$handler = $this->createHandler();
|
||||
$this->givenHomework('Devoir sans rendu', '2026-04-15');
|
||||
|
||||
$result = $handler(new GetStudentHomeworkQuery(
|
||||
studentId: self::STUDENT_ID,
|
||||
tenantId: self::TENANT_ID,
|
||||
));
|
||||
|
||||
self::assertCount(1, $result);
|
||||
self::assertNull($result[0]->submissionStatus);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itReturnsSubmissionStatusWhenDraftExists(): void
|
||||
{
|
||||
$handler = $this->createHandler();
|
||||
$homework = $this->givenHomework('Devoir avec brouillon', '2026-04-15');
|
||||
|
||||
$submission = HomeworkSubmission::creerBrouillon(
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
homeworkId: $homework->id,
|
||||
studentId: UserId::fromString(self::STUDENT_ID),
|
||||
responseHtml: '<p>Brouillon</p>',
|
||||
now: new DateTimeImmutable('2026-03-24 10:00:00'),
|
||||
);
|
||||
$this->submissionRepository->save($submission);
|
||||
|
||||
$result = $handler(new GetStudentHomeworkQuery(
|
||||
studentId: self::STUDENT_ID,
|
||||
tenantId: self::TENANT_ID,
|
||||
));
|
||||
|
||||
self::assertCount(1, $result);
|
||||
self::assertSame('draft', $result[0]->submissionStatus);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itReturnsSubmittedStatusAfterSubmission(): void
|
||||
{
|
||||
$handler = $this->createHandler();
|
||||
$homework = $this->givenHomework('Devoir soumis', '2026-04-15');
|
||||
|
||||
$submission = HomeworkSubmission::creerBrouillon(
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
homeworkId: $homework->id,
|
||||
studentId: UserId::fromString(self::STUDENT_ID),
|
||||
responseHtml: '<p>Réponse</p>',
|
||||
now: new DateTimeImmutable('2026-03-24 10:00:00'),
|
||||
);
|
||||
$submission->soumettre(
|
||||
dueDate: new DateTimeImmutable('2026-04-15'),
|
||||
now: new DateTimeImmutable('2026-03-24 11:00:00'),
|
||||
);
|
||||
$this->submissionRepository->save($submission);
|
||||
|
||||
$result = $handler(new GetStudentHomeworkQuery(
|
||||
studentId: self::STUDENT_ID,
|
||||
tenantId: self::TENANT_ID,
|
||||
));
|
||||
|
||||
self::assertCount(1, $result);
|
||||
self::assertSame('submitted', $result[0]->submissionStatus);
|
||||
}
|
||||
|
||||
private function createHandler(?string $classId = self::CLASS_ID): GetStudentHomeworkHandler
|
||||
{
|
||||
$studentClassReader = new class($classId) implements StudentClassReader {
|
||||
@@ -231,6 +302,7 @@ final class GetStudentHomeworkHandlerTest extends TestCase
|
||||
$studentClassReader,
|
||||
$this->homeworkRepository,
|
||||
$this->attachmentRepository,
|
||||
$this->submissionRepository,
|
||||
$displayReader,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user