tsukuyomi 0.5.3

Asynchronous Web framework for Rust
Documentation
use {
    http::{header, Request, StatusCode},
    tsukuyomi::{
        config::prelude::*, //
        extractor,
        App,
    },
    tsukuyomi_server::test::ResponseExt,
};

#[test]
fn empty_routes() -> tsukuyomi_server::Result<()> {
    let app = App::create(())?;
    let mut server = tsukuyomi_server::test::server(app)?;

    let response = server.perform("/")?;
    assert_eq!(response.status(), StatusCode::NOT_FOUND);

    Ok(())
}

#[test]
fn single_route() -> tsukuyomi_server::Result<()> {
    let app = App::create(
        path!("/hello") //
            .to(endpoint::call(|| "Tsukuyomi")),
    )?;
    let mut server = tsukuyomi_server::test::server(app)?;

    let response = server.perform("/hello")?;

    assert_eq!(response.status(), StatusCode::OK);
    assert_eq!(
        response.header(header::CONTENT_TYPE)?,
        "text/plain; charset=utf-8"
    );
    assert_eq!(response.header(header::CONTENT_LENGTH)?, "9");
    assert_eq!(*response.body().to_bytes(), b"Tsukuyomi"[..]);

    Ok(())
}

#[test]
fn post_body() -> tsukuyomi_server::Result<()> {
    let app = App::create(
        path!("/hello") //
            .to(endpoint::post()
                .extract(tsukuyomi::extractor::body::plain())
                .call(|body: String| body)),
    )?;
    let mut server = tsukuyomi_server::test::server(app)?;

    let response = server.perform(
        Request::post("/hello") //
            .body("Hello, Tsukuyomi."),
    )?;

    assert_eq!(response.status(), StatusCode::OK);
    assert_eq!(
        response.header(header::CONTENT_TYPE)?,
        "text/plain; charset=utf-8"
    );
    assert_eq!(response.header(header::CONTENT_LENGTH)?, "17");
    assert_eq!(*response.body().to_bytes(), b"Hello, Tsukuyomi."[..]);

    Ok(())
}

#[test]
fn cookies() -> tsukuyomi_server::Result<()> {
    use cookie::Cookie;
    use time::Duration;

    let expires_in = time::now() + Duration::days(7);

    let app = App::create(chain![
        path!("/login") //
            .to(endpoint::any()
                .extract(extractor::ready(move |input| {
                    input.cookies.jar()?.add(
                        Cookie::build("session", "dummy_session_id")
                            .domain("www.example.com")
                            .expires(expires_in)
                            .finish(),
                    );
                    Ok::<_, tsukuyomi::error::Error>(())
                }))
                .call(|| "Logged in")),
        path!("/logout") //
            .to(endpoint::any()
                .extract(extractor::ready(|input| {
                    input.cookies.jar()?.remove(Cookie::named("session"));
                    Ok::<_, tsukuyomi::error::Error>(())
                }))
                .call(|| "Logged out")),
    ])?;
    let mut server = tsukuyomi_server::test::server(app)?;

    let response = server.perform("/login")?;

    let cookie_str = response.header(header::SET_COOKIE)?.to_str()?;
    let cookie = Cookie::parse_encoded(cookie_str)?;
    assert_eq!(cookie.name(), "session");
    assert_eq!(cookie.domain(), Some("www.example.com"));
    assert_eq!(
        cookie.expires().map(|tm| tm.to_timespec().sec),
        Some(expires_in.to_timespec().sec)
    );

    let response = server.perform(Request::get("/logout").header(header::COOKIE, cookie_str))?;

    let cookie_str = response.header(header::SET_COOKIE)?.to_str()?;
    let cookie = Cookie::parse_encoded(cookie_str)?;
    assert_eq!(cookie.name(), "session");
    assert_eq!(cookie.value(), "");
    assert_eq!(cookie.max_age(), Some(Duration::zero()));
    assert!(cookie.expires().map_or(false, |tm| tm < time::now()));

    let response = server.perform("/logout")?;
    assert!(!response.headers().contains_key(header::SET_COOKIE));

    Ok(())
}

#[test]
fn default_options() -> tsukuyomi_server::Result<()> {
    let app = App::create(
        path!("/path")
            .to(endpoint::allow_only("GET, POST")?.call(|| "reply"))
            .modify(tsukuyomi::modifiers::default_options()),
    )?;
    let mut server = tsukuyomi_server::test::server(app)?;

    let response = server.perform("/path")?;
    assert_eq!(response.status(), 200);
    assert_eq!(response.body().to_utf8()?, "reply");

    let response = server.perform(Request::options("/path"))?;
    assert_eq!(response.status(), 204);
    assert_eq!(response.header(header::ALLOW)?, "GET, POST, OPTIONS");
    assert_eq!(response.header(header::CONTENT_LENGTH)?, "0");

    Ok(())
}

#[test]
fn map_output() -> tsukuyomi_server::Result<()> {
    let app = App::create(
        path!("/")
            .to(endpoint::reply(42))
            .modify(tsukuyomi::modifiers::map_output(|num: u32| num.to_string())),
    )?;
    let mut server = tsukuyomi_server::test::server(app)?;

    let response = server.perform("/")?;
    assert_eq!(response.body().to_utf8()?, "42");

    Ok(())
}

#[test]
fn scoped_fallback() -> tsukuyomi_server::Result<()> {
    use std::sync::{Arc, Mutex};

    let marker = Arc::new(Mutex::new(vec![]));

    let app = App::create(chain![
        path!("*") //
            .to(endpoint::call({
                let marker = marker.clone();
                move || {
                    marker.lock().unwrap().push("F1");
                    "f1"
                }
            })),
        mount("/api/v1/").with(chain![
            path!("*") //
                .to(endpoint::call({
                    let marker = marker.clone();
                    move || {
                        marker.lock().unwrap().push("F2");
                        "f2"
                    }
                })),
            path!("/posts") //
                .to(endpoint::post().reply("posts")),
            mount("/events").with(
                path!("/new") //
                    .to(endpoint::post().reply("new_event")),
            ),
        ]),
    ])?;

    let mut server = tsukuyomi_server::test::server(app)?;

    let _ = server.perform("/")?;
    assert_eq!(&**marker.lock().unwrap(), &*vec!["F1"]);

    marker.lock().unwrap().clear();
    let _ = server.perform("/api/v1/p")?;
    assert_eq!(&**marker.lock().unwrap(), &*vec!["F2"]);

    marker.lock().unwrap().clear();
    let _ = server.perform("/api/v1/posts")?;
    assert!(marker.lock().unwrap().is_empty());

    marker.lock().unwrap().clear();
    let _ = server.perform("/api/v1/events/new")?;
    assert!(marker.lock().unwrap().is_empty());

    Ok(())
}