sift-query-sqlx 0.1.0

PostgreSQL renderer and sqlx executor for SiftQuery
Documentation
#[cfg(all(feature = "axum", feature = "actix-web", feature = "poem"))]
use sift_query_sqlx::SearchParamConfig;
use sift_query_sqlx::SearchParams;

const FORM: &str = "id=2&id=1&id-op=il&name=%E4%B8%AD+%2B%26%3D";
const MEDIA: &str = "application/x-www-form-urlencoded;charset=UTF-8";

#[test]
fn transport_rules_and_query_budgets_are_shared() {
    let limits = sift_query_sqlx::core::QueryLimits::default();
    let get = SearchParams::parse_request("GET", Some(FORM), None, &[], &limits).unwrap();
    let post =
        SearchParams::parse_request("POST", None, Some(MEDIA), FORM.as_bytes(), &limits).unwrap();
    assert_eq!(get, post);
    assert_eq!(post.raw().entries()[3].1, "δΈ­ +&=");
    for (method, query, media, body, status, code) in [
        (
            "POST",
            Some("id=1"),
            Some(MEDIA),
            "id=2",
            400,
            "conflicting_query_body",
        ),
        (
            "POST",
            None,
            Some("application/json"),
            "{}",
            415,
            "unsupported_query_media_type",
        ),
        ("PUT", None, None, "", 405, "query_method_not_allowed"),
        ("POST", None, Some(MEDIA), "id=%GG", 400, "invalid_query"),
    ] {
        let error = SearchParams::parse_request(method, query, media, body.as_bytes(), &limits)
            .unwrap_err();
        assert_eq!((error.0.status, error.0.code), (status, code));
    }
    let limits = sift_query_sqlx::core::QueryLimits {
        max_raw_query_bytes: 3,
        ..limits
    };
    assert_eq!(
        SearchParams::parse_request("GET", Some("id=1"), None, &[], &limits)
            .unwrap_err()
            .0
            .code,
        "query_too_long"
    );
    assert_eq!(
        SearchParams::parse_request("POST", None, Some(MEDIA), b"id=1", &limits)
            .unwrap_err()
            .0
            .code,
        "query_body_too_large"
    );
}

#[cfg(all(feature = "axum", feature = "actix-web", feature = "poem"))]
#[actix_web::test]
async fn actual_extractors_accept_get_post_and_enforce_configured_body_limits() {
    use axum::extract::FromRequest;
    for method in ["GET", "POST"] {
        let uri = if method == "GET" {
            format!("/users?{FORM}")
        } else {
            "/users".to_owned()
        };
        let body = if method == "POST" { FORM } else { "" };
        let axum_request = axum::http::Request::builder()
            .method(method)
            .uri(&uri)
            .header("content-type", MEDIA)
            .body(axum::body::Body::from(body))
            .unwrap();
        let axum_params = SearchParams::from_request(axum_request, &()).await.unwrap();
        let (actix_request, mut payload) = actix_web::test::TestRequest::with_uri(&uri)
            .method(method.parse().unwrap())
            .insert_header(("content-type", MEDIA))
            .set_payload(body)
            .to_http_parts();
        let actix_params =
            <SearchParams as actix_web::FromRequest>::from_request(&actix_request, &mut payload)
                .await
                .unwrap();
        let (poem_request, mut body) = poem::Request::builder()
            .method(method.parse().unwrap())
            .uri(uri.parse().unwrap())
            .content_type(MEDIA)
            .body(body)
            .split();
        let poem_params =
            <SearchParams as poem::FromRequest>::from_request(&poem_request, &mut body)
                .await
                .unwrap();
        assert_eq!(axum_params, actix_params);
        assert_eq!(axum_params, poem_params);
    }
    let config = SearchParamConfig {
        limits: sift_query_sqlx::core::QueryLimits {
            max_raw_query_bytes: 3,
            ..Default::default()
        },
        ..Default::default()
    };
    let request = axum::http::Request::builder()
        .method("POST")
        .uri("/users")
        .header("content-type", MEDIA)
        .extension(config.clone())
        .body(axum::body::Body::from("id=1"))
        .unwrap();
    assert_eq!(
        SearchParams::from_request(request, &())
            .await
            .unwrap_err()
            .0
            .status,
        413
    );
    let (request, mut payload) = actix_web::test::TestRequest::post()
        .uri("/users")
        .insert_header(("content-type", MEDIA))
        .app_data(config.clone())
        .set_payload("id=1")
        .to_http_parts();
    assert_eq!(
        <SearchParams as actix_web::FromRequest>::from_request(&request, &mut payload)
            .await
            .unwrap_err()
            .0
            .status,
        413
    );
    let (request, mut body) = poem::Request::builder()
        .method(poem::http::Method::POST)
        .uri_str("/users")
        .extension(config)
        .content_type(MEDIA)
        .body("id=1")
        .split();
    assert_eq!(
        <SearchParams as poem::FromRequest>::from_request(&request, &mut body)
            .await
            .unwrap_err()
            .status()
            .as_u16(),
        413
    );
}