#![allow(clippy::unwrap_used, clippy::expect_used)]
mod common;
use common::TestServer;
use http_body_util::Full;
use hyper::body::Bytes;
use hyper::Response;
use tokio::net::TcpListener;
#[tokio::test]
async fn server_sends_empty_response() {
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
let _server_task = tokio::spawn(async move {
let (stream, _) = listener.accept().await.unwrap();
drop(stream); });
let mut easy = liburlx::Easy::new();
easy.url(&format!("http://{addr}/")).unwrap();
let result = easy.perform_async().await;
assert!(result.is_err(), "empty response should error");
}
#[tokio::test]
async fn large_response_body() {
let large_body = "x".repeat(1_000_000); let body_clone = large_body.clone();
let server =
TestServer::start(move |_req| Response::new(Full::new(Bytes::from(body_clone.clone()))))
.await;
let mut easy = liburlx::Easy::new();
easy.url(&server.url("/")).unwrap();
let response = easy.perform_async().await.unwrap();
assert_eq!(response.status(), 200);
assert_eq!(response.body().len(), 1_000_000);
}
#[tokio::test]
async fn status_100_continue() {
let server = TestServer::start(|_req| Response::new(Full::new(Bytes::from("ok")))).await;
let mut easy = liburlx::Easy::new();
easy.url(&server.url("/")).unwrap();
let response = easy.perform_async().await.unwrap();
assert_eq!(response.status(), 200);
}
#[tokio::test]
async fn status_418_teapot() {
let server = TestServer::start(|_req| {
Response::builder().status(418).body(Full::new(Bytes::from("I'm a teapot"))).unwrap()
})
.await;
let mut easy = liburlx::Easy::new();
easy.url(&server.url("/")).unwrap();
let response = easy.perform_async().await.unwrap();
assert_eq!(response.status(), 418);
assert_eq!(response.body(), b"I'm a teapot");
}
#[tokio::test]
async fn status_503_service_unavailable() {
let server = TestServer::start(|_req| {
Response::builder()
.status(503)
.header("Retry-After", "120")
.body(Full::new(Bytes::from("service unavailable")))
.unwrap()
})
.await;
let mut easy = liburlx::Easy::new();
easy.url(&server.url("/")).unwrap();
let response = easy.perform_async().await.unwrap();
assert_eq!(response.status(), 503);
assert_eq!(response.header("retry-after"), Some("120"));
}
#[tokio::test]
async fn empty_url_error() {
let mut easy = liburlx::Easy::new();
let result = easy.url("");
assert!(result.is_err(), "empty URL should error");
}
#[tokio::test]
async fn perform_without_url() {
let mut easy = liburlx::Easy::new();
let result = easy.perform_async().await;
assert!(result.is_err(), "perform without URL should error");
}
#[tokio::test]
async fn sequential_requests_different_paths() {
let server = TestServer::start(|req| {
let path = req.uri().path().to_string();
Response::new(Full::new(Bytes::from(path)))
})
.await;
let mut easy = liburlx::Easy::new();
for i in 0..5 {
let path = format!("/path{i}");
easy.url(&server.url(&path)).unwrap();
let response = easy.perform_async().await.unwrap();
assert_eq!(response.status(), 200);
assert_eq!(response.body(), path.as_bytes());
}
}
#[tokio::test]
async fn very_short_timeout() {
let mut easy = liburlx::Easy::new();
easy.url("http://192.0.2.1:12345/").unwrap();
easy.connect_timeout(std::time::Duration::from_millis(1));
let result = easy.perform_async().await;
assert!(result.is_err());
}
#[tokio::test]
async fn multi_mix_errors_and_successes() {
let server = TestServer::start(|_req| Response::new(Full::new(Bytes::from("ok")))).await;
let mut multi = liburlx::Multi::new();
let mut good = liburlx::Easy::new();
good.url(&server.url("/good")).unwrap();
multi.add(good);
let bad_listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let bad_addr = bad_listener.local_addr().unwrap();
drop(bad_listener);
let mut bad = liburlx::Easy::new();
bad.url(&format!("http://{bad_addr}/")).unwrap();
multi.add(bad);
let results = multi.perform().await;
assert_eq!(results.len(), 2);
assert!(results.iter().any(Result::is_ok), "at least one should succeed");
}
#[tokio::test]
async fn response_no_content_type() {
let server = TestServer::start(|_req| {
Response::builder().status(200).body(Full::new(Bytes::from("raw bytes"))).unwrap()
})
.await;
let mut easy = liburlx::Easy::new();
easy.url(&server.url("/")).unwrap();
let response = easy.perform_async().await.unwrap();
assert_eq!(response.status(), 200);
assert_eq!(response.body(), b"raw bytes");
}
#[tokio::test]
async fn content_length_zero() {
let server = TestServer::start(|_req| {
Response::builder()
.status(200)
.header("Content-Length", "0")
.body(Full::new(Bytes::new()))
.unwrap()
})
.await;
let mut easy = liburlx::Easy::new();
easy.url(&server.url("/")).unwrap();
let response = easy.perform_async().await.unwrap();
assert_eq!(response.status(), 200);
assert!(response.body().is_empty());
}
#[tokio::test]
async fn many_set_cookies() {
let server = TestServer::start(|_req| {
let mut builder = Response::builder();
for i in 0..20 {
builder = builder.header("Set-Cookie", format!("cookie{i}=val{i}"));
}
builder.body(Full::new(Bytes::from("ok"))).unwrap()
})
.await;
let mut easy = liburlx::Easy::new();
easy.cookie_jar(true);
easy.url(&server.url("/set")).unwrap();
let response = easy.perform_async().await.unwrap();
assert_eq!(response.status(), 200);
}