assignmentRepository = new InMemoryTeacherAssignmentRepository(); $this->userRepository = new InMemoryUserRepository(); $this->classRepository = new InMemoryClassRepository(); $this->subjectRepository = new InMemorySubjectRepository(); $this->clock = new class implements Clock { public function now(): DateTimeImmutable { return new DateTimeImmutable('2026-02-12 10:00:00'); } }; $this->seedTestData(); } #[Test] public function itCreatesAssignmentSuccessfully(): void { $handler = $this->createHandler(); $command = $this->createCommand(); $assignment = $handler($command); self::assertNotEmpty((string) $assignment->id); self::assertSame(AssignmentStatus::ACTIVE, $assignment->status); self::assertTrue($assignment->estActive()); self::assertNull($assignment->endDate); } #[Test] public function itPersistsAssignmentInRepository(): void { $handler = $this->createHandler(); $command = $this->createCommand(); $created = $handler($command); $assignment = $this->assignmentRepository->get( TeacherAssignmentId::fromString((string) $created->id), TenantId::fromString(self::TENANT_ID), ); self::assertSame(AssignmentStatus::ACTIVE, $assignment->status); } #[Test] public function itThrowsExceptionWhenDuplicateAssignment(): void { $handler = $this->createHandler(); $command = $this->createCommand(); $handler($command); $this->expectException(AffectationDejaExistanteException::class); $handler($command); } #[Test] public function itAllowsSameTeacherDifferentSubject(): void { $secondSubjectId = '550e8400-e29b-41d4-a716-446655440031'; $subject2 = Subject::creer( tenantId: TenantId::fromString(self::TENANT_ID), schoolId: SchoolId::fromString(self::SCHOOL_ID), name: new SubjectName('Français'), code: new SubjectCode('FR'), color: null, createdAt: new DateTimeImmutable('2026-01-15'), ); // Reconstitute with known ID for test predictability $subject2 = Subject::reconstitute( id: \App\Administration\Domain\Model\Subject\SubjectId::fromString($secondSubjectId), tenantId: TenantId::fromString(self::TENANT_ID), schoolId: SchoolId::fromString(self::SCHOOL_ID), name: new SubjectName('Français'), code: new SubjectCode('FR'), color: null, status: \App\Administration\Domain\Model\Subject\SubjectStatus::ACTIVE, description: null, createdAt: new DateTimeImmutable('2026-01-15'), updatedAt: new DateTimeImmutable('2026-01-15'), deletedAt: null, ); $this->subjectRepository->save($subject2); $handler = $this->createHandler(); $assignment1 = $handler($this->createCommand()); $assignment2 = $handler($this->createCommand(subjectId: $secondSubjectId)); self::assertFalse($assignment1->id->equals($assignment2->id)); } #[Test] public function itAllowsDifferentTeacherSameClassSubject(): void { $secondTeacherId = '550e8400-e29b-41d4-a716-446655440011'; $teacher2 = User::creer( email: new Email('teacher2@example.com'), role: Role::PROF, tenantId: TenantId::fromString(self::TENANT_ID), schoolName: 'École Test', dateNaissance: null, createdAt: new DateTimeImmutable('2026-01-15'), ); // Reconstitute with known ID $teacher2 = User::reconstitute( id: \App\Administration\Domain\Model\User\UserId::fromString($secondTeacherId), email: new Email('teacher2@example.com'), roles: [Role::PROF], tenantId: TenantId::fromString(self::TENANT_ID), schoolName: 'École Test', statut: \App\Administration\Domain\Model\User\StatutCompte::EN_ATTENTE, dateNaissance: null, createdAt: new DateTimeImmutable('2026-01-15'), hashedPassword: null, activatedAt: null, consentementParental: null, ); $this->userRepository->save($teacher2); $handler = $this->createHandler(); $assignment1 = $handler($this->createCommand()); $assignment2 = $handler($this->createCommand(teacherId: $secondTeacherId)); self::assertFalse($assignment1->id->equals($assignment2->id)); } #[Test] public function itThrowsWhenTeacherDoesNotExist(): void { $handler = $this->createHandler(); $this->expectException(UserNotFoundException::class); $handler($this->createCommand(teacherId: '550e8400-e29b-41d4-a716-446655440099')); } #[Test] public function itThrowsWhenClassDoesNotExist(): void { $handler = $this->createHandler(); $this->expectException(ClasseNotFoundException::class); $handler($this->createCommand(classId: '550e8400-e29b-41d4-a716-446655440099')); } #[Test] public function itThrowsWhenSubjectDoesNotExist(): void { $handler = $this->createHandler(); $this->expectException(SubjectNotFoundException::class); $handler($this->createCommand(subjectId: '550e8400-e29b-41d4-a716-446655440099')); } private function seedTestData(): void { $tenantId = TenantId::fromString(self::TENANT_ID); // Create teacher $teacher = User::reconstitute( id: \App\Administration\Domain\Model\User\UserId::fromString(self::TEACHER_ID), email: new Email('teacher@example.com'), roles: [Role::PROF], tenantId: $tenantId, schoolName: 'École Test', statut: \App\Administration\Domain\Model\User\StatutCompte::EN_ATTENTE, dateNaissance: null, createdAt: new DateTimeImmutable('2026-01-15'), hashedPassword: null, activatedAt: null, consentementParental: null, ); $this->userRepository->save($teacher); // Create class $class = SchoolClass::reconstitute( id: \App\Administration\Domain\Model\SchoolClass\ClassId::fromString(self::CLASS_ID), tenantId: $tenantId, 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: \App\Administration\Domain\Model\SchoolClass\ClassStatus::ACTIVE, description: null, createdAt: new DateTimeImmutable('2026-01-15'), updatedAt: new DateTimeImmutable('2026-01-15'), deletedAt: null, ); $this->classRepository->save($class); // Create subject $subject = Subject::reconstitute( id: \App\Administration\Domain\Model\Subject\SubjectId::fromString(self::SUBJECT_ID), tenantId: $tenantId, schoolId: SchoolId::fromString(self::SCHOOL_ID), name: new SubjectName('Mathématiques'), code: new SubjectCode('MATH'), color: null, status: \App\Administration\Domain\Model\Subject\SubjectStatus::ACTIVE, description: null, createdAt: new DateTimeImmutable('2026-01-15'), updatedAt: new DateTimeImmutable('2026-01-15'), deletedAt: null, ); $this->subjectRepository->save($subject); } private function createHandler(): AssignTeacherHandler { return new AssignTeacherHandler( $this->assignmentRepository, $this->userRepository, $this->classRepository, $this->subjectRepository, $this->clock, ); } private function createCommand( ?string $teacherId = null, ?string $classId = null, ?string $subjectId = null, ): AssignTeacherCommand { return new AssignTeacherCommand( tenantId: self::TENANT_ID, teacherId: $teacherId ?? self::TEACHER_ID, classId: $classId ?? self::CLASS_ID, subjectId: $subjectId ?? self::SUBJECT_ID, academicYearId: self::ACADEMIC_YEAR_ID, ); } }