73 lines
2.6 KiB
Rust
73 lines
2.6 KiB
Rust
//! Live validation against the real SoundCloud API. `#[ignore]`d so it never
|
|
//! runs in CI or hits the network by default; run it deliberately with
|
|
//!
|
|
//! ```sh
|
|
//! cargo test -p soundclouddy --test live -- --ignored --nocapture
|
|
//! # optional: SOUNDCLOUD_CLIENT_ID=<id> to skip the scrape,
|
|
//! # SOUNDCLOUD_OAUTH=<token> to also exercise the personal calls.
|
|
//! ```
|
|
//!
|
|
//! It exercises the real endpoints end-to-end (scrape client_id → search →
|
|
//! resolve stream URL) so the `ScApi` DTOs and the `client_id` scrape are
|
|
//! confirmed against the live shapes — the drift risk called out in
|
|
//! architecture/soundcloud-provider.md.
|
|
|
|
use std::time::Duration;
|
|
|
|
use soundclouddy::api::{Sc, ScApi};
|
|
|
|
fn api() -> ScApi {
|
|
ScApi::new(
|
|
std::env::var("SOUNDCLOUD_CLIENT_ID").ok(),
|
|
None,
|
|
std::env::var("SOUNDCLOUD_OAUTH").ok(),
|
|
Duration::from_secs(30),
|
|
)
|
|
.expect("client builds")
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "hits the real SoundCloud API (scrapes a client_id)"]
|
|
async fn live_scrape_search_and_stream() {
|
|
let api = api();
|
|
// Scrapes a client_id if none was provided; a failure here means the
|
|
// scrape parsers need updating against the current soundcloud.com.
|
|
api.ensure_ready().await.expect("obtain a client_id");
|
|
println!(
|
|
"client_id acquired: {}",
|
|
api.cached_client_id().await.is_some()
|
|
);
|
|
|
|
// "lofi" returns freely-streamable (policy=MONETIZE) tracks; a label
|
|
// artist like Boards of Canada would be Go+/preview-only and 404 the media
|
|
// exchange (that restriction is expected, not a provider bug).
|
|
let tracks = api.search_tracks("lofi", 5).await.expect("search decodes");
|
|
assert!(!tracks.is_empty(), "search returns tracks");
|
|
let first = &tracks[0];
|
|
println!(
|
|
"first track: {} — {} ({})",
|
|
first.artist, first.title, first.id
|
|
);
|
|
|
|
// Resolve a playable media URL (progressive mp3, or an HLS m3u8 fallback).
|
|
let url = api
|
|
.resolve_stream_url(&first.id)
|
|
.await
|
|
.expect("stream url resolves");
|
|
assert!(url.starts_with("http"), "stream URL is absolute: {url}");
|
|
println!("stream url resolves (len {})", url.len());
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "hits the real SoundCloud API; requires SOUNDCLOUD_OAUTH"]
|
|
async fn live_personal_likes() {
|
|
if std::env::var("SOUNDCLOUD_OAUTH").is_err() {
|
|
eprintln!("SOUNDCLOUD_OAUTH unset — skipping personal test");
|
|
return;
|
|
}
|
|
let api = api();
|
|
api.ensure_ready().await.expect("client_id");
|
|
let likes = api.my_likes(5).await.expect("likes decode");
|
|
println!("liked tracks: {}", likes.len());
|
|
}
|