poem-openapi 0.6.4

OpenAPI support for Poem.
Documentation
use poem::{
    http::{StatusCode, Uri},
    Endpoint, IntoEndpoint, Request,
};
use poem_openapi::{
    registry::{MetaApi, MetaSchema},
    types::ParseFromJSON,
    validation,
    validation::ValidatorMeta,
    Object, OpenApi, OpenApiService,
};
use serde_json::json;

#[test]
fn test_multiple_of() {
    #[derive(Object, Debug, Eq, PartialEq)]
    struct A {
        #[oai(multiple_of = "10")]
        n: i32,
    }

    assert_eq!(A::parse_from_json(json!({ "n": 20 })).unwrap(), A { n: 20 });
    assert_eq!(
        A::parse_from_json(json!({ "n": 25 }))
            .unwrap_err()
            .into_message(),
        "failed to parse \"A\": field `n` verification failed. multipleOf(10)"
    );

    let mut schema = MetaSchema::new("string");
    validation::MultipleOf::new(10.0).update_meta(&mut schema);
    assert_eq!(schema.multiple_of, Some(10.0));
}

#[test]
fn test_maximum() {
    #[derive(Object, Debug, Eq, PartialEq)]
    struct A {
        #[oai(maximum(value = "500"))]
        n: i32,
    }

    assert_eq!(
        A::parse_from_json(json!({ "n": 400 })).unwrap(),
        A { n: 400 }
    );
    assert_eq!(
        A::parse_from_json(json!({ "n": 500 })).unwrap(),
        A { n: 500 }
    );
    assert_eq!(
        A::parse_from_json(json!({ "n": 530 }))
            .unwrap_err()
            .into_message(),
        "failed to parse \"A\": field `n` verification failed. maximum(500, exclusive: false)"
    );

    let mut schema = MetaSchema::new("string");
    validation::Maximum::new(10.0, false).update_meta(&mut schema);
    assert_eq!(schema.maximum, Some(10.0));
    assert_eq!(schema.exclusive_maximum, None);
}

#[test]
fn test_maximum_exclusive() {
    #[derive(Object, Debug, Eq, PartialEq)]
    struct A {
        #[oai(maximum(value = "500", exclusive))]
        n: i32,
    }

    assert_eq!(
        A::parse_from_json(json!({ "n": 400 })).unwrap(),
        A { n: 400 }
    );
    assert_eq!(
        A::parse_from_json(json!({ "n": 500 }))
            .unwrap_err()
            .into_message(),
        "failed to parse \"A\": field `n` verification failed. maximum(500, exclusive: true)"
    );
    assert_eq!(
        A::parse_from_json(json!({ "n": 530 }))
            .unwrap_err()
            .into_message(),
        "failed to parse \"A\": field `n` verification failed. maximum(500, exclusive: true)"
    );

    let mut schema = MetaSchema::new("string");
    validation::Maximum::new(10.0, true).update_meta(&mut schema);
    assert_eq!(schema.maximum, Some(10.0));
    assert_eq!(schema.exclusive_maximum, Some(true));
}

#[test]
fn test_max_length() {
    #[derive(Object, Debug, Eq, PartialEq)]
    struct A {
        #[oai(max_length = "5")]
        value: String,
    }

    assert_eq!(
        A::parse_from_json(json!({ "value": "abcd" })).unwrap(),
        A {
            value: "abcd".to_string()
        }
    );
    assert_eq!(
        A::parse_from_json(json!({ "value": "abcdef" }))
            .unwrap_err()
            .into_message(),
        "failed to parse \"A\": field `value` verification failed. maxLength(5)"
    );

    let mut schema = MetaSchema::new("string");
    validation::MaxLength::new(10).update_meta(&mut schema);
    assert_eq!(schema.max_length, Some(10));
}

#[test]
fn test_min_length() {
    #[derive(Object, Debug, Eq, PartialEq)]
    struct A {
        #[oai(min_length = "5")]
        value: String,
    }

    assert_eq!(
        A::parse_from_json(json!({ "value": "abcdef" })).unwrap(),
        A {
            value: "abcdef".to_string()
        }
    );
    assert_eq!(
        A::parse_from_json(json!({ "value": "abcd" }))
            .unwrap_err()
            .into_message(),
        "failed to parse \"A\": field `value` verification failed. minLength(5)"
    );

    let mut schema = MetaSchema::new("string");
    validation::MinLength::new(10).update_meta(&mut schema);
    assert_eq!(schema.min_length, Some(10));
}

#[test]
fn test_pattern() {
    #[derive(Object, Debug, Eq, PartialEq)]
    struct A {
        #[oai(pattern = r#"\[.*\]"#)]
        value: String,
    }

    assert_eq!(
        A::parse_from_json(json!({ "value": "[123]" })).unwrap(),
        A {
            value: "[123]".to_string()
        }
    );
    assert_eq!(
        A::parse_from_json(json!({ "value": "123" }))
            .unwrap_err()
            .into_message(),
        r#"failed to parse "A": field `value` verification failed. pattern("\[.*\]")"#
    );

    let mut schema = MetaSchema::new("string");
    validation::Pattern::new(r#"\[.*\]"#).update_meta(&mut schema);
    assert_eq!(schema.pattern.as_deref(), Some(r#"\[.*\]"#));
}

#[test]
fn test_max_items() {
    #[derive(Object, Debug, Eq, PartialEq)]
    struct A {
        #[oai(max_items = "3")]
        values: Vec<String>,
    }

    assert_eq!(
        A::parse_from_json(json!({ "values": ["1", "2", "3"] })).unwrap(),
        A {
            values: vec!["1".to_string(), "2".to_string(), "3".to_string()],
        }
    );
    assert_eq!(
        A::parse_from_json(json!({ "values": ["1", "2", "3", "4"] }))
            .unwrap_err()
            .into_message(),
        "failed to parse \"A\": field `values` verification failed. maxItems(3)"
    );

    let mut schema = MetaSchema::new("string");
    validation::MaxItems::new(10).update_meta(&mut schema);
    assert_eq!(schema.max_items, Some(10));
}

#[test]
fn test_min_items() {
    #[derive(Object, Debug, Eq, PartialEq)]
    struct A {
        #[oai(min_items = "4")]
        values: Vec<String>,
    }

    assert_eq!(
        A::parse_from_json(json!({ "values": ["1", "2", "3", "4"] })).unwrap(),
        A {
            values: vec![
                "1".to_string(),
                "2".to_string(),
                "3".to_string(),
                "4".to_string()
            ],
        }
    );
    assert_eq!(
        A::parse_from_json(json!({ "values": ["1", "2", "3"] }))
            .unwrap_err()
            .into_message(),
        "failed to parse \"A\": field `values` verification failed. minItems(4)"
    );

    let mut schema = MetaSchema::new("string");
    validation::MinItems::new(10).update_meta(&mut schema);
    assert_eq!(schema.min_items, Some(10));
}

#[test]
fn test_unique_items() {
    #[derive(Object, Debug, Eq, PartialEq)]
    struct A {
        #[oai(unique_items)]
        values: Vec<String>,
    }

    assert_eq!(
        A::parse_from_json(json!({ "values": ["1", "2", "3"] })).unwrap(),
        A {
            values: vec!["1".to_string(), "2".to_string(), "3".to_string(),],
        }
    );
    assert_eq!(
        A::parse_from_json(json!({ "values": ["1", "2", "2"] }))
            .unwrap_err()
            .into_message(),
        "failed to parse \"A\": field `values` verification failed. uniqueItems()"
    );

    let mut schema = MetaSchema::new("string");
    validation::UniqueItems.update_meta(&mut schema);
    assert_eq!(schema.unique_items, Some(true));
}

#[tokio::test]
async fn param_validator() {
    struct Api;

    #[OpenApi]
    impl Api {
        #[oai(path = "/", method = "get")]
        async fn test(
            &self,
            #[oai(name = "v", in = "query", maximum(value = "100", exclusive))] _v: i32,
        ) {
        }
    }

    let api = OpenApiService::new(Api).into_endpoint();
    let mut resp = api
        .call(Request::builder().uri(Uri::from_static("/?v=999")).finish())
        .await;
    assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
    assert_eq!(
        resp.take_body().into_string().await.unwrap(),
        "failed to parse param `v`: verification failed. maximum(100, exclusive: true)"
    );

    let meta: MetaApi = Api::meta().remove(0);
    assert_eq!(
        meta.paths[0].operations[0].params[0]
            .schema
            .unwrap_inline()
            .maximum,
        Some(100.0)
    );
    assert_eq!(
        meta.paths[0].operations[0].params[0]
            .schema
            .unwrap_inline()
            .exclusive_maximum,
        Some(true)
    );

    let resp = api
        .call(Request::builder().uri(Uri::from_static("/?v=50")).finish())
        .await;
    assert_eq!(resp.status(), StatusCode::OK);
}

#[test]
fn test_option() {
    #[derive(Object, Debug, Eq, PartialEq)]
    struct A {
        #[oai(multiple_of = "10")]
        n: Option<i32>,
    }

    assert_eq!(
        A::parse_from_json(json!({ "n": 20 })).unwrap(),
        A { n: Some(20) }
    );

    assert_eq!(
        A::parse_from_json(json!({ "n": null })).unwrap(),
        A { n: None }
    );

    assert_eq!(
        A::parse_from_json(json!({ "n": 25 }))
            .unwrap_err()
            .into_message(),
        "failed to parse \"A\": field `n` verification failed. multipleOf(10)"
    );
}

#[test]
fn test_multiple_validators() {
    #[derive(Object, Debug, Eq, PartialEq)]
    struct A {
        #[oai(multiple_of = "10", maximum(value = "500"))]
        n: i32,
    }

    assert_eq!(A::parse_from_json(json!({ "n": 20 })).unwrap(), A { n: 20 });
    assert_eq!(
        A::parse_from_json(json!({ "n": 25 }))
            .unwrap_err()
            .into_message(),
        "failed to parse \"A\": field `n` verification failed. multipleOf(10)"
    );

    assert_eq!(
        A::parse_from_json(json!({ "n": 530 }))
            .unwrap_err()
            .into_message(),
        "failed to parse \"A\": field `n` verification failed. maximum(500, exclusive: false)"
    );
}