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.bean.swagger;
18  
19  import static org.apache.juneau.TestUtils.*;
20  import static org.apache.juneau.bean.swagger.SwaggerBuilder.*;
21  import static org.apache.juneau.commons.utils.CollectionUtils.*;
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.collections.*;
29  import org.junit.jupiter.api.*;
30  
31  /**
32   * Testcase for {@link ParameterInfo}.
33   */
34  class ParameterInfo_Test extends TestBase {
35  
36  	@Nested class A_basicTests extends TestBase {
37  
38  		private static final BeanTester<ParameterInfo> TESTER =
39  			testBean(
40  				bean()
41  					.setAllowEmptyValue(true)
42  					.setCollectionFormat("a")
43  					.setDefault("b")
44  					.setDescription("c")
45  					.setEnum(set("d"))
46  					.setExample("e")
47  					.setExamples(map("f", "g"))
48  					.setExclusiveMaximum(true)
49  					.setExclusiveMinimum(true)
50  					.setFormat("h")
51  					.setIn("i")
52  					.setItems(items().setType("j"))
53  					.setMaximum(1)
54  					.setMaxItems(2)
55  					.setMaxLength(3)
56  					.setMinimum(4)
57  					.setMinItems(5)
58  					.setMinLength(6)
59  					.setMultipleOf(7)
60  					.setName("k")
61  					.setPattern("l")
62  					.setRequired(true)
63  					.setSchema(schemaInfo().setType("m"))
64  					.setType("n")
65  					.setUniqueItems(true)
66  			)
67  			.props("allowEmptyValue,collectionFormat,default,description,enum,example,examples,exclusiveMaximum,exclusiveMinimum,format,in,items{type},maximum,maxItems,maxLength,minimum,minItems,minLength,multipleOf,name,pattern,required,schema{type},type,uniqueItems")
68  			.vals("true,a,b,c,[d],e,{f=g},true,true,h,i,{j},1,2,3,4,5,6,7,k,l,true,{m},n,true")
69  			.json("{allowEmptyValue:true,collectionFormat:'a','default':'b',description:'c','enum':['d'],example:'e',examples:{f:'g'},exclusiveMaximum:true,exclusiveMinimum:true,format:'h','in':'i',items:{type:'j'},maxItems:2,maxLength:3,maximum:1,minItems:5,minLength:6,minimum:4,multipleOf:7,name:'k',pattern:'l',required:true,schema:{type:'m'},type:'n',uniqueItems:true}")
70  			.string("{'allowEmptyValue':true,'collectionFormat':'a','default':'b','description':'c','enum':['d'],'example':'e','examples':{'f':'g'},'exclusiveMaximum':true,'exclusiveMinimum':true,'format':'h','in':'i','items':{'type':'j'},'maxItems':2,'maxLength':3,'maximum':1,'minItems':5,'minLength':6,'minimum':4,'multipleOf':7,'name':'k','pattern':'l','required':true,'schema':{'type':'m'},'type':'n','uniqueItems':true}".replace('\'', '"'))
71  		;
72  
73  		@Test void a01_gettersAndSetters() {
74  			TESTER.assertGettersAndSetters();
75  		}
76  
77  		@Test void a02_copy() {
78  			TESTER.assertCopy();
79  		}
80  
81  		@Test void a03_toJson() {
82  			TESTER.assertToJson();
83  		}
84  
85  		@Test void a04_fromJson() {
86  			TESTER.assertFromJson();
87  		}
88  
89  		@Test void a05_roundTrip() {
90  			TESTER.assertRoundTrip();
91  		}
92  
93  		@Test void a06_toString() {
94  			TESTER.assertToString();
95  		}
96  
97  		@Test void a07_keySet() {
98  			assertList(TESTER.bean().keySet(), "allowEmptyValue", "collectionFormat", "default", "description", "enum", "example", "examples", "exclusiveMaximum", "exclusiveMinimum", "format", "in", "items", "maxItems", "maxLength", "maximum", "minItems", "minLength", "minimum", "multipleOf", "name", "pattern", "required", "schema", "type", "uniqueItems");
99  		}
100 
101 		@Test void a08_nullParameters() {
102 			var x = bean();
103 			assertThrows(IllegalArgumentException.class, () -> x.get(null, String.class));
104 			assertThrows(IllegalArgumentException.class, () -> x.set(null, "value"));
105 		}
106 
107 		@Test void a09_addMethods() {
108 			var x = bean().addEnum("a1");
109 			assertNotNull(x);
110 			assertNotNull(x.getEnum());
111 		}
112 
113 		@Test void a10_setEnumVarargs() {
114 			var x = bean().setEnum("a", "b", "c");
115 			assertBean(x, "enum", "[a,b,c]");
116 		}
117 
118 		@Test void a11_setEnumVarargsEmpty() {
119 			var x = bean().setEnum();
120 			assertBean(x, "enum", "<null>");
121 		}
122 
123 		@Test void a12_setEnumVarargsNull() {
124 			var x = bean().setEnum((Object[])null);
125 			assertBean(x, "enum", "<null>");
126 		}
127 
128 		@Test void a13_asMap() {
129 			assertBean(
130 				bean()
131 					.setDescription("a")
132 					.setIn("b")
133 					.setName("c")
134 					.set("x1", "x1a")
135 					.asMap(),
136 				"description,in,name,x1",
137 				"a,b,c,x1a"
138 			);
139 		}
140 
141 		@Test void a14_extraKeys() {
142 			var x = bean().set("x1", "x1a").set("x2", "x2a");
143 			assertList(x.extraKeys(), "x1", "x2");
144 			assertEmpty(bean().extraKeys());
145 		}
146 
147 		@Test void a15_copyFrom() {
148 			var x = bean().setName("a").setIn("b");
149 			var y = bean().copyFrom(x);
150 			assertBean(y, "name,in", "a,b");
151 
152 			y = bean().setName("c").copyFrom(x);
153 			assertBean(y, "name,in", "a,b");
154 
155 			y = bean().setName("c").copyFrom(null);
156 			assertBean(y, "name", "c");
157 		}
158 
159 		@Test void a16_setInPathSetsRequired() {
160 			var x = bean().setIn("path");
161 			assertBean(x, "in,required", "path,true");
162 		}
163 
164 		@Test void a17_setInNonPathDoesNotSetRequired() {
165 			var x = bean().setIn("query");
166 			assertBean(x, "in,required", "query,<null>");
167 		}
168 
169 		@Test void a18_setNameWithBodyInDoesNotSetName() {
170 			var x = bean().setIn("body").setName("test");
171 			assertBean(x, "in,name", "body,<null>");
172 		}
173 
174 		@Test void a19_setNameWithNonBodyInSetsName() {
175 			var x = bean().setIn("query").setName("test");
176 			assertBean(x, "in,name", "query,test");
177 		}
178 
179 		@Test void a20_copyFromAllProperties() {
180 			var source = bean()
181 				.setName("a")
182 				.setIn("b")
183 				.setDescription("c")
184 				.setType("d")
185 				.setFormat("e")
186 				.setPattern("f")
187 				.setCollectionFormat("g")
188 				.setMaximum(1)
189 				.setMinimum(2)
190 				.setMultipleOf(3)
191 				.setMaxLength(4)
192 				.setMinLength(5)
193 				.setMaxItems(6)
194 				.setMinItems(7)
195 				.setRequired(true)
196 				.setAllowEmptyValue(true)
197 				.setExclusiveMaximum(true)
198 				.setExclusiveMinimum(true)
199 				.setUniqueItems(true)
200 				.setSchema(schemaInfo().setType("h"))
201 				.setItems(items().setType("i"))
202 				.setDefault("j")
203 				.setEnum(set("k"))
204 				.setExample("l")
205 				.setExamples(map("m1","m2"));
206 
207 			var target = bean().copyFrom(source);
208 			assertBean(target,
209 				"name,in,description,type,format,pattern,collectionFormat,maximum,minimum,multipleOf,maxLength,minLength,maxItems,minItems,required,allowEmptyValue,exclusiveMaximum,exclusiveMinimum,uniqueItems,schema{type},items{type},default,enum,example,examples",
210 				"a,b,c,d,e,f,g,1,2,3,4,5,6,7,true,true,true,true,true,{h},{i},j,[k],l,{m1=m2}"
211 			);
212 		}
213 
214 		@Test void a21_copyFromPartialProperties() {
215 			var source = bean()
216 				.setName("a")
217 				.setIn("b")
218 				.setDescription("c");
219 			// Leave other properties null
220 
221 			var target = bean()
222 				.setType("d")
223 				.setFormat("e")
224 				.copyFrom(source);
225 
226 			assertBean(
227 				target,
228 				"name,in,description,type,format",
229 				"a,b,c,d,e"
230 			);
231 
232 			// Entirely empty.
233 			assertJson(
234 				"{}",
235 				bean().copyFrom(bean())
236 			);
237 		}
238 
239 		@Test void a22_strictMode() {
240 			assertThrows(RuntimeException.class, () -> bean().strict().set("foo", "bar"));
241 			assertDoesNotThrow(() -> bean().set("foo", "bar"));
242 
243 			assertFalse(bean().isStrict());
244 			assertTrue(bean().strict().isStrict());
245 			assertFalse(bean().strict(false).isStrict());
246 
247 			var x = bean().strict();
248 			var y = bean(); // not strict
249 			assertThrowsWithMessage(RuntimeException.class, "Invalid value passed in to setCollectionFormat(String).  Value='invalid', valid values=['csv','ssv','tsv','pipes','multi']", () -> x.setCollectionFormat("invalid"));
250 			assertDoesNotThrow(() -> x.setCollectionFormat("csv"));
251 			assertDoesNotThrow(() -> x.setCollectionFormat("ssv"));
252 			assertDoesNotThrow(() -> x.setCollectionFormat("tsv"));
253 			assertDoesNotThrow(() -> x.setCollectionFormat("pipes"));
254 			assertDoesNotThrow(() -> x.setCollectionFormat("multi"));
255 			assertDoesNotThrow(() -> y.setCollectionFormat("invalid"));
256 
257 			assertThrowsWithMessage(RuntimeException.class, "Invalid value passed in to setIn(String).  Value='invalid', valid values=['query','header','path','formData','body']", () -> x.setIn("invalid"));
258 			assertDoesNotThrow(() -> x.setIn("query"));
259 			assertDoesNotThrow(() -> x.setIn("header"));
260 			assertDoesNotThrow(() -> x.setIn("path"));
261 			assertDoesNotThrow(() -> x.setIn("formData"));
262 			assertDoesNotThrow(() -> x.setIn("body"));
263 			assertDoesNotThrow(() -> y.setIn("invalid"));
264 
265 			assertThrowsWithMessage(RuntimeException.class, "Invalid value passed in to setType(String).  Value='invalid', valid values=['string','number','integer','boolean','array','file']", () -> x.setType("invalid"));
266 			assertDoesNotThrow(() -> x.setType("string"));
267 			assertDoesNotThrow(() -> x.setType("number"));
268 			assertDoesNotThrow(() -> x.setType("integer"));
269 			assertDoesNotThrow(() -> x.setType("boolean"));
270 			assertDoesNotThrow(() -> x.setType("array"));
271 			assertDoesNotThrow(() -> x.setType("file"));
272 			assertDoesNotThrow(() -> y.setType("invalid"));
273 
274 			assertThrowsWithMessage(RuntimeException.class, "Invalid value passed in to setCollectionFormat(String).  Value='null', valid values=['csv','ssv','tsv','pipes','multi']", () -> x.setCollectionFormat(null));
275 
276 			assertThrowsWithMessage(RuntimeException.class, "Invalid value passed in to setIn(String).  Value='null', valid values=['query','header','path','formData','body']", () -> x.setIn(null));
277 
278 			assertThrowsWithMessage(RuntimeException.class, "Invalid value passed in to setType(String).  Value='null', valid values=['string','number','integer','boolean','array','file']", () -> x.setType(null));
279 		}
280 	}
281 
282 	@Nested class B_emptyTests extends TestBase {
283 
284 		private static final BeanTester<ParameterInfo> TESTER =
285 			testBean(bean())
286 			.props("description,in,name,required,schema,type,format,allowEmptyValue,items,collectionFormat,default,maximum,exclusiveMaximum,minimum,exclusiveMinimum,maxLength,minLength,pattern,maxItems,minItems,uniqueItems,enum,multipleOf")
287 			.vals("<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>")
288 			.json("{}")
289 			.string("{}")
290 		;
291 
292 		@Test void b01_gettersAndSetters() {
293 			TESTER.assertGettersAndSetters();
294 		}
295 
296 		@Test void b02_copy() {
297 			TESTER.assertCopy();
298 		}
299 
300 		@Test void b03_toJson() {
301 			TESTER.assertToJson();
302 		}
303 
304 		@Test void b04_fromJson() {
305 			TESTER.assertFromJson();
306 		}
307 
308 		@Test void b05_roundTrip() {
309 			TESTER.assertRoundTrip();
310 		}
311 
312 		@Test void b06_toString() {
313 			TESTER.assertToString();
314 		}
315 
316 		@Test void b07_keySet() {
317 			assertEmpty(TESTER.bean().keySet());
318 		}
319 	}
320 
321 	@Nested class C_extraProperties extends TestBase {
322 
323 		private static final BeanTester<ParameterInfo> TESTER =
324 			testBean(
325 				bean()
326 					.set("allowEmptyValue", true)
327 					.set("collectionFormat", "a")
328 					.set("default", "b")
329 					.set("description", "c")
330 					.set("enum", set("d"))
331 					.set("example", "e")
332 					.set("examples", map("f", "g"))
333 					.set("exclusiveMaximum", true)
334 					.set("exclusiveMinimum", true)
335 					.set("format", "h")
336 					.set("in", "f")
337 					.set("items", items().setType("g"))
338 					.set("maximum", 1)
339 					.set("maxItems", 2)
340 					.set("maxLength", 3)
341 					.set("minimum", 4)
342 					.set("minItems", 5)
343 					.set("minLength", 6)
344 					.set("multipleOf", 7)
345 					.set("name", "h")
346 					.set("pattern", "i")
347 					.set("required", true)
348 					.set("schema", schemaInfo().setType("j"))
349 					.set("type", "k")
350 					.set("uniqueItems", true)
351 					.set("x3", "x3a")
352 					.set("x4", null)
353 			)
354 			.props("allowEmptyValue,collectionFormat,default,description,enum,example,examples,exclusiveMaximum,exclusiveMinimum,format,in,items{type},maximum,maxItems,maxLength,minimum,minItems,minLength,multipleOf,name,pattern,required,schema{type},type,uniqueItems,x3,x4")
355 			.vals("true,a,b,c,[d],e,{f=g},true,true,h,f,{g},1,2,3,4,5,6,7,h,i,true,{j},k,true,x3a,<null>")
356 			.json("{allowEmptyValue:true,collectionFormat:'a','default':'b',description:'c','enum':['d'],example:'e',examples:{f:'g'},exclusiveMaximum:true,exclusiveMinimum:true,format:'h','in':'f',items:{type:'g'},maxItems:2,maxLength:3,maximum:1,minItems:5,minLength:6,minimum:4,multipleOf:7,name:'h',pattern:'i',required:true,schema:{type:'j'},type:'k',uniqueItems:true,x3:'x3a'}")
357 			.string("{'allowEmptyValue':true,'collectionFormat':'a','default':'b','description':'c','enum':['d'],'example':'e','examples':{'f':'g'},'exclusiveMaximum':true,'exclusiveMinimum':true,'format':'h','in':'f','items':{'type':'g'},'maxItems':2,'maxLength':3,'maximum':1,'minItems':5,'minLength':6,'minimum':4,'multipleOf':7,'name':'h','pattern':'i','required':true,'schema':{'type':'j'},'type':'k','uniqueItems':true,'x3':'x3a'}".replace('\'', '"'))
358 		;
359 
360 		@Test void c01_gettersAndSetters() {
361 			TESTER.assertGettersAndSetters();
362 		}
363 
364 		@Test void c02_copy() {
365 			TESTER.assertCopy();
366 		}
367 
368 		@Test void c03_toJson() {
369 			TESTER.assertToJson();
370 		}
371 
372 		@Test void c04_fromJson() {
373 			TESTER.assertFromJson();
374 		}
375 
376 		@Test void c05_roundTrip() {
377 			TESTER.assertRoundTrip();
378 		}
379 
380 		@Test void c06_toString() {
381 			TESTER.assertToString();
382 		}
383 
384 		@Test void c07_keySet() {
385 			assertList(TESTER.bean().keySet(), "allowEmptyValue", "collectionFormat", "default", "description", "enum", "example", "examples", "exclusiveMaximum", "exclusiveMinimum", "format", "in", "items", "maxItems", "maxLength", "maximum", "minItems", "minLength", "minimum", "multipleOf", "name", "pattern", "required", "schema", "type", "uniqueItems", "x3", "x4");
386 		}
387 
388 		@Test void c08_get() {
389 			assertMapped(
390 				TESTER.bean(), (obj,prop) -> obj.get(prop, Object.class),
391 				"allowEmptyValue,collectionFormat,default,description,enum,example,examples,exclusiveMaximum,exclusiveMinimum,format,in,items{type},maxItems,maxLength,maximum,minItems,minLength,minimum,multipleOf,name,pattern,required,schema{type},type,uniqueItems,x3,x4",
392 				"true,a,b,c,[d],e,{f=g},true,true,h,f,{g},2,3,1,5,6,4,7,h,i,true,{j},k,true,x3a,<null>"
393 			);
394 		}
395 
396 		@Test void c09_getTypes() {
397 			assertMapped(
398 				TESTER.bean(), (obj,prop) -> cns(obj.get(prop, Object.class)),
399 				"allowEmptyValue,collectionFormat,default,description,enum,example,examples,exclusiveMaximum,exclusiveMinimum,format,in,items,maxItems,maxLength,maximum,minItems,minLength,minimum,multipleOf,name,pattern,required,schema,type,uniqueItems,x3,x4",
400 				"Boolean,String,String,String,LinkedHashSet,String,LinkedHashMap,Boolean,Boolean,String,String,Items,Integer,Integer,Integer,Integer,Integer,Integer,Integer,String,String,Boolean,SchemaInfo,String,Boolean,String,<null>"
401 			);
402 		}
403 
404 		@Test void c10_nullPropertyValue() {
405 			assertThrows(IllegalArgumentException.class, ()->bean().get(null));
406 			assertThrows(IllegalArgumentException.class, ()->bean().get(null, String.class));
407 			assertThrows(IllegalArgumentException.class, ()->bean().set(null, "a"));
408 		}
409 	}
410 
411 	@Nested class D_refs extends TestBase {
412 
413 		@Test void d01_resolveRefs_schema() {
414 			var swagger = swagger()
415 				.addDefinition("Pet", JsonMap.of("type", "object", "title", "Pet"));
416 
417 			assertBean(
418 				parameterInfo().setName("pet").setIn("body").setSchema(schemaInfo().setRef("#/definitions/Pet")).resolveRefs(swagger, new ArrayDeque<>(), 10),
419 				"name,in,schema{type,title}",
420 				"pet,body,{object,Pet}"
421 			);
422 		}
423 
424 		@Test void d02_resolveRefs_items() {
425 			var swagger = swagger()
426 				.addDefinition("Pet", JsonMap.of("type", "object", "title", "Pet"));
427 
428 			assertBean(
429 				parameterInfo().setName("pets").setIn("query").setType("array").setItems(items().setRef("#/definitions/Pet")).resolveRefs(swagger, new ArrayDeque<>(), 10),
430 				"name,in,type,items{type,title}",
431 				"pets,query,array,{object,Pet}"
432 			);
433 		}
434 
435 		@Test void d03_resolveRefs_maxDepth() {
436 			var swagger = swagger()
437 				.addDefinition("Pet", JsonMap.of("type", "object", "title", "Pet"));
438 
439 			assertBean(
440 				parameterInfo().setName("pet").setIn("body").setSchema(schemaInfo().setRef("#/definitions/Pet")).resolveRefs(swagger, new ArrayDeque<>(), 0),
441 				"name,in,schema{ref}",
442 				"pet,body,{#/definitions/Pet}"
443 			);
444 		}
445 
446 		@Test void d04_resolveRefs_nullSchema() {
447 			var swagger = swagger()
448 				.addDefinition("Pet", JsonMap.of("type", "object", "title", "Pet"));
449 
450 			assertBean(
451 				parameterInfo().setName("pet").setIn("body").resolveRefs(swagger, new ArrayDeque<>(), 10),
452 				"name,in,schema",
453 				"pet,body,<null>"
454 			);
455 		}
456 
457 		@Test void d05_resolveRefs_nullItems() {
458 			var swagger = swagger()
459 				.addDefinition("Pet", JsonMap.of("type", "object", "title", "Pet"));
460 
461 			assertBean(
462 				parameterInfo().setName("pets").setIn("query").setType("array").resolveRefs(swagger, new ArrayDeque<>(), 10),
463 				"name,in,type,items",
464 				"pets,query,array,<null>"
465 			);
466 		}
467 
468 		@Test void d06_resolveRefs_bothNull() {
469 			var swagger = swagger()
470 				.addDefinition("Pet", JsonMap.of("type", "object", "title", "Pet"));
471 
472 			assertBean(
473 				parameterInfo().setName("test").setIn("query").resolveRefs(swagger, new ArrayDeque<>(), 10),
474 				"name,in,schema,items",
475 				"test,query,<null>,<null>"
476 			);
477 		}
478 	}
479 
480 	//---------------------------------------------------------------------------------------------
481 	// Helper methods
482 	//---------------------------------------------------------------------------------------------
483 
484 	private static ParameterInfo bean() {
485 		return parameterInfo();
486 	}
487 }