service = new SteamService(); Cache::flush(); } // ========================================== // Search Tests with Mocked Database // ========================================== public function test_search_finds_exact_match_in_database(): void { // Insert test data SteamApp::query()->insert([ ['appid' => 292030, 'name' => 'The Witcher 3: Wild Hunt'], ['appid' => 1091500, 'name' => 'Cyberpunk 2077'], ['appid' => 1245620, 'name' => 'ELDEN RING'], ]); // Search should find exact match $result = $this->service->search('Cyberpunk 2077'); $this->assertNotNull($result); $this->assertSame(1091500, $result); } public function test_search_finds_match_from_scene_release_name(): void { SteamApp::query()->insert([ ['appid' => 292030, 'name' => 'The Witcher 3: Wild Hunt'], ]); $result = $this->service->search('The.Witcher.3.Wild.Hunt-CODEX'); $this->assertNotNull($result); $this->assertSame(292030, $result); } public function test_search_returns_null_for_no_match(): void { SteamApp::query()->insert([ ['appid' => 292030, 'name' => 'The Witcher 3: Wild Hunt'], ]); $result = $this->service->search('NonExistent Game Title XYZ'); $this->assertNull($result); } public function test_search_caches_successful_result(): void { SteamApp::query()->insert([ ['appid' => 1091500, 'name' => 'Cyberpunk 2077'], ]); // First search $result1 = $this->service->search('Cyberpunk 2077'); $this->assertSame(1091500, $result1); // Delete from database SteamApp::query()->where('appid', 1091500)->delete(); // Second search should return cached result $result2 = $this->service->search('Cyberpunk 2077'); $this->assertSame(1091500, $result2); } public function test_search_caches_failed_lookup(): void { // Search for non-existent game $this->service->search('NonExistent Game 12345'); // Add the game to database SteamApp::query()->insert([ ['appid' => 99999, 'name' => 'NonExistent Game 12345'], ]); // Search should still return null due to cache $result = $this->service->search('NonExistent Game 12345'); $this->assertNull($result); } public function test_search_multiple_returns_collection(): void { SteamApp::query()->insert([ ['appid' => 292030, 'name' => 'The Witcher 3: Wild Hunt'], ['appid' => 20900, 'name' => 'The Witcher'], ['appid' => 20920, 'name' => 'The Witcher 2: Assassins of Kings'], ]); $results = $this->service->searchMultiple('Witcher', 10); $this->assertNotEmpty($results); $this->assertGreaterThanOrEqual(1, $results->count()); } // ========================================== // API Tests with HTTP Mocking // ========================================== public function test_get_game_details_returns_formatted_data(): void { Http::fake([ 'store.steampowered.com/api/appdetails*' => Http::response([ '1091500' => [ 'success' => true, 'data' => [ 'type' => 'game', 'name' => 'Cyberpunk 2077', 'steam_appid' => 1091500, 'short_description' => 'An open-world RPG set in Night City.', 'detailed_description' => 'Full description here...', 'about_the_game' => 'About the game...', 'header_image' => 'https://cdn.steam.com/header.jpg', 'background' => 'https://cdn.steam.com/background.jpg', 'publishers' => ['CD PROJEKT RED'], 'developers' => ['CD PROJEKT RED'], 'genres' => [ ['id' => '1', 'description' => 'Action'], ['id' => '3', 'description' => 'RPG'], ], 'categories' => [ ['id' => 2, 'description' => 'Single-player'], ], 'release_date' => [ 'coming_soon' => false, 'date' => 'Dec 10, 2020', ], 'metacritic' => [ 'score' => 86, 'url' => 'https://www.metacritic.com/game/cyberpunk-2077', ], 'platforms' => [ 'windows' => true, 'mac' => false, 'linux' => false, ], 'price_overview' => [ 'currency' => 'USD', 'initial' => 5999, 'final' => 2999, 'discount_percent' => 50, 'final_formatted' => '$29.99', ], 'screenshots' => [ [ 'id' => 0, 'path_thumbnail' => 'https://cdn.steam.com/ss_thumb.jpg', 'path_full' => 'https://cdn.steam.com/ss_full.jpg', ], ], 'movies' => [ [ 'id' => 256123, 'name' => 'Launch Trailer', 'thumbnail' => 'https://cdn.steam.com/movie_thumb.jpg', 'mp4' => [ '480' => 'https://cdn.steam.com/movie_480.mp4', 'max' => 'https://cdn.steam.com/movie_max.mp4', ], ], ], 'achievements' => [ 'total' => 44, ], 'recommendations' => [ 'total' => 500000, ], ], ], ]), ]); $result = $this->service->getGameDetails(1091500); $this->assertIsArray($result); $this->assertSame('Cyberpunk 2077', $result['title']); $this->assertSame(1091500, $result['steamid']); $this->assertSame('game', $result['type']); $this->assertSame('CD PROJEKT RED', $result['publisher']); $this->assertSame(['CD PROJEKT RED'], $result['developers']); $this->assertSame(86, $result['metacritic_score']); $this->assertStringContainsString('Action', $result['genres']); $this->assertContains('Windows', $result['platforms']); $this->assertNotEmpty($result['screenshots']); $this->assertNotEmpty($result['movies']); $this->assertSame(44, $result['achievements']); } public function test_get_game_details_returns_false_on_api_failure(): void { Http::fake([ 'store.steampowered.com/api/appdetails*' => Http::response([ '99999' => [ 'success' => false, ], ]), ]); $result = $this->service->getGameDetails(99999); $this->assertFalse($result); } public function test_get_game_details_caches_result(): void { Http::fake([ 'store.steampowered.com/api/appdetails*' => Http::response([ '1091500' => [ 'success' => true, 'data' => [ 'type' => 'game', 'name' => 'Cyberpunk 2077', 'steam_appid' => 1091500, ], ], ]), ]); // First call $result1 = $this->service->getGameDetails(1091500); $this->assertSame('Cyberpunk 2077', $result1['title']); // Second call should use cache (no HTTP request) Http::fake([ 'store.steampowered.com/api/appdetails*' => Http::response([ '1091500' => [ 'success' => true, 'data' => [ 'type' => 'game', 'name' => 'Different Name', 'steam_appid' => 1091500, ], ], ]), ]); $result2 = $this->service->getGameDetails(1091500); $this->assertSame('Cyberpunk 2077', $result2['title']); // Still cached } public function test_get_reviews_summary(): void { Http::fake([ 'store.steampowered.com/api/appreviews/*' => Http::response([ 'success' => 1, 'query_summary' => [ 'total_positive' => 400000, 'total_negative' => 100000, 'total_reviews' => 500000, 'review_score' => 8, 'review_score_desc' => 'Very Positive', ], ]), ]); $result = $this->service->getReviewsSummary(1091500); $this->assertIsArray($result); $this->assertSame(400000, $result['total_positive']); $this->assertSame(100000, $result['total_negative']); $this->assertSame(500000, $result['total_reviews']); $this->assertSame('Very Positive', $result['review_score_desc']); } public function test_get_full_app_list(): void { Http::fake([ 'api.steampowered.com/ISteamApps/GetAppList/v2/*' => Http::response([ 'applist' => [ 'apps' => [ ['appid' => 10, 'name' => 'Counter-Strike'], ['appid' => 20, 'name' => 'Team Fortress Classic'], ['appid' => 30, 'name' => 'Day of Defeat'], ], ], ]), ]); $result = $this->service->getFullAppList(); $this->assertIsArray($result); $this->assertCount(3, $result); $this->assertSame(10, $result[0]['appid']); $this->assertSame('Counter-Strike', $result[0]['name']); } // ========================================== // Populate Apps Table Tests // ========================================== public function test_populate_steam_apps_table(): void { Http::fake([ 'api.steampowered.com/ISteamApps/GetAppList/v2/*' => Http::response([ 'applist' => [ 'apps' => [ ['appid' => 10, 'name' => 'Counter-Strike'], ['appid' => 20, 'name' => 'Team Fortress Classic'], ['appid' => 30, 'name' => 'Day of Defeat'], ], ], ]), ]); $stats = $this->service->populateSteamAppsTable(); $this->assertArrayHasKey('inserted', $stats); $this->assertArrayHasKey('skipped', $stats); $this->assertArrayHasKey('errors', $stats); $this->assertSame(3, $stats['inserted']); // Verify data in database $this->assertDatabaseHas('steam_apps', ['appid' => 10, 'name' => 'Counter-Strike']); $this->assertDatabaseHas('steam_apps', ['appid' => 20, 'name' => 'Team Fortress Classic']); } public function test_populate_steam_apps_table_skips_duplicates(): void { // Pre-insert some apps SteamApp::query()->insert([ ['appid' => 10, 'name' => 'Counter-Strike'], ]); Http::fake([ 'api.steampowered.com/ISteamApps/GetAppList/v2/*' => Http::response([ 'applist' => [ 'apps' => [ ['appid' => 10, 'name' => 'Counter-Strike'], ['appid' => 20, 'name' => 'Team Fortress Classic'], ], ], ]), ]); $stats = $this->service->populateSteamAppsTable(); $this->assertSame(1, $stats['inserted']); $this->assertSame(1, $stats['skipped']); } // ========================================== // Price Handling Tests // ========================================== public function test_handles_free_game(): void { Http::fake([ 'store.steampowered.com/api/appdetails*' => Http::response([ '570' => [ 'success' => true, 'data' => [ 'type' => 'game', 'name' => 'Dota 2', 'steam_appid' => 570, 'is_free' => true, ], ], ]), ]); $result = $this->service->getGameDetails(570); $this->assertIsArray($result); $this->assertNotNull($result['price']); $this->assertSame(0.0, $result['price']['final']); $this->assertSame('Free', $result['price']['final_formatted']); } public function test_handles_game_on_sale(): void { Http::fake([ 'store.steampowered.com/api/appdetails*' => Http::response([ '1091500' => [ 'success' => true, 'data' => [ 'type' => 'game', 'name' => 'Cyberpunk 2077', 'steam_appid' => 1091500, 'price_overview' => [ 'currency' => 'USD', 'initial' => 5999, 'final' => 2999, 'discount_percent' => 50, 'final_formatted' => '$29.99', ], ], ], ]), ]); $result = $this->service->getGameDetails(1091500); $this->assertSame(59.99, $result['price']['initial']); $this->assertSame(29.99, $result['price']['final']); $this->assertSame(50, $result['price']['discount_percent']); } // ========================================== // Edge Cases // ========================================== public function test_handles_empty_search_term(): void { $result = $this->service->search(''); $this->assertNull($result); $result = $this->service->search(' '); $this->assertNull($result); } public function test_clean_title_handles_url_encoded_input(): void { $result = $this->service->cleanTitle('The%20Witcher%203'); $this->assertSame('The Witcher 3', $result); } public function test_handles_api_timeout(): void { Http::fake([ 'store.steampowered.com/api/appdetails*' => function () { throw new \Illuminate\Http\Client\ConnectionException('Connection timed out'); }, ]); $result = $this->service->getGameDetails(1091500); $this->assertFalse($result); } }