diff --git a/.github/main.workflow b/.github/main.workflow index b6d446b1..5828a763 100644 --- a/.github/main.workflow +++ b/.github/main.workflow @@ -15,19 +15,31 @@ action "checkout-submodules" { action "test-stable" { needs = ["checkout-submodules"] uses = "docker://rust:1.36" - args = "cargo test --features psl" + args = "cargo test --features psl -- --test-threads=8" + env = { + RUST_BACKTRACE = "1" + RUST_LOG = "warn" + } } action "test-nightly" { needs = ["checkout-submodules"] uses = "docker://rustlang/rust:nightly" - args = "cargo test --features psl,nightly" + args = "cargo test --features psl,nightly -- --test-threads=8" + env = { + RUST_BACKTRACE = "1" + RUST_LOG = "warn" + } } action "examples" { needs = ["checkout-submodules"] uses = "docker://rust:1.36" args = "cargo run --release --example simple" + env = { + RUST_BACKTRACE = "1" + RUST_LOG = "warn" + } } action "release-published" { diff --git a/.gitignore b/.gitignore index aff68af4..8094f6ea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ .vscode target Cargo.lock -/image.jpg diff --git a/src/client.rs b/src/client.rs index 6ad54aa2..4c411d28 100644 --- a/src/client.rs +++ b/src/client.rs @@ -40,10 +40,6 @@ lazy_static! { /// .redirect_policy(RedirectPolicy::Limit(10)) /// .preferred_http_version(http::Version::HTTP_2) /// .build()?; -/// -/// let mut response = client.get("https://example.org")?; -/// let body = response.body_mut().text()?; -/// println!("{}", body); /// # Ok::<(), chttp::Error>(()) /// ``` #[derive(Default)] @@ -264,7 +260,7 @@ impl fmt::Debug for HttpClientBuilder { /// /// ## Examples /// -/// ``` +/// ```no_run /// use chttp::prelude::*; /// /// // Create a new client using reasonable defaults. @@ -280,7 +276,7 @@ impl fmt::Debug for HttpClientBuilder { /// /// Customizing the client configuration: /// -/// ``` +/// ```no_run /// use chttp::{config::RedirectPolicy, prelude::*}; /// use std::time::Duration; /// @@ -395,7 +391,7 @@ impl HttpClient { /// /// ## Examples /// - /// ``` + /// ```no_run /// use chttp::prelude::*; /// /// let client = HttpClient::default(); @@ -431,7 +427,7 @@ impl HttpClient { /// /// ## Examples /// - /// ``` + /// ```no_run /// use chttp::prelude::*; /// /// let client = HttpClient::default(); @@ -511,7 +507,7 @@ impl HttpClient { /// /// ## Examples /// - /// ``` + /// ```no_run /// use chttp::prelude::*; /// /// let client = HttpClient::default(); diff --git a/src/lib.rs b/src/lib.rs index 55281589..0309e1e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ //! Sending requests is as easy as calling a single function. Let's make a //! simple GET request to an example website: //! -//! ``` +//! ```no_run //! use chttp::prelude::*; //! //! let mut response = chttp::get("https://example.org")?; @@ -20,14 +20,14 @@ //! Sending a POST request is also easy, and takes an additional argument for //! the request body: //! -//! ``` +//! ```no_run //! let response = chttp::post("https://httpbin.org/post", "make me a salad")?; //! # Ok::<(), chttp::Error>(()) //! ``` //! //! cHTTP provides several other simple functions for common HTTP request types: //! -//! ``` +//! ```no_run //! chttp::put("https://httpbin.org/put", "have a salad")?; //! chttp::head("https://httpbin.org/get")?; //! chttp::delete("https://httpbin.org/delete")?; @@ -39,7 +39,7 @@ //! cHTTP is not limited to canned HTTP verbs; you can customize requests by //! creating your own `Request` object and then `send`ing that. //! -//! ``` +//! ```no_run //! use chttp::prelude::*; //! //! let response = Request::post("https://httpbin.org/post") @@ -60,7 +60,7 @@ //! methods provided by the [`RequestBuilderExt`](prelude::RequestBuilderExt) //! trait: //! -//! ``` +//! ```no_run //! use chttp::prelude::*; //! use std::time::Duration; //! @@ -98,7 +98,7 @@ //! an asynchronous variant that ends with `_async` in the name. Here is our //! first example rewritten to use async/await syntax (nightly only): //! -//! ``` +//! ```no_run //! # #![cfg_attr(feature = "nightly", feature(async_await))] //! # use chttp::prelude::*; //! # diff --git a/src/response.rs b/src/response.rs index 20b671d5..01c11d36 100644 --- a/src/response.rs +++ b/src/response.rs @@ -24,7 +24,7 @@ pub trait ResponseExt { /// /// ## Examples /// - /// ``` + /// ```no_run /// # use chttp::prelude::*; /// chttp::get("https://httpbin.org/image/jpeg")? /// .copy_to_file("image.jpg")?; diff --git a/tests/headers.rs b/tests/headers.rs new file mode 100644 index 00000000..f51e10df --- /dev/null +++ b/tests/headers.rs @@ -0,0 +1,28 @@ +use mockito::{Matcher, mock, server_url}; + +speculate::speculate! { + before { + env_logger::try_init().ok(); + } + + test "accept headers populated by default" { + let m = mock("GET", "/") + .match_header("accept", "*/*") + .match_header("accept-encoding", "deflate, gzip") + .create(); + + chttp::get(server_url()).unwrap(); + + m.assert(); + } + + test "user agent contains expected format" { + let m = mock("GET", "/") + .match_header("user-agent", Matcher::Regex(r"^curl/\S+ chttp/\S+$".into())) + .create(); + + chttp::get(server_url()).unwrap(); + + m.assert(); + } +} diff --git a/tests/methods.rs b/tests/methods.rs new file mode 100644 index 00000000..05eb8f71 --- /dev/null +++ b/tests/methods.rs @@ -0,0 +1,62 @@ +use chttp::prelude::*; +use mockito::{mock, server_url}; + +speculate::speculate! { + before { + env_logger::try_init().ok(); + } + + test "GET request" { + let m = mock("GET", "/").create(); + + chttp::get(server_url()).unwrap(); + + m.assert(); + } + + test "HEAD request" { + let m = mock("HEAD", "/").create(); + + chttp::head(server_url()).unwrap(); + + m.assert(); + } + + test "POST request" { + let m = mock("POST", "/").create(); + + chttp::post(server_url(), ()).unwrap(); + + m.assert(); + } + + test "PUT request" { + let m = mock("PUT", "/").create(); + + chttp::put(server_url(), ()).unwrap(); + + m.assert(); + } + + test "DELETE request" { + let m = mock("DELETE", "/").create(); + + chttp::delete(server_url()).unwrap(); + + m.assert(); + } + + test "arbitrary FOOBAR request" { + let m = mock("FOOBAR", "/").create(); + + Request::builder() + .method("FOOBAR") + .uri(server_url()) + .body(()) + .unwrap() + .send() + .unwrap(); + + m.assert(); + } +} diff --git a/tests/redirects.rs b/tests/redirects.rs index 39ebbe31..2289d27d 100644 --- a/tests/redirects.rs +++ b/tests/redirects.rs @@ -2,33 +2,31 @@ use chttp::config::RedirectPolicy; use chttp::prelude::*; use mockito::{mock, server_url}; -mod utils; - speculate::speculate! { before { - utils::logging(); + env_logger::try_init().ok(); } test "response 301 no follow" { let m = mock("GET", "/") .with_status(301) - .with_header("Location", "/b") + .with_header("Location", "/2") .create(); let response = chttp::get(server_url()).unwrap(); assert_eq!(response.status(), 301); - assert_eq!(response.headers()["Location"], "/b"); + assert_eq!(response.headers()["Location"], "/2"); m.assert(); } test "response 301 auto follow" { let m1 = mock("GET", "/") .with_status(301) - .with_header("Location", "/b") + .with_header("Location", "/2") .create(); - let m2 = mock("GET", "/b") + let m2 = mock("GET", "/2") .with_status(200) .with_body("ok") .create(); @@ -47,13 +45,42 @@ speculate::speculate! { m2.assert(); } - test "redirect limit is respected" { + test "headers are reset every redirect" { let m1 = mock("GET", "/") .with_status(301) .with_header("Location", "/b") + .with_header("X-Foo", "aaa") + .with_header("X-Bar", "zzz") .create(); let m2 = mock("GET", "/b") + .with_header("X-Foo", "bbb") + .with_header("X-Baz", "zzz") + .create(); + + let response = Request::get(server_url()) + .redirect_policy(RedirectPolicy::Follow) + .body(()) + .unwrap() + .send() + .unwrap(); + + assert_eq!(response.status(), 200); + assert_eq!(response.headers()["X-Foo"], "bbb"); + assert_eq!(response.headers()["X-Baz"], "zzz"); + assert!(!response.headers().contains_key("X-Bar")); + + m1.assert(); + m2.assert(); + } + + test "redirect limit is respected" { + let m1 = mock("GET", "/") + .with_status(301) + .with_header("Location", "/2") + .create(); + + let m2 = mock("GET", "/2") .with_status(301) .with_header("Location", "/") .create(); diff --git a/tests/response.rs b/tests/response_body.rs similarity index 81% rename from tests/response.rs rename to tests/response_body.rs index 413aeadb..98ae9c2f 100644 --- a/tests/response.rs +++ b/tests/response_body.rs @@ -1,35 +1,33 @@ use mockito::{mock, server_url}; -mod utils; - speculate::speculate! { before { - utils::logging(); + env_logger::try_init().ok(); } test "simple response body" { - let mock = mock("GET", "/") + let m = mock("GET", "/") .with_body("hello world") .create(); let mut response = chttp::get(server_url()).unwrap(); let response_text = response.body_mut().text().unwrap(); - assert_eq!(response_text, "hello world"); - mock.assert(); + assert_eq!(response_text, "hello world"); + m.assert(); } test "large response body" { let body = "wow so large ".repeat(1000); - let mock = mock("GET", "/") + let m = mock("GET", "/") .with_body(&body) .create(); let mut response = chttp::get(server_url()).unwrap(); let response_text = response.body_mut().text().unwrap(); - assert_eq!(response_text, body); - mock.assert(); + assert_eq!(response_text, body); + m.assert(); } } diff --git a/tests/status.rs b/tests/status.rs new file mode 100644 index 00000000..ab16d225 --- /dev/null +++ b/tests/status.rs @@ -0,0 +1,20 @@ +use mockito::{mock, server_url}; + +speculate::speculate! { + before { + env_logger::try_init().ok(); + } + + test "returns correct response code" { + for status in [200u16, 202, 204, 302, 308, 400, 403, 404, 418, 429, 451, 500, 503].iter() { + let m = mock("GET", "/") + .with_status(*status as usize) + .create(); + + let response = chttp::get(server_url()).unwrap(); + + assert_eq!(response.status(), *status); + m.assert(); + } + } +} diff --git a/tests/timeouts.rs b/tests/timeouts.rs index 692c793e..d947e0d3 100644 --- a/tests/timeouts.rs +++ b/tests/timeouts.rs @@ -3,11 +3,9 @@ use mockito::{mock, server_url}; use std::thread::sleep; use std::time::Duration; -mod utils; - speculate::speculate! { before { - utils::logging(); + env_logger::try_init().ok(); } /// Issue #3 diff --git a/tests/utils/mod.rs b/tests/utils/mod.rs deleted file mode 100644 index 170fe3aa..00000000 --- a/tests/utils/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -use std::env; -use std::sync::Once; - -pub fn logging() { - static ONCE: Once = Once::new(); - - ONCE.call_once(|| { - env::set_var("RUST_BACKTRACE", "1"); - env::set_var("RUST_LOG", "chttp=debug,curl=debug"); - env_logger::init(); - }); -}