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