diff --git a/proxmox/src/test/mod.rs b/proxmox/src/test/mod.rs index 82ac3e20..e3c4d418 100644 --- a/proxmox/src/test/mod.rs +++ b/proxmox/src/test/mod.rs @@ -1,2 +1,3 @@ pub mod io; pub mod task; +mod schema_verification; diff --git a/proxmox/src/test/schema_verification.rs b/proxmox/src/test/schema_verification.rs new file mode 100644 index 00000000..6338ff63 --- /dev/null +++ b/proxmox/src/test/schema_verification.rs @@ -0,0 +1,131 @@ +use anyhow::{bail, Error}; +use serde_json::{json, Value}; + +use crate::api::schema::*; + +static STRING_SCHEMA: Schema = StringSchema::new("A test string").schema(); + +static SIMPLE_OBJECT_SCHEMA: Schema = ObjectSchema::new( + "simple object schema", + &[ + ("prop1", false, &STRING_SCHEMA), + ("prop2", true, &STRING_SCHEMA), + ("prop3", false, &STRING_SCHEMA), + ] +).schema(); + +static SIMPLE_ARRAY_SCHEMA: Schema = ArraySchema::new("String list.", &STRING_SCHEMA).schema(); + +static NESTED_OBJECT_SCHEMA: Schema = ObjectSchema::new( + "nested object schema", + &[ + ("arr1", false, &SIMPLE_ARRAY_SCHEMA), + ("obj1", false, &SIMPLE_OBJECT_SCHEMA), + ("prop1", false, &STRING_SCHEMA), + ] +).schema(); + +fn compare_error( + expected: &[(&str, &str)], + err: Error, +) -> Result<(), Error> { + let err = match err.downcast_ref::() { + Some(err) => err, + None => bail!("unable to downcast error: {}", err), + }; + + let result = crate::try_block!({ + let errors = err.errors(); + + if errors.len() != expected.len() { + bail!("error list has different length: {} != {}", expected.len(), errors.len()); + } + + for i in 0..expected.len() { + if expected[i].0 != errors[i].0 { + bail!("error {} path differs: '{}' != '{}'", i, expected[i].0, errors[i].0); + } + if expected[i].1 != errors[i].1.to_string() { + bail!("error {} message differs: '{}' != '{}'", i, expected[i].1, errors[i].1); + } + + } + + Ok(()) + }); + + if result.is_err() { + println!("GOT: {:?}", err); + } + + result +} + +fn test_verify( + schema: &Schema, + data: &Value, + expected_errors: &[(&str, &str)], +) -> Result<(), Error> { + match verify_json(data, schema) { + Ok(_) => bail!("expected errors, but got Ok()"), + Err(err) => compare_error(expected_errors, err)?, + } + Ok(()) +} + +#[test] +fn verify_simple_object() -> Result<(), Error> { + + let simple_value = json!({"prop1": 1, "prop4": "abc"}); + + test_verify( + &SIMPLE_OBJECT_SCHEMA, + &simple_value, + &[ + ("prop1", "Expected string value."), + ("prop4", "schema does not allow additional properties."), + ("prop3", "property is missing and it is not optional."), + ], + )?; + + Ok(()) +} + +#[test] +fn verify_nested_object1() -> Result<(), Error> { + + let nested_value = json!({"prop1": 1, "prop4": "abc"}); + + test_verify( + &NESTED_OBJECT_SCHEMA, + &nested_value, + &[ + ("prop1", "Expected string value."), + ("prop4", "schema does not allow additional properties."), + ("arr1", "property is missing and it is not optional."), + ("obj1", "property is missing and it is not optional."), + ], + )?; + + Ok(()) +} + +#[test] +fn verify_nested_object2() -> Result<(), Error> { + + let nested_value = json!({"prop1": 1, "prop4": "abc", "obj1": {}, "arr1": ["abc", 0]}); + + test_verify( + &NESTED_OBJECT_SCHEMA, + &nested_value, + &[ + ("arr1/[1]", "Expected string value."), + ("obj1/prop1", "property is missing and it is not optional."), + ("obj1/prop3", "property is missing and it is not optional."), + ("prop1", "Expected string value."), + ("prop4", "schema does not allow additional properties."), + ], + )?; + + Ok(()) +}