//! Integration tests for TVDB API client //! //! These tests make real API calls to TheTVDB and require a valid API key. //! Set the TVDB_API_KEY environment variable before running these tests. use movie_mapper::service::tvdb_api::TVDBClient; use movie_mapper::utils::TVDBError; /// Get API key from environment or return a default test value fn get_api_key() -> String { std::env::var("TVDB_API_KEY").unwrap_or_else(|_| { panic!("TVDB_API_KEY environment variable must be set for integration tests") }) } #[tokio::test] #[ignore = "Integration test - requires real API calls"] async fn test_tvdb_authenticate_success() { let api_key = get_api_key(); let mut client = TVDBClient::new(&api_key).expect("Failed to create client"); // This should succeed with a valid API key let result = client.authenticate().await; assert!(result.is_ok(), "Authentication should succeed"); assert!(client.token.is_some(), "Token should be set after authentication"); } #[tokio::test] #[ignore = "Integration test - requires real API calls"] async fn test_tvdb_search_finding_broken_bad() { let api_key = get_api_key(); let mut client = TVDBClient::new(&api_key).expect("Failed to create client"); // Authenticate first client.authenticate().await.expect("Authentication failed"); // Search for a well-known show let shows = client.search("Breaking Bad").await.expect("Search failed"); assert!(!shows.is_empty(), "Should find at least one show"); // Verify the first result looks reasonable let first_show = &shows[0]; assert_eq!(first_show.series_name, "Breaking Bad", "First result should be Breaking Bad"); assert!(first_show.id > 0, "Show should have a valid ID"); assert!(!first_show.overview.is_empty(), "Show should have an overview"); } #[tokio::test] #[ignore = "Integration test - requires real API calls"] async fn test_tvdb_search_no_results() { let api_key = get_api_key(); let mut client = TVDBClient::new(&api_key).expect("Failed to create client"); client.authenticate().await.expect("Authentication failed"); // Search for something that shouldn't exist let shows = client.search("NonExistentShow12345XYZ").await.expect("Search should not fail"); // Should return empty array, not error assert!(shows.is_empty(), "Should not find any shows for non-existent query"); } #[tokio::test] #[ignore = "Integration test - requires real API calls"] async fn test_tvdb_search_partial_match() { let api_key = get_api_key(); let mut client = TVDBClient::new(&api_key).expect("Failed to create client"); client.authenticate().await.expect("Authentication failed"); // Search with partial name let shows = client.search("Breaking").await.expect("Search should not fail"); // Should find Breaking Bad and possibly other shows with "Breaking" in name assert!(!shows.is_empty(), "Should find shows with 'Breaking' in name"); // Verify Breaking Bad is in results let breaking_in_results = shows.iter().any(|s| s.series_name.contains("Breaking")); assert!(breaking_in_results, "Should find shows containing 'Breaking'"); } #[tokio::test] #[ignore = "Integration test - requires real API calls"] async fn test_tvdb_get_show_details() { let api_key = get_api_key(); let mut client = TVDBClient::new(&api_key).expect("Failed to create client"); client.authenticate().await.expect("Authentication failed"); // First search to get a show ID let shows = client.search("Breaking Bad").await.expect("Search failed"); let show_id = shows[0].id; // Get detailed information let details = client.get_show_details(show_id).await.expect("Get show details failed"); assert_eq!(details.id, show_id, "Details should match show ID"); assert_eq!(details.name, "Breaking Bad", "Show name should match"); assert!(!details.overview.is_empty(), "Should have an overview"); assert!(!details.seasons.is_empty(), "Should have at least one season"); } #[tokio::test] #[ignore = "Integration test - requires real API calls"] async fn test_tvdb_get_season_episodes() { let api_key = get_api_key(); let mut client = TVDBClient::new(&api_key).expect("Failed to create client"); client.authenticate().await.expect("Authentication failed"); // First search to get a show ID let shows = client.search("Breaking Bad").await.expect("Search failed"); let show_id = shows[0].id; // Get episodes for season 1 let episodes = client.get_season_episodes(show_id, 1).await.expect("Get episodes failed"); assert!(!episodes.is_empty(), "Should have at least one episode in season 1"); // Verify episode structure let first_episode = &episodes[0]; assert_eq!(first_episode.season_number, 1, "Episode should be from season 1"); assert!(first_episode.number > 0, "Episode should have valid number"); assert!(!first_episode.name.is_empty(), "Episode should have a name"); } #[tokio::test] #[ignore = "Integration test - requires real API calls"] async fn test_tvdb_token_caching() { let api_key = get_api_key(); let mut client = TVDBClient::new(&api_key).expect("Failed to create client"); // First authentication client.authenticate().await.expect("First auth failed"); let first_token = client.token.clone().expect("Should have token"); // Call search again - should use cached token let shows = client.search("Breaking Bad").await.expect("Search failed"); // Verify we got results assert!(!shows.is_empty(), "Should find shows"); // Token should still be the same (cached) let second_token = client.token.clone().expect("Should still have token"); assert_eq!(first_token, second_token, "Token should be cached"); } #[tokio::test] #[ignore = "Integration test - requires real API calls"] async fn test_tvdb_multiple_searches() { let api_key = get_api_key(); let mut client = TVDBClient::new(&api_key).expect("Failed to create client"); client.authenticate().await.expect("Authentication failed"); // Perform multiple searches let search_terms = vec!["Breaking Bad", "Better Call Saul", "The Wire"]; for term in search_terms { let shows = client.search(term).await.expect(&format!("Search for {} failed", term)); println!("Search for '{}': Found {} results", term, shows.len()); } } #[tokio::test] #[ignore = "Integration test - requires real API calls"] async fn test_tvdb_full_workflow() { let api_key = get_api_key(); let mut client = TVDBClient::new(&api_key).expect("Failed to create client"); // 1. Authenticate client.authenticate().await.expect("Authentication failed"); // 2. Search for a show let shows = client.search("Breaking Bad").await.expect("Search failed"); assert!(!shows.is_empty(), "Should find shows"); let show_id = shows[0].id; // 3. Get show details let details = client.get_show_details(show_id).await.expect("Get details failed"); println!("Show: {} ({})", details.name, details.status); println!("Seasons: {}", details.seasons.len()); // 4. Get episodes for first season if !details.seasons.is_empty() { let season_number = details.seasons[0].number; let episodes = client.get_season_episodes(show_id, season_number) .await .expect("Get episodes failed"); println!("Season {} has {} episodes", season_number, episodes.len()); // 5. Verify episode structure for episode in &episodes { println!(" - E{:02}: {}", episode.number, episode.name); } } } #[tokio::test] #[ignore = "Integration test - requires real API calls"] async fn test_tvdb_error_handling_invalid_key() { // Test with an invalid key to verify error handling let mut client = TVDBClient::new("invalid-api-key-12345").expect("Failed to create client"); let result = client.authenticate().await; // Should fail with authentication error assert!(result.is_err(), "Authentication should fail with invalid key"); // The error should be related to authentication let err = result.unwrap_err(); println!("Expected error: {}", err); } #[tokio::test] #[ignore = "Integration test - requires real API calls"] async fn test_tvdb_rate_limit_simulation() { let api_key = get_api_key(); let mut client = TVDBClient::new(&api_key).expect("Failed to create client"); client.authenticate().await.expect("Authentication failed"); // Make many rapid requests to potentially hit rate limit // This test may take some time and should be run with patience for i in 0..5 { let shows = client.search("Breaking Bad").await; match shows { Ok(_) => { println!("Request {}: Success", i + 1); } Err(e) => { println!("Request {}: Error - {}", i + 1, e); // If we hit rate limit, that's actually expected behavior break; } } // Small delay between requests tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; } }