calculateDistribution(3, 10); $this->assertSame(1, $distribution['low']['count']); $this->assertSame(8, $distribution['medium']['count']); $this->assertSame(1, $distribution['high']['count']); $this->assertSame(10, array_sum(array_column($distribution, 'count'))); } public function test_category_three_difficulty_buckets_keep_extreme_low_out_of_target_low_band(): void { $service = new DifficultyDistributionService(); $this->assertSame('other', $service->classifyQuestionByDifficulty(0.06, 3)); $this->assertSame('secondary', $service->classifyQuestionByDifficulty(0.36, 3)); $this->assertSame('primary_low', $service->classifyQuestionByDifficulty(0.42, 3)); $this->assertSame('primary_medium', $service->classifyQuestionByDifficulty(0.62, 3)); $this->assertSame('primary_high', $service->classifyQuestionByDifficulty(0.80, 3)); } public function test_category_three_supplement_order_prefers_higher_difficulty_before_low_fallback(): void { $service = new DifficultyDistributionService(); $this->assertSame( ['primary_medium', 'primary_high', 'primary_low', 'secondary', 'other'], $service->getSupplementOrder(3) ); } public function test_category_four_difficulty_buckets_use_split_high_mid_low_ranges(): void { $service = new DifficultyDistributionService(); $this->assertSame('secondary', $service->classifyQuestionByDifficulty(0.49, 4)); $this->assertSame('primary_low', $service->classifyQuestionByDifficulty(0.50, 4)); $this->assertSame('primary_medium', $service->classifyQuestionByDifficulty(0.80, 4)); $this->assertSame('primary_high', $service->classifyQuestionByDifficulty(0.92, 4)); } public function test_category_four_supplement_order_prefers_higher_difficulty_first(): void { $service = new DifficultyDistributionService(); $this->assertSame( ['primary_high', 'primary_medium', 'primary_low', 'secondary', 'other'], $service->getSupplementOrder(4) ); } public function test_category_three_answer_floor_replaces_low_answer_without_losing_total_count(): void { $service = app(LearningAnalyticsService::class); $reflection = new ReflectionClass($service); $method = $reflection->getMethod('enforceAnswerDifficultyFloor'); $method->setAccessible(true); $selected = [ $this->question(1, 'choice', 0.3), $this->question(2, 'fill', 0.4), $this->question(3, 'answer', 0.06), $this->question(4, 'answer', 0.5), ]; $pool = array_merge($selected, [ $this->question(5, 'answer', 0.62), $this->question(6, 'fill', 0.58), ]); $result = $method->invoke($service, $pool, $selected, 3, 4); $this->assertCount(4, $result); $this->assertFalse(collect($result)->contains( fn (array $question) => $question['question_type'] === 'answer' && $question['difficulty'] < 0.4 )); $this->assertTrue(collect($result)->contains( fn (array $question) => $question['id'] === 5 )); } private function question(int $id, string $type, float $difficulty): array { return [ 'id' => $id, 'question_type' => $type, 'difficulty' => $difficulty, 'stem' => "Question {$id}", 'kp_code' => 'KP001', 'solution' => 'solution', ]; } }