| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- <?php
- namespace Tests\Unit;
- use App\Services\WorldCup\Repositories\WorldCupOddsRepositoryInterface;
- use App\Services\WorldCup\WorldCupOddsService;
- use Tests\TestCase;
- class WorldCupOddsServiceTest extends TestCase
- {
- public function test_save_odds_updates_previous_odds_and_active_state()
- {
- $repository = new InMemoryWorldCupOddsRepository();
- $service = new WorldCupOddsService($repository);
- $result = $service->saveOdds([
- 'market' => '1x2',
- 'match_id' => 10,
- 'selection' => 'home',
- 'decimal_odds' => 2.35,
- 'is_active' => 1,
- 'locked_weight' => 9,
- ]);
- $this->assertTrue($result['success']);
- $this->assertSame(2.15, $repository->odds[0]['previous_odds']);
- $this->assertSame(2.35, $repository->odds[0]['decimal_odds']);
- $this->assertSame(1, $repository->odds[0]['is_active']);
- $this->assertSame(9, $repository->odds[0]['locked_weight']);
- }
- public function test_save_winner_market_allows_null_match_id()
- {
- $repository = new InMemoryWorldCupOddsRepository();
- $service = new WorldCupOddsService($repository);
- $result = $service->saveOdds([
- 'market' => 'winner',
- 'match_id' => null,
- 'selection' => 'Brazil',
- 'decimal_odds' => 6.5,
- 'is_active' => 1,
- 'locked_weight' => 99,
- ]);
- $this->assertTrue($result['success']);
- $created = $repository->findOdds('winner', null, 'Brazil');
- $this->assertSame('winner', $created['market']);
- $this->assertNull($created['match_id']);
- }
- public function test_match_odds_only_returns_active_1x2_odds_grouped_by_match()
- {
- $repository = new InMemoryWorldCupOddsRepository();
- $service = new WorldCupOddsService($repository);
- $odds = $service->activeMatchOdds([10, 11]);
- $this->assertArrayHasKey(10, $odds);
- $this->assertCount(2, $odds[10]);
- $this->assertSame('home', $odds[10][0]['selection']);
- $this->assertArrayNotHasKey(11, $odds);
- }
- public function test_list_odds_includes_match_team_pair_for_admin_table()
- {
- $repository = new InMemoryWorldCupOddsRepository();
- $service = new WorldCupOddsService($repository);
- $odds = $service->listOdds();
- $this->assertSame('Brazil', $odds[0]['home_team']);
- $this->assertSame('Serbia', $odds[0]['away_team']);
- $this->assertSame('Brazil vs Serbia', $odds[0]['team_pair']);
- $this->assertSame('Brazil 胜', $odds[0]['selection_label']);
- $this->assertSame('平局', $odds[1]['selection_label']);
- $this->assertSame('-', $odds[3]['team_pair']);
- $this->assertSame('Brazil', $odds[3]['selection_label']);
- }
- public function test_validation_rejects_invalid_market_or_decimal_odds()
- {
- $service = new WorldCupOddsService(new InMemoryWorldCupOddsRepository());
- $invalidMarket = $service->saveOdds([
- 'market' => 'spread',
- 'match_id' => 10,
- 'selection' => 'home',
- 'decimal_odds' => 2.0,
- ]);
- $invalidOdds = $service->saveOdds([
- 'market' => '1x2',
- 'match_id' => 10,
- 'selection' => 'home',
- 'decimal_odds' => 1.0,
- ]);
- $this->assertFalse($invalidMarket['success']);
- $this->assertSame('Invalid market', $invalidMarket['message']);
- $this->assertFalse($invalidOdds['success']);
- $this->assertSame('Decimal odds must be greater than 1', $invalidOdds['message']);
- }
- public function test_imports_1x2_odds_from_csv_rows()
- {
- $repository = new InMemoryWorldCupOddsRepository();
- $service = new WorldCupOddsService($repository);
- $result = $service->importOddsRows('1x2', [
- [
- 'match_id' => '10',
- 'home_win' => '2.40',
- 'draw' => '3.25',
- 'away_win' => '3.10',
- ],
- [
- 'match_id' => '',
- 'home_win' => '2.00',
- 'draw' => '3.00',
- 'away_win' => '4.00',
- ],
- ]);
- $this->assertTrue($result['success']);
- $this->assertSame(3, $result['data']['updated']);
- $this->assertSame(0, $result['data']['skipped']);
- $this->assertCount(1, $result['data']['errors']);
- $this->assertSame(2.40, $repository->findOdds('1x2', 10, 'home')['decimal_odds']);
- $this->assertSame(3.25, $repository->findOdds('1x2', 10, 'draw')['decimal_odds']);
- $this->assertSame(3.10, $repository->findOdds('1x2', 10, 'away')['decimal_odds']);
- $this->assertSame(2.15, $repository->findOdds('1x2', 10, 'home')['previous_odds']);
- }
- public function test_imports_winner_odds_from_draftkings_decimal()
- {
- $repository = new InMemoryWorldCupOddsRepository();
- $service = new WorldCupOddsService($repository);
- $result = $service->importOddsRows('winner', [
- [
- 'team_name' => 'Brazil',
- 'draftkings_decimal' => '10.00',
- ],
- [
- 'team_name' => 'Argentina',
- 'draftkings_decimal' => '10.50',
- ],
- [
- 'team_name' => 'France',
- 'draftkings_decimal' => '',
- ],
- ]);
- $this->assertTrue($result['success']);
- $this->assertSame(2, $result['data']['updated']);
- $this->assertCount(1, $result['data']['errors']);
- $this->assertSame(10.00, $repository->findOdds('winner', null, 'Brazil')['decimal_odds']);
- $this->assertSame(10.50, $repository->findOdds('winner', null, 'Argentina')['decimal_odds']);
- $this->assertSame(6.5, $repository->findOdds('winner', null, 'Brazil')['previous_odds']);
- }
- }
- class InMemoryWorldCupOddsRepository implements WorldCupOddsRepositoryInterface
- {
- public $odds = [
- [
- 'odds_id' => 1,
- 'market' => '1x2',
- 'match_id' => 10,
- 'selection' => 'home',
- 'decimal_odds' => 2.15,
- 'previous_odds' => 2.05,
- 'is_active' => 1,
- 'locked_weight' => 1,
- 'home_team' => 'Brazil',
- 'away_team' => 'Serbia',
- ],
- [
- 'odds_id' => 2,
- 'market' => '1x2',
- 'match_id' => 10,
- 'selection' => 'draw',
- 'decimal_odds' => 3.2,
- 'previous_odds' => null,
- 'is_active' => 1,
- 'locked_weight' => 0,
- 'home_team' => 'Brazil',
- 'away_team' => 'Serbia',
- ],
- [
- 'odds_id' => 3,
- 'market' => '1x2',
- 'match_id' => 11,
- 'selection' => 'home',
- 'decimal_odds' => 1.8,
- 'previous_odds' => null,
- 'is_active' => 0,
- 'locked_weight' => 0,
- 'home_team' => 'Argentina',
- 'away_team' => 'France',
- ],
- [
- 'odds_id' => 4,
- 'market' => 'winner',
- 'match_id' => null,
- 'selection' => 'Brazil',
- 'decimal_odds' => 6.5,
- 'previous_odds' => null,
- 'is_active' => 1,
- 'locked_weight' => 99,
- 'home_team' => null,
- 'away_team' => null,
- ],
- ];
- public function allOdds(array $filters = []): array
- {
- return $this->odds;
- }
- public function activeMatchOdds(array $matchIds): array
- {
- return array_values(array_filter($this->odds, function (array $odds) use ($matchIds) {
- return $odds['market'] === '1x2'
- && in_array((int)$odds['match_id'], $matchIds, true)
- && (int)$odds['is_active'] === 1;
- }));
- }
- public function activeWinnerOdds(): array
- {
- return array_values(array_filter($this->odds, function (array $odds) {
- return $odds['market'] === 'winner' && (int)$odds['is_active'] === 1;
- }));
- }
- public function findOdds(string $market, ?int $matchId, string $selection): ?array
- {
- foreach ($this->odds as $odds) {
- if ($odds['market'] === $market
- && $odds['match_id'] === $matchId
- && $odds['selection'] === $selection) {
- return $odds;
- }
- }
- return null;
- }
- public function upsertOdds(array $odds): array
- {
- foreach ($this->odds as $index => $current) {
- if ($current['market'] === $odds['market']
- && $current['match_id'] === $odds['match_id']
- && $current['selection'] === $odds['selection']) {
- $odds['odds_id'] = $current['odds_id'];
- $this->odds[$index] = array_merge($current, $odds);
- return $this->odds[$index];
- }
- }
- $odds['odds_id'] = count($this->odds) + 1;
- $this->odds[] = $odds;
- return $odds;
- }
- }
|