merge: clear the clippy backlog and unify lock helpers (D1-warnings, D3)
51 clippy warnings -> 0, with 8 justified #[allow]s (IPC arity, specta wire types, and the 9 test-only await-holding-lock sites). 27 raw lock calls moved to the poison-tolerant helpers - all of them test code; production was already clean. Caught a non-neutral clippy --fix: removing the redundant 'use hostname;' in credentials.rs orphaned its #[cfg(target_os = "linux")] onto SERVICE_NAME, which would have cfg'd the constant out of every non-Linux build. Compiles clean on Linux, so only Windows/macOS CI would have caught it.
This commit is contained in:
@@ -1134,6 +1134,16 @@ impl MediaRepository for HybridRepository {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
// `GATE_TEST_LOCK` below serialises the tests that flip the process-global
|
||||
// `INCLUDE_CATALOG_BROWSE` flag, so its guard is deliberately held across
|
||||
// the `.await` of the repository call under test — that await *is* the
|
||||
// critical section. This is not the production deadlock hazard the lint
|
||||
// targets: the lock is test-only, uncontended outside these tests, and each
|
||||
// `#[tokio::test]` runs on its own single-threaded runtime, so a held guard
|
||||
// cannot block another task on the same worker. Restructuring around it
|
||||
// would reintroduce the flag race the lock exists to prevent.
|
||||
#![allow(clippy::await_holding_lock)]
|
||||
|
||||
use super::*;
|
||||
use std::sync::Mutex;
|
||||
|
||||
|
||||
@@ -2510,6 +2510,16 @@ impl MediaRepository for OfflineRepository {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
// `CATALOG_BROWSE_LOCK` below serialises the tests that flip the
|
||||
// process-global `INCLUDE_CATALOG_BROWSE` flag, so its guard is deliberately
|
||||
// held across the `.await` of the query under test — that await *is* the
|
||||
// critical section. This is not the production deadlock hazard the lint
|
||||
// targets: the lock is test-only, uncontended outside these tests, and each
|
||||
// `#[tokio::test]` runs on its own single-threaded runtime, so a held guard
|
||||
// cannot block another task on the same worker. Restructuring around it
|
||||
// would reintroduce the flag race the lock exists to prevent.
|
||||
#![allow(clippy::await_holding_lock)]
|
||||
|
||||
use super::*;
|
||||
use crate::storage::db_service::RusqliteService;
|
||||
use rusqlite::Connection;
|
||||
@@ -2524,9 +2534,8 @@ mod tests {
|
||||
static CATALOG_BROWSE_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
|
||||
|
||||
fn lock_catalog_browse() -> std::sync::MutexGuard<'static, ()> {
|
||||
CATALOG_BROWSE_LOCK
|
||||
.lock()
|
||||
.unwrap_or_else(|poisoned| poisoned.into_inner())
|
||||
use crate::utils::lock::MutexSafe;
|
||||
CATALOG_BROWSE_LOCK.lock_safe()
|
||||
}
|
||||
|
||||
/// TRACES: UR-065 | DR-108 | UT-111
|
||||
|
||||
@@ -990,7 +990,7 @@ struct JellyfinMediaSource {
|
||||
}
|
||||
|
||||
impl JellyfinItem {
|
||||
fn to_media_item(self, server_id: String) -> MediaItem {
|
||||
fn into_media_item(self, server_id: String) -> MediaItem {
|
||||
// Extract image tags before consuming self
|
||||
let primary_tag = self.image_tags.as_ref().and_then(|tags| tags.primary());
|
||||
let backdrop_tags = self.backdrop_image_tags;
|
||||
@@ -1129,7 +1129,7 @@ impl MediaRepository for OnlineRepository {
|
||||
items: response
|
||||
.items
|
||||
.into_iter()
|
||||
.map(|item| item.to_media_item(self.user_id.clone()))
|
||||
.map(|item| item.into_media_item(self.user_id.clone()))
|
||||
.collect(),
|
||||
total_record_count: response.total_record_count,
|
||||
})
|
||||
@@ -1150,7 +1150,7 @@ impl MediaRepository for OnlineRepository {
|
||||
let endpoint = format!("/Users/{}/Items/{}?Fields=BackdropImageTags,ParentBackdropImageTags,People,MediaStreams,MediaSources,PremiereDate,UserData", self.user_id, item_id);
|
||||
|
||||
let item: JellyfinItem = self.get_json(&endpoint).await?;
|
||||
let media_item = item.to_media_item(self.user_id.clone());
|
||||
let media_item = item.into_media_item(self.user_id.clone());
|
||||
|
||||
Ok(media_item)
|
||||
}
|
||||
@@ -1165,7 +1165,7 @@ impl MediaRepository for OnlineRepository {
|
||||
let items: Vec<JellyfinItem> = self.get_json(&endpoint).await?;
|
||||
Ok(items
|
||||
.into_iter()
|
||||
.map(|item| item.to_media_item(self.user_id.clone()))
|
||||
.map(|item| item.into_media_item(self.user_id.clone()))
|
||||
.collect())
|
||||
}
|
||||
|
||||
@@ -1196,7 +1196,7 @@ impl MediaRepository for OnlineRepository {
|
||||
Ok(response
|
||||
.items
|
||||
.into_iter()
|
||||
.map(|item| item.to_media_item(self.user_id.clone()))
|
||||
.map(|item| item.into_media_item(self.user_id.clone()))
|
||||
.collect())
|
||||
}
|
||||
|
||||
@@ -1215,7 +1215,7 @@ impl MediaRepository for OnlineRepository {
|
||||
Ok(response
|
||||
.items
|
||||
.into_iter()
|
||||
.map(|item| item.to_media_item(self.user_id.clone()))
|
||||
.map(|item| item.into_media_item(self.user_id.clone()))
|
||||
.collect())
|
||||
}
|
||||
|
||||
@@ -1235,7 +1235,7 @@ impl MediaRepository for OnlineRepository {
|
||||
let items: Vec<MediaItem> = response
|
||||
.items
|
||||
.into_iter()
|
||||
.map(|item| item.to_media_item(self.user_id.clone()))
|
||||
.map(|item| item.into_media_item(self.user_id.clone()))
|
||||
.collect();
|
||||
|
||||
debug!("[get_recently_played_audio] Fetched {} items", items.len());
|
||||
@@ -1258,7 +1258,7 @@ impl MediaRepository for OnlineRepository {
|
||||
"[get_recently_played_audio] Grouping item '{}' into album '{}'",
|
||||
item.name, key
|
||||
);
|
||||
album_map.entry(key).or_insert_with(Vec::new).push(item);
|
||||
album_map.entry(key).or_default().push(item);
|
||||
} else {
|
||||
debug!(
|
||||
"[get_recently_played_audio] No album_id or album_name for item: '{}'",
|
||||
@@ -1373,7 +1373,7 @@ impl MediaRepository for OnlineRepository {
|
||||
Ok(response
|
||||
.items
|
||||
.into_iter()
|
||||
.map(|item| item.to_media_item(self.user_id.clone()))
|
||||
.map(|item| item.into_media_item(self.user_id.clone()))
|
||||
.collect())
|
||||
}
|
||||
|
||||
@@ -1393,7 +1393,7 @@ impl MediaRepository for OnlineRepository {
|
||||
Ok(response
|
||||
.items
|
||||
.into_iter()
|
||||
.map(|item| item.to_media_item(self.user_id.clone()))
|
||||
.map(|item| item.into_media_item(self.user_id.clone()))
|
||||
.collect())
|
||||
}
|
||||
|
||||
@@ -1503,7 +1503,7 @@ impl MediaRepository for OnlineRepository {
|
||||
items: response
|
||||
.items
|
||||
.into_iter()
|
||||
.map(|item| item.to_media_item(self.user_id.clone()))
|
||||
.map(|item| item.into_media_item(self.user_id.clone()))
|
||||
.collect(),
|
||||
total_record_count: response.total_record_count,
|
||||
})
|
||||
@@ -1881,7 +1881,7 @@ impl MediaRepository for OnlineRepository {
|
||||
Ok(response
|
||||
.items
|
||||
.into_iter()
|
||||
.map(|item| item.to_media_item(self.server_url.clone()))
|
||||
.map(|item| item.into_media_item(self.server_url.clone()))
|
||||
.collect())
|
||||
}
|
||||
|
||||
@@ -1894,7 +1894,7 @@ impl MediaRepository for OnlineRepository {
|
||||
let items = response
|
||||
.items
|
||||
.into_iter()
|
||||
.map(|item| item.to_media_item(self.server_url.clone()))
|
||||
.map(|item| item.into_media_item(self.server_url.clone()))
|
||||
.collect();
|
||||
Ok(SearchResult {
|
||||
items,
|
||||
@@ -2231,7 +2231,7 @@ impl MediaRepository for OnlineRepository {
|
||||
items: response
|
||||
.items
|
||||
.into_iter()
|
||||
.map(|item| item.to_media_item(self.user_id.clone()))
|
||||
.map(|item| item.into_media_item(self.user_id.clone()))
|
||||
.collect(),
|
||||
total_record_count: response.total_record_count,
|
||||
})
|
||||
@@ -2374,7 +2374,7 @@ impl MediaRepository for OnlineRepository {
|
||||
async fn get_person(&self, person_id: &str) -> Result<MediaItem, RepoError> {
|
||||
let endpoint = format!("/Users/{}/Items/{}", self.user_id, person_id);
|
||||
let item: JellyfinItem = self.get_json(&endpoint).await?;
|
||||
Ok(item.to_media_item(self.user_id.clone()))
|
||||
Ok(item.into_media_item(self.user_id.clone()))
|
||||
}
|
||||
|
||||
/// A person's filmography — every item they are credited on.
|
||||
@@ -2407,7 +2407,7 @@ impl MediaRepository for OnlineRepository {
|
||||
items: response
|
||||
.items
|
||||
.into_iter()
|
||||
.map(|item| item.to_media_item(self.user_id.clone()))
|
||||
.map(|item| item.into_media_item(self.user_id.clone()))
|
||||
.collect(),
|
||||
total_record_count: response.total_record_count,
|
||||
})
|
||||
@@ -2431,7 +2431,7 @@ impl MediaRepository for OnlineRepository {
|
||||
items: response
|
||||
.items
|
||||
.into_iter()
|
||||
.map(|item| item.to_media_item(self.user_id.clone()))
|
||||
.map(|item| item.into_media_item(self.user_id.clone()))
|
||||
.collect(),
|
||||
total_record_count: response.total_record_count,
|
||||
})
|
||||
@@ -2519,7 +2519,7 @@ impl MediaRepository for OnlineRepository {
|
||||
.into_iter()
|
||||
.map(|pi| PlaylistEntry {
|
||||
playlist_item_id: pi.playlist_item_id,
|
||||
item: pi.item.to_media_item(self.user_id.clone()),
|
||||
item: pi.item.into_media_item(self.user_id.clone()),
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
@@ -3017,7 +3017,7 @@ mod tests {
|
||||
}))
|
||||
.expect("fixture must deserialize");
|
||||
|
||||
let streams = item.to_media_item("server-1".to_string()).media_streams;
|
||||
let streams = item.into_media_item("server-1".to_string()).media_streams;
|
||||
let streams = streams.expect("the item carries streams");
|
||||
let deliverable = |index: i32| {
|
||||
streams
|
||||
@@ -3612,7 +3612,7 @@ mod tests {
|
||||
}"#;
|
||||
|
||||
let item: JellyfinItem = serde_json::from_str(json).expect("item should deserialize");
|
||||
let media = item.to_media_item("server1".to_string());
|
||||
let media = item.into_media_item("server1".to_string());
|
||||
|
||||
let user_data = media.user_data.expect("user data should be mapped");
|
||||
assert_eq!(user_data.is_favorite, Some(true));
|
||||
@@ -3632,7 +3632,7 @@ mod tests {
|
||||
let json = r#"{"Id": "x", "Name": "No User Data", "Type": "Movie"}"#;
|
||||
|
||||
let item: JellyfinItem = serde_json::from_str(json).expect("item should deserialize");
|
||||
let media = item.to_media_item("server1".to_string());
|
||||
let media = item.into_media_item("server1".to_string());
|
||||
|
||||
assert!(media.user_data.is_none());
|
||||
}
|
||||
@@ -3676,7 +3676,7 @@ mod tests {
|
||||
}"#;
|
||||
|
||||
let jellyfin_item: JellyfinItem = serde_json::from_str(json).expect("Failed to parse");
|
||||
let media_item = jellyfin_item.to_media_item("test-server-id".to_string());
|
||||
let media_item = jellyfin_item.into_media_item("test-server-id".to_string());
|
||||
|
||||
assert_eq!(media_item.id, "album456");
|
||||
assert_eq!(media_item.name, "Love and Theft");
|
||||
|
||||
Reference in New Issue
Block a user