strawcore/tests/foundation_smoke.rs
Kayos 46201c731f Phase 1 — Foundation
Mirror NPE's dependency-free spine in Rust:

* exceptions   — NetworkError + ParsingError + ContentUnavailable
                 + ExtractionError tree, with reqwest/serde_json conversions
* localization — Localization + ContentCountry, default (en, GB)
* downloader/  — Downloader trait, Request builder, Response,
                 reqwest blocking default impl
* page         — continuation-token carrier
* image        — Image + ImageSet + ResolutionLevel
                 (HEIGHT_UNKNOWN/WIDTH_UNKNOWN = -1)
* metainfo     — title/content/url/url_text grab-bag
* service      — StreamingService trait + LinkType + ServiceInfo
* newpipe      — process-global Downloader / Localization /
                 ContentCountry singleton

Foundational invariants nailed down (per SPEC §3):
* HTTP non-2xx returns Ok(Response); only 429 throws NetworkError::Recaptcha
* Response header keys lowercase-normalized
* Request.add_header PARITY with NPE bug (silent overwrite);
  append_header is our clean addition
* default Localization is en-GB
* No cookie jar in the default downloader

Tests: 7 unit + 7 live smoke against httpbin.org (gated on
'online-tests' feature). All green.
2026-05-24 16:32:36 -07:00

95 lines
3.3 KiB
Rust

// Phase 1 smoke — exercises the foundation against live httpbin.org.
//
// Per SPEC §4 Phase 1 "Done when": build a Request, send through default
// Downloader, parse Response, confirm latest_url follows redirects.
//
// These tests hit the network — gated on the `online` cfg so CI offline
// runs aren't broken.
#![cfg(feature = "online-tests")]
use std::sync::Arc;
use strawcore::downloader::request::Request;
use strawcore::downloader::ReqwestDownloader;
use strawcore::exceptions::NetworkError;
use strawcore::localization::{ContentCountry, Localization};
use strawcore::{Downloader, NewPipe};
#[test]
fn get_through_default_downloader() {
let dl = ReqwestDownloader::new().expect("build downloader");
let resp = dl.get("https://httpbin.org/get").expect("transport");
assert_eq!(resp.response_code(), 200);
assert!(resp.response_body().contains("\"url\""));
}
#[test]
fn latest_url_follows_redirects() {
let dl = ReqwestDownloader::new().expect("build downloader");
let resp = dl
.get("https://httpbin.org/redirect/3")
.expect("transport");
assert_eq!(resp.response_code(), 200);
assert!(
resp.latest_url().ends_with("/get"),
"latest_url should land at /get after 3 redirects, got {}",
resp.latest_url()
);
}
#[test]
fn non_2xx_returns_ok_not_err() {
let dl = ReqwestDownloader::new().expect("build downloader");
let resp = dl.get("https://httpbin.org/status/404").expect("transport");
assert_eq!(resp.response_code(), 404);
}
#[test]
fn http_429_surfaces_as_recaptcha_err() {
let dl = ReqwestDownloader::new().expect("build downloader");
let err = dl.get("https://httpbin.org/status/429").expect_err("429 must be NetworkError");
match err {
NetworkError::Recaptcha { url } => assert!(url.contains("/status/429")),
other => panic!("expected Recaptcha, got {other:?}"),
}
}
#[test]
fn localization_header_attached_when_enabled() {
let dl = ReqwestDownloader::new().expect("build downloader");
let req = Request::get("https://httpbin.org/headers")
.localization(Some(Localization::new("en", Some("GB".into()))))
.build();
let resp = dl.execute(req).expect("transport");
assert_eq!(resp.response_code(), 200);
assert!(
resp.response_body().to_ascii_lowercase().contains("accept-language"),
"Accept-Language should be echoed by httpbin"
);
assert!(resp.response_body().contains("en-GB"));
}
#[test]
fn header_keys_lowercased_in_response() {
let dl = ReqwestDownloader::new().expect("build downloader");
let resp = dl.get("https://httpbin.org/get").expect("transport");
for (k, _) in resp.response_headers() {
assert_eq!(k, &k.to_ascii_lowercase(), "header key {k} not lowercased");
}
}
#[test]
fn newpipe_singleton_wires_downloader() {
let dl = Arc::new(ReqwestDownloader::new().expect("build downloader"));
NewPipe::init_full(
dl.clone(),
Localization::default(),
ContentCountry::default(),
);
let from_global = NewPipe::downloader().expect("downloader registered");
let resp = from_global.get("https://httpbin.org/get").expect("transport");
assert_eq!(resp.response_code(), 200);
assert_eq!(NewPipe::preferred_localization().localization_code(), "en-GB");
}