subjectRepository = new InMemorySubjectRepository(); $this->handler = new GetSubjectsHandler($this->subjectRepository); } #[Test] public function returnsAllActiveSubjectsForTenantAndSchool(): void { $this->seedSubjects(); $query = new GetSubjectsQuery( tenantId: self::TENANT_ID, schoolId: self::SCHOOL_ID, ); $result = ($this->handler)($query); self::assertCount(3, $result->items); self::assertSame(3, $result->total); self::assertSame(1, $result->page); self::assertSame(30, $result->limit); } #[Test] public function filtersSubjectsByName(): void { $this->seedSubjects(); $query = new GetSubjectsQuery( tenantId: self::TENANT_ID, schoolId: self::SCHOOL_ID, search: 'Mathématiques', ); $result = ($this->handler)($query); self::assertCount(1, $result->items); self::assertSame('Mathématiques', $result->items[0]->name); } #[Test] public function filtersSubjectsByCode(): void { $this->seedSubjects(); $query = new GetSubjectsQuery( tenantId: self::TENANT_ID, schoolId: self::SCHOOL_ID, search: 'FR', ); $result = ($this->handler)($query); self::assertCount(1, $result->items); self::assertSame('FR', $result->items[0]->code); } #[Test] public function searchIsCaseInsensitive(): void { $this->seedSubjects(); $query = new GetSubjectsQuery( tenantId: self::TENANT_ID, schoolId: self::SCHOOL_ID, search: 'math', ); $result = ($this->handler)($query); self::assertCount(1, $result->items); } #[Test] public function paginatesResults(): void { $this->seedSubjects(); $query = new GetSubjectsQuery( tenantId: self::TENANT_ID, schoolId: self::SCHOOL_ID, page: 1, limit: 2, ); $result = ($this->handler)($query); self::assertCount(2, $result->items); self::assertSame(3, $result->total); self::assertSame(1, $result->page); self::assertSame(2, $result->limit); self::assertSame(2, $result->totalPages()); } #[Test] public function returnsSecondPage(): void { $this->seedSubjects(); $query = new GetSubjectsQuery( tenantId: self::TENANT_ID, schoolId: self::SCHOOL_ID, page: 2, limit: 2, ); $result = ($this->handler)($query); self::assertCount(1, $result->items); self::assertSame(3, $result->total); self::assertSame(2, $result->page); } #[Test] public function returnsEmptyWhenNoMatches(): void { $this->seedSubjects(); $query = new GetSubjectsQuery( tenantId: self::TENANT_ID, schoolId: self::SCHOOL_ID, search: 'nonexistent', ); $result = ($this->handler)($query); self::assertCount(0, $result->items); self::assertSame(0, $result->total); } #[Test] public function clampsInvalidPageToOne(): void { $query = new GetSubjectsQuery( tenantId: self::TENANT_ID, schoolId: self::SCHOOL_ID, page: -1, ); self::assertSame(1, $query->page); } #[Test] public function clampsExcessiveLimitToMax(): void { $query = new GetSubjectsQuery( tenantId: self::TENANT_ID, schoolId: self::SCHOOL_ID, limit: 999, ); self::assertSame(100, $query->limit); } private function seedSubjects(): void { $tenantId = TenantId::fromString(self::TENANT_ID); $schoolId = SchoolId::fromString(self::SCHOOL_ID); $now = new DateTimeImmutable('2026-02-01 10:00:00'); $this->subjectRepository->save(Subject::creer( tenantId: $tenantId, schoolId: $schoolId, name: new SubjectName('Mathématiques'), code: new SubjectCode('MATH'), color: new SubjectColor('#3B82F6'), createdAt: $now, )); $this->subjectRepository->save(Subject::creer( tenantId: $tenantId, schoolId: $schoolId, name: new SubjectName('Français'), code: new SubjectCode('FR'), color: new SubjectColor('#EF4444'), createdAt: $now, )); $this->subjectRepository->save(Subject::creer( tenantId: $tenantId, schoolId: $schoolId, name: new SubjectName('Histoire-Géo'), code: new SubjectCode('HG'), color: new SubjectColor('#10B981'), createdAt: $now, )); } }