Files
watch/tests/integration.rs
2026-03-21 15:08:28 -04:00

308 lines
8.1 KiB
Rust

use chrono::Utc;
use sea_orm::ActiveValue::Set;
use shanty_data::DataResult;
use shanty_db::entities::wanted_item::{ItemType, WantedStatus};
use shanty_db::{Database, queries};
use shanty_tag::provider::*;
use shanty_watch::{add_album, add_artist, add_track, library_summary, list_items, remove_item};
async fn test_db() -> Database {
Database::new("sqlite::memory:")
.await
.expect("failed to create test database")
}
/// Insert a fake track into the DB to simulate an indexed library.
async fn insert_track(db: &Database, artist: &str, title: &str, album: &str) {
let now = Utc::now().naive_utc();
let artist_rec = queries::artists::upsert(db.conn(), artist, None)
.await
.unwrap();
let album_rec = queries::albums::upsert(db.conn(), album, artist, None, Some(artist_rec.id))
.await
.unwrap();
let active = shanty_db::entities::track::ActiveModel {
file_path: Set(format!("/music/{artist}/{album}/{title}.mp3")),
title: Set(Some(title.to_string())),
artist: Set(Some(artist.to_string())),
album: Set(Some(album.to_string())),
album_artist: Set(Some(artist.to_string())),
file_size: Set(1_000_000),
artist_id: Set(Some(artist_rec.id)),
album_id: Set(Some(album_rec.id)),
added_at: Set(now),
updated_at: Set(now),
..Default::default()
};
queries::tracks::upsert(db.conn(), active).await.unwrap();
}
/// Mock provider that returns a tracklist for known releases.
struct MockProvider;
impl MetadataProvider for MockProvider {
async fn search_recording(
&self,
_artist: &str,
_title: &str,
) -> DataResult<Vec<RecordingMatch>> {
Ok(vec![])
}
async fn search_release(&self, _artist: &str, _album: &str) -> DataResult<Vec<ReleaseMatch>> {
Ok(vec![ReleaseMatch {
mbid: "release-123".into(),
title: "Test Album".into(),
artist: "Test Artist".into(),
artist_mbid: Some("artist-456".into()),
date: Some("2024".into()),
track_count: Some(2),
score: 100,
}])
}
async fn get_recording(&self, _mbid: &str) -> DataResult<RecordingDetails> {
Err(shanty_data::DataError::Other("not found".into()))
}
async fn search_artist(
&self,
_query: &str,
_limit: u32,
) -> DataResult<Vec<ArtistSearchResult>> {
Ok(vec![ArtistSearchResult {
mbid: "artist-456".into(),
name: "Test Artist".into(),
disambiguation: None,
country: None,
artist_type: None,
score: 100,
}])
}
async fn get_artist_releases(
&self,
_mbid: &str,
_limit: u32,
) -> DataResult<Vec<DiscographyEntry>> {
Ok(vec![DiscographyEntry {
mbid: "release-123".into(),
title: "Test Album".into(),
date: Some("2024".into()),
release_type: Some("Album".into()),
track_count: Some(2),
}])
}
async fn get_release_tracks(&self, _release_mbid: &str) -> DataResult<Vec<ReleaseTrack>> {
Ok(vec![
ReleaseTrack {
recording_mbid: "rec-1".into(),
title: "Track One".into(),
track_number: Some(1),
disc_number: Some(1),
duration_ms: Some(180_000),
},
ReleaseTrack {
recording_mbid: "rec-2".into(),
title: "Track Two".into(),
track_number: Some(2),
disc_number: Some(1),
duration_ms: Some(200_000),
},
])
}
async fn get_artist_release_groups(
&self,
_artist_mbid: &str,
) -> DataResult<Vec<shanty_tag::provider::ReleaseGroupEntry>> {
Ok(vec![ReleaseGroupEntry {
mbid: "rg-123".into(),
title: "Test Album".into(),
primary_type: Some("Album".into()),
secondary_types: vec![],
first_release_date: Some("2024".into()),
first_release_mbid: Some("release-123".into()),
}])
}
async fn resolve_release_from_group(&self, release_group_mbid: &str) -> DataResult<String> {
if release_group_mbid == "rg-123" {
Ok("release-123".into())
} else {
Err(shanty_data::DataError::Other(format!(
"no releases for release-group {release_group_mbid}"
)))
}
}
}
#[tokio::test]
async fn test_add_track_wanted() {
let db = test_db().await;
let provider = MockProvider;
let entry = add_track(
db.conn(),
Some("Radiohead"),
Some("Creep"),
None,
&provider,
None,
)
.await
.unwrap();
assert_eq!(entry.item_type, ItemType::Track);
assert_eq!(entry.name, "Creep");
assert_eq!(entry.status, WantedStatus::Wanted);
}
#[tokio::test]
async fn test_add_track_auto_owned() {
let db = test_db().await;
let provider = MockProvider;
insert_track(&db, "Pink Floyd", "Time", "DSOTM").await;
let entry = add_track(
db.conn(),
Some("Pink Floyd"),
Some("Time"),
None,
&provider,
None,
)
.await
.unwrap();
assert_eq!(entry.status, WantedStatus::Owned);
}
#[tokio::test]
async fn test_add_album_expands_to_tracks() {
let db = test_db().await;
let provider = MockProvider;
let summary = add_album(
db.conn(),
Some("Test Artist"),
Some("Test Album"),
None,
&provider,
None,
)
.await
.unwrap();
assert_eq!(summary.tracks_added, 2);
let items = list_items(db.conn(), Some(WantedStatus::Wanted), None, None)
.await
.unwrap();
assert_eq!(items.len(), 2);
assert_eq!(items[0].item_type, ItemType::Track);
}
#[tokio::test]
async fn test_add_artist_expands_to_tracks() {
let db = test_db().await;
let provider = MockProvider;
let summary = add_artist(db.conn(), Some("Test Artist"), None, &provider, None)
.await
.unwrap();
assert_eq!(summary.tracks_added, 2);
let items = list_items(db.conn(), None, None, None).await.unwrap();
assert_eq!(items.len(), 2);
}
#[tokio::test]
async fn test_list_items_with_filters() {
let db = test_db().await;
let provider = MockProvider;
add_track(
db.conn(),
Some("Radiohead"),
Some("Creep"),
None,
&provider,
None,
)
.await
.unwrap();
add_track(
db.conn(),
Some("Tool"),
Some("Lateralus"),
None,
&provider,
None,
)
.await
.unwrap();
let all = list_items(db.conn(), None, None, None).await.unwrap();
assert_eq!(all.len(), 2);
let wanted = list_items(db.conn(), Some(WantedStatus::Wanted), None, None)
.await
.unwrap();
assert_eq!(wanted.len(), 2);
let radiohead = list_items(db.conn(), None, Some("Radiohead"), None)
.await
.unwrap();
assert_eq!(radiohead.len(), 1);
}
#[tokio::test]
async fn test_remove_item() {
let db = test_db().await;
let provider = MockProvider;
let entry = add_track(
db.conn(),
Some("Radiohead"),
Some("Creep"),
None,
&provider,
None,
)
.await
.unwrap();
remove_item(db.conn(), entry.id).await.unwrap();
let all = list_items(db.conn(), None, None, None).await.unwrap();
assert!(all.is_empty());
}
#[tokio::test]
async fn test_library_summary() {
let db = test_db().await;
let provider = MockProvider;
insert_track(&db, "Pink Floyd", "Time", "DSOTM").await;
add_track(
db.conn(),
Some("Radiohead"),
Some("Creep"),
None,
&provider,
None,
)
.await
.unwrap();
add_track(
db.conn(),
Some("Pink Floyd"),
Some("Time"),
None,
&provider,
None,
)
.await
.unwrap();
let summary = library_summary(db.conn()).await.unwrap();
assert_eq!(summary.total_items, 2);
assert_eq!(summary.wanted, 1);
assert_eq!(summary.owned, 1);
}