crabidy/absdy/tests/live.rs

74 lines
2.6 KiB
Rust

//! Live validation against a real audiobookshelf server. `#[ignore]`d so it
//! never runs in CI or hits the network by default; run it deliberately with
//!
//! ```sh
//! ABS_BASE_URL=https://host ABS_API_KEY=<key> \
//! cargo test -p absdy --test live -- --ignored --nocapture
//! ```
//!
//! It exercises the real endpoints end-to-end (libraries → items → search →
//! detail) so the `AbsApi` DTOs are confirmed against the live JSON shapes —
//! the drift risk called out in architecture/audiobookshelf-provider.md.
use std::time::Duration;
use absdy::api::{Abs, AbsApi};
fn creds() -> Option<(String, String)> {
let base = std::env::var("ABS_BASE_URL").ok()?;
let key = std::env::var("ABS_API_KEY").ok()?;
(!base.is_empty() && !key.is_empty()).then_some((base, key))
}
#[tokio::test]
#[ignore = "hits a real audiobookshelf server; set ABS_BASE_URL and ABS_API_KEY"]
async fn live_browse_search_and_detail() {
let Some((base, key)) = creds() else {
eprintln!("ABS_BASE_URL / ABS_API_KEY unset — skipping live test");
return;
};
let api = AbsApi::new(base, key, Duration::from_secs(30)).expect("client");
let libraries = api.libraries().await.expect("libraries decode");
assert!(!libraries.is_empty(), "server has at least one library");
let book_lib = libraries
.iter()
.find(|l| l.is_book)
.expect("at least one book library");
println!("book library: {} ({})", book_lib.name, book_lib.id);
let items = api
.library_items(&book_lib.id, 5)
.await
.expect("items decode");
assert!(!items.is_empty(), "book library has items");
let with_audio = items
.iter()
.find(|b| b.num_audio_files > 0)
.expect("an audiobook item");
println!(
"item: {} by {} ({} files)",
with_audio.title, with_audio.author, with_audio.num_audio_files
);
let detail = api
.item_detail(&with_audio.id)
.await
.expect("detail decode");
assert!(!detail.tracks.is_empty(), "audiobook has tracks");
let first = &detail.tracks[0];
println!("track0: ino={} title={}", first.ino, first.title);
// The stream URL is fully derivable and carries the token.
let url = api.stream_url(&with_audio.id, &first.ino);
assert!(url.contains(&format!("/api/items/{}/file/{}", with_audio.id, first.ino)));
assert!(url.contains("token="));
// Search should decode too (term may legitimately match nothing).
let hits = api
.search_items(&book_lib.id, "a", 3)
.await
.expect("search decode");
println!("search 'a' -> {} hits", hits.len());
}