chore(repository): delete online_integration_test.rs, which never compiled

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) <noreply@anthropic.com>
This commit is contained in:
2026-09-08 20:09:11 +02:00
co-authored by Claude Opus 5
parent b1844f673e
commit f0c33a52c2
@@ -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::<Vec<_>>()
.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"));
}
}