DifficultyDistributionServiceTest.php 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. <?php
  2. namespace Tests\Unit;
  3. use App\Services\DifficultyDistributionService;
  4. use App\Services\LearningAnalyticsService;
  5. use ReflectionClass;
  6. use Tests\TestCase;
  7. class DifficultyDistributionServiceTest extends TestCase
  8. {
  9. public function test_category_three_distribution_keeps_only_ten_percent_low_band(): void
  10. {
  11. $service = new DifficultyDistributionService();
  12. $distribution = $service->calculateDistribution(3, 10);
  13. $this->assertSame(1, $distribution['low']['count']);
  14. $this->assertSame(8, $distribution['medium']['count']);
  15. $this->assertSame(1, $distribution['high']['count']);
  16. $this->assertSame(10, array_sum(array_column($distribution, 'count')));
  17. }
  18. public function test_category_three_difficulty_buckets_keep_extreme_low_out_of_target_low_band(): void
  19. {
  20. $service = new DifficultyDistributionService();
  21. $this->assertSame('other', $service->classifyQuestionByDifficulty(0.06, 3));
  22. $this->assertSame('secondary', $service->classifyQuestionByDifficulty(0.36, 3));
  23. $this->assertSame('primary_low', $service->classifyQuestionByDifficulty(0.42, 3));
  24. $this->assertSame('primary_medium', $service->classifyQuestionByDifficulty(0.62, 3));
  25. $this->assertSame('primary_high', $service->classifyQuestionByDifficulty(0.80, 3));
  26. }
  27. public function test_category_three_supplement_order_prefers_higher_difficulty_before_low_fallback(): void
  28. {
  29. $service = new DifficultyDistributionService();
  30. $this->assertSame(
  31. ['primary_medium', 'primary_high', 'primary_low', 'secondary', 'other'],
  32. $service->getSupplementOrder(3)
  33. );
  34. }
  35. public function test_category_four_difficulty_buckets_use_split_high_mid_low_ranges(): void
  36. {
  37. $service = new DifficultyDistributionService();
  38. $this->assertSame('secondary', $service->classifyQuestionByDifficulty(0.49, 4));
  39. $this->assertSame('primary_low', $service->classifyQuestionByDifficulty(0.50, 4));
  40. $this->assertSame('primary_medium', $service->classifyQuestionByDifficulty(0.80, 4));
  41. $this->assertSame('primary_high', $service->classifyQuestionByDifficulty(0.92, 4));
  42. }
  43. public function test_category_four_supplement_order_prefers_higher_difficulty_first(): void
  44. {
  45. $service = new DifficultyDistributionService();
  46. $this->assertSame(
  47. ['primary_high', 'primary_medium', 'primary_low', 'secondary', 'other'],
  48. $service->getSupplementOrder(4)
  49. );
  50. }
  51. public function test_category_three_answer_floor_replaces_low_answer_without_losing_total_count(): void
  52. {
  53. $service = app(LearningAnalyticsService::class);
  54. $reflection = new ReflectionClass($service);
  55. $method = $reflection->getMethod('enforceAnswerDifficultyFloor');
  56. $method->setAccessible(true);
  57. $selected = [
  58. $this->question(1, 'choice', 0.3),
  59. $this->question(2, 'fill', 0.4),
  60. $this->question(3, 'answer', 0.06),
  61. $this->question(4, 'answer', 0.5),
  62. ];
  63. $pool = array_merge($selected, [
  64. $this->question(5, 'answer', 0.62),
  65. $this->question(6, 'fill', 0.58),
  66. ]);
  67. $result = $method->invoke($service, $pool, $selected, 3, 4);
  68. $this->assertCount(4, $result);
  69. $this->assertFalse(collect($result)->contains(
  70. fn (array $question) => $question['question_type'] === 'answer' && $question['difficulty'] < 0.4
  71. ));
  72. $this->assertTrue(collect($result)->contains(
  73. fn (array $question) => $question['id'] === 5
  74. ));
  75. }
  76. private function question(int $id, string $type, float $difficulty): array
  77. {
  78. return [
  79. 'id' => $id,
  80. 'question_type' => $type,
  81. 'difficulty' => $difficulty,
  82. 'stem' => "Question {$id}",
  83. 'kp_code' => 'KP001',
  84. 'solution' => 'solution',
  85. ];
  86. }
  87. }