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