createReader(evaluations: 0, grades: 0)); $stats = $handler(new GetSubjectGradeStatsQuery( tenantId: self::TENANT_ID, subjectId: self::SUBJECT_ID, )); self::assertSame(0, $stats->evaluationCount); self::assertSame(0, $stats->gradeCount); self::assertFalse($stats->hasGrades()); } #[Test] public function itReturnsCountsWhenEvaluationsExist(): void { $handler = new GetSubjectGradeStatsHandler($this->createReader(evaluations: 3, grades: 42)); $stats = $handler(new GetSubjectGradeStatsQuery( tenantId: self::TENANT_ID, subjectId: self::SUBJECT_ID, )); self::assertSame(3, $stats->evaluationCount); self::assertSame(42, $stats->gradeCount); self::assertTrue($stats->hasGrades()); } #[Test] public function itConsidersSubjectWithEvaluationsButNoGradesAsHavingImpact(): void { $handler = new GetSubjectGradeStatsHandler($this->createReader(evaluations: 2, grades: 0)); $stats = $handler(new GetSubjectGradeStatsQuery( tenantId: self::TENANT_ID, subjectId: self::SUBJECT_ID, )); self::assertSame(2, $stats->evaluationCount); self::assertSame(0, $stats->gradeCount); self::assertTrue($stats->hasGrades(), 'Une évaluation sans notes reste un impact à signaler.'); } #[Test] public function itConsidersSubjectWithGradesButNoEvaluationsAsHavingImpact(): void { // Théoriquement impossible via la FK grades.evaluation_id → evaluations(id), // mais on couvre la logique `||` du value object contre toute régression. $handler = new GetSubjectGradeStatsHandler($this->createReader(evaluations: 0, grades: 5)); $stats = $handler(new GetSubjectGradeStatsQuery( tenantId: self::TENANT_ID, subjectId: self::SUBJECT_ID, )); self::assertSame(0, $stats->evaluationCount); self::assertSame(5, $stats->gradeCount); self::assertTrue($stats->hasGrades()); } #[Test] public function itPassesQueryParamsToReader(): void { $reader = new class implements SubjectGradeStatsReader { public ?string $receivedTenantId = null; public ?string $receivedSubjectId = null; #[Override] public function countForSubject(TenantId $tenantId, SubjectId $subjectId): SubjectGradeStats { $this->receivedTenantId = (string) $tenantId; $this->receivedSubjectId = (string) $subjectId; return new SubjectGradeStats(0, 0); } }; $handler = new GetSubjectGradeStatsHandler($reader); $handler(new GetSubjectGradeStatsQuery( tenantId: self::TENANT_ID, subjectId: self::SUBJECT_ID, )); self::assertSame(self::TENANT_ID, $reader->receivedTenantId); self::assertSame(self::SUBJECT_ID, $reader->receivedSubjectId); } private function createReader(int $evaluations, int $grades): SubjectGradeStatsReader { return new class($evaluations, $grades) implements SubjectGradeStatsReader { public function __construct( private int $evaluations, private int $grades, ) { } #[Override] public function countForSubject(TenantId $tenantId, SubjectId $subjectId): SubjectGradeStats { return new SubjectGradeStats($this->evaluations, $this->grades); } }; } }