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