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.net.*;
24  import java.util.*;
25  
26  import org.apache.juneau.*;
27  import org.apache.juneau.collections.*;
28  import org.junit.jupiter.api.*;
29  
30  /**
31   * Testcase for {@link SchemaInfo}.
32   */
33  class SchemaInfo_Test extends TestBase {
34  
35  	@Nested class A_basicTests extends TestBase {
36  
37  		private static final BeanTester<SchemaInfo> TESTER =
38  			testBean(
39  				bean()
40  					.setAdditionalProperties(schemaInfo().setType("a"))
41  					.setAllOf(set(schemaInfo().setType("b")))
42  					.setDefault("c")
43  					.setDescription("d")
44  					.setDiscriminator("e")
45  					.setEnum(set("f"))
46  					.setExample("g")
47  					.setExclusiveMaximum(true)
48  					.setExclusiveMinimum(true)
49  					.setExternalDocs(externalDocumentation().setUrl(URI.create("h")))
50  					.setFormat("i")
51  					.setItems(items().setType("j"))
52  					.setMaximum(1)
53  					.setMaxItems(2)
54  					.setMaxLength(3)
55  					.setMaxProperties(4)
56  					.setMinimum(5)
57  					.setMinItems(6)
58  					.setMinLength(7)
59  					.setMinProperties(8)
60  					.setMultipleOf(9)
61  					.setPattern("k")
62  					.setProperties(map("x1", schemaInfo().setType("x2")))
63  					.setReadOnly(true)
64  					.setRef("l")
65  					.setRequired(true)
66  					.setRequiredProperties("m")
67  					.setTitle("n")
68  					.setType("o")
69  					.setUniqueItems(true)
70  					.setXml(xml().setName("p"))
71  			)
72  			.props("additionalProperties{type},allOf{#{type}},default,description,discriminator,enum,example,exclusiveMaximum,exclusiveMinimum,externalDocs{url},format,items{type},maximum,maxItems,maxLength,maxProperties,minimum,minItems,minLength,minProperties,multipleOf,pattern,properties{x1{type}},readOnly,ref,required,requiredProperties,title,type,uniqueItems,xml{name}")
73  			.vals("{a},{[{b}]},c,d,e,[f],g,true,true,{h},i,{j},1,2,3,4,5,6,7,8,9,k,{{x2}},true,l,true,[m],n,o,true,{p}")
74  			.json("{'$ref':'l',additionalProperties:{type:'a'},allOf:[{type:'b'}],'default':'c',description:'d',discriminator:'e','enum':['f'],example:'g',exclusiveMaximum:true,exclusiveMinimum:true,externalDocs:{url:'h'},format:'i',items:{type:'j'},maxItems:2,maxLength:3,maxProperties:4,maximum:1,minItems:6,minLength:7,minProperties:8,minimum:5,multipleOf:9,pattern:'k',properties:{x1:{type:'x2'}},readOnly:true,required:true,requiredProperties:['m'],title:'n',type:'o',uniqueItems:true,xml:{name:'p'}}")
75  			.string("{'$ref':'l','additionalProperties':{'type':'a'},'allOf':[{'type':'b'}],'default':'c','description':'d','discriminator':'e','enum':['f'],'example':'g','exclusiveMaximum':true,'exclusiveMinimum':true,'externalDocs':{'url':'h'},'format':'i','items':{'type':'j'},'maxItems':2,'maxLength':3,'maxProperties':4,'maximum':1,'minItems':6,'minLength':7,'minProperties':8,'minimum':5,'multipleOf':9,'pattern':'k','properties':{'x1':{'type':'x2'}},'readOnly':true,'required':true,'requiredProperties':['m'],'title':'n','type':'o','uniqueItems':true,'xml':{'name':'p'}}".replace('\'', '"'))
76  		;
77  
78  		@Test void a01_gettersAndSetters() {
79  			TESTER.assertGettersAndSetters();
80  		}
81  
82  		@Test void a02_copy() {
83  			TESTER.assertCopy();
84  		}
85  
86  		@Test void a03_toJson() {
87  			TESTER.assertToJson();
88  		}
89  
90  		@Test void a04_fromJson() {
91  			TESTER.assertFromJson();
92  		}
93  
94  		@Test void a05_roundTrip() {
95  			TESTER.assertRoundTrip();
96  		}
97  
98  		@Test void a06_toString() {
99  			TESTER.assertToString();
100 		}
101 
102 		@Test void a07_keySet() {
103 			assertList(TESTER.bean().keySet(), "$ref", "additionalProperties", "allOf", "default", "description", "discriminator", "enum", "example", "exclusiveMaximum", "exclusiveMinimum", "externalDocs", "format", "items", "maxItems", "maxLength", "maxProperties", "maximum", "minItems", "minLength", "minProperties", "minimum", "multipleOf", "pattern", "properties", "readOnly", "required", "requiredProperties", "title", "type", "uniqueItems", "xml");
104 		}
105 
106 		@Test void a08_addMethods() {
107 			var x = bean()
108 				.addEnum("a1")
109 				.setAllOf(schemaInfo().setTitle("a1"))
110 				.addAllOf(schemaInfo().setTitle("a2"))
111 			;
112 
113 			assertNotNull(x);
114 			assertNotNull(x.getEnum());
115 			assertNotNull(x.getAllOf());
116 		}
117 
118 		@Test void a09_asMap() {
119 			assertBean(
120 				bean()
121 					.setDescription("a")
122 					.setTitle("b")
123 					.setType("c")
124 					.set("x1", "x1a")
125 					.asMap(),
126 				"description,title,type,x1",
127 				"a,b,c,x1a"
128 			);
129 		}
130 
131 		@Test void a10_extraKeys() {
132 			var x = bean().set("x1", "x1a").set("x2", "x2a");
133 			assertList(x.extraKeys(), "x1", "x2");
134 			assertEmpty(bean().extraKeys());
135 		}
136 
137 		@Test void a11_strictMode() {
138 			assertThrows(RuntimeException.class, () -> bean().strict().set("foo", "bar"));
139 			assertDoesNotThrow(() -> bean().set("foo", "bar"));
140 
141 			assertFalse(bean().isStrict());
142 			assertTrue(bean().strict().isStrict());
143 			assertFalse(bean().strict(false).isStrict());
144 		}
145 
146 		@Test void a12_collectionSetters() {
147 			var x = bean()
148 				.setAllOf(list(schemaInfo().setType("a"), schemaInfo().setType("b")))
149 				.setEnum(list("c", "d"))
150 				.setRequiredProperties(list("e", "f"));
151 
152 			assertBean(x,
153 				"allOf{#{type}},enum,requiredProperties",
154 				"{[{a},{b}]},[c,d],[e,f]"
155 			);
156 		}
157 
158 		@Test void a13_varargAdders() {
159 			var x = bean()
160 				.addAllOf(schemaInfo().setType("a1"))
161 				.addAllOf(schemaInfo().setType("a2"))
162 				.addEnum("b1")
163 				.addEnum("b2")
164 				.addRequiredProperties("c1")
165 				.addRequiredProperties("c2");
166 
167 			assertBean(x,
168 				"allOf{#{type}},enum,requiredProperties",
169 				"{[{a1},{a2}]},[b1,b2],[c1,c2]"
170 			);
171 		}
172 
173 		@Test void a14_collectionAdders() {
174 			// Test that Collection versions of addX methods exist
175 			// Due to Java method resolution preferring varargs over Collection,
176 			// we test the basic functionality with varargs versions
177 			var x = bean()
178 				.addAllOf(list(schemaInfo().setType("a1")))
179 				.addAllOf(list(schemaInfo().setType("a2")))
180 				.addEnum(list("b1"))
181 				.addEnum(list("b2"))
182 				.addRequiredProperties(list("c1"))
183 				.addRequiredProperties(list("c2"));
184 
185 			assertBean(x,
186 				"allOf{#{type}},enum,requiredProperties",
187 				"{[{a1},{a2}]},[b1,b2],[c1,c2]"
188 			);
189 		}
190 	}
191 
192 	@Nested class B_emptyTests extends TestBase {
193 
194 		private static final BeanTester<SchemaInfo> TESTER =
195 			testBean(bean())
196 			.props("description,title,type,format,items,collectionFormat,default,maximum,exclusiveMaximum,minimum,exclusiveMinimum,maxLength,minLength,pattern,maxItems,minItems,uniqueItems,enum,multipleOf,discriminator,readOnly,xml,externalDocs,example,required,properties,additionalProperties,allOf")
197 			.vals("<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>")
198 			.json("{}")
199 			.string("{}")
200 		;
201 
202 		@Test void b01_gettersAndSetters() {
203 			TESTER.assertGettersAndSetters();
204 		}
205 
206 		@Test void b02_copy() {
207 			TESTER.assertCopy();
208 		}
209 
210 		@Test void b03_toJson() {
211 			TESTER.assertToJson();
212 		}
213 
214 		@Test void b04_fromJson() {
215 			TESTER.assertFromJson();
216 		}
217 
218 		@Test void b05_roundTrip() {
219 			TESTER.assertRoundTrip();
220 		}
221 
222 		@Test void b06_toString() {
223 			TESTER.assertToString();
224 		}
225 
226 		@Test void b07_keySet() {
227 			assertEmpty(TESTER.bean().keySet());
228 		}
229 
230 		@Test void b08_nullParameters() {
231 			var x = bean();
232 			assertThrows(IllegalArgumentException.class, () -> x.get(null, String.class));
233 			assertThrows(IllegalArgumentException.class, () -> x.set(null, "value"));
234 		}
235 	}
236 
237 	@Nested class C_extraProperties extends TestBase {
238 
239 		private static final BeanTester<SchemaInfo> TESTER =
240 			testBean(
241 				bean()
242 					.set("additionalProperties", schemaInfo().setType("a"))
243 					.set("allOf", set(schemaInfo().setType("b")))
244 					.set("default", "c")
245 					.set("description", "d")
246 					.set("discriminator", "e")
247 					.set("enum", set("f"))
248 					.set("example", "g")
249 					.set("exclusiveMaximum", true)
250 					.set("exclusiveMinimum", true)
251 					.set("externalDocs", externalDocumentation().setUrl(URI.create("h")))
252 					.set("format", "i")
253 					.set("items", items().setType("j"))
254 					.set("maximum", 1)
255 					.set("maxItems", 2)
256 					.set("maxLength", 3)
257 					.set("maxProperties", 4)
258 					.set("minimum", 5)
259 					.set("minItems", 6)
260 					.set("minLength", 7)
261 					.set("minProperties", 8)
262 					.set("multipleOf", 9)
263 					.set("pattern", "k")
264 					.set("properties", map("x1", schemaInfo().setType("x2")))
265 					.set("readOnly", true)
266 					.set("$ref", "l")
267 					.set("required", true)
268 					.set("requiredProperties", set("m"))
269 					.set("title", "n")
270 					.set("type", "o")
271 					.set("uniqueItems", true)
272 					.set("xml", xml().setName("p"))
273 					.set("x1", "x1a")
274 					.set("x2", null)
275 			)
276 			.props("additionalProperties{type},allOf,default,description,discriminator,enum,example,exclusiveMaximum,exclusiveMinimum,externalDocs{url},format,items{type},maximum,maxItems,maxLength,maxProperties,minimum,minItems,minLength,minProperties,multipleOf,pattern,properties{x1{type}},readOnly,ref,required,requiredProperties,title,type,uniqueItems,xml{name},x1,x2")
277 			.vals("{a},[{\"type\":\"b\"}],c,d,e,[f],g,true,true,{h},i,{j},1,2,3,4,5,6,7,8,9,k,{{x2}},true,l,true,[m],n,o,true,{p},x1a,<null>")
278 			.json("{'$ref':'l',additionalProperties:{type:'a'},allOf:[{type:'b'}],'default':'c',description:'d',discriminator:'e','enum':['f'],example:'g',exclusiveMaximum:true,exclusiveMinimum:true,externalDocs:{url:'h'},format:'i',items:{type:'j'},maxItems:2,maxLength:3,maxProperties:4,maximum:1,minItems:6,minLength:7,minProperties:8,minimum:5,multipleOf:9,pattern:'k',properties:{x1:{type:'x2'}},readOnly:true,required:true,requiredProperties:['m'],title:'n',type:'o',uniqueItems:true,x1:'x1a',xml:{name:'p'}}")
279 			.string("{'$ref':'l','additionalProperties':{'type':'a'},'allOf':[{'type':'b'}],'default':'c','description':'d','discriminator':'e','enum':['f'],'example':'g','exclusiveMaximum':true,'exclusiveMinimum':true,'externalDocs':{'url':'h'},'format':'i','items':{'type':'j'},'maxItems':2,'maxLength':3,'maxProperties':4,'maximum':1,'minItems':6,'minLength':7,'minProperties':8,'minimum':5,'multipleOf':9,'pattern':'k','properties':{'x1':{'type':'x2'}},'readOnly':true,'required':true,'requiredProperties':['m'],'title':'n','type':'o','uniqueItems':true,'x1':'x1a','xml':{'name':'p'}}".replace('\'', '"'))
280 		;
281 
282 		@Test void c01_gettersAndSetters() {
283 			TESTER.assertGettersAndSetters();
284 		}
285 
286 		@Test void c02_copy() {
287 			TESTER.assertCopy();
288 		}
289 
290 		@Test void c03_toJson() {
291 			TESTER.assertToJson();
292 		}
293 
294 		@Test void c04_fromJson() {
295 			TESTER.assertFromJson();
296 		}
297 
298 		@Test void c05_roundTrip() {
299 			TESTER.assertRoundTrip();
300 		}
301 
302 		@Test void c06_toString() {
303 			TESTER.assertToString();
304 		}
305 
306 		@Test void c07_keySet() {
307 			assertList(TESTER.bean().keySet(), "$ref", "additionalProperties", "allOf", "default", "description", "discriminator", "enum", "example", "exclusiveMaximum", "exclusiveMinimum", "externalDocs", "format", "items", "maxItems", "maxLength", "maxProperties", "maximum", "minItems", "minLength", "minProperties", "minimum", "multipleOf", "pattern", "properties", "readOnly", "required", "requiredProperties", "title", "type", "uniqueItems", "x1", "x2", "xml");
308 		}
309 
310 		@Test void c08_get() {
311 			assertMapped(
312 				TESTER.bean(), (obj,prop) -> obj.get(prop, Object.class),
313 				"additionalProperties{type},allOf{#{type}},default,description,discriminator,enum,example,exclusiveMaximum,exclusiveMinimum,externalDocs{url},format,items{type},maximum,maxItems,maxLength,maxProperties,minimum,minItems,minLength,minProperties,multipleOf,pattern,properties{x1{type}},readOnly,$ref,required,requiredProperties,title,type,uniqueItems,xml{name},x1,x2",
314 				"{a},{[{b}]},c,d,e,[f],g,true,true,{h},i,{j},1,2,3,4,5,6,7,8,9,k,{{x2}},true,l,true,[m],n,o,true,{p},x1a,<null>"
315 			);
316 		}
317 
318 		@Test void c09_getTypes() {
319 			assertMapped(
320 				TESTER.bean(), (obj,prop) -> simpleClassNameOf(obj.get(prop, Object.class)),
321 				"additionalProperties,allOf,default,description,discriminator,enum,example,exclusiveMaximum,exclusiveMinimum,externalDocs,format,items,maximum,maxItems,maxLength,maxProperties,minimum,minItems,minLength,minProperties,multipleOf,pattern,properties,readOnly,$ref,required,requiredProperties,title,type,uniqueItems,xml,x1,x2",
322 				"SchemaInfo,LinkedHashSet,String,String,String,LinkedHashSet,String,Boolean,Boolean,ExternalDocumentation,String,Items,Integer,Integer,Integer,Integer,Integer,Integer,Integer,Integer,Integer,String,LinkedHashMap,Boolean,String,Boolean,LinkedHashSet,String,String,Boolean,Xml,String,<null>"
323 			);
324 		}
325 
326 		@Test void c10_nullPropertyValue() {
327 			assertThrows(IllegalArgumentException.class, ()->bean().get(null));
328 			assertThrows(IllegalArgumentException.class, ()->bean().get(null, String.class));
329 			assertThrows(IllegalArgumentException.class, ()->bean().set(null, "a"));
330 		}
331 	}
332 
333 	@Nested class D_refs extends TestBase {
334 
335 		@Test void d01_resolveRefs_basic() {
336 			var swagger = swagger()
337 				.addDefinition("Pet", JsonMap.of("type", "object", "title", "Pet"))
338 				.addDefinition("Pets", JsonMap.of("type", "array", "items", JsonMap.of("$ref", "#/definitions/Pet")));
339 
340 			assertBean(
341 				schemaInfo().setRef("#/definitions/Pet").resolveRefs(swagger, new ArrayDeque<>(), 10),
342 				"type,title",
343 				"object,Pet"
344 			);
345 		}
346 
347 		@Test void d02_resolveRefs_nested() {
348 			var swagger = swagger()
349 				.addDefinition("Pet", JsonMap.of("type", "object", "title", "Pet"))
350 				.addDefinition("Pets", JsonMap.of("type", "array", "items", JsonMap.of("$ref", "#/definitions/Pet")));
351 
352 			assertBean(
353 				schemaInfo().setRef("#/definitions/Pets").resolveRefs(swagger, new ArrayDeque<>(), 10),
354 				"type,items{type,title}",
355 				"array,{object,Pet}"
356 			);
357 		}
358 
359 		@Test void d03_resolveRefs_maxDepth() {
360 			var swagger = swagger()
361 				.addDefinition("Pet", JsonMap.of("type", "object", "title", "Pet"))
362 				.addDefinition("Pets", JsonMap.of("type", "array", "items", JsonMap.of("$ref", "#/definitions/Pet")));
363 
364 			assertBean(
365 				schemaInfo().setRef("#/definitions/Pets").resolveRefs(swagger, new ArrayDeque<>(), 1),
366 				"type,items{ref}",
367 				"array,{#/definitions/Pet}"
368 			);
369 		}
370 
371 		@Test void d04_resolveRefs_circular() {
372 			var swagger = swagger()
373 				.addDefinition("A", JsonMap.of("type", "object", "title", "A", "properties", JsonMap.of("b", JsonMap.of("$ref", "#/definitions/B"))))
374 				.addDefinition("B", JsonMap.of("type", "object", "title", "B", "properties", JsonMap.of("a", JsonMap.of("$ref", "#/definitions/A"))));
375 
376 			assertBean(
377 				schemaInfo().setRef("#/definitions/A").resolveRefs(swagger, new ArrayDeque<>(), 10),
378 				"type,title,properties{b{type,title,properties{a{ref}}}}",
379 				"object,A,{{object,B,{{#/definitions/A}}}}"
380 			);
381 		}
382 
383 		@Test void d05_resolveRefs_properties() {
384 			var swagger = swagger()
385 				.addDefinition("Pet", JsonMap.of("type", "object", "title", "Pet"));
386 
387 			assertBean(
388 				schemaInfo().setType("object").addProperty("pet", schemaInfo().setRef("#/definitions/Pet")).resolveRefs(swagger, new ArrayDeque<>(), 10),
389 				"type,properties{pet{type,title}}",
390 				"object,{{object,Pet}}"
391 			);
392 		}
393 
394 		@Test void d06_resolveRefs_additionalProperties() {
395 			var swagger = swagger()
396 				.addDefinition("Pet", JsonMap.of("type", "object", "title", "Pet"));
397 
398 			assertBean(
399 				schemaInfo().setType("object").setAdditionalProperties(schemaInfo().setRef("#/definitions/Pet")).resolveRefs(swagger, new ArrayDeque<>(), 10),
400 				"type,additionalProperties{type,title}",
401 				"object,{object,Pet}"
402 			);
403 		}
404 	}
405 
406 
407 	//---------------------------------------------------------------------------------------------
408 	// Helper methods
409 	//---------------------------------------------------------------------------------------------
410 
411 	private static SchemaInfo bean() {
412 		return schemaInfo();
413 	}
414 
415 }