classRepository = new InMemoryClassRepository(); $this->handler = new GetClassesHandler($this->classRepository); } #[Test] public function returnsAllActiveClassesForTenantAndYear(): void { $this->seedClasses(); $query = new GetClassesQuery( tenantId: self::TENANT_ID, academicYearId: self::ACADEMIC_YEAR_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 filtersClassesByName(): void { $this->seedClasses(); $query = new GetClassesQuery( tenantId: self::TENANT_ID, academicYearId: self::ACADEMIC_YEAR_ID, search: '6ème', ); $result = ($this->handler)($query); self::assertCount(1, $result->items); self::assertSame('6ème A', $result->items[0]->name); } #[Test] public function filtersClassesByLevel(): void { $this->seedClasses(); $query = new GetClassesQuery( tenantId: self::TENANT_ID, academicYearId: self::ACADEMIC_YEAR_ID, search: SchoolLevel::CM2->value, ); $result = ($this->handler)($query); self::assertCount(1, $result->items); self::assertSame('CM2 B', $result->items[0]->name); } #[Test] public function searchIsCaseInsensitive(): void { $this->seedClasses(); $query = new GetClassesQuery( tenantId: self::TENANT_ID, academicYearId: self::ACADEMIC_YEAR_ID, search: 'cm2', ); $result = ($this->handler)($query); self::assertCount(1, $result->items); } #[Test] public function paginatesResults(): void { $this->seedClasses(); $query = new GetClassesQuery( tenantId: self::TENANT_ID, academicYearId: self::ACADEMIC_YEAR_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->seedClasses(); $query = new GetClassesQuery( tenantId: self::TENANT_ID, academicYearId: self::ACADEMIC_YEAR_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->seedClasses(); $query = new GetClassesQuery( tenantId: self::TENANT_ID, academicYearId: self::ACADEMIC_YEAR_ID, search: 'nonexistent', ); $result = ($this->handler)($query); self::assertCount(0, $result->items); self::assertSame(0, $result->total); } #[Test] public function clampsInvalidPageToOne(): void { $this->seedClasses(); $query = new GetClassesQuery( tenantId: self::TENANT_ID, academicYearId: self::ACADEMIC_YEAR_ID, page: -1, ); self::assertSame(1, $query->page); } #[Test] public function clampsExcessiveLimitToMax(): void { $query = new GetClassesQuery( tenantId: self::TENANT_ID, academicYearId: self::ACADEMIC_YEAR_ID, limit: 999, ); self::assertSame(100, $query->limit); } private function seedClasses(): void { $tenantId = TenantId::fromString(self::TENANT_ID); $schoolId = SchoolId::generate(); $academicYearId = AcademicYearId::fromString(self::ACADEMIC_YEAR_ID); $now = new DateTimeImmutable('2026-02-01 10:00:00'); $this->classRepository->save(SchoolClass::creer( tenantId: $tenantId, schoolId: $schoolId, academicYearId: $academicYearId, name: new ClassName('6ème A'), level: SchoolLevel::SIXIEME, capacity: 30, createdAt: $now, )); $this->classRepository->save(SchoolClass::creer( tenantId: $tenantId, schoolId: $schoolId, academicYearId: $academicYearId, name: new ClassName('CM2 B'), level: SchoolLevel::CM2, capacity: 25, createdAt: $now, )); $this->classRepository->save(SchoolClass::creer( tenantId: $tenantId, schoolId: $schoolId, academicYearId: $academicYearId, name: new ClassName('CP Alpha'), level: SchoolLevel::CP, capacity: 20, createdAt: $now, )); } }