Web

Testing

Tests need no server, no port and no browser. app().test("") builds your app on a fresh in-memory database, migrated and seeded, and testing::Client browses it like a visitor. Run them with cargo test.

A route test#

use rustclamp::web::testing::Client;

fn app(env: &str) -> rustclamp::web::Router {
    my_site::app().test(env).0
}

#[test]
fn health_returns_json() {
    let routes = app("");
    let response = Client::new(&routes).get("/api/health");
    assert_eq!((response.status, response.content_type), (200, "application/json"));
}

test returns the database as well, let (routes, db) = my_site::app().test(""), so a test can check what a form stored.

Forms, sessions and CSRF#

The client keeps the cookies it is given, so the session carries over between requests. post sends the CSRF token of the last page it fetched, as a browser submitting that page's form would, so open the page first:

let client = Client::new(&routes);
client.get("/contact");
let sent = client.post("/contact", &[("name", "Neo"), ("email", "[email protected]")]);
assert_eq!((sent.status, sent.location()), (302, Some("/contact")));
assert!(client.get("/contact").see("Thanks, your message was received."));

// No token: proves the group really has csrf().
assert_eq!(client.post_raw("/contact", &[("name", "Neo")]).status, 419);

What to assert#

response.status, response.content_typeThe status and type
response.see("text")The body contains text, as written (so check escaping with see("<b>"))
response.location(), response.header("retry-after")Headers
response.body_text()The whole body

Settings in tests#

Pass .env lines to test instead of reading the machine's .env. Rate limits count per client, so from sets the address:

#[test]
fn api_is_throttled_per_client() {
    let routes = app("API_PER_MINUTE=1\n");
    let client = Client::new(&routes).from("10.0.0.1");
    assert_eq!(client.get("/api/health").status, 200);
    assert_eq!(client.get("/api/health").status, 429);
}

Without the app#

A bare Router answers a Request directly, for testing a handler or middleware alone: routes.handle(&Request::get("/search?q=clamp")). Build requests with Request::post(path).with_body("a=1"), .with_header(...) and .with_peer(ip).