278 lines
8.1 KiB
Rust
278 lines
8.1 KiB
Rust
use chrono::Utc;
|
|
use sea_orm::*;
|
|
use shanty_db::entities::download_queue::DownloadStatus;
|
|
use shanty_db::entities::wanted_item::{ItemType, WantedStatus};
|
|
use shanty_db::{Database, queries};
|
|
|
|
async fn test_db() -> Database {
|
|
Database::new("sqlite::memory:").await.expect("failed to create test database")
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_database_creation_and_migrations() {
|
|
let _db = test_db().await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_artist_crud() {
|
|
let db = test_db().await;
|
|
let conn = db.conn();
|
|
|
|
// Create
|
|
let artist = queries::artists::upsert(conn, "Pink Floyd", None)
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(artist.name, "Pink Floyd");
|
|
assert!(artist.musicbrainz_id.is_none());
|
|
|
|
// Read
|
|
let found = queries::artists::get_by_id(conn, artist.id).await.unwrap();
|
|
assert_eq!(found.name, "Pink Floyd");
|
|
|
|
// Find by name
|
|
let found = queries::artists::find_by_name(conn, "Pink Floyd")
|
|
.await
|
|
.unwrap();
|
|
assert!(found.is_some());
|
|
|
|
// Upsert (same name, should return existing)
|
|
let same = queries::artists::upsert(conn, "Pink Floyd", None)
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(same.id, artist.id);
|
|
|
|
// Upsert with musicbrainz_id updates existing
|
|
let updated = queries::artists::upsert(conn, "Pink Floyd", Some("mb-123"))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(updated.id, artist.id);
|
|
assert_eq!(updated.musicbrainz_id.as_deref(), Some("mb-123"));
|
|
|
|
// List
|
|
let all = queries::artists::list(conn, 100, 0).await.unwrap();
|
|
assert_eq!(all.len(), 1);
|
|
|
|
// Update top songs
|
|
let updated = queries::artists::update_top_songs(conn, artist.id, r#"["Time","Money"]"#)
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(updated.top_songs, r#"["Time","Money"]"#);
|
|
|
|
// Delete
|
|
queries::artists::delete(conn, artist.id).await.unwrap();
|
|
let all = queries::artists::list(conn, 100, 0).await.unwrap();
|
|
assert!(all.is_empty());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_album_crud() {
|
|
let db = test_db().await;
|
|
let conn = db.conn();
|
|
|
|
let artist = queries::artists::upsert(conn, "Pink Floyd", None)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Create album
|
|
let album = queries::albums::upsert(
|
|
conn,
|
|
"The Dark Side of the Moon",
|
|
"Pink Floyd",
|
|
None,
|
|
Some(artist.id),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(album.name, "The Dark Side of the Moon");
|
|
assert_eq!(album.artist_id, Some(artist.id));
|
|
|
|
// Upsert same album returns existing
|
|
let same = queries::albums::upsert(
|
|
conn,
|
|
"The Dark Side of the Moon",
|
|
"Pink Floyd",
|
|
None,
|
|
Some(artist.id),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(same.id, album.id);
|
|
|
|
// Get by artist
|
|
let albums = queries::albums::get_by_artist(conn, artist.id)
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(albums.len(), 1);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_track_upsert_and_search() {
|
|
let db = test_db().await;
|
|
let conn = db.conn();
|
|
|
|
let now = Utc::now().naive_utc();
|
|
|
|
// Insert a track with partial metadata
|
|
let active = shanty_db::entities::track::ActiveModel {
|
|
file_path: Set("/music/time.flac".to_string()),
|
|
title: Set(Some("Time".to_string())),
|
|
artist: Set(Some("Pink Floyd".to_string())),
|
|
album: Set(Some("The Dark Side of the Moon".to_string())),
|
|
file_size: Set(42_000_000),
|
|
added_at: Set(now),
|
|
updated_at: Set(now),
|
|
..Default::default()
|
|
};
|
|
let track = queries::tracks::upsert(conn, active).await.unwrap();
|
|
assert_eq!(track.title.as_deref(), Some("Time"));
|
|
assert!(track.artist_id.is_none()); // no FK yet
|
|
|
|
// Upsert same file_path should update, not duplicate
|
|
let active2 = shanty_db::entities::track::ActiveModel {
|
|
file_path: Set("/music/time.flac".to_string()),
|
|
title: Set(Some("Time".to_string())),
|
|
artist: Set(Some("Pink Floyd".to_string())),
|
|
album: Set(Some("The Dark Side of the Moon".to_string())),
|
|
file_size: Set(42_000_000),
|
|
bitrate: Set(Some(1411)),
|
|
..Default::default()
|
|
};
|
|
let updated = queries::tracks::upsert(conn, active2).await.unwrap();
|
|
assert_eq!(updated.id, track.id);
|
|
assert_eq!(updated.bitrate, Some(1411));
|
|
|
|
// Search
|
|
let results = queries::tracks::search(conn, "Time").await.unwrap();
|
|
assert_eq!(results.len(), 1);
|
|
|
|
let results = queries::tracks::search(conn, "Pink Floyd").await.unwrap();
|
|
assert_eq!(results.len(), 1);
|
|
|
|
// Untagged (no musicbrainz_id)
|
|
let untagged = queries::tracks::get_untagged(conn).await.unwrap();
|
|
assert_eq!(untagged.len(), 1);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_wanted_items_lifecycle() {
|
|
let db = test_db().await;
|
|
let conn = db.conn();
|
|
|
|
let artist = queries::artists::upsert(conn, "Radiohead", None)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Add wanted item
|
|
let item = queries::wanted::add(conn, ItemType::Artist, "Radiohead", None, Some(artist.id), None, None)
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(item.status, WantedStatus::Wanted);
|
|
assert_eq!(item.item_type, ItemType::Artist);
|
|
|
|
// List with filter
|
|
let wanted = queries::wanted::list(conn, Some(WantedStatus::Wanted))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(wanted.len(), 1);
|
|
|
|
let downloaded = queries::wanted::list(conn, Some(WantedStatus::Downloaded))
|
|
.await
|
|
.unwrap();
|
|
assert!(downloaded.is_empty());
|
|
|
|
// Update status
|
|
let updated = queries::wanted::update_status(conn, item.id, WantedStatus::Downloaded)
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(updated.status, WantedStatus::Downloaded);
|
|
|
|
// Remove
|
|
queries::wanted::remove(conn, item.id).await.unwrap();
|
|
let all = queries::wanted::list(conn, None).await.unwrap();
|
|
assert!(all.is_empty());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_download_queue_lifecycle() {
|
|
let db = test_db().await;
|
|
let conn = db.conn();
|
|
|
|
// Enqueue
|
|
let item = queries::downloads::enqueue(conn, "Pink Floyd Time", None, "ytdlp")
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(item.status, DownloadStatus::Pending);
|
|
assert_eq!(item.query, "Pink Floyd Time");
|
|
|
|
// Get next pending
|
|
let next = queries::downloads::get_next_pending(conn).await.unwrap();
|
|
assert!(next.is_some());
|
|
assert_eq!(next.unwrap().id, item.id);
|
|
|
|
// Update to downloading
|
|
queries::downloads::update_status(conn, item.id, DownloadStatus::Downloading, None)
|
|
.await
|
|
.unwrap();
|
|
|
|
// No more pending
|
|
let next = queries::downloads::get_next_pending(conn).await.unwrap();
|
|
assert!(next.is_none());
|
|
|
|
// Fail it
|
|
queries::downloads::update_status(
|
|
conn,
|
|
item.id,
|
|
DownloadStatus::Failed,
|
|
Some("network error"),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
// List failed
|
|
let failed = queries::downloads::list(conn, Some(DownloadStatus::Failed))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(failed.len(), 1);
|
|
assert_eq!(failed[0].error_message.as_deref(), Some("network error"));
|
|
|
|
// Retry
|
|
queries::downloads::retry_failed(conn, item.id).await.unwrap();
|
|
let pending = queries::downloads::list(conn, Some(DownloadStatus::Pending))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(pending.len(), 1);
|
|
assert_eq!(pending[0].retry_count, 1);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_search_cache_ttl() {
|
|
let db = test_db().await;
|
|
let conn = db.conn();
|
|
|
|
// Set cache with long TTL
|
|
queries::cache::set(conn, "test_query", "musicbrainz", r#"{"results":[]}"#, 3600)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Get should return the value
|
|
let result = queries::cache::get(conn, "test_query").await.unwrap();
|
|
assert_eq!(result.as_deref(), Some(r#"{"results":[]}"#));
|
|
|
|
// Non-existent key
|
|
let result = queries::cache::get(conn, "nonexistent").await.unwrap();
|
|
assert!(result.is_none());
|
|
|
|
// Set cache with 0-second TTL (already expired)
|
|
queries::cache::set(conn, "expired_query", "musicbrainz", r#"{"old":true}"#, 0)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Should not return expired entry
|
|
let result = queries::cache::get(conn, "expired_query").await.unwrap();
|
|
assert!(result.is_none());
|
|
|
|
// Purge expired
|
|
let purged = queries::cache::purge_expired(conn).await.unwrap();
|
|
assert_eq!(purged, 1);
|
|
}
|