WorldCupMatchFavoriteServiceTest.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. <?php
  2. namespace Tests\Unit;
  3. use App\Services\WorldCup\Repositories\WorldCupMatchRepositoryInterface;
  4. use App\Services\WorldCup\WorldCupMatchFavoriteService;
  5. use Carbon\Carbon;
  6. use Tests\TestCase;
  7. class WorldCupMatchFavoriteServiceTest extends TestCase
  8. {
  9. public function test_toggle_favorite_adds_and_removes_match()
  10. {
  11. $repository = new InMemoryWorldCupMatchRepository();
  12. $service = new WorldCupMatchFavoriteService($repository);
  13. $now = Carbon::parse('2026-06-05 12:00:00');
  14. $added = $service->toggleFavorite(1001, 10, $now);
  15. $removed = $service->toggleFavorite(1001, 10, $now);
  16. $this->assertTrue($added['success']);
  17. $this->assertTrue($added['is_favorite']);
  18. $this->assertTrue($removed['success']);
  19. $this->assertFalse($removed['is_favorite']);
  20. }
  21. public function test_match_list_marks_only_current_user_favorites()
  22. {
  23. $repository = new InMemoryWorldCupMatchRepository();
  24. $repository->addFavorite(1001, 10);
  25. $repository->addFavorite(2002, 11);
  26. $service = new WorldCupMatchFavoriteService($repository);
  27. $matches = $service->listMatches(1001, false, Carbon::parse('2026-06-05 12:00:00'));
  28. $this->assertTrue($matches[0]['is_favorite']);
  29. $this->assertFalse($matches[1]['is_favorite']);
  30. }
  31. public function test_match_list_keeps_schedule_metadata_for_frontend()
  32. {
  33. $repository = new InMemoryWorldCupMatchRepository();
  34. $service = new WorldCupMatchFavoriteService($repository);
  35. $matches = $service->listMatches(1001, false, Carbon::parse('2026-06-05 12:00:00'));
  36. $this->assertSame(1, $matches[0]['match_no']);
  37. $this->assertSame('group', $matches[0]['stage']);
  38. $this->assertSame('A', $matches[0]['group_name']);
  39. $this->assertSame('Mexico City Stadium', $matches[0]['venue']);
  40. }
  41. public function test_match_list_returns_closed_future_schedule_as_not_bettable()
  42. {
  43. $repository = new InMemoryWorldCupMatchRepository();
  44. $service = new WorldCupMatchFavoriteService($repository);
  45. $matches = $service->listMatches(1001, false, Carbon::parse('2026-06-05 12:00:00'));
  46. $this->assertSame(73, $matches[2]['match_no']);
  47. $this->assertSame('Winner 73 A', $matches[2]['home_team']);
  48. $this->assertFalse($matches[2]['is_bettable']);
  49. }
  50. public function test_favorite_filter_returns_only_favorites_and_keeps_cutoff_filter()
  51. {
  52. $repository = new InMemoryWorldCupMatchRepository();
  53. $repository->addFavorite(1001, 10);
  54. $repository->addFavorite(1001, 12);
  55. $service = new WorldCupMatchFavoriteService($repository);
  56. $matches = $service->listMatches(1001, true, Carbon::parse('2026-06-05 12:00:00'));
  57. $this->assertCount(1, $matches);
  58. $this->assertSame(10, $matches[0]['match_id']);
  59. $this->assertTrue($matches[0]['is_favorite']);
  60. }
  61. }
  62. class InMemoryWorldCupMatchRepository implements WorldCupMatchRepositoryInterface
  63. {
  64. private $favorites = [];
  65. private $matches = [
  66. [
  67. 'match_id' => 10,
  68. 'match_no' => 1,
  69. 'competition' => 'World Cup',
  70. 'stage' => 'group',
  71. 'group_name' => 'A',
  72. 'home_team' => 'Mexico',
  73. 'away_team' => 'South Africa',
  74. 'venue' => 'Mexico City Stadium',
  75. 'kickoff_at' => '2026-06-05 15:00:00',
  76. 'status' => 'scheduled',
  77. ],
  78. [
  79. 'match_id' => 11,
  80. 'match_no' => 2,
  81. 'competition' => 'World Cup',
  82. 'stage' => 'group',
  83. 'group_name' => 'A',
  84. 'home_team' => 'France',
  85. 'away_team' => 'Spain',
  86. 'venue' => 'Estadio Guadalajara',
  87. 'kickoff_at' => '2026-06-05 18:00:00',
  88. 'status' => 'scheduled',
  89. ],
  90. [
  91. 'match_id' => 12,
  92. 'match_no' => 3,
  93. 'competition' => 'World Cup',
  94. 'stage' => 'group',
  95. 'group_name' => 'B',
  96. 'home_team' => 'Germany',
  97. 'away_team' => 'Italy',
  98. 'venue' => 'Toronto Stadium',
  99. 'kickoff_at' => '2026-06-05 12:30:00',
  100. 'status' => 'scheduled',
  101. ],
  102. [
  103. 'match_id' => 73,
  104. 'match_no' => 73,
  105. 'competition' => 'World Cup',
  106. 'stage' => 'round_32',
  107. 'group_name' => null,
  108. 'home_team' => 'Winner 73 A',
  109. 'away_team' => 'Winner 73 B',
  110. 'venue' => 'Los Angeles Stadium',
  111. 'kickoff_at' => '2026-07-01 12:00:00',
  112. 'status' => 'closed',
  113. ],
  114. ];
  115. public function getScheduleMatches(Carbon $now): array
  116. {
  117. $cutoff = $now->copy()->addHour();
  118. return array_values(array_filter($this->matches, function (array $match) use ($cutoff) {
  119. return in_array($match['status'], ['scheduled', 'closed'], true)
  120. && Carbon::parse($match['kickoff_at'])->gt($cutoff);
  121. }));
  122. }
  123. public function getOpenMatches(Carbon $now): array
  124. {
  125. $cutoff = $now->copy()->addHour();
  126. return array_values(array_filter($this->matches, function (array $match) use ($cutoff) {
  127. return $match['status'] === 'scheduled'
  128. && Carbon::parse($match['kickoff_at'])->gt($cutoff);
  129. }));
  130. }
  131. public function isOpenMatch(int $matchId, Carbon $now): bool
  132. {
  133. foreach ($this->getOpenMatches($now) as $match) {
  134. if ((int)$match['match_id'] === $matchId) {
  135. return true;
  136. }
  137. }
  138. return false;
  139. }
  140. public function getFavoriteMatchIds(int $userId): array
  141. {
  142. return array_values($this->favorites[$userId] ?? []);
  143. }
  144. public function isFavorite(int $userId, int $matchId): bool
  145. {
  146. return in_array($matchId, $this->favorites[$userId] ?? [], true);
  147. }
  148. public function addFavorite(int $userId, int $matchId): void
  149. {
  150. if (!isset($this->favorites[$userId])) {
  151. $this->favorites[$userId] = [];
  152. }
  153. if (!$this->isFavorite($userId, $matchId)) {
  154. $this->favorites[$userId][] = $matchId;
  155. }
  156. }
  157. public function removeFavorite(int $userId, int $matchId): void
  158. {
  159. $this->favorites[$userId] = array_values(array_filter(
  160. $this->favorites[$userId] ?? [],
  161. function ($favoriteMatchId) use ($matchId) {
  162. return (int)$favoriteMatchId !== $matchId;
  163. }
  164. ));
  165. }
  166. }