74 lines
2.6 KiB
Rust
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());
|
|
}
|