]> git.proxmox.com Git - proxmox.git/blob - proxmox-schema/tests/schema_verification.rs
schema: drop periods after errors
[proxmox.git] / proxmox-schema / tests / schema_verification.rs
1 use anyhow::{bail, Error};
2 use serde_json::{json, Value};
3
4 use proxmox_schema::*;
5
6 static STRING_SCHEMA: Schema = StringSchema::new("A test string").schema();
7
8 static SIMPLE_OBJECT_SCHEMA: Schema = ObjectSchema::new(
9 "simple object schema",
10 &[
11 ("prop1", false, &STRING_SCHEMA),
12 ("prop2", true, &STRING_SCHEMA),
13 ("prop3", false, &STRING_SCHEMA),
14 ],
15 )
16 .schema();
17
18 static SIMPLE_PROPERTY_STRING_SCHEMA: Schema = StringSchema::new("simple property string")
19 .format(&ApiStringFormat::PropertyString(&SIMPLE_OBJECT_SCHEMA))
20 .schema();
21
22 static SIMPLE_ARRAY_SCHEMA: Schema = ArraySchema::new("String list.", &STRING_SCHEMA).schema();
23
24 static NESTED_OBJECT_SCHEMA: Schema = ObjectSchema::new(
25 "nested object schema",
26 &[
27 ("arr1", false, &SIMPLE_ARRAY_SCHEMA),
28 ("obj1", false, &SIMPLE_OBJECT_SCHEMA),
29 ("prop1", false, &STRING_SCHEMA),
30 ],
31 )
32 .schema();
33
34 static NESTED_PROPERTY_SCHEMA: Schema = ObjectSchema::new(
35 "object with property strings",
36 &[("ps1", false, &SIMPLE_PROPERTY_STRING_SCHEMA)],
37 )
38 .schema();
39
40 static ANOTHER_OBJECT_SCHEMA: Schema = ObjectSchema::new(
41 "another simple object schema",
42 &[
43 ("another1", false, &STRING_SCHEMA),
44 ("another2", true, &STRING_SCHEMA),
45 ],
46 )
47 .schema();
48
49 static OBJECT_WITH_ADDITIONAL: Schema = ObjectSchema::new(
50 "object allowing additional properties",
51 &[
52 ("regular1", false, &STRING_SCHEMA),
53 ("regular2", true, &STRING_SCHEMA),
54 ],
55 )
56 .additional_properties(true)
57 .schema();
58
59 static ALL_OF_SCHEMA_NO_ADDITIONAL: Schema = AllOfSchema::new(
60 "flattening 2 objects together",
61 &[&SIMPLE_OBJECT_SCHEMA, &ANOTHER_OBJECT_SCHEMA],
62 )
63 .schema();
64
65 static ALL_OF_SCHEMA_ADDITIONAL: Schema = AllOfSchema::new(
66 "flattening 2 objects together where 1 allows additional properties",
67 &[&SIMPLE_OBJECT_SCHEMA, &OBJECT_WITH_ADDITIONAL],
68 )
69 .schema();
70
71 fn compare_error(expected: &[(&str, &str)], err: Error) -> Result<(), Error> {
72 let err = match err.downcast_ref::<ParameterError>() {
73 Some(err) => err,
74 None => bail!("unable to downcast error: {}", err),
75 };
76
77 let result = (move || {
78 let errors = err.errors();
79
80 if errors.len() != expected.len() {
81 bail!(
82 "error list has different length: {} != {}",
83 expected.len(),
84 errors.len()
85 );
86 }
87
88 for i in 0..expected.len() {
89 if expected[i].0 != errors[i].0 {
90 bail!(
91 "error {} path differs: '{}' != '{}'",
92 i,
93 expected[i].0,
94 errors[i].0
95 );
96 }
97 if expected[i].1 != errors[i].1.to_string() {
98 bail!(
99 "error {} message differs: '{}' != '{}'",
100 i,
101 expected[i].1,
102 errors[i].1
103 );
104 }
105 }
106
107 Ok(())
108 })();
109
110 if result.is_err() {
111 println!("GOT: {:?}", err);
112 }
113
114 result
115 }
116
117 fn test_verify(
118 schema: &Schema,
119 data: &Value,
120 expected_errors: &[(&str, &str)],
121 ) -> Result<(), Error> {
122 match schema.verify_json(data) {
123 Ok(_) => bail!("expected errors, but got Ok()"),
124 Err(err) => compare_error(expected_errors, err)?,
125 }
126 Ok(())
127 }
128
129 #[test]
130 fn verify_simple_object() -> Result<(), Error> {
131 let simple_value = json!({"prop1": 1, "prop4": "abc"});
132
133 test_verify(
134 &SIMPLE_OBJECT_SCHEMA,
135 &simple_value,
136 &[
137 ("prop1", "Expected string value."),
138 ("prop4", "schema does not allow additional properties"),
139 ("prop3", "property is missing and it is not optional"),
140 ],
141 )?;
142
143 Ok(())
144 }
145
146 #[test]
147 fn verify_nested_object1() -> Result<(), Error> {
148 let nested_value = json!({"prop1": 1, "prop4": "abc"});
149
150 test_verify(
151 &NESTED_OBJECT_SCHEMA,
152 &nested_value,
153 &[
154 ("prop1", "Expected string value."),
155 ("prop4", "schema does not allow additional properties"),
156 ("arr1", "property is missing and it is not optional"),
157 ("obj1", "property is missing and it is not optional"),
158 ],
159 )?;
160
161 Ok(())
162 }
163
164 #[test]
165 fn verify_nested_object2() -> Result<(), Error> {
166 let nested_value = json!({"prop1": 1, "prop4": "abc", "obj1": {}, "arr1": ["abc", 0]});
167
168 test_verify(
169 &NESTED_OBJECT_SCHEMA,
170 &nested_value,
171 &[
172 ("arr1/[1]", "Expected string value."),
173 ("obj1/prop1", "property is missing and it is not optional"),
174 ("obj1/prop3", "property is missing and it is not optional"),
175 ("prop1", "Expected string value."),
176 ("prop4", "schema does not allow additional properties"),
177 ],
178 )?;
179
180 Ok(())
181 }
182
183 #[test]
184 fn verify_nested_property1() -> Result<(), Error> {
185 let value = json!({"ps1": "abc"});
186
187 test_verify(
188 &NESTED_PROPERTY_SCHEMA,
189 &value,
190 &[(
191 "ps1",
192 "value without key, but schema does not define a default key",
193 )],
194 )?;
195
196 Ok(())
197 }
198
199 #[test]
200 fn verify_nested_property2() -> Result<(), Error> {
201 let value = json!({"ps1": "abc=1"});
202
203 test_verify(
204 &NESTED_PROPERTY_SCHEMA,
205 &value,
206 &[
207 ("ps1/abc", "schema does not allow additional properties"),
208 ("ps1/prop1", "property is missing and it is not optional"),
209 ("ps1/prop3", "property is missing and it is not optional"),
210 ],
211 )?;
212
213 Ok(())
214 }
215
216 #[test]
217 fn verify_nested_property3() -> Result<(), Error> {
218 let value = json!({"ps1": ""});
219
220 test_verify(
221 &NESTED_PROPERTY_SCHEMA,
222 &value,
223 &[
224 ("ps1/prop1", "property is missing and it is not optional"),
225 ("ps1/prop3", "property is missing and it is not optional"),
226 ],
227 )?;
228
229 Ok(())
230 }
231
232 #[test]
233 fn verify_all_of_schema() -> Result<(), Error> {
234 let value = json!({
235 "prop1": "hello",
236 "prop3": "hello",
237 "another1": "another hello",
238 });
239 ALL_OF_SCHEMA_NO_ADDITIONAL
240 .verify_json(&value)
241 .expect("all of schema failed to verify valid object");
242
243 let value = json!({
244 "prop1": "hello",
245 "prop3": "hello",
246 });
247 test_verify(
248 &ALL_OF_SCHEMA_NO_ADDITIONAL,
249 &value,
250 &[("another1", "property is missing and it is not optional")],
251 )?;
252
253 let value = json!({
254 "prop1": "hello",
255 "prop3": "hello",
256 "another1": "another hello",
257 "additional": "additional value",
258 });
259 test_verify(
260 &ALL_OF_SCHEMA_NO_ADDITIONAL,
261 &value,
262 &[("additional", "schema does not allow additional properties")],
263 )?;
264
265 Ok(())
266 }
267
268 #[test]
269 fn verify_all_of_schema_with_additional() -> Result<(), Error> {
270 let value = json!({
271 "prop1": "hello",
272 "prop3": "hello",
273 "regular1": "another hello",
274 "more": "additional property",
275 });
276 ALL_OF_SCHEMA_ADDITIONAL
277 .verify_json(&value)
278 .expect("all of schema failed to verify valid object");
279
280 let value = json!({
281 "prop1": "hello",
282 "prop3": "hello",
283 "more": "additional property",
284 });
285 test_verify(
286 &ALL_OF_SCHEMA_ADDITIONAL,
287 &value,
288 &[("regular1", "property is missing and it is not optional")],
289 )?;
290
291 Ok(())
292 }