This repository has been archived on 2026-05-27. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
rustypipe/tests/sulkta_smoke.rs
Kayos 947f67834a tests: smoke — switch HEAD to Range GET + iOS UA
YouTube googlevideo CDN 403s HEAD requests + 403s requests with a
non-client User-Agent. Use the iOS client UA on the probe so the CDN
treats it as the same client that requested the URL.
2026-05-24 11:56:41 -07:00

127 lines
4.2 KiB
Rust

//! Sulkta-fork smoke tests for the player pipeline.
//!
//! Verifies the patched default client order (`Ios, Tv` without botguard) plus
//! the soft-fail DeobfData::extract works against current YouTube player.js.
//!
//! Run with: `cargo test --test sulkta_smoke -- --nocapture`
use rstest::{fixture, rstest};
use rustypipe::client::{ClientType, RustyPipe};
/// A stable, long-running, public-domain music video. Used by upstream
/// tests too (`n4tK7LYFxI0` = Spektrem - Shine, NCS).
const TEST_VIDEO_ID: &str = "n4tK7LYFxI0";
#[fixture]
fn rp() -> RustyPipe {
RustyPipe::builder()
.storage_dir(env!("CARGO_MANIFEST_DIR"))
.build()
.unwrap()
}
/// Sanity: iOS path returns stream URLs and never touches the deobf code.
#[rstest]
#[tokio::test]
async fn ios_player_returns_streams(rp: RustyPipe) {
let pd = rp
.query()
.player_from_client(TEST_VIDEO_ID, ClientType::Ios)
.await
.expect("iOS player_from_client should succeed");
assert_eq!(pd.details.id, TEST_VIDEO_ID);
assert!(
!pd.video_streams.is_empty() || !pd.video_only_streams.is_empty(),
"expected at least one video stream"
);
assert!(
!pd.audio_streams.is_empty(),
"expected at least one audio stream"
);
}
/// Sanity: TV path (which sets `needs_deobf=true` for the sig_timestamp request
/// payload, but the soft-fail patch keeps the call alive even when sig_fn/nsig_fn
/// regex extraction fails on a rotated player.js).
#[rstest]
#[tokio::test]
async fn tv_player_returns_streams(rp: RustyPipe) {
let pd = rp
.query()
.player_from_client(TEST_VIDEO_ID, ClientType::Tv)
.await
.expect("TV player_from_client should succeed even when sig deobf regex misses");
assert_eq!(pd.details.id, TEST_VIDEO_ID);
assert!(
!pd.video_streams.is_empty() || !pd.video_only_streams.is_empty(),
"expected at least one TV video stream"
);
}
/// The patched default-client order should pick iOS as primary and return
/// playable streams in the absence of botguard signing.
#[rstest]
#[tokio::test]
async fn default_client_order_returns_streams(rp: RustyPipe) {
let order = rp.query().player_client_order();
eprintln!("default client order (no botguard): {order:?}");
assert_eq!(
order[0],
ClientType::Ios,
"iOS should be the no-botguard primary"
);
let pd = rp
.query()
.player(TEST_VIDEO_ID)
.await
.expect("default-clients player() should succeed");
assert_eq!(pd.details.id, TEST_VIDEO_ID);
assert!(
!pd.video_streams.is_empty() || !pd.video_only_streams.is_empty(),
"expected at least one video stream from the default-clients path"
);
assert!(
!pd.audio_streams.is_empty(),
"expected at least one audio stream from the default-clients path"
);
// Probe one returned audio stream to confirm YT actually serves it.
// GET with Range 0-1023 + an iOS User-Agent because YT's googlevideo
// CDN tends to 403 HEAD requests and UA mismatches.
let stream_url = pd
.audio_streams
.first()
.expect("at least one audio stream")
.url
.clone();
eprintln!("probing first audio URL: {}", &stream_url[..stream_url.len().min(180)]);
let client = reqwest::Client::builder()
.user_agent(
"com.google.ios.youtube/19.45.4 (iPhone16,2; U; CPU iOS 18_1 like Mac OS X; en_US)",
)
.build()
.unwrap();
let resp = client
.get(&stream_url)
.header("Range", "bytes=0-1023")
.send()
.await
.expect("GET request to YT CDN should not error");
let status = resp.status();
let body_len = resp.bytes().await.map(|b| b.len()).unwrap_or(0);
eprintln!("response: {} bytes, status {}", body_len, status);
assert!(
status.is_success() || status.is_redirection(),
"audio URL Range-GET returned non-OK status: {} (body={} bytes; URL may need visitor_data or po_token)",
status,
body_len
);
assert!(
body_len > 0,
"audio URL returned OK but zero bytes — likely a sig-required URL we couldn't deobf"
);
}