Initial commit
This commit is contained in:
277
tests/integration.rs
Normal file
277
tests/integration.rs
Normal file
@@ -0,0 +1,277 @@
|
||||
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, 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);
|
||||
}
|
||||
Reference in New Issue
Block a user