rtest 0.2.2

integration test building framework
Documentation
use reqwest::Method;
use rtest_derive::rtest;

use crate::{shop, Webserver, WebserverTestError, WebserverTestError::*, WebserverWithItems};

#[rtest(module = "webshop::items")]
pub async fn validate_items_without_mediatype(server: &Webserver) -> Result<(), WebserverTestError> {
    let res = server.req(Method::POST, "items").send().await.map_err(Reqwest)?;
    assert_eq!(415, res.status());
    Ok(())
}

#[rtest(module = "webshop::items")]
pub async fn validate_items_without_info(server: &Webserver) -> Result<(), WebserverTestError> {
    let res = server
        .req(Method::POST, "items")
        .json(&())
        .send()
        .await
        .map_err(Reqwest)?;
    assert_eq!(422, res.status());
    Ok(())
}

// Todo: implement a way to allow Any(Webserver, WebserverWithItems)
/// Note: Webserver as Input and Output not tool, as if we fail, mid-test the
/// Webserver becomes tainted
#[rtest(module = "webshop::items")]
pub async fn create_delete_item(server: Webserver) -> Result<Webserver, WebserverTestError> {
    let res = server
        .req(Method::POST, "items")
        .json(&shop::ItemInfo {
            title: "Car".to_owned(),
            price: 4000.0,
        })
        .send()
        .await
        .map_err(Reqwest)?;
    let item_id = res.json::<shop::ItemId>().await.map_err(Response)?;

    let res = server
        .req(Method::DELETE, format!("items/{item_id}"))
        .send()
        .await
        .map_err(Reqwest)?;
    assert_eq!(200, res.status());

    Ok(server)
}

#[rtest(module = "webshop::items")]
pub async fn add_items(server: Webserver) -> Result<WebserverWithItems, WebserverTestError> {
    let res = server
        .req(Method::POST, "items")
        .json(&shop::ItemInfo {
            title: "Book".to_owned(),
            price: 1.5,
        })
        .send()
        .await
        .map_err(Reqwest)?;
    let book_id = res.json::<shop::ItemId>().await.map_err(Response)?;

    let res = server
        .req(Method::POST, "items")
        .json(&shop::ItemInfo {
            title: "Cookies".to_owned(),
            price: 2.0,
        })
        .send()
        .await
        .map_err(Reqwest)?;
    let cookie_id = res.json::<shop::ItemId>().await.map_err(Response)?;
    assert_eq!(cookie_id, book_id + 1);

    Ok(WebserverWithItems {
        webserver: server,
        book_id,
        cookie_id,
    })
}

#[rtest(module = "webshop::items")]
pub async fn get_items(server: &WebserverWithItems) -> Result<(), WebserverTestError> {
    let book_id = server.book_id;
    let cookie_id = server.cookie_id;

    let res = server
        .webserver
        .req(Method::GET, format!("items/{book_id}"))
        .send()
        .await
        .map_err(Reqwest)?;
    assert_eq!(200, res.status());
    let book: shop::Item = res.json::<shop::Item>().await.map_err(Response)?;
    assert_eq!(book_id, book.id);
    assert_eq!("Book", &book.title);

    let res = server
        .webserver
        .req(Method::GET, format!("items/{cookie_id}"))
        .send()
        .await
        .map_err(Reqwest)?;
    assert_eq!(200, res.status());
    let cookie: shop::Item = res.json::<shop::Item>().await.map_err(Response)?;
    assert_eq!(cookie_id, cookie.id);
    assert_eq!("Cookies", &cookie.title);

    Ok(())
}

#[rtest(module = "webshop::items")]
pub async fn undeploy_items_server(server: WebserverWithItems) -> Result<Webserver, WebserverTestError> {
    let book_id = server.book_id;
    let cookie_id = server.cookie_id;
    let webserver = server.webserver;

    let res = webserver
        .req(Method::DELETE, format!("items/{book_id}"))
        .send()
        .await
        .map_err(Reqwest)?;
    assert_eq!(200, res.status());
    let res = webserver
        .req(Method::DELETE, format!("items/{cookie_id}"))
        .send()
        .await
        .map_err(Reqwest)?;
    assert_eq!(200, res.status());

    Ok(webserver)
}