subjectRepository = new InMemorySubjectRepository(); $this->clock = new class implements Clock { public function now(): DateTimeImmutable { return new DateTimeImmutable('2026-01-31 10:00:00'); } }; } #[Test] public function itCreatesSubjectSuccessfully(): void { $handler = new CreateSubjectHandler($this->subjectRepository, $this->clock); $command = new CreateSubjectCommand( tenantId: self::TENANT_ID, schoolId: self::SCHOOL_ID, name: 'Mathématiques', code: 'MATH', color: '#3B82F6', ); $subject = $handler($command); self::assertNotEmpty((string) $subject->id); self::assertSame('Mathématiques', (string) $subject->name); self::assertSame('MATH', (string) $subject->code); self::assertNotNull($subject->color); self::assertSame('#3B82F6', (string) $subject->color); self::assertSame(SubjectStatus::ACTIVE, $subject->status); } #[Test] public function itCreatesSubjectWithNullColor(): void { $handler = new CreateSubjectHandler($this->subjectRepository, $this->clock); $command = new CreateSubjectCommand( tenantId: self::TENANT_ID, schoolId: self::SCHOOL_ID, name: 'Arts plastiques', code: 'ART', color: null, ); $subject = $handler($command); self::assertNotEmpty((string) $subject->id); self::assertSame('Arts plastiques', (string) $subject->name); self::assertSame('ART', (string) $subject->code); self::assertNull($subject->color); } #[Test] public function itPersistsSubjectInRepository(): void { $handler = new CreateSubjectHandler($this->subjectRepository, $this->clock); $command = new CreateSubjectCommand( tenantId: self::TENANT_ID, schoolId: self::SCHOOL_ID, name: 'Mathématiques', code: 'MATH', color: '#3B82F6', ); $createdSubject = $handler($command); $subject = $this->subjectRepository->get( SubjectId::fromString((string) $createdSubject->id), ); self::assertSame('Mathématiques', (string) $subject->name); self::assertSame('MATH', (string) $subject->code); self::assertSame(SubjectStatus::ACTIVE, $subject->status); } #[Test] public function itThrowsExceptionWhenSubjectCodeAlreadyExists(): void { $handler = new CreateSubjectHandler($this->subjectRepository, $this->clock); $command = new CreateSubjectCommand( tenantId: self::TENANT_ID, schoolId: self::SCHOOL_ID, name: 'Mathématiques', code: 'MATH', color: '#3B82F6', ); // First creation should succeed $handler($command); // Second creation with same code should throw $this->expectException(SubjectDejaExistanteException::class); $this->expectExceptionMessage('Une matière avec le code "MATH" existe déjà dans cet établissement.'); $commandDuplicate = new CreateSubjectCommand( tenantId: self::TENANT_ID, schoolId: self::SCHOOL_ID, name: 'Maths avancées', // Different name, same code code: 'MATH', color: '#EF4444', ); $handler($commandDuplicate); } #[Test] public function itAllowsSameCodeInDifferentTenant(): void { $handler = new CreateSubjectHandler($this->subjectRepository, $this->clock); // Create in tenant 1 $command1 = new CreateSubjectCommand( tenantId: self::TENANT_ID, schoolId: self::SCHOOL_ID, name: 'Mathématiques', code: 'MATH', color: '#3B82F6', ); $subject1 = $handler($command1); // Create same code in tenant 2 should succeed $command2 = new CreateSubjectCommand( tenantId: '550e8400-e29b-41d4-a716-446655440099', schoolId: self::SCHOOL_ID, name: 'Mathématiques', code: 'MATH', color: '#3B82F6', ); $subject2 = $handler($command2); self::assertFalse($subject1->id->equals($subject2->id)); self::assertSame('MATH', (string) $subject1->code); self::assertSame('MATH', (string) $subject2->code); } #[Test] public function itAllowsSameCodeInDifferentSchool(): void { $handler = new CreateSubjectHandler($this->subjectRepository, $this->clock); // Create in school 1 $command1 = new CreateSubjectCommand( tenantId: self::TENANT_ID, schoolId: self::SCHOOL_ID, name: 'Mathématiques', code: 'MATH', color: '#3B82F6', ); $subject1 = $handler($command1); // Create same code in school 2 should succeed $command2 = new CreateSubjectCommand( tenantId: self::TENANT_ID, schoolId: '550e8400-e29b-41d4-a716-446655440099', name: 'Mathématiques', code: 'MATH', color: '#3B82F6', ); $subject2 = $handler($command2); self::assertFalse($subject1->id->equals($subject2->id)); self::assertSame('MATH', (string) $subject1->code); self::assertSame('MATH', (string) $subject2->code); } #[Test] public function itNormalizesCodeToUppercase(): void { $handler = new CreateSubjectHandler($this->subjectRepository, $this->clock); $command = new CreateSubjectCommand( tenantId: self::TENANT_ID, schoolId: self::SCHOOL_ID, name: 'Mathématiques', code: 'math', color: '#3B82F6', ); $subject = $handler($command); self::assertSame('MATH', (string) $subject->code); } }