#![cfg(feature = "live-tests")]
use fetchkit::{FetchError, FetchOptions, FetchRequest, FetchResponse, FetcherRegistry};
fn live_options() -> FetchOptions {
FetchOptions {
enable_markdown: true,
enable_text: true,
..Default::default()
}
}
fn registry() -> FetcherRegistry {
FetcherRegistry::with_defaults()
}
fn is_network_error(err: &FetchError) -> bool {
matches!(
err,
FetchError::FirstByteTimeout
| FetchError::BlockedUrl
| FetchError::ConnectError(_)
| FetchError::ClientBuildError(_)
| FetchError::RequestError(_)
)
}
async fn fetch_or_skip(url: &str) -> Option<FetchResponse> {
let req = FetchRequest::new(url);
match registry().fetch(req, live_options()).await {
Ok(resp) => Some(resp),
Err(e) if is_network_error(&e) => {
eprintln!("SKIPPED (network): {url} — {e}");
None
}
Err(e) => panic!("unexpected fetcher error for {url}: {e}"),
}
}
async fn fetch_markdown_or_skip(url: &str) -> Option<FetchResponse> {
let req = FetchRequest::new(url).as_markdown();
match registry().fetch(req, live_options()).await {
Ok(resp) => Some(resp),
Err(e) if is_network_error(&e) => {
eprintln!("SKIPPED (network): {url} — {e}");
None
}
Err(e) => panic!("unexpected fetcher error for {url}: {e}"),
}
}
mod live_github_repo {
use super::*;
#[tokio::test]
async fn fetches_repo_metadata() {
let Some(resp) = fetch_or_skip("https://github.com/rust-lang/rust").await else {
return;
};
assert_eq!(resp.status_code, 200);
let content = resp.content.expect("should have content");
assert!(
content.contains("rust-lang/rust") || content.to_lowercase().contains("rust"),
"content should mention the repo"
);
assert!(!content.is_empty());
}
}
mod live_github_issue {
use super::*;
#[tokio::test]
async fn fetches_issue() {
let Some(resp) = fetch_or_skip("https://github.com/rust-lang/rust/issues/1").await else {
return;
};
assert_eq!(resp.status_code, 200);
let content = resp.content.expect("should have content");
assert!(!content.is_empty());
}
}
mod live_github_code {
use super::*;
#[tokio::test]
async fn fetches_source_file() {
let Some(resp) =
fetch_or_skip("https://github.com/rust-lang/rust/blob/master/README.md").await
else {
return;
};
assert_eq!(resp.status_code, 200);
let content = resp.content.expect("should have content");
assert!(
content.to_lowercase().contains("rust"),
"README should mention Rust"
);
}
}
mod live_twitter {
use super::*;
#[tokio::test]
async fn fetches_tweet() {
let Some(resp) = fetch_or_skip("https://x.com/rustlang/status/1821986021505405014").await
else {
return;
};
if resp.status_code == 200 {
assert!(resp.content.is_some());
}
}
}
mod live_stackoverflow {
use super::*;
#[tokio::test]
async fn fetches_question() {
let Some(resp) = fetch_or_skip(
"https://stackoverflow.com/questions/218384/what-is-a-nullpointerexception-and-how-do-i-fix-it",
).await else {
return;
};
assert_eq!(resp.status_code, 200);
let content = resp.content.expect("should have content");
assert!(
content.to_lowercase().contains("null"),
"content should mention null"
);
}
}
mod live_package_registry {
use super::*;
#[tokio::test]
async fn fetches_crate() {
let Some(resp) = fetch_or_skip("https://crates.io/crates/serde").await else {
return;
};
assert_eq!(resp.status_code, 200);
let content = resp.content.expect("should have content");
assert!(
content.to_lowercase().contains("serde"),
"content should mention serde"
);
}
#[tokio::test]
async fn fetches_pypi_package() {
let Some(resp) = fetch_or_skip("https://pypi.org/project/requests/").await else {
return;
};
assert_eq!(resp.status_code, 200);
let content = resp.content.expect("should have content");
assert!(
content.to_lowercase().contains("requests"),
"content should mention requests"
);
}
#[tokio::test]
async fn fetches_npm_package() {
let Some(resp) = fetch_or_skip("https://www.npmjs.com/package/express").await else {
return;
};
assert_eq!(resp.status_code, 200);
let content = resp.content.expect("should have content");
assert!(
content.to_lowercase().contains("express"),
"content should mention express"
);
}
}
mod live_wikipedia {
use super::*;
#[tokio::test]
async fn fetches_article() {
let Some(resp) =
fetch_or_skip("https://en.wikipedia.org/wiki/Rust_(programming_language)").await
else {
return;
};
assert_eq!(resp.status_code, 200);
let content = resp.content.expect("should have content");
assert!(
content.to_lowercase().contains("rust"),
"article should mention Rust"
);
}
}
mod live_youtube {
use super::*;
#[tokio::test]
async fn fetches_video_metadata() {
let Some(resp) = fetch_or_skip("https://www.youtube.com/watch?v=jNQXAC9IVRw").await else {
return;
};
assert_eq!(resp.status_code, 200);
let content = resp.content.expect("should have content");
assert!(!content.is_empty());
}
}
mod live_arxiv {
use super::*;
#[tokio::test]
async fn fetches_paper() {
let Some(resp) = fetch_or_skip("https://arxiv.org/abs/1706.03762").await else {
return;
};
assert_eq!(resp.status_code, 200);
let content = resp.content.expect("should have content");
assert!(
content.to_lowercase().contains("attention"),
"paper should mention attention"
);
}
}
mod live_hackernews {
use super::*;
#[tokio::test]
async fn fetches_story() {
let Some(resp) = fetch_or_skip("https://news.ycombinator.com/item?id=1").await else {
return;
};
assert_eq!(resp.status_code, 200);
let content = resp.content.expect("should have content");
assert!(!content.is_empty());
}
}
mod live_rss_feed {
use super::*;
#[tokio::test]
async fn fetches_rss() {
let Some(resp) = fetch_or_skip("https://blog.rust-lang.org/feed.xml").await else {
return;
};
assert_eq!(resp.status_code, 200);
let content = resp.content.expect("should have content");
assert!(
content.to_lowercase().contains("rust"),
"Rust blog feed should mention Rust"
);
}
}
mod live_docs_site {
use super::*;
#[tokio::test]
async fn fetches_docs_rs() {
let Some(resp) = fetch_or_skip("https://docs.rs/serde/latest/serde/").await else {
return;
};
assert_eq!(resp.status_code, 200);
let content = resp.content.expect("should have content");
assert!(
content.to_lowercase().contains("serde"),
"docs.rs page should mention serde"
);
}
}
mod live_default {
use super::*;
#[tokio::test]
async fn fetches_plain_html() {
let Some(resp) = fetch_markdown_or_skip("https://example.com").await else {
return;
};
assert_eq!(resp.status_code, 200);
let content = resp.content.expect("should have content");
assert!(
content.contains("Example Domain"),
"example.com should contain 'Example Domain'"
);
assert_eq!(resp.format, Some("markdown".to_string()));
}
}