use std::{ collections::HashMap, fmt::{self, Display, Formatter}, ops::Range, }; use poem::{http::StatusCode, test::TestClient, Result}; use poem_openapi::{ param::Query, payload::Payload, registry::{MetaApi, MetaSchema, Registry}, types::{multipart::JsonField, ParseFromJSON, Type}, validation, validation::ValidatorMeta, Multipart, Object, OpenApi, OpenApiService, Validator, }; use serde_json::json; #[test] fn test_u64() { #[derive(Object, Debug, Eq, PartialEq)] struct A { n: u64, } assert_eq!( A::parse_from_json(Some(json!({ "n": 1 }))).unwrap(), A { n: 1 } ); } #[test] fn test_multiple_of() { #[derive(Object, Debug, Eq, PartialEq)] struct A { #[oai(validator(multiple_of = "10"))] n: i32, } assert_eq!( A::parse_from_json(Some(json!({ "n": 20 }))).unwrap(), A { n: 20 } ); assert_eq!( A::parse_from_json(Some(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(validator(maximum(value = "500")))] n: i32, } assert_eq!( A::parse_from_json(Some(json!({ "n": 400 }))).unwrap(), A { n: 400 } ); assert_eq!( A::parse_from_json(Some(json!({ "n": 500 }))).unwrap(), A { n: 500 } ); assert_eq!( A::parse_from_json(Some(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(validator(maximum(value = "500", exclusive)))] n: i32, } assert_eq!( A::parse_from_json(Some(json!({ "n": 400 }))).unwrap(), A { n: 400 } ); assert_eq!( A::parse_from_json(Some(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(Some(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(validator(max_length = "5"))] value: String, } assert_eq!( A::parse_from_json(Some(json!({ "value": "abcd" }))).unwrap(), A { value: "abcd".to_string() } ); assert_eq!( A::parse_from_json(Some(json!({ "value": "abcde" }))).unwrap(), A { value: "abcde".to_string() } ); assert_eq!( A::parse_from_json(Some(json!({ "value": "שלום!" }))).unwrap(), A { value: "שלום!".to_string() } ); assert_eq!( A::parse_from_json(Some(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(validator(min_length = "5"))] value: String, } assert_eq!( A::parse_from_json(Some(json!({ "value": "abcde" }))).unwrap(), A { value: "abcde".to_string() } ); assert_eq!( A::parse_from_json(Some(json!({ "value": "abcdef" }))).unwrap(), A { value: "abcdef".to_string() } ); assert_eq!( A::parse_from_json(Some(json!({ "value": "abcd" }))) .unwrap_err() .into_message(), "failed to parse \"A\": field `value` verification failed. minLength(5)" ); assert_eq!( A::parse_from_json(Some(json!({ "value": "שלום" }))) .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(validator(pattern = r#"\[.*\]"#))] value: String, } assert_eq!( A::parse_from_json(Some(json!({ "value": "[123]" }))).unwrap(), A { value: "[123]".to_string() } ); assert_eq!( A::parse_from_json(Some(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(validator(max_items = "3"))] values: Vec, } assert_eq!( A::parse_from_json(Some(json!({ "values": ["1", "2", "3"] }))).unwrap(), A { values: vec!["1".to_string(), "2".to_string(), "3".to_string()], } ); assert_eq!( A::parse_from_json(Some(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(validator(min_items = "4"))] values: Vec, } assert_eq!( A::parse_from_json(Some(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(Some(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(validator(unique_items))] values: Vec, } assert_eq!( A::parse_from_json(Some(json!({ "values": ["1", "2", "3"] }))).unwrap(), A { values: vec!["1".to_string(), "2".to_string(), "3".to_string(),], } ); assert_eq!( A::parse_from_json(Some(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 test1( &self, #[oai(name = "v", validator(maximum(value = "100", exclusive)))] _v: Query, ) { } #[oai(path = "/test2", method = "get")] async fn test2( &self, #[oai(name = "v", validator(maximum(value = "100", exclusive)))] _v: Query, ) -> Result<()> { Ok(()) } } let api = OpenApiService::new(Api, "test1", "1.0"); let cli = TestClient::new(api); let resp = cli.get("/").query("v", &999).send().await; resp.assert_status(StatusCode::BAD_REQUEST); resp.assert_text( "failed to parse parameter `v`: verification failed. maximum(100, exclusive: true)", ) .await; 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) ); cli.get("/") .query("v", &50) .send() .await .assert_status_is_ok(); let resp = cli.get("/test2").query("v", &101).send().await; resp.assert_status(StatusCode::BAD_REQUEST); resp.assert_text( "failed to parse parameter `v`: verification failed. maximum(100, exclusive: true)", ) .await; cli.get("/test2") .query("v", &50) .send() .await .assert_status_is_ok(); } #[test] fn test_option() { #[derive(Object, Debug, Eq, PartialEq)] struct A { #[oai(validator(multiple_of = "10"))] n1: Option, #[oai(validator(multiple_of = "10"))] n2: Option>, } assert_eq!( A::parse_from_json(Some(json!({ "n1": 20 }))).unwrap(), A { n1: Some(20), n2: None } ); assert_eq!( A::parse_from_json(Some(json!({ "n2": 20 }))).unwrap(), A { n1: None, n2: Some(Some(20)), } ); assert_eq!( A::parse_from_json(Some(json!({ "n2": null }))).unwrap(), A { n1: None, n2: None } ); assert_eq!( A::parse_from_json(Some(json!({ "n1": 25 }))) .unwrap_err() .into_message(), "failed to parse \"A\": field `n1` verification failed. multipleOf(10)" ); assert_eq!( A::parse_from_json(Some(json!({ "n2": 25 }))) .unwrap_err() .into_message(), "failed to parse \"A\": field `n2` verification failed. multipleOf(10)" ); } #[test] fn test_multiple_validators() { #[derive(Object, Debug, Eq, PartialEq)] struct A { #[oai(validator(multiple_of = "10", maximum(value = "500")))] n: i32, } assert_eq!( A::parse_from_json(Some(json!({ "n": 20 }))).unwrap(), A { n: 20 } ); assert_eq!( A::parse_from_json(Some(json!({ "n": 25 }))) .unwrap_err() .into_message(), "failed to parse \"A\": field `n` verification failed. multipleOf(10)" ); assert_eq!( A::parse_from_json(Some(json!({ "n": 530 }))) .unwrap_err() .into_message(), "failed to parse \"A\": field `n` verification failed. maximum(500, exclusive: false)" ); } #[test] fn test_unsigned_integers() { #[derive(Object, Debug, Eq, PartialEq)] struct A { u8: u8, u16: u16, u32: u32, u64: u64, } assert_eq!( A::parse_from_json(Some(json!({ "u8": u8::MAX as u64, "u16": u16::MAX as u64, "u32": u32::MAX as u64, "u64": u64::MAX, }))) .unwrap(), A { u8: u8::MAX, u16: u16::MAX, u32: u32::MAX, u64: u64::MAX, } ); assert_eq!( A::parse_from_json(Some(json!({ "u8": u8::MAX as u64 + 1, "u16": u16::MAX as u64, "u32": u32::MAX as u64, "u64": u64::MAX, }))) .unwrap_err() .into_message(), "failed to parse \"integer(uint8)\": Only integers from 0 to 255 are accepted. (occurred while parsing \"A\")" ); } #[test] fn test_list_on_object() { #[derive(Object, Debug, Eq, PartialEq)] struct A { #[oai(validator(maximum(value = "10")))] n: Vec, } assert_eq!( A::parse_from_json(Some(json!({ "n": [1, 2, 3] }))).unwrap(), A { n: vec![1, 2, 3] } ); assert_eq!( A::parse_from_json(Some(json!({ "n": [1, 2, 3, 25] }))) .unwrap_err() .into_message(), "failed to parse \"A\": field `n` verification failed. maximum(10, exclusive: false)" ); let mut registry = Registry::default(); A::register(&mut registry); let schema = registry.schemas.get_mut("A").unwrap(); let (name, field_n) = schema.properties.remove(0); assert_eq!(name, "n"); let schema_n = field_n.unwrap_inline(); let schema_items = schema_n.items.as_ref().unwrap(); let schema_items = schema_items.unwrap_inline(); assert_eq!(schema_items.maximum, Some(10.0)); } #[test] fn test_list_on_multipart() { #[derive(Multipart, Debug, Eq, PartialEq)] struct A { #[oai(validator(maximum(value = "32")))] values: Vec>, } let schema_ref = A::schema_ref(); let schema: &MetaSchema = schema_ref.unwrap_inline(); assert_eq!(schema.ty, "object"); assert_eq!(schema.properties.len(), 1); assert_eq!(schema.properties[0].0, "values"); let schema_values = schema.properties[0].1.unwrap_inline(); assert_eq!(schema_values.ty, "array"); let schema_items = schema_values.items.as_ref().unwrap(); let schema_items = schema_items.unwrap_inline(); assert_eq!(schema_items.maximum, Some(32.0)); } #[test] fn test_both_max_items_and_max_length() { #[derive(Object, Debug, Eq, PartialEq)] struct A { #[oai(validator(max_items = 2, max_length = 3))] values: Vec, } assert_eq!( A::parse_from_json(Some(json!({ "values": ["a", "b"] }))).unwrap(), A { values: vec!["a".to_string(), "b".to_string()] } ); assert_eq!( A::parse_from_json(Some(json!({ "values": ["a", "b", "c"] }))) .unwrap_err() .into_message(), "failed to parse \"A\": field `values` verification failed. maxItems(2)" ); assert_eq!( A::parse_from_json(Some(json!({ "values": ["a", "bcde"] }))) .unwrap_err() .into_message(), "failed to parse \"A\": field `values` verification failed. maxLength(3)" ); } #[test] fn test_max_properties() { #[derive(Object, Debug, Eq, PartialEq)] struct A { #[oai(validator(max_properties = 3))] values: HashMap, } assert_eq!( A::parse_from_json(Some(json!({ "values": { "a": 1, "b": 2, "c": 3, }, }))) .unwrap(), A { values: { let mut map = HashMap::new(); map.insert("a".to_string(), 1); map.insert("b".to_string(), 2); map.insert("c".to_string(), 3); map }, } ); assert_eq!( A::parse_from_json(Some(json!({ "values": { "a": 1, "b": 2, "c": 3, "d": 4, }, }))) .unwrap_err() .into_message(), "failed to parse \"A\": field `values` verification failed. maxProperties(3)" ); let mut schema = MetaSchema::new("string"); validation::MaxProperties::new(3).update_meta(&mut schema); assert_eq!(schema.max_properties, Some(3)); } #[test] fn test_min_properties() { #[derive(Object, Debug, Eq, PartialEq)] struct A { #[oai(validator(min_properties = 2))] values: HashMap, } assert_eq!( A::parse_from_json(Some(json!({ "values": { "a": 1, "b": 2, "c": 3, }, }))) .unwrap(), A { values: { let mut map = HashMap::new(); map.insert("a".to_string(), 1); map.insert("b".to_string(), 2); map.insert("c".to_string(), 3); map }, } ); assert_eq!( A::parse_from_json(Some(json!({ "values": { "a": 1, }, }))) .unwrap_err() .into_message(), "failed to parse \"A\": field `values` verification failed. minProperties(2)" ); let mut schema = MetaSchema::new("string"); validation::MinProperties::new(3).update_meta(&mut schema); assert_eq!(schema.min_properties, Some(3)); } #[test] fn test_map_on_object() { #[derive(Object, Debug, Eq, PartialEq)] struct A { #[oai(validator(maximum(value = "100")))] values: HashMap, } assert_eq!( A::parse_from_json(Some(json!({ "values": { "a": 1, "b": 2, "c": 3, }, }))) .unwrap(), A { values: { let mut map = HashMap::new(); map.insert("a".to_string(), 1); map.insert("b".to_string(), 2); map.insert("c".to_string(), 3); map }, } ); assert_eq!( A::parse_from_json(Some(json!({ "values": { "a": 1, "b": 101, }, }))) .unwrap_err() .into_message(), "failed to parse \"A\": field `values` verification failed. maximum(100, exclusive: false)" ); let mut registry = Registry::default(); A::register(&mut registry); let schema = registry.schemas.get_mut("A").unwrap(); let (name, field_values) = schema.properties.remove(0); assert_eq!(name, "values"); let schema_values = field_values.unwrap_inline(); let schema_properties = schema_values.additional_properties.as_ref().unwrap(); let schema_properties = schema_properties.unwrap_inline(); assert_eq!(schema_properties.maximum, Some(100.0)); } #[test] fn test_custom_validator() { struct MyIntValidator(Range); impl Display for MyIntValidator { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.write_str("MyIntValidator") } } impl Validator for MyIntValidator { fn check(&self, value: &i32) -> bool { self.0.contains(value) } } #[derive(Object, Debug, Eq, PartialEq)] struct A { #[oai(validator(custom = "MyIntValidator(100..200)"))] value: i32, } assert_eq!( A::parse_from_json(Some(json!({ "value": 150, }))) .unwrap(), A { value: 150 } ); assert_eq!( A::parse_from_json(Some(json!({ "value": 200, }))) .unwrap_err() .into_message(), "failed to parse \"A\": field `value` verification failed. MyIntValidator" ); }