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