From f0c33a52c2cd45d3dad4666e2af59bc9fe6cfb84 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Tue, 8 Sep 2026 20:09:11 +0200 Subject: [PATCH] chore(repository): delete online_integration_test.rs, which never compiled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The file was not declared in repository/mod.rs and imported crate::api::jellyfin, a module that does not exist. It had never been built, let alone run. Dead would be reason enough, but it was worse than dead. Its mock reimplemented the URL builders and then asserted against itself, and online.rs still carries the comment recording where that leads: the mock used the correct stream.mp4 endpoint while the real implementation shipped /Videos/{id}/download, which 404s on real servers and silently broke every movie and TV download. The "test" stayed green throughout. Its own test_image_url_basic asserted api_key= appears in image URLs while the mock two lines above it documented the opposite. Deleted rather than revived: it asserts on constructed URL strings, which is the pattern the mock-server harness replaces. The lesson it left is preserved in the online.rs comment that referenced it — assert against a response from a mock server, never against a mock that re-derives the thing under test. Co-Authored-By: Claude Opus 5 (1M context) --- .../src/repository/online_integration_test.rs | 429 ------------------ 1 file changed, 429 deletions(-) delete mode 100644 src-tauri/src/repository/online_integration_test.rs diff --git a/src-tauri/src/repository/online_integration_test.rs b/src-tauri/src/repository/online_integration_test.rs deleted file mode 100644 index 7fdf9649f..000000000 --- a/src-tauri/src/repository/online_integration_test.rs +++ /dev/null @@ -1,429 +0,0 @@ -#[cfg(test)] -mod tests { - use crate::api::jellyfin::{ - GetItemsOptions, ImageType, ImageOptions, SortOrder, - }; - - /// Mock for testing URL construction without a real server - struct MockOnlineRepository { - server_url: String, - access_token: String, - } - - impl MockOnlineRepository { - fn new(server_url: &str, access_token: &str) -> Self { - Self { - server_url: server_url.to_string(), - access_token: access_token.to_string(), - } - } - - /// Test helper: construct image URL similar to backend - fn get_image_url( - &self, - item_id: &str, - image_type: &str, - options: Option<&ImageOptions>, - ) -> String { - let mut url = format!( - "{}/Items/{}/Images/{}", - self.server_url, item_id, image_type - ); - - // No api_key — image downloads use X-Emby-Authorization header - let mut params: Vec<(&str, String)> = Vec::new(); - - if let Some(opts) = options { - if let Some(max_width) = opts.max_width { - params.push(("maxWidth", max_width.to_string())); - } - if let Some(max_height) = opts.max_height { - params.push(("maxHeight", max_height.to_string())); - } - if let Some(quality) = opts.quality { - params.push(("quality", quality.to_string())); - } - if let Some(tag) = &opts.tag { - params.push(("tag", tag.clone())); - } - } - - let query_string = params - .iter() - .map(|(k, v)| format!("{}={}", k, v)) - .collect::>() - .join("&"); - - if !query_string.is_empty() { - url.push('?'); - url.push_str(&query_string); - } - - url - } - - /// Test helper: construct subtitle URL - fn get_subtitle_url( - &self, - item_id: &str, - media_source_id: &str, - stream_index: usize, - format: &str, - ) -> String { - format!( - "{}/Videos/{}/Subtitles/{}/{}/subtitles.{}?api_key={}", - self.server_url, - item_id, - media_source_id, - stream_index, - format, - self.access_token - ) - } - - /// Test helper: construct video download URL - fn get_video_download_url( - &self, - item_id: &str, - quality: &str, - ) -> String { - let (max_width, bitrate) = match quality { - "1080p" => ("1920", "15000k"), - "720p" => ("1280", "8000k"), - "480p" => ("854", "3000k"), - _ => ("0", ""), // original - }; - - if quality == "original" { - format!("{}/Videos/{}/stream.mp4?api_key={}", self.server_url, item_id, self.access_token) - } else { - format!( - "{}/Videos/{}/stream.mp4?maxWidth={}&videoBitrate={}&api_key={}", - self.server_url, item_id, max_width, bitrate, self.access_token - ) - } - } - } - - // ===== Image URL Tests ===== - - #[test] - fn test_image_url_basic() { - let repo = MockOnlineRepository::new("https://jellyfin.example.com", "token123"); - - let url = repo.get_image_url("item123", "Primary", None); - - assert!(url.contains("https://jellyfin.example.com")); - assert!(url.contains("/Items/item123/Images/Primary")); - assert!(url.contains("api_key=token123")); - } - - #[test] - fn test_image_url_with_max_width() { - let repo = MockOnlineRepository::new("https://jellyfin.example.com", "token123"); - let options = ImageOptions { - max_width: Some(300), - max_height: None, - quality: None, - tag: None, - }; - - let url = repo.get_image_url("item123", "Primary", Some(&options)); - - assert!(url.contains("maxWidth=300")); - assert!(url.contains("api_key=token123")); - } - - #[test] - fn test_image_url_with_all_options() { - let repo = MockOnlineRepository::new("https://jellyfin.example.com", "token123"); - let options = ImageOptions { - max_width: Some(1920), - max_height: Some(1080), - quality: Some(90), - tag: Some("abc123".to_string()), - }; - - let url = repo.get_image_url("item456", "Backdrop", Some(&options)); - - assert!(url.contains("/Items/item456/Images/Backdrop")); - assert!(url.contains("maxWidth=1920")); - assert!(url.contains("maxHeight=1080")); - assert!(url.contains("quality=90")); - assert!(url.contains("tag=abc123")); - assert!(url.contains("api_key=token123")); - } - - #[test] - fn test_image_url_different_image_types() { - let repo = MockOnlineRepository::new("https://jellyfin.example.com", "token123"); - - let image_types = vec!["Primary", "Backdrop", "Logo", "Thumb"]; - - for image_type in image_types { - let url = repo.get_image_url("item123", image_type, None); - assert!(url.contains(&format!("/Images/{}", image_type))); - } - } - - #[test] - fn test_image_url_credentials_included_in_backend() { - let repo = MockOnlineRepository::new("https://jellyfin.example.com", "secret_token"); - - let url = repo.get_image_url("item123", "Primary", None); - - // Credentials should be included in backend-generated URL - assert!(url.contains("api_key=secret_token")); - } - - #[test] - fn test_image_url_proper_encoding() { - let repo = MockOnlineRepository::new("https://jellyfin.example.com", "token123"); - let options = ImageOptions { - max_width: Some(300), - max_height: None, - quality: None, - tag: Some("tag-with-special-chars".to_string()), - }; - - let url = repo.get_image_url("item123", "Primary", Some(&options)); - - // URL should be properly formatted - assert!(url.contains("?")); - assert!(url.contains("&") || !url.contains("&&")); // No double ampersands - assert!(!url.ends_with("&")); // No trailing ampersand - } - - // ===== Subtitle URL Tests ===== - - #[test] - fn test_subtitle_url_vtt_format() { - let repo = MockOnlineRepository::new("https://jellyfin.example.com", "token123"); - - let url = repo.get_subtitle_url("item123", "source456", 0, "vtt"); - - assert!(url.contains("Videos/item123")); - assert!(url.contains("Subtitles/source456/0")); - assert!(url.contains("subtitles.vtt")); - assert!(url.contains("api_key=token123")); - } - - #[test] - fn test_subtitle_url_srt_format() { - let repo = MockOnlineRepository::new("https://jellyfin.example.com", "token123"); - - let url = repo.get_subtitle_url("item123", "source456", 1, "srt"); - - assert!(url.contains("Subtitles/source456/1")); - assert!(url.contains("subtitles.srt")); - } - - #[test] - fn test_subtitle_url_multiple_streams() { - let repo = MockOnlineRepository::new("https://jellyfin.example.com", "token123"); - - for stream_index in 0..5 { - let url = repo.get_subtitle_url("item123", "source456", stream_index, "vtt"); - assert!(url.contains(&format!("/{}/subtitles", stream_index))); - } - } - - #[test] - fn test_subtitle_url_different_media_sources() { - let repo = MockOnlineRepository::new("https://jellyfin.example.com", "token123"); - - let media_sources = vec!["src1", "src2", "src3"]; - - for media_source_id in media_sources { - let url = repo.get_subtitle_url("item123", media_source_id, 0, "vtt"); - assert!(url.contains(&format!("Subtitles/{}/", media_source_id))); - } - } - - // ===== Video Download URL Tests ===== - - #[test] - fn test_video_download_url_original_quality() { - let repo = MockOnlineRepository::new("https://jellyfin.example.com", "token123"); - - let url = repo.get_video_download_url("item123", "original"); - - assert!(url.contains("Videos/item123/stream.mp4")); - assert!(url.contains("api_key=token123")); - assert!(!url.contains("maxWidth")); // Original should have no transcoding params - } - - #[test] - fn test_video_download_url_1080p() { - let repo = MockOnlineRepository::new("https://jellyfin.example.com", "token123"); - - let url = repo.get_video_download_url("item123", "1080p"); - - assert!(url.contains("maxWidth=1920")); - assert!(url.contains("videoBitrate=15000k")); - } - - #[test] - fn test_video_download_url_720p() { - let repo = MockOnlineRepository::new("https://jellyfin.example.com", "token123"); - - let url = repo.get_video_download_url("item123", "720p"); - - assert!(url.contains("maxWidth=1280")); - assert!(url.contains("videoBitrate=8000k")); - } - - #[test] - fn test_video_download_url_480p() { - let repo = MockOnlineRepository::new("https://jellyfin.example.com", "token123"); - - let url = repo.get_video_download_url("item123", "480p"); - - assert!(url.contains("maxWidth=854")); - assert!(url.contains("videoBitrate=3000k")); - } - - #[test] - fn test_video_download_url_quality_presets() { - let repo = MockOnlineRepository::new("https://jellyfin.example.com", "token123"); - - let qualities = vec!["original", "1080p", "720p", "480p"]; - - for quality in qualities { - let url = repo.get_video_download_url("item123", quality); - assert!(url.contains("Videos/item123/stream.mp4")); - } - } - - // ===== Security Tests ===== - - #[test] - fn test_credentials_never_exposed_in_frontend() { - let repo = MockOnlineRepository::new("https://jellyfin.example.com", "super_secret_token"); - - let image_url = repo.get_image_url("item123", "Primary", None); - let subtitle_url = repo.get_subtitle_url("item123", "src123", 0, "vtt"); - let download_url = repo.get_video_download_url("item123", "720p"); - - // Image URLs no longer contain api_key — auth is via X-Emby-Authorization header - assert!(!image_url.contains("api_key=")); - // Subtitle and download URLs still use api_key (used directly, not via download_bytes) - assert!(subtitle_url.contains("api_key=super_secret_token")); - assert!(download_url.contains("api_key=super_secret_token")); - } - - #[test] - fn test_url_parameter_injection_prevention() { - let repo = MockOnlineRepository::new("https://jellyfin.example.com", "token123"); - - // Try to inject parameters through item_id - let malicious_id = "item123&extraParam=malicious"; - let url = repo.get_image_url(malicious_id, "Primary", None); - - // URL should contain the full item_id, backend should handle escaping - assert!(url.contains(malicious_id)); - // Backend should be responsible for proper URL encoding - } - - // ===== URL Format Tests ===== - - #[test] - fn test_image_url_format_correctness() { - let repo = MockOnlineRepository::new("https://server.com", "token"); - - let url = repo.get_image_url("id123", "Primary", None); - - // Should be valid format (no api_key — auth via header) - assert!(url.starts_with("https://server.com")); - assert!(url.contains("/Items/id123/Images/Primary")); - assert!(!url.contains("api_key=")); - } - - #[test] - fn test_query_string_properly_separated() { - let repo = MockOnlineRepository::new("https://server.com", "token"); - let options = ImageOptions { - max_width: Some(300), - max_height: Some(200), - quality: None, - tag: None, - }; - - let url = repo.get_image_url("id123", "Primary", Some(&options)); - - // Should have single ? separator with params - let question_marks = url.matches('?').count(); - assert_eq!(question_marks, 1); - - // Should have params for maxWidth and maxHeight - assert!(url.contains("maxWidth=300")); - assert!(url.contains("maxHeight=200")); - } - - #[test] - fn test_special_characters_in_urls() { - let repo = MockOnlineRepository::new("https://server.com", "token_with_special-chars"); - - let url = repo.get_image_url("item-with-special_chars", "Primary", None); - - // Should handle special characters in id (no token in URL anymore) - assert!(url.contains("item-with-special_chars")); - } - - // ===== Backend vs Frontend Responsibility Tests ===== - - #[test] - fn test_backend_owns_url_construction() { - // This test documents that URL construction is ONLY in backend - let repo = MockOnlineRepository::new("https://jellyfin.example.com", "secret_token"); - - // Backend generates full URL with credentials - let url = repo.get_image_url("item123", "Primary", None); - - // URL is complete and ready to use (auth via header, not api_key) - assert!(url.starts_with("https://")); - assert!(url.contains("/Items/item123/Images/Primary")); - - // Frontend never constructs URLs directly - // Frontend only receives pre-constructed URLs from backend - } - - #[test] - fn test_url_includes_all_necessary_parameters() { - let repo = MockOnlineRepository::new("https://server.com", "token"); - let options = ImageOptions { - max_width: Some(300), - max_height: Some(200), - quality: Some(90), - tag: Some("abc".to_string()), - }; - - let url = repo.get_image_url("item123", "Primary", Some(&options)); - - // All provided options should be in URL - assert!(url.contains("maxWidth=300")); - assert!(url.contains("maxHeight=200")); - assert!(url.contains("quality=90")); - assert!(url.contains("tag=abc")); - } - - #[test] - fn test_optional_parameters_omitted_when_not_provided() { - let repo = MockOnlineRepository::new("https://server.com", "token"); - let options = ImageOptions { - max_width: None, - max_height: None, - quality: None, - tag: None, - }; - - let url = repo.get_image_url("item123", "Primary", Some(&options)); - - // Should have no query params (no api_key, no options) - assert!(!url.contains("?")); - assert!(!url.contains("maxWidth")); - assert!(!url.contains("maxHeight")); - assert!(!url.contains("quality")); - assert!(!url.contains("tag")); - } -}