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 Items}.
33   */
34  class Items_Test extends TestBase {
35  
36  	@Nested class A_basicTests extends TestBase {
37  
38  		private static final BeanTester<Items> TESTER =
39  			testBean(
40  				bean()
41  					.setCollectionFormat("a")
42  					.setDefault("b")
43  					.setEnum("c")
44  					.setExclusiveMaximum(true)
45  					.setExclusiveMinimum(true)
46  					.setFormat("d")
47  					.setItems(items().setType("e"))
48  					.setMaximum(1)
49  					.setMaxItems(2)
50  					.setMaxLength(3)
51  					.setMinimum(4)
52  					.setMinItems(5)
53  					.setMinLength(6)
54  					.setMultipleOf(7)
55  					.setPattern("f")
56  					.setRef("g")
57  					.setType("h")
58  					.setUniqueItems(true)
59  			)
60  			.props("collectionFormat,default,enum,exclusiveMaximum,exclusiveMinimum,format,items{type},maximum,maxItems,maxLength,minimum,minItems,minLength,multipleOf,pattern,ref,type,uniqueItems")
61  			.vals("a,b,[c],true,true,d,{e},1,2,3,4,5,6,7,f,g,h,true")
62  			.json("{'$ref':'g',collectionFormat:'a','default':'b','enum':['c'],exclusiveMaximum:true,exclusiveMinimum:true,format:'d',items:{type:'e'},maxItems:2,maxLength:3,maximum:1,minItems:5,minLength:6,minimum:4,multipleOf:7,pattern:'f',type:'h',uniqueItems:true}")
63  			.string("{'$ref':'g','collectionFormat':'a','default':'b','enum':['c'],'exclusiveMaximum':true,'exclusiveMinimum':true,'format':'d','items':{'type':'e'},'maxItems':2,'maxLength':3,'maximum':1,'minItems':5,'minLength':6,'minimum':4,'multipleOf':7,'pattern':'f','type':'h','uniqueItems':true}".replace('\'', '"'))
64  		;
65  
66  		@Test void a01_gettersAndSetters() {
67  			TESTER.assertGettersAndSetters();
68  		}
69  
70  		@Test void a02_copy() {
71  			TESTER.assertCopy();
72  		}
73  
74  		@Test void a03_toJson() {
75  			TESTER.assertToJson();
76  		}
77  
78  		@Test void a04_fromJson() {
79  			TESTER.assertFromJson();
80  		}
81  
82  		@Test void a05_roundTrip() {
83  			TESTER.assertRoundTrip();
84  		}
85  
86  		@Test void a06_toString() {
87  			TESTER.assertToString();
88  		}
89  
90  		@Test void a07_keySet() {
91  			assertList(TESTER.bean().keySet(), "$ref", "collectionFormat", "default", "enum", "exclusiveMaximum", "exclusiveMinimum", "format", "items", "maxItems", "maxLength", "maximum", "minItems", "minLength", "minimum", "multipleOf", "pattern", "type", "uniqueItems");
92  		}
93  
94  		@Test void a08_nullParameters() {
95  			var x = bean();
96  			assertThrows(IllegalArgumentException.class, () -> x.get(null, String.class));
97  			assertThrows(IllegalArgumentException.class, () -> x.set(null, "value"));
98  		}
99  
100 		@Test void a09_addMethods() {
101 			var x = bean().addEnum("a1");
102 			assertNotNull(x);
103 			assertNotNull(x.getEnum());
104 		}
105 
106 		@Test void a10_asMap() {
107 			assertBean(
108 				bean()
109 					.setFormat("a")
110 					.setType("b")
111 					.set("x1", "x1a")
112 					.asMap(),
113 				"format,type,x1",
114 				"a,b,x1a"
115 			);
116 		}
117 
118 		@Test void a11_extraKeys() {
119 			var x = bean().set("x1", "x1a").set("x2", "x2a");
120 			assertList(x.extraKeys(), "x1", "x2");
121 			assertEmpty(bean().extraKeys());
122 		}
123 
124 		@Test void a12_strictMode() {
125 			assertThrows(RuntimeException.class, () -> bean().strict().set("foo", "bar"));
126 			assertDoesNotThrow(() -> bean().set("foo", "bar"));
127 
128 			assertFalse(bean().isStrict());
129 			assertTrue(bean().strict().isStrict());
130 			assertFalse(bean().strict(false).isStrict());
131 
132 			var x = bean().strict();
133 			var y = bean(); // not strict
134 
135 			assertThrowsWithMessage(RuntimeException.class, "Invalid value passed in to setCollectionFormat(String).  Value='invalid', valid values=['csv','ssv','tsv','pipes','multi']", () -> x.setCollectionFormat("invalid"));
136 			assertDoesNotThrow(() -> x.setCollectionFormat("csv"));
137 			assertDoesNotThrow(() -> x.setCollectionFormat("ssv"));
138 			assertDoesNotThrow(() -> x.setCollectionFormat("tsv"));
139 			assertDoesNotThrow(() -> x.setCollectionFormat("pipes"));
140 			assertDoesNotThrow(() -> x.setCollectionFormat("multi"));
141 			assertDoesNotThrow(() -> y.setCollectionFormat("invalid"));
142 
143 			assertThrowsWithMessage(RuntimeException.class, "Invalid value passed in to setType(String).  Value='invalid', valid values=['string','number','integer','boolean','array']", () -> x.setType("invalid"));
144 			assertDoesNotThrow(() -> x.setType("string"));
145 			assertDoesNotThrow(() -> x.setType("number"));
146 			assertDoesNotThrow(() -> x.setType("integer"));
147 			assertDoesNotThrow(() -> x.setType("boolean"));
148 			assertDoesNotThrow(() -> x.setType("array"));
149 			assertDoesNotThrow(() -> y.setType("invalid"));
150 		}
151 	}
152 
153 	@Nested class B_emptyTests extends TestBase {
154 
155 		private static final BeanTester<Items> TESTER =
156 			testBean(bean())
157 			.props("format,type,collectionFormat,default,maximum,exclusiveMaximum,minimum,exclusiveMinimum,maxLength,minLength,pattern,maxItems,minItems,uniqueItems,enum,multipleOf,items")
158 			.vals("<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>")
159 			.json("{}")
160 			.string("{}")
161 		;
162 
163 		@Test void b01_gettersAndSetters() {
164 			TESTER.assertGettersAndSetters();
165 		}
166 
167 		@Test void b02_copy() {
168 			TESTER.assertCopy();
169 		}
170 
171 		@Test void b03_toJson() {
172 			TESTER.assertToJson();
173 		}
174 
175 		@Test void b04_fromJson() {
176 			TESTER.assertFromJson();
177 		}
178 
179 		@Test void b05_roundTrip() {
180 			TESTER.assertRoundTrip();
181 		}
182 
183 		@Test void b06_toString() {
184 			TESTER.assertToString();
185 		}
186 
187 		@Test void b07_keySet() {
188 			assertEmpty(TESTER.bean().keySet());
189 		}
190 	}
191 
192 	@Nested class C_extraProperties extends TestBase {
193 
194 		private static final BeanTester<Items> TESTER =
195 			testBean(
196 				bean()
197 					.set("collectionFormat", "a")
198 					.set("default", "b")
199 					.set("enum", set("c"))
200 					.set("exclusiveMaximum", true)
201 					.set("exclusiveMinimum", true)
202 					.set("format", "d")
203 					.set("items", items().setType("e"))
204 					.set("maximum", 1)
205 					.set("maxItems", 2)
206 					.set("maxLength", 3)
207 					.set("minimum", 4)
208 					.set("minItems", 5)
209 					.set("minLength", 6)
210 					.set("multipleOf", 7)
211 					.set("pattern", "f")
212 					.set("$ref", "g")
213 					.set("type", "h")
214 					.set("uniqueItems", true)
215 					.set("x1", "x1a")
216 					.set("x2", null)
217 			)
218 			.props("collectionFormat,default,enum,exclusiveMaximum,exclusiveMinimum,format,items{type},maximum,maxItems,maxLength,minimum,minItems,minLength,multipleOf,pattern,ref,type,uniqueItems,x1,x2")
219 			.vals("a,b,[c],true,true,d,{e},1,2,3,4,5,6,7,f,g,h,true,x1a,<null>")
220 			.json("{'$ref':'g',collectionFormat:'a','default':'b','enum':['c'],exclusiveMaximum:true,exclusiveMinimum:true,format:'d',items:{type:'e'},maxItems:2,maxLength:3,maximum:1,minItems:5,minLength:6,minimum:4,multipleOf:7,pattern:'f',type:'h',uniqueItems:true,x1:'x1a'}")
221 			.string("{'$ref':'g','collectionFormat':'a','default':'b','enum':['c'],'exclusiveMaximum':true,'exclusiveMinimum':true,'format':'d','items':{'type':'e'},'maxItems':2,'maxLength':3,'maximum':1,'minItems':5,'minLength':6,'minimum':4,'multipleOf':7,'pattern':'f','type':'h','uniqueItems':true,'x1':'x1a'}".replace('\'', '"'))
222 		;
223 
224 		@Test void c01_gettersAndSetters() {
225 			TESTER.assertGettersAndSetters();
226 		}
227 
228 		@Test void c02_copy() {
229 			TESTER.assertCopy();
230 		}
231 
232 		@Test void c03_toJson() {
233 			TESTER.assertToJson();
234 		}
235 
236 		@Test void c04_fromJson() {
237 			TESTER.assertFromJson();
238 		}
239 
240 		@Test void c05_roundTrip() {
241 			TESTER.assertRoundTrip();
242 		}
243 
244 		@Test void c06_toString() {
245 			TESTER.assertToString();
246 		}
247 
248 		@Test void c07_keySet() {
249 			assertList(TESTER.bean().keySet(), "$ref", "collectionFormat", "default", "enum", "exclusiveMaximum", "exclusiveMinimum", "format", "items", "maxItems", "maxLength", "maximum", "minItems", "minLength", "minimum", "multipleOf", "pattern", "type", "uniqueItems", "x1", "x2");
250 		}
251 
252 		@Test void c08_get() {
253 			assertMapped(
254 				TESTER.bean(), (obj,prop) -> obj.get(prop, Object.class),
255 				"collectionFormat,default,enum,exclusiveMaximum,exclusiveMinimum,format,items{type},maximum,maxItems,maxLength,minimum,minItems,minLength,multipleOf,pattern,$ref,type,uniqueItems,x1,x2",
256 				"a,b,[c],true,true,d,{e},1,2,3,4,5,6,7,f,g,h,true,x1a,<null>"
257 			);
258 		}
259 
260 		@Test void c09_getTypes() {
261 			assertMapped(
262 				TESTER.bean(), (obj,prop) -> cns(obj.get(prop, Object.class)),
263 				"collectionFormat,default,enum,exclusiveMaximum,exclusiveMinimum,format,items,maximum,maxItems,maxLength,minimum,minItems,minLength,multipleOf,pattern,$ref,type,uniqueItems,x1,x2",
264 				"String,String,LinkedHashSet,Boolean,Boolean,String,Items,Integer,Integer,Integer,Integer,Integer,Integer,Integer,String,String,String,Boolean,String,<null>"
265 			);
266 		}
267 
268 		@Test void c10_nullPropertyValue() {
269 			assertThrows(IllegalArgumentException.class, ()->bean().get(null));
270 			assertThrows(IllegalArgumentException.class, ()->bean().get(null, String.class));
271 			assertThrows(IllegalArgumentException.class, ()->bean().set(null, "a"));
272 		}
273 	}
274 
275 	@Nested class D_refs extends TestBase {
276 
277 		@Test void d01_resolveRefs_basic() {
278 			var swagger = swagger()
279 				.addDefinition("MyItem", JsonMap.of("type", "string"));
280 
281 			assertBean(
282 				items().setRef("#/definitions/MyItem").resolveRefs(swagger, new ArrayDeque<>(), 10),
283 				"type",
284 				"string"
285 			);
286 		}
287 
288 		@Test void d02_resolveRefs_nestedItems() {
289 			var swagger = swagger()
290 				.addDefinition("MyItem", JsonMap.of("type", "string"))
291 				.addDefinition("MyArray", JsonMap.of("type", "array", "items", JsonMap.of("$ref", "#/definitions/MyItem")));
292 
293 			assertBean(
294 				items().setRef("#/definitions/MyArray").resolveRefs(swagger, new ArrayDeque<>(), 10),
295 				"type,items{type}",
296 				"array,{string}"
297 			);
298 		}
299 
300 		@Test void d03_resolveRefs_maxDepth() {
301 			var swagger = swagger()
302 				.addDefinition("MyItem", JsonMap.of("type", "string"))
303 				.addDefinition("MyArray", JsonMap.of("type", "array", "items", JsonMap.of("$ref", "#/definitions/MyItem")));
304 
305 			assertBean(
306 				items().setRef("#/definitions/MyArray").resolveRefs(swagger, new ArrayDeque<>(), 1),
307 				"type,items{ref}",
308 				"array,{#/definitions/MyItem}"
309 			);
310 		}
311 
312 		@Test void d04_resolveRefs_noRefNoItems() {
313 			// Test resolveRefs when both ref and items are null (covers the missing branch)
314 			var swagger = swagger();
315 			var items = items()
316 				.setType("string")
317 				.setFormat("text");
318 
319 			var result = items.resolveRefs(swagger, new ArrayDeque<>(), 10);
320 
321 			// Should return the same object unchanged
322 			assertSame(items, result);
323 			assertEquals("string", result.getType());
324 			assertEquals("text", result.getFormat());
325 		}
326 	}
327 
328 	//---------------------------------------------------------------------------------------------
329 	// Helper methods
330 	//---------------------------------------------------------------------------------------------
331 
332 	private static Items bean() {
333 		return items();
334 	}
335 }