View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.juneau.httppart;
18  
19  import static org.apache.juneau.TestUtils.*;
20  import static org.junit.jupiter.api.Assertions.*;
21  
22  import java.util.*;
23  
24  import org.apache.juneau.*;
25  import org.apache.juneau.annotation.*;
26  import org.apache.juneau.http.annotation.*;
27  import org.junit.jupiter.api.*;
28  
29  class HttpPartSchema_Response_Test extends TestBase {
30  
31  	//-----------------------------------------------------------------------------------------------------------------
32  	// Basic test
33  	//-----------------------------------------------------------------------------------------------------------------
34  	@Test void a01_basic() {
35  		assertDoesNotThrow(()->HttpPartSchema.create().build());
36  	}
37  
38  	//-----------------------------------------------------------------------------------------------------------------
39  	// @Response
40  	//-----------------------------------------------------------------------------------------------------------------
41  
42  	@Response(
43  		schema=@Schema(
44  			t="number",
45  			f="int32",
46  			max="1",
47  			min="2",
48  			mo="3",
49  			p="4",
50  			maxl=1,
51  			minl=2,
52  			maxi=3,
53  			mini=4,
54  			maxp=5,
55  			minp=6,
56  			emax=true,
57  			emin=true,
58  			ui=true,
59  			df={"c1","c2"},
60  			e="e1,e2",
61  			items=@Items(
62  				t="integer",
63  				f="int64",
64  				cf="ssv",
65  				max="5",
66  				min="6",
67  				mo="7",
68  				p="8",
69  				maxl=5,
70  				minl=6,
71  				maxi=7,
72  				mini=8,
73  				emax=false,
74  				emin=false,
75  				ui=false,
76  				df={"c3","c4"},
77  				e="e3,e4",
78  				items=@SubItems(
79  					t="string",
80  					f="float",
81  					cf="tsv",
82  					max="9",
83  					min="10",
84  					mo="11",
85  					p="12",
86  					maxl=9,
87  					minl=10,
88  					maxi=11,
89  					mini=12,
90  					emax=true,
91  					emin=true,
92  					ui=true,
93  					df={"c5","c6"},
94  					e="e5,e6",
95  					items={
96  						"type:'array',",
97  						"format:'double',",
98  						"collectionFormat:'pipes',",
99  						"maximum:'13',",
100 						"minimum:'14',",
101 						"multipleOf:'15',",
102 						"pattern:'16',",
103 						"maxLength:13,",
104 						"minLength:14,",
105 						"maxItems:15,",
106 						"minItems:16,",
107 						"exclusiveMaximum:false,",
108 						"exclusiveMinimum:false,",
109 						"uniqueItems:false,",
110 						"default:'c7\\nc8',",
111 						"enum:['e7','e8']",
112 					}
113 				)
114 			)
115 		)
116 	)
117 	public static class A05 {}
118 
119 	@Test void a05_basic_nestedItems_onClass() {
120 		var s = HttpPartSchema.create().apply(Response.class, A05.class).noValidate().build();
121 
122 		assertBean(
123 			s,
124 			"type,format,maximum,minimum,multipleOf,pattern,maxLength,minLength,maxItems,minItems,maxProperties,minProperties,exclusiveMaximum,exclusiveMinimum,uniqueItems,enum,default",
125 			"NUMBER,INT32,1,2,3,4,1,2,3,4,5,6,true,true,true,[e1,e2],c1\nc2"
126 		);
127 
128 		var items = s.getItems();
129 		assertBean(
130 			items,
131 			"type,format,collectionFormat,maximum,minimum,multipleOf,pattern,maxLength,minLength,maxItems,minItems,exclusiveMaximum,exclusiveMinimum,uniqueItems,enum,default",
132 			"INTEGER,INT64,SSV,5,6,7,8,5,6,7,8,false,false,false,[e3,e4],c3\nc4"
133 		);
134 
135 		items = items.getItems();
136 		assertBean(
137 			items,
138 			"type,format,collectionFormat,maximum,minimum,multipleOf,pattern,maxLength,minLength,maxItems,minItems,exclusiveMaximum,exclusiveMinimum,uniqueItems,enum,default",
139 			"STRING,FLOAT,TSV,9,10,11,12,9,10,11,12,true,true,true,[e5,e6],c5\nc6"
140 		);
141 
142 		items = items.getItems();
143 		assertBean(
144 			items,
145 			"type,format,collectionFormat,maximum,minimum,multipleOf,pattern,maxLength,minLength,maxItems,minItems,exclusiveMaximum,exclusiveMinimum,uniqueItems,enum,default",
146 			"ARRAY,DOUBLE,PIPES,13,14,15,16,13,14,15,16,false,false,false,[e7,e8],c7\nc8"
147 		);
148 	}
149 
150 	//-----------------------------------------------------------------------------------------------------------------
151 	// String input validations.
152 	//-----------------------------------------------------------------------------------------------------------------
153 
154 	@Response(
155 		schema=@Schema(
156 			p="x.*",
157 			aev=true
158 		)
159 	)
160 	public static class B02a {}
161 
162 	@Test void b02a_pattern() throws Exception {
163 		var s = HttpPartSchema.create().apply(Response.class, B02a.class).build();
164 
165 		s.validateInput("x");
166 		s.validateInput("xx");
167 
168 		assertThrowsWithMessage(SchemaValidationException.class, "Value does not match expected pattern.  Must match pattern: x.*", ()->s.validateInput(""));
169 		assertThrowsWithMessage(SchemaValidationException.class, "Value does not match expected pattern.  Must match pattern: x.*", ()->s.validateInput("y"));
170 	}
171 
172 	@Response(
173 		schema=@Schema(
174 			items=@Items(
175 				p="w.*",
176 				items=@SubItems(
177 					p="x.*",
178 					items={
179 						"pattern:'y.*',",
180 						"items:{pattern:'z.*'}"
181 					}
182 				)
183 			)
184 		)
185 	)
186 	public static class B02b {}
187 
188 	@Response(
189 		schema=@Schema(
190 			minl=2, maxl=3
191 		)
192 	)
193 	public static class B03a {}
194 
195 	@Test void b03a_length() throws Exception {
196 		var s = HttpPartSchema.create().apply(Response.class, B03a.class).build();
197 		s.validateInput("12");
198 		s.validateInput("123");
199 		s.validateInput(null);
200 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum length of value not met.", ()->s.validateInput("1"));
201 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum length of value exceeded.", ()->s.validateInput("1234"));
202 	}
203 
204 	@Response(
205 		schema=@Schema(
206 			items=@Items(
207 				minl=2, maxl=3,
208 				items=@SubItems(
209 					minl=3, maxl=4,
210 					items={
211 						"minLength:4,maxLength:5,",
212 						"items:{minLength:5,maxLength:6}"
213 					}
214 				)
215 			)
216 		)
217 	)
218 	public static class B03b {}
219 
220 	@Test void b03b_length_items() throws Exception {
221 		var s = HttpPartSchema.create().apply(Response.class, B03b.class).build();
222 
223 		s.getItems().validateInput("12");
224 		s.getItems().getItems().validateInput("123");
225 		s.getItems().getItems().getItems().validateInput("1234");
226 		s.getItems().getItems().getItems().getItems().validateInput("12345");
227 
228 		s.getItems().validateInput("123");
229 		s.getItems().getItems().validateInput("1234");
230 		s.getItems().getItems().getItems().validateInput("12345");
231 		s.getItems().getItems().getItems().getItems().validateInput("123456");
232 
233 		s.getItems().validateInput(null);
234 		s.getItems().getItems().validateInput(null);
235 		s.getItems().getItems().getItems().validateInput(null);
236 		s.getItems().getItems().getItems().getItems().validateInput(null);
237 
238 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum length of value not met.", ()->s.getItems().validateInput("1"));
239 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum length of value not met.", ()->s.getItems().getItems().validateInput("12"));
240 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum length of value not met.", ()->s.getItems().getItems().getItems().validateInput("123"));
241 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum length of value not met.", ()->s.getItems().getItems().getItems().getItems().validateInput("1234"));
242 
243 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum length of value exceeded.", ()->s.getItems().validateInput("1234"));
244 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum length of value exceeded.", ()->s.getItems().getItems().validateInput("12345"));
245 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum length of value exceeded.", ()->s.getItems().getItems().getItems().validateInput("123456"));
246 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum length of value exceeded.", ()->s.getItems().getItems().getItems().getItems().validateInput("1234567"));
247 	}
248 
249 	@Response(schema=@Schema(e="X,Y"))
250 	public static class B04a {}
251 
252 	@Test void b04a_enum() throws Exception {
253 		var s = HttpPartSchema.create().apply(Response.class, B04a.class).build();
254 		s.validateInput("X");
255 		s.validateInput("Y");
256 		s.validateInput(null);
257 		assertThrowsWithMessage(SchemaValidationException.class, "Value does not match one of the expected values.  Must be one of the following:  X, Y", ()->s.validateInput("Z"));
258 	}
259 
260 	@Response(schema=@Schema(e=" X , Y "))
261 	public static class B04b {}
262 
263 	@Test void b04b_enum() throws Exception {
264 		var s = HttpPartSchema.create().apply(Response.class, B04b.class).build();
265 		s.validateInput("X");
266 		s.validateInput("Y");
267 		s.validateInput(null);
268 		assertThrowsWithMessage(SchemaValidationException.class, "Value does not match one of the expected values.  Must be one of the following:  X, Y", ()->s.validateInput("Z"));
269 	}
270 
271 	@Response(schema=@Schema(e="X,Y"))
272 	public static class B04c {}
273 
274 	@Test void b04c_enum_json() throws Exception {
275 		var s = HttpPartSchema.create().apply(Response.class, B04c.class).build();
276 		s.validateInput("X");
277 		s.validateInput("Y");
278 		s.validateInput(null);
279 		assertThrowsWithMessage(SchemaValidationException.class, "Value does not match one of the expected values.  Must be one of the following:  X, Y", ()->s.validateInput("Z"));
280 	}
281 
282 	@Response(
283 		schema=@Schema(
284 			items=@Items(
285 				e="W",
286 				items=@SubItems(
287 					e="X",
288 					items={
289 						"enum:['Y'],",
290 						"items:{enum:['Z']}"
291 					}
292 				)
293 			)
294 		)
295 	)
296 	public static class B04d {}
297 
298 	@Test void b04d_enum_items() throws Exception {
299 		var s = HttpPartSchema.create().apply(Response.class, B04d.class).build();
300 
301 		s.getItems().validateInput("W");
302 		s.getItems().getItems().validateInput("X");
303 		s.getItems().getItems().getItems().validateInput("Y");
304 		s.getItems().getItems().getItems().getItems().validateInput("Z");
305 
306 		assertThrowsWithMessage(SchemaValidationException.class, "Value does not match one of the expected values.  Must be one of the following:  W", ()->s.getItems().validateInput("V"));
307 		assertThrowsWithMessage(SchemaValidationException.class, "Value does not match one of the expected values.  Must be one of the following:  X", ()->s.getItems().getItems().validateInput("V"));
308 		assertThrowsWithMessage(SchemaValidationException.class, "Value does not match one of the expected values.  Must be one of the following:  Y", ()->s.getItems().getItems().getItems().validateInput("V"));
309 		assertThrowsWithMessage(SchemaValidationException.class, "Value does not match one of the expected values.  Must be one of the following:  Z", ()->s.getItems().getItems().getItems().getItems().validateInput("V"));
310 	}
311 
312 	//-----------------------------------------------------------------------------------------------------------------
313 	// Numeric validations
314 	//-----------------------------------------------------------------------------------------------------------------
315 
316 	@Response(schema=@Schema(min="10", max="100"))
317 	public static class C01a {}
318 
319 	@Test void c01a_minmax_ints() throws Exception {
320 		var s = HttpPartSchema.create().apply(Response.class, C01a.class).build();
321 		s.validateOutput(10, BeanContext.DEFAULT);
322 		s.validateOutput(100, BeanContext.DEFAULT);
323 		s.validateOutput(null, BeanContext.DEFAULT);
324 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum value not met.", ()->s.validateOutput(9, BeanContext.DEFAULT));
325 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum value exceeded.", ()->s.validateOutput(101, BeanContext.DEFAULT));
326 	}
327 
328 	@Response(
329 		schema=@Schema(
330 			items=@Items(
331 				min="10", max="100",
332 				items=@SubItems(
333 					min="100", max="1000",
334 					items={
335 						"minimum:1000,maximum:10000,",
336 						"items:{minimum:10000,maximum:100000}"
337 					}
338 				)
339 			)
340 		)
341 	)
342 	public static class C01b {}
343 
344 	@Test void c01b_minmax_ints_items() throws Exception {
345 		var s = HttpPartSchema.create().apply(Response.class, C01b.class).build();
346 
347 		s.getItems().validateOutput(10, BeanContext.DEFAULT);
348 		s.getItems().getItems().validateOutput(100, BeanContext.DEFAULT);
349 		s.getItems().getItems().getItems().validateOutput(1000, BeanContext.DEFAULT);
350 		s.getItems().getItems().getItems().getItems().validateOutput(10000, BeanContext.DEFAULT);
351 
352 		s.getItems().validateOutput(100, BeanContext.DEFAULT);
353 		s.getItems().getItems().validateOutput(1000, BeanContext.DEFAULT);
354 		s.getItems().getItems().getItems().validateOutput(10000, BeanContext.DEFAULT);
355 		s.getItems().getItems().getItems().getItems().validateOutput(100000, BeanContext.DEFAULT);
356 
357 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum value not met.", ()->s.getItems().validateOutput(9, BeanContext.DEFAULT));
358 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum value not met.", ()->s.getItems().getItems().validateOutput(99, BeanContext.DEFAULT));
359 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum value not met.", ()->s.getItems().getItems().getItems().validateOutput(999, BeanContext.DEFAULT));
360 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum value not met.", ()->s.getItems().getItems().getItems().getItems().validateOutput(9999, BeanContext.DEFAULT));
361 
362 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum value exceeded.", ()->s.getItems().validateOutput(101, BeanContext.DEFAULT));
363 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum value exceeded.", ()->s.getItems().getItems().validateOutput(1001, BeanContext.DEFAULT));
364 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum value exceeded.", ()->s.getItems().getItems().getItems().validateOutput(10001, BeanContext.DEFAULT));
365 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum value exceeded.", ()->s.getItems().getItems().getItems().getItems().validateOutput(100001, BeanContext.DEFAULT));
366 	}
367 
368 	@Response(schema=@Schema(min="10", max="100", emin=true, emax=true))
369 	public static class C02a {}
370 
371 	@Test void c02a_minmax_exclusive() throws Exception {
372 		var s = HttpPartSchema.create().apply(Response.class, C02a.class).build();
373 		s.validateOutput(11, BeanContext.DEFAULT);
374 		s.validateOutput(99, BeanContext.DEFAULT);
375 		s.validateOutput(null, BeanContext.DEFAULT);
376 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum value not met.", ()->s.validateOutput(10, BeanContext.DEFAULT));
377 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum value exceeded.", ()->s.validateOutput(100, BeanContext.DEFAULT));
378 	}
379 
380 	@Response(
381 		schema=@Schema(
382 			items=@Items(
383 				min="10", max="100", emin=true, emax=true,
384 				items=@SubItems(
385 					min="100", max="1000", emin=true, emax=true,
386 					items={
387 						"minimum:1000,maximum:10000,exclusiveMinimum:true,exclusiveMaximum:true,",
388 						"items:{minimum:10000,maximum:100000,exclusiveMinimum:true,exclusiveMaximum:true}"
389 					}
390 				)
391 			)
392 		)
393 	)
394 	public static class C02b {}
395 
396 	@Test void c02b_minmax_exclusive_items() throws Exception {
397 		var s = HttpPartSchema.create().apply(Response.class, C02b.class).build();
398 
399 		s.getItems().validateOutput(11, BeanContext.DEFAULT);
400 		s.getItems().getItems().validateOutput(101, BeanContext.DEFAULT);
401 		s.getItems().getItems().getItems().validateOutput(1001, BeanContext.DEFAULT);
402 		s.getItems().getItems().getItems().getItems().validateOutput(10001, BeanContext.DEFAULT);
403 
404 		s.getItems().validateOutput(99, BeanContext.DEFAULT);
405 		s.getItems().getItems().validateOutput(999, BeanContext.DEFAULT);
406 		s.getItems().getItems().getItems().validateOutput(9999, BeanContext.DEFAULT);
407 		s.getItems().getItems().getItems().getItems().validateOutput(99999, BeanContext.DEFAULT);
408 
409 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum value not met.", ()->s.getItems().validateOutput(10, BeanContext.DEFAULT));
410 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum value not met.", ()->s.getItems().getItems().validateOutput(100, BeanContext.DEFAULT));
411 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum value not met.", ()->s.getItems().getItems().getItems().validateOutput(1000, BeanContext.DEFAULT));
412 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum value not met.", ()->s.getItems().getItems().getItems().getItems().validateOutput(10000, BeanContext.DEFAULT));
413 
414 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum value exceeded.", ()->s.getItems().validateOutput(100, BeanContext.DEFAULT));
415 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum value exceeded.", ()->s.getItems().getItems().validateOutput(1000, BeanContext.DEFAULT));
416 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum value exceeded.", ()->s.getItems().getItems().getItems().validateOutput(10000, BeanContext.DEFAULT));
417 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum value exceeded.", ()->s.getItems().getItems().getItems().getItems().validateOutput(100000, BeanContext.DEFAULT));
418 	}
419 
420 	@Response(schema=@Schema(min="10.1", max="100.1"))
421 	public static class C03a {}
422 
423 	@Test void c03_minmax_floats() throws Exception {
424 		var s = HttpPartSchema.create().apply(Response.class, C03a.class).build();
425 		s.validateOutput(10.1f, BeanContext.DEFAULT);
426 		s.validateOutput(100.1f, BeanContext.DEFAULT);
427 		s.validateOutput(null, BeanContext.DEFAULT);
428 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum value not met.", ()->s.validateOutput(10f, BeanContext.DEFAULT));
429 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum value exceeded.", ()->s.validateOutput(100.2f, BeanContext.DEFAULT));
430 	}
431 
432 	@Response(
433 		schema=@Schema(
434 			items=@Items(
435 				min="10.1", max="100.1",
436 				items=@SubItems(
437 					min="100.1", max="1000.1",
438 					items={
439 						"minimum:1000.1,maximum:10000.1,",
440 						"items:{minimum:10000.1,maximum:100000.1}"
441 					}
442 				)
443 			)
444 		)
445 	)
446 	public static class C03b {}
447 
448 	@Test void c03b_minmax_floats_items() throws Exception {
449 		var s = HttpPartSchema.create().apply(Response.class, C03b.class).build();
450 
451 		s.getItems().validateOutput(10.1f, BeanContext.DEFAULT);
452 		s.getItems().getItems().validateOutput(100.1f, BeanContext.DEFAULT);
453 		s.getItems().getItems().getItems().validateOutput(1000.1f, BeanContext.DEFAULT);
454 		s.getItems().getItems().getItems().getItems().validateOutput(10000.1f, BeanContext.DEFAULT);
455 
456 		s.getItems().validateOutput(100.1f, BeanContext.DEFAULT);
457 		s.getItems().getItems().validateOutput(1000.1f, BeanContext.DEFAULT);
458 		s.getItems().getItems().getItems().validateOutput(10000.1f, BeanContext.DEFAULT);
459 		s.getItems().getItems().getItems().getItems().validateOutput(100000.1f, BeanContext.DEFAULT);
460 
461 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum value not met.", ()->s.getItems().validateOutput(10f, BeanContext.DEFAULT));
462 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum value not met.", ()->s.getItems().getItems().validateOutput(100f, BeanContext.DEFAULT));
463 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum value not met.", ()->s.getItems().getItems().getItems().validateOutput(1000f, BeanContext.DEFAULT));
464 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum value not met.", ()->s.getItems().getItems().getItems().getItems().validateOutput(10000f, BeanContext.DEFAULT));
465 
466 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum value exceeded.", ()->s.getItems().validateOutput(100.2f, BeanContext.DEFAULT));
467 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum value exceeded.", ()->s.getItems().getItems().validateOutput(1000.2f, BeanContext.DEFAULT));
468 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum value exceeded.", ()->s.getItems().getItems().getItems().validateOutput(10000.2f, BeanContext.DEFAULT));
469 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum value exceeded.", ()->s.getItems().getItems().getItems().getItems().validateOutput(100000.2f, BeanContext.DEFAULT));
470 	}
471 
472 	@Response(schema=@Schema(min="10.1", max="100.1", emin=true, emax=true))
473 	public static class C04a {}
474 
475 	@Test void c04a_minmax_floats_exclusive() throws Exception {
476 		var s = HttpPartSchema.create().apply(Response.class, C04a.class).build();
477 		s.validateOutput(10.2f, BeanContext.DEFAULT);
478 		s.validateOutput(100f, BeanContext.DEFAULT);
479 		s.validateOutput(null, BeanContext.DEFAULT);
480 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum value not met.", ()->s.validateOutput(10.1f, BeanContext.DEFAULT));
481 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum value exceeded.", ()->s.validateOutput(100.1f, BeanContext.DEFAULT));
482 	}
483 
484 	@Response(
485 		schema=@Schema(
486 			items=@Items(
487 				min="10.1", max="100.1", emin=true, emax=true,
488 				items=@SubItems(
489 					min="100.1", max="1000.1", emin=true, emax=true,
490 					items={
491 						"minimum:1000.1,maximum:10000.1,exclusiveMinimum:true,exclusiveMaximum:true,",
492 						"items:{minimum:10000.1,maximum:100000.1,exclusiveMinimum:true,exclusiveMaximum:true}"
493 					}
494 				)
495 			)
496 		)
497 	)
498 	public static class C04b {}
499 
500 	@Test void c04b_minmax_floats_exclusive_items() throws Exception {
501 		var s = HttpPartSchema.create().apply(Response.class, C04b.class).build();
502 
503 		s.getItems().validateOutput(10.2f, BeanContext.DEFAULT);
504 		s.getItems().getItems().validateOutput(100.2f, BeanContext.DEFAULT);
505 		s.getItems().getItems().getItems().validateOutput(1000.2f, BeanContext.DEFAULT);
506 		s.getItems().getItems().getItems().getItems().validateOutput(10000.2f, BeanContext.DEFAULT);
507 
508 		s.getItems().validateOutput(100f, BeanContext.DEFAULT);
509 		s.getItems().getItems().validateOutput(1000f, BeanContext.DEFAULT);
510 		s.getItems().getItems().getItems().validateOutput(10000f, BeanContext.DEFAULT);
511 		s.getItems().getItems().getItems().getItems().validateOutput(100000f, BeanContext.DEFAULT);
512 
513 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum value not met.", ()->s.getItems().validateOutput(10.1f, BeanContext.DEFAULT));
514 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum value not met.", ()->s.getItems().getItems().validateOutput(100.1f, BeanContext.DEFAULT));
515 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum value not met.", ()->s.getItems().getItems().getItems().validateOutput(1000.1f, BeanContext.DEFAULT));
516 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum value not met.", ()->s.getItems().getItems().getItems().getItems().validateOutput(10000.1f, BeanContext.DEFAULT));
517 
518 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum value exceeded.", ()->s.getItems().validateOutput(100.1f, BeanContext.DEFAULT));
519 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum value exceeded.", ()->s.getItems().getItems().validateOutput(1000.1f, BeanContext.DEFAULT));
520 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum value exceeded.", ()->s.getItems().getItems().getItems().validateOutput(10000.1f, BeanContext.DEFAULT));
521 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum value exceeded.", ()->s.getItems().getItems().getItems().getItems().validateOutput(100000.1f, BeanContext.DEFAULT));
522 	}
523 
524 	@Response(schema=@Schema(mo="10"))
525 	public static class C05a {}
526 
527 	@Test void c05a_multipleOf() throws Exception {
528 		var s = HttpPartSchema.create().apply(Response.class, C05a.class).build();
529 		s.validateOutput(0, BeanContext.DEFAULT);
530 		s.validateOutput(10, BeanContext.DEFAULT);
531 		s.validateOutput(20, BeanContext.DEFAULT);
532 		s.validateOutput(10f, BeanContext.DEFAULT);
533 		s.validateOutput(20f, BeanContext.DEFAULT);
534 		s.validateOutput(null, BeanContext.DEFAULT);
535 		assertThrowsWithMessage(SchemaValidationException.class, "Multiple-of not met.", ()->s.validateOutput(11, BeanContext.DEFAULT));
536 	}
537 
538 	@Response(
539 		schema=@Schema(
540 			items=@Items(
541 				mo="10",
542 				items=@SubItems(
543 					mo="100",
544 					items={
545 						"multipleOf:1000,",
546 						"items:{multipleOf:10000}"
547 					}
548 				)
549 			)
550 		)
551 	)
552 	public static class C05b {}
553 
554 	@Test void c05b_multipleOf_items() throws Exception {
555 		var s = HttpPartSchema.create().apply(Response.class, C05b.class).build();
556 
557 		s.getItems().validateOutput(0, BeanContext.DEFAULT);
558 		s.getItems().getItems().validateOutput(0, BeanContext.DEFAULT);
559 		s.getItems().getItems().getItems().validateOutput(0, BeanContext.DEFAULT);
560 		s.getItems().getItems().getItems().getItems().validateOutput(0, BeanContext.DEFAULT);
561 
562 		s.getItems().validateOutput(10, BeanContext.DEFAULT);
563 		s.getItems().getItems().validateOutput(100, BeanContext.DEFAULT);
564 		s.getItems().getItems().getItems().validateOutput(1000, BeanContext.DEFAULT);
565 		s.getItems().getItems().getItems().getItems().validateOutput(10000, BeanContext.DEFAULT);
566 
567 		s.getItems().validateOutput(20, BeanContext.DEFAULT);
568 		s.getItems().getItems().validateOutput(200, BeanContext.DEFAULT);
569 		s.getItems().getItems().getItems().validateOutput(2000, BeanContext.DEFAULT);
570 		s.getItems().getItems().getItems().getItems().validateOutput(20000, BeanContext.DEFAULT);
571 
572 		s.getItems().validateOutput(10f, BeanContext.DEFAULT);
573 		s.getItems().getItems().validateOutput(100f, BeanContext.DEFAULT);
574 		s.getItems().getItems().getItems().validateOutput(1000f, BeanContext.DEFAULT);
575 		s.getItems().getItems().getItems().getItems().validateOutput(10000f, BeanContext.DEFAULT);
576 
577 		s.getItems().validateOutput(20f, BeanContext.DEFAULT);
578 		s.getItems().getItems().validateOutput(200f, BeanContext.DEFAULT);
579 		s.getItems().getItems().getItems().validateOutput(2000f, BeanContext.DEFAULT);
580 		s.getItems().getItems().getItems().getItems().validateOutput(20000f, BeanContext.DEFAULT);
581 
582 		assertThrowsWithMessage(SchemaValidationException.class, "Multiple-of not met.", ()->s.getItems().validateOutput(11, BeanContext.DEFAULT));
583 		assertThrowsWithMessage(SchemaValidationException.class, "Multiple-of not met.", ()->s.getItems().getItems().validateOutput(101, BeanContext.DEFAULT));
584 		assertThrowsWithMessage(SchemaValidationException.class, "Multiple-of not met.", ()->s.getItems().getItems().getItems().validateOutput(1001, BeanContext.DEFAULT));
585 		assertThrowsWithMessage(SchemaValidationException.class, "Multiple-of not met.", ()->s.getItems().getItems().getItems().getItems().validateOutput(10001, BeanContext.DEFAULT));
586 	}
587 
588 	@Response(schema=@Schema(mo="10.1"))
589 	public static class C06a {}
590 
591 	@Test void c06a_multipleOf_floats() throws Exception {
592 		var s = HttpPartSchema.create().apply(Response.class, C06a.class).build();
593 		s.validateOutput(0, BeanContext.DEFAULT);
594 		s.validateOutput(10.1f, BeanContext.DEFAULT);
595 		s.validateOutput(20.2f, BeanContext.DEFAULT);
596 		s.validateOutput(null, BeanContext.DEFAULT);
597 		assertThrowsWithMessage(SchemaValidationException.class, "Multiple-of not met.", ()->s.validateOutput(10.2f, BeanContext.DEFAULT));
598 	}
599 
600 	@Response(
601 		schema=@Schema(
602 			items=@Items(
603 				mo="10.1",
604 				items=@SubItems(
605 					mo="100.1",
606 					items={
607 						"multipleOf:1000.1,",
608 						"items:{multipleOf:10000.1}"
609 					}
610 				)
611 			)
612 		)
613 	)
614 	public static class C06b {}
615 
616 	@Test void c06b_multipleOf_floats_items() throws Exception {
617 		var s = HttpPartSchema.create().apply(Response.class, C06b.class).build();
618 
619 		s.getItems().validateOutput(0, BeanContext.DEFAULT);
620 		s.getItems().getItems().validateOutput(0, BeanContext.DEFAULT);
621 		s.getItems().getItems().getItems().validateOutput(0, BeanContext.DEFAULT);
622 		s.getItems().getItems().getItems().getItems().validateOutput(0, BeanContext.DEFAULT);
623 
624 		s.getItems().validateOutput(10.1f, BeanContext.DEFAULT);
625 		s.getItems().getItems().validateOutput(100.1f, BeanContext.DEFAULT);
626 		s.getItems().getItems().getItems().validateOutput(1000.1f, BeanContext.DEFAULT);
627 		s.getItems().getItems().getItems().getItems().validateOutput(10000.1f, BeanContext.DEFAULT);
628 
629 		s.getItems().validateOutput(20.2f, BeanContext.DEFAULT);
630 		s.getItems().getItems().validateOutput(200.2f, BeanContext.DEFAULT);
631 		s.getItems().getItems().getItems().validateOutput(2000.2f, BeanContext.DEFAULT);
632 		s.getItems().getItems().getItems().getItems().validateOutput(20000.2f, BeanContext.DEFAULT);
633 
634 		assertThrowsWithMessage(SchemaValidationException.class, "Multiple-of not met.", ()->s.getItems().validateOutput(10.2f, BeanContext.DEFAULT));
635 		assertThrowsWithMessage(SchemaValidationException.class, "Multiple-of not met.", ()->s.getItems().getItems().validateOutput(100.2f, BeanContext.DEFAULT));
636 		assertThrowsWithMessage(SchemaValidationException.class, "Multiple-of not met.", ()->s.getItems().getItems().getItems().validateOutput(1000.2f, BeanContext.DEFAULT));
637 		assertThrowsWithMessage(SchemaValidationException.class, "Multiple-of not met.", ()->s.getItems().getItems().getItems().getItems().validateOutput(10000.2f, BeanContext.DEFAULT));
638 	}
639 
640 	//-----------------------------------------------------------------------------------------------------------------
641 	// Collections/Array validations
642 	//-----------------------------------------------------------------------------------------------------------------
643 
644 	@Response(
645 		schema=@Schema(
646 			items=@Items(
647 				ui=true,
648 				items=@SubItems(
649 					ui=true,
650 					items={
651 						"uniqueItems:true,",
652 						"items:{uniqueItems:true}"
653 					}
654 				)
655 			)
656 		)
657 	)
658 	public static class D01 {}
659 
660 	@Test void d01a_uniqueItems_arrays() throws Exception {
661 		var s = HttpPartSchema.create().apply(Response.class, D01.class).build();
662 
663 		var good = split("a,b");
664 		var bad = split("a,a");
665 
666 		s.getItems().validateOutput(good, BeanContext.DEFAULT);
667 		s.getItems().getItems().validateOutput(good, BeanContext.DEFAULT);
668 		s.getItems().getItems().getItems().validateOutput(good, BeanContext.DEFAULT);
669 		s.getItems().getItems().getItems().getItems().validateOutput(good, BeanContext.DEFAULT);
670 		s.getItems().validateOutput(null, BeanContext.DEFAULT);
671 
672 		assertThrowsWithMessage(SchemaValidationException.class, "Duplicate items not allowed.", ()->s.getItems().validateOutput(bad, BeanContext.DEFAULT));
673 		assertThrowsWithMessage(SchemaValidationException.class, "Duplicate items not allowed.", ()->s.getItems().getItems().validateOutput(bad, BeanContext.DEFAULT));
674 		assertThrowsWithMessage(SchemaValidationException.class, "Duplicate items not allowed.", ()->s.getItems().getItems().getItems().validateOutput(bad, BeanContext.DEFAULT));
675 		assertThrowsWithMessage(SchemaValidationException.class, "Duplicate items not allowed.", ()->s.getItems().getItems().getItems().getItems().validateOutput(bad, BeanContext.DEFAULT));
676 	}
677 
678 	@Test void d01b_uniqueItems_collections() throws Exception {
679 		var s = HttpPartSchema.create().apply(Response.class, D01.class).build();
680 
681 		List<String>
682 			good = alist("a","b"),
683 			bad = alist("a","a");
684 
685 		s.getItems().validateOutput(good, BeanContext.DEFAULT);
686 		s.getItems().getItems().validateOutput(good, BeanContext.DEFAULT);
687 		s.getItems().getItems().getItems().validateOutput(good, BeanContext.DEFAULT);
688 		s.getItems().getItems().getItems().getItems().validateOutput(good, BeanContext.DEFAULT);
689 		s.getItems().validateOutput(null, BeanContext.DEFAULT);
690 
691 		assertThrowsWithMessage(SchemaValidationException.class, "Duplicate items not allowed.", ()->s.getItems().validateOutput(bad, BeanContext.DEFAULT));
692 		assertThrowsWithMessage(SchemaValidationException.class, "Duplicate items not allowed.", ()->s.getItems().getItems().validateOutput(bad, BeanContext.DEFAULT));
693 		assertThrowsWithMessage(SchemaValidationException.class, "Duplicate items not allowed.", ()->s.getItems().getItems().getItems().validateOutput(bad, BeanContext.DEFAULT));
694 		assertThrowsWithMessage(SchemaValidationException.class, "Duplicate items not allowed.", ()->s.getItems().getItems().getItems().getItems().validateOutput(bad, BeanContext.DEFAULT));
695 	}
696 
697 	@Response(
698 		schema=@Schema(
699 			items=@Items(
700 				mini=1, maxi=2,
701 				items=@SubItems(
702 					mini=2, maxi=3,
703 					items={
704 						"minItems:3,maxItems:4,",
705 						"items:{minItems:4,maxItems:5}"
706 					}
707 				)
708 			)
709 		)
710 	)
711 	public static class D02 {}
712 
713 	@Test void d02a_minMaxItems_arrays() throws Exception {
714 		var s = HttpPartSchema.create().apply(Response.class, D02.class).build();
715 
716 		s.getItems().validateOutput(split("1"), BeanContext.DEFAULT);
717 		s.getItems().getItems().validateOutput(split("1,2"), BeanContext.DEFAULT);
718 		s.getItems().getItems().getItems().validateOutput(split("1,2,3"), BeanContext.DEFAULT);
719 		s.getItems().getItems().getItems().getItems().validateOutput(split("1,2,3,4"), BeanContext.DEFAULT);
720 
721 		s.getItems().validateOutput(split("1,2"), BeanContext.DEFAULT);
722 		s.getItems().getItems().validateOutput(split("1,2,3"), BeanContext.DEFAULT);
723 		s.getItems().getItems().getItems().validateOutput(split("1,2,3,4"), BeanContext.DEFAULT);
724 		s.getItems().getItems().getItems().getItems().validateOutput(split("1,2,3,4,5"), BeanContext.DEFAULT);
725 
726 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum number of items not met.", ()->s.getItems().validateOutput(new String[0], BeanContext.DEFAULT));
727 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum number of items not met.", ()->s.getItems().getItems().validateOutput(split("1"), BeanContext.DEFAULT));
728 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum number of items not met.", ()->s.getItems().getItems().getItems().validateOutput(split("1,2"), BeanContext.DEFAULT));
729 		assertThrowsWithMessage(SchemaValidationException.class, "Minimum number of items not met.", ()->s.getItems().getItems().getItems().getItems().validateOutput(split("1,2,3"), BeanContext.DEFAULT));
730 
731 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum number of items exceeded.", ()->s.getItems().validateOutput(split("1,2,3"), BeanContext.DEFAULT));
732 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum number of items exceeded.", ()->s.getItems().getItems().validateOutput(split("1,2,3,4"), BeanContext.DEFAULT));
733 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum number of items exceeded.", ()->s.getItems().getItems().getItems().validateOutput(split("1,2,3,4,5"), BeanContext.DEFAULT));
734 		assertThrowsWithMessage(SchemaValidationException.class, "Maximum number of items exceeded.", ()->s.getItems().getItems().getItems().getItems().validateOutput(split("1,2,3,4,5,6"), BeanContext.DEFAULT));
735 	}
736 }