get(Connection::class); $this->sharedConnection = $connection; /** @var PaginatedSubjectsReader $reader */ $reader = static::getContainer()->get(PaginatedSubjectsReader::class); $this->reader = $reader; } protected function tearDown(): void { $this->cleanupSubjectStatsData([self::TENANT_A, self::TENANT_B]); parent::tearDown(); } protected function connection(): Connection { return $this->sharedConnection; } #[Test] public function paginatedResultExposesZeroStatsWhenSubjectHasNoData(): void { $this->insertSubject(self::TENANT_A, self::SCHOOL_ID, 'Matière vide', 'EMPTY'); $result = $this->reader->findPaginated( tenantId: self::TENANT_A, schoolId: self::SCHOOL_ID, search: 'Matière vide', page: 1, limit: 30, ); self::assertCount(1, $result->items); $dto = $result->items[0]; self::assertSame('Matière vide', $dto->name); self::assertSame(0, $dto->teacherCount); self::assertSame(0, $dto->classCount); self::assertSame(0, $dto->evaluationCount); self::assertSame(0, $dto->gradeCount); self::assertFalse($dto->hasGrades()); } #[Test] public function paginatedResultCountsEvaluationsGradesTeachersAndClassesPerSubject(): void { $subjectId = $this->insertSubject(self::TENANT_A, self::SCHOOL_ID, 'Mathématiques', 'MATH2'); $classId = $this->insertClass(self::TENANT_A, self::SCHOOL_ID); $teacherId = $this->insertUser(self::TENANT_A, 'paginated-teacher@test.local'); $studentId = $this->insertUser(self::TENANT_A, 'paginated-student@test.local'); $this->insertTeacherAssignment(self::TENANT_A, $teacherId, $classId, $subjectId, self::SCHOOL_ID); $eval1 = $this->insertEvaluation(self::TENANT_A, $classId, $subjectId, $teacherId, 'Eval 1'); $eval2 = $this->insertEvaluation(self::TENANT_A, $classId, $subjectId, $teacherId, 'Eval 2'); $this->insertGrade(self::TENANT_A, $eval1, $studentId, 15.0); $this->insertGrade(self::TENANT_A, $eval2, $studentId, 18.0); $result = $this->reader->findPaginated( tenantId: self::TENANT_A, schoolId: self::SCHOOL_ID, search: 'Mathématiques', page: 1, limit: 30, ); self::assertCount(1, $result->items); $dto = $result->items[0]; self::assertSame(1, $dto->teacherCount); self::assertSame(1, $dto->classCount); self::assertSame(2, $dto->evaluationCount); self::assertSame(2, $dto->gradeCount); self::assertTrue($dto->hasGrades()); } #[Test] public function paginatedResultDoesNotLeakStatsFromOtherTenants(): void { $subjectA = $this->insertSubject(self::TENANT_A, self::SCHOOL_ID, 'Isolation', 'ISO'); // Données tenant B avec le même nom de matière $subjectB = $this->insertSubject(self::TENANT_B, self::SCHOOL_ID, 'Isolation', 'ISO'); $classB = $this->insertClass(self::TENANT_B, self::SCHOOL_ID); $teacherB = $this->insertUser(self::TENANT_B, 'isolation-teacher@test.local'); $studentB = $this->insertUser(self::TENANT_B, 'isolation-student@test.local'); $this->insertTeacherAssignment(self::TENANT_B, $teacherB, $classB, $subjectB, self::SCHOOL_ID); $evalB = $this->insertEvaluation(self::TENANT_B, $classB, $subjectB, $teacherB, 'Eval B'); $this->insertGrade(self::TENANT_B, $evalB, $studentB, 10.0); $resultA = $this->reader->findPaginated( tenantId: self::TENANT_A, schoolId: self::SCHOOL_ID, search: 'Isolation', page: 1, limit: 30, ); self::assertCount(1, $resultA->items); $dto = $resultA->items[0]; self::assertSame($subjectA, $dto->id); self::assertSame(0, $dto->teacherCount); self::assertSame(0, $dto->classCount); self::assertSame(0, $dto->evaluationCount); self::assertSame(0, $dto->gradeCount); } }