1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.juneau.bean.openapi3;
18
19 import static org.apache.juneau.TestUtils.*;
20 import static org.apache.juneau.bean.openapi3.OpenApiBuilder.*;
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
34
35 class Items_Test extends TestBase {
36
37 @Nested class A_basicTests extends TestBase {
38
39 private static final BeanTester<Items> TESTER =
40 testBean(
41 bean()
42 .setCollectionFormat("a")
43 .setDefault("b")
44 .setEnum(l("c1", "c2"))
45 .setExclusiveMaximum(true)
46 .setExclusiveMinimum(true)
47 .setFormat("d")
48 .setItems(bean().setType("e"))
49 .setMaxItems(1)
50 .setMaxLength(2)
51 .setMaximum(3)
52 .setMinItems(4)
53 .setMinLength(5)
54 .setMinimum(6)
55 .setMultipleOf(7)
56 .setPattern("f")
57 .setRef("g")
58 .setType("h")
59 .setUniqueItems(true)
60 )
61 .props("collectionFormat,default,enum,exclusiveMaximum,exclusiveMinimum,format,items{type},maximum,maxItems,maxLength,minimum,minItems,minLength,multipleOf,pattern,ref,type,uniqueItems")
62 .vals("a,b,[c1,c2],true,true,d,{e},3,1,2,6,4,5,7,f,g,h,true")
63 .json("{'$ref':'g',collectionFormat:'a','default':'b','enum':['c1','c2'],exclusiveMaximum:true,exclusiveMinimum:true,format:'d',items:{type:'e'},maxItems:1,maxLength:2,maximum:3,minItems:4,minLength:5,minimum:6,multipleOf:7,pattern:'f',type:'h',uniqueItems:true}")
64 .string("{'$ref':'g','collectionFormat':'a','default':'b','enum':['c1','c2'],'exclusiveMaximum':true,'exclusiveMinimum':true,'format':'d','items':{'type':'e'},'maxItems':1,'maxLength':2,'maximum':3,'minItems':4,'minLength':5,'minimum':6,'multipleOf':7,'pattern':'f','type':'h','uniqueItems':true}".replace('\'','"'))
65 ;
66
67 @Test void a01_gettersAndSetters() {
68 TESTER.assertGettersAndSetters();
69 }
70
71 @Test void a02_copy() {
72 TESTER.assertCopy();
73 }
74
75 @Test void a03_toJson() {
76 TESTER.assertToJson();
77 }
78
79 @Test void a04_fromJson() {
80 TESTER.assertFromJson();
81 }
82
83 @Test void a05_roundTrip() {
84 TESTER.assertRoundTrip();
85 }
86
87 @Test void a06_toString() {
88 TESTER.assertToString();
89 }
90
91 @Test void a07_keySet() {
92 assertList(TESTER.bean().keySet(), "$ref", "collectionFormat", "default", "enum", "exclusiveMaximum", "exclusiveMinimum", "format", "items", "maxItems", "maxLength", "maximum", "minItems", "minLength", "minimum", "multipleOf", "pattern", "type", "uniqueItems");
93 }
94
95 @Test void a08_nullParameters() {
96 var x = bean();
97 assertThrows(IllegalArgumentException.class, () -> x.get(null, String.class));
98 assertThrows(IllegalArgumentException.class, () -> x.set(null, "value"));
99 }
100
101 @Test void a08b_getSetRef() {
102
103 var x = bean();
104 x.set("$ref", "#/components/schemas/MyItem");
105 assertEquals("#/components/schemas/MyItem", x.get("$ref", String.class));
106 assertEquals("#/components/schemas/MyItem", x.getRef());
107 }
108
109 @Test void a09_addMethods() {
110 assertBean(
111 bean()
112 .addEnum("a1", "a2"),
113 "enum",
114 "[a1,a2]"
115 );
116 }
117
118 @Test void a10_asMap() {
119 assertBean(
120 bean()
121 .setType("a")
122 .set("x1", "x1a")
123 .asMap(),
124 "type,x1",
125 "a,x1a"
126 );
127 }
128
129 @Test void a11_extraKeys() {
130 var x = bean().set("x1", "x1a").set("x2", "x2a");
131 assertList(x.extraKeys(), "x1", "x2");
132 assertEmpty(bean().extraKeys());
133 }
134
135 @Test void a12_getItemsProperty() {
136 var x = bean().setItems(bean().setType("a"));
137 assertBean(x.get("items", Items.class), "type", "a");
138 }
139
140 @Test void a13_strictMode() {
141 assertThrows(RuntimeException.class, () -> bean().strict().set("foo", "bar"));
142 assertDoesNotThrow(() -> bean().set("foo", "bar"));
143
144 assertFalse(bean().isStrict());
145 assertTrue(bean().strict().isStrict());
146 assertFalse(bean().strict(false).isStrict());
147
148 var x = bean().strict();
149 var y = bean();
150 assertThrowsWithMessage(IllegalArgumentException.class, "Invalid value passed in to setType(String). Value='invalid', valid values=['string','number','integer','boolean','array']", () -> x.setType("invalid"));
151 assertDoesNotThrow(() -> x.setType("string"));
152 assertDoesNotThrow(() -> x.setType("number"));
153 assertDoesNotThrow(() -> x.setType("integer"));
154 assertDoesNotThrow(() -> x.setType("boolean"));
155 assertDoesNotThrow(() -> x.setType("array"));
156 assertDoesNotThrow(() -> y.setType("invalid"));
157
158 assertThrowsWithMessage(RuntimeException.class, "Invalid value passed in to setCollectionFormat(String). Value='invalid', valid values=[csv, ssv, tsv, pipes, multi]", () -> x.setCollectionFormat("invalid"));
159 assertDoesNotThrow(() -> x.setCollectionFormat("csv"));
160 assertDoesNotThrow(() -> x.setCollectionFormat("ssv"));
161 assertDoesNotThrow(() -> x.setCollectionFormat("tsv"));
162 assertDoesNotThrow(() -> x.setCollectionFormat("pipes"));
163 assertDoesNotThrow(() -> x.setCollectionFormat("multi"));
164 assertDoesNotThrow(() -> y.setCollectionFormat("invalid"));
165 }
166 }
167
168 @Nested class B_emptyTests extends TestBase {
169
170 private static final BeanTester<Items> TESTER =
171 testBean(bean())
172 .props("type,format,items,collectionFormat,default,maximum,exclusiveMaximum,minimum,exclusiveMinimum,maxLength,minLength,pattern,maxItems,minItems,uniqueItems,enum,multipleOf,ref")
173 .vals("<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>")
174 .json("{}")
175 .string("{}")
176 ;
177
178 @Test void b01_gettersAndSetters() {
179 TESTER.assertGettersAndSetters();
180 }
181
182 @Test void b02_copy() {
183 TESTER.assertCopy();
184 }
185
186 @Test void b03_toJson() {
187 TESTER.assertToJson();
188 }
189
190 @Test void b04_fromJson() {
191 TESTER.assertFromJson();
192 }
193
194 @Test void b05_roundTrip() {
195 TESTER.assertRoundTrip();
196 }
197
198 @Test void b06_toString() {
199 TESTER.assertToString();
200 }
201
202 @Test void b07_keySet() {
203 assertEmpty(TESTER.bean().keySet());
204 }
205 }
206
207 @Nested class C_extraProperties extends TestBase {
208 private static final BeanTester<Items> TESTER =
209 testBean(
210 bean()
211 .set("additionalItems", schemaInfo("a"))
212 .set("allOf", l(schemaInfo("b1"), schemaInfo("b2")))
213 .set("collectionFormat", "c")
214 .set("default", "d")
215 .set("discriminator", "e")
216 .set("enum", l("f1", "f2"))
217 .set("example", "g")
218 .set("exclusiveMaximum", true)
219 .set("exclusiveMinimum", true)
220 .set("externalDocs", externalDocumentation().setUrl(URI.create("h")))
221 .set("format", "i")
222 .set("items", bean().setType("j"))
223 .set("maxItems", 1)
224 .set("maxLength", 2)
225 .set("maxProperties", 3)
226 .set("maximum", 4)
227 .set("minItems", 5)
228 .set("minLength", 6)
229 .set("minProperties", 7)
230 .set("minimum", 8)
231 .set("multipleOf", 9)
232 .set("pattern", "k")
233 .set("properties", map("l1", schemaInfo("l2")))
234 .set("readOnly", true)
235 .set("required", l("m1", "m2"))
236 .set("title", "n")
237 .set("type", "o")
238 .set("uniqueItems", true)
239 .set("xml", xml().setName("p"))
240 .set("x1", "x1a")
241 .set("x2", null)
242 )
243 .props("additionalItems{type},allOf{#{type}},collectionFormat,default,discriminator,enum{#{toString}},example,exclusiveMaximum,exclusiveMinimum,externalDocs{url},format,items{type},maxItems,maxLength,maxProperties,maximum,minItems,minLength,minProperties,minimum,multipleOf,pattern,properties{l1{type}},readOnly,required{#{toString}},title,type,uniqueItems,xml{name},x1,x2")
244 .vals("{a},{[{b1},{b2}]},c,d,e,{[{f1},{f2}]},g,true,true,{h},i,{j},1,2,3,4,5,6,7,8,9,k,{{l2}},true,{[{m1},{m2}]},n,o,true,{p},x1a,<null>")
245 .json("{additionalItems:{type:'a'},allOf:[{type:'b1'},{type:'b2'}],collectionFormat:'c','default':'d',discriminator:'e','enum':['f1','f2'],example:'g',exclusiveMaximum:true,exclusiveMinimum:true,externalDocs:{url:'h'},format:'i',items:{type:'j'},maxItems:1,maxLength:2,maxProperties:3,maximum:4,minItems:5,minLength:6,minProperties:7,minimum:8,multipleOf:9,pattern:'k',properties:{l1:{type:'l2'}},readOnly:true,required:['m1','m2'],title:'n',type:'o',uniqueItems:true,x1:'x1a',xml:{name:'p'}}")
246 .string("{'additionalItems':{'type':'a'},'allOf':[{'type':'b1'},{'type':'b2'}],'collectionFormat':'c','default':'d','discriminator':'e','enum':['f1','f2'],'example':'g','exclusiveMaximum':true,'exclusiveMinimum':true,'externalDocs':{'url':'h'},'format':'i','items':{'type':'j'},'maxItems':1,'maxLength':2,'maxProperties':3,'maximum':4,'minItems':5,'minLength':6,'minProperties':7,'minimum':8,'multipleOf':9,'pattern':'k','properties':{'l1':{'type':'l2'}},'readOnly':true,'required':['m1','m2'],'title':'n','type':'o','uniqueItems':true,'x1':'x1a','xml':{'name':'p'}}".replace('\'', '"'))
247 ;
248
249 @Test void c01_gettersAndSetters() {
250 TESTER.assertGettersAndSetters();
251 }
252
253 @Test void c02_copy() {
254 TESTER.assertCopy();
255 }
256
257 @Test void c03_toJson() {
258 TESTER.assertToJson();
259 }
260
261 @Test void c04_fromJson() {
262 TESTER.assertFromJson();
263 }
264
265 @Test void c05_roundTrip() {
266 TESTER.assertRoundTrip();
267 }
268
269 @Test void c06_toString() {
270 TESTER.assertToString();
271 }
272
273 @Test void c07_keySet() {
274 assertList(TESTER.bean().keySet(), "collectionFormat", "default", "enum", "exclusiveMaximum", "exclusiveMinimum", "format", "items", "maxItems", "maxLength", "maximum", "minItems", "minLength", "minimum", "multipleOf", "pattern", "type", "uniqueItems", "additionalItems", "allOf", "discriminator", "example", "externalDocs", "maxProperties", "minProperties", "properties", "readOnly", "required", "title", "xml", "x1", "x2");
275 }
276
277 @Test void c08_get() {
278 assertMapped(
279 TESTER.bean(), (obj,prop) -> obj.get(prop, Object.class),
280 "additionalItems{type},allOf{#{type}},collectionFormat,default,discriminator,enum{#{toString}},example,exclusiveMaximum,exclusiveMinimum,externalDocs{url},format,items{type},maxItems,maxLength,maxProperties,maximum,minItems,minLength,minProperties,minimum,multipleOf,pattern,properties{l1{type}},readOnly,required{#{toString}},title,type,uniqueItems,xml{name},x1,x2",
281 "{a},{[{b1},{b2}]},c,d,e,{[{f1},{f2}]},g,true,true,{h},i,{j},1,2,3,4,5,6,7,8,9,k,{{l2}},true,{[{m1},{m2}]},n,o,true,{p},x1a,<null>"
282 );
283 }
284
285 @Test void c09_getTypes() {
286 assertMapped(
287 TESTER.bean(), (obj,prop) -> cns(obj.get(prop, Object.class)),
288 "additionalItems,allOf,collectionFormat,default,discriminator,enum,example,exclusiveMaximum,exclusiveMinimum,externalDocs,format,items,maxItems,maxLength,maxProperties,maximum,minItems,minLength,minProperties,minimum,multipleOf,pattern,properties,readOnly,required,title,type,uniqueItems,xml,x1,x2",
289 "SchemaInfo,ArrayList,String,String,String,ArrayList,String,Boolean,Boolean,ExternalDocumentation,String,Items,Integer,Integer,Integer,Integer,Integer,Integer,Integer,Integer,Integer,String,LinkedHashMap,Boolean,ArrayList,String,String,Boolean,Xml,String,<null>"
290 );
291 }
292
293 @Test void c10_nullPropertyValue() {
294 assertThrows(IllegalArgumentException.class, ()->bean().get(null));
295 assertThrows(IllegalArgumentException.class, ()->bean().get(null, String.class));
296 assertThrows(IllegalArgumentException.class, ()->bean().set(null, "a"));
297 }
298 }
299
300 @Nested class D_refs extends TestBase {
301
302 @Test void d01_resolveRefs_basic() {
303 var openApi = openApi()
304 .setComponents(components().setSchemas(m(
305 "MyItem", schemaInfo().setType("string")
306 )));
307 assertBean(
308 items().setRef("#/components/schemas/MyItem").resolveRefs(openApi, new ArrayDeque<>(), 10),
309 "type",
310 "string"
311 );
312 }
313
314 @Test void d02_resolveRefs_nestedItems() {
315 var openApi = openApi()
316 .setComponents(components().setSchemas(m(
317 "MyItem", schemaInfo().setType("string"),
318 "MyArray", schemaInfo().setType("array").setItems(items().setRef("#/components/schemas/MyItem"))
319 )));
320
321 assertBean(
322 items().setRef("#/components/schemas/MyArray").resolveRefs(openApi, new ArrayDeque<>(), 10),
323 "type,items{type}",
324 "array,{string}"
325 );
326 }
327
328 @Test void d03_resolveRefs_maxDepth() {
329 var openApi = openApi()
330 .setComponents(components().setSchemas(m(
331 "MyItem", schemaInfo().setType("string"),
332 "MyArray", schemaInfo().setType("array").setItems(items().setRef("#/components/schemas/MyItem"))
333 )));
334 assertBean(
335 items().setRef("#/components/schemas/MyArray").resolveRefs(openApi, new ArrayDeque<>(), 1),
336 "type,items{ref}",
337 "array,{#/components/schemas/MyItem}"
338 );
339 }
340
341 @Test void d04_resolveRefsWithRef() {
342 var openApi = openApi()
343 .setComponents(components().setSchemas(m(
344 "MyItem", schemaInfo().setType("string")
345 )));
346
347 assertBean(
348 items().setRef("#/components/schemas/MyItem").resolveRefs(openApi, new ArrayDeque<>(), 10),
349 "type",
350 "string"
351 );
352
353 var refStack = new ArrayDeque<String>();
354 refStack.add("#/components/schemas/MyItem");
355
356
357 assertBean(
358 items().setRef("#/components/schemas/MyItem").resolveRefs(openApi, refStack, 10),
359 "ref",
360 "#/components/schemas/MyItem"
361 );
362
363
364 assertBean(
365 items().setRef("#/components/schemas/MyItem").resolveRefs(openApi, new ArrayDeque<>(), 0),
366 "ref",
367 "#/components/schemas/MyItem"
368 );
369
370
371 assertBean(
372 items()
373 .set("properties", JsonMap.of("prop1", JsonMap.of("$ref", "#/components/schemas/MyItem")))
374 .resolveRefs(openApi, new ArrayDeque<>(), 10),
375 "properties{prop1{type}}",
376 "{{string}}"
377 );
378
379
380 assertBean(
381 items().setItems(items().setRef("#/components/schemas/MyItem")).resolveRefs(openApi, new ArrayDeque<>(), 10),
382 "items{type}",
383 "{string}"
384 );
385
386
387 assertBean(
388 items().set("example", "test").resolveRefs(openApi, new ArrayDeque<>(), 10),
389 "example",
390 "<null>"
391 );
392
393
394 assertBean(
395 items().setType("string").resolveRefs(openApi, new ArrayDeque<>(), 10),
396 "type,example",
397 "string,<null>"
398 );
399
400
401 assertBean(
402 items().setType("string").resolveRefs(openApi, new ArrayDeque<>(), 10),
403 "type,items,example",
404 "string,<null>,<null>"
405 );
406
407
408 assertBean(
409 items().setType("string").resolveRefs(openApi, new ArrayDeque<>(), 10),
410 "type,example",
411 "string,<null>"
412 );
413 }
414
415 @Test void d04_resolveRefs_noRefNoItems() {
416
417 var openApi = openApi()
418 .setComponents(components().setSchemas(map("MyItem", schemaInfo().setType("string"))));
419
420 var items = bean()
421 .setType("string")
422 .setFormat("text");
423
424 var result = items.resolveRefs(openApi, new ArrayDeque<>(), 10);
425
426
427 assertSame(items, result);
428 assertEquals("string", result.getType());
429 assertEquals("text", result.getFormat());
430 }
431
432 @Test void d05_resolveRefs_circularReference() {
433
434 var openApi = openApi()
435 .setComponents(components().setSchemas(map(
436 "Item1", schemaInfo().setRef("#/components/schemas/Item2"),
437 "Item2", schemaInfo().setRef("#/components/schemas/Item1")
438 )));
439
440 var refStack = new ArrayDeque<String>();
441 refStack.add("#/components/schemas/Item1");
442
443 var item = items()
444 .setType("object")
445 .set("properties", JsonMap.of("prop1", JsonMap.of("$ref", "#/components/schemas/Item1")));
446 var result = item.resolveRefs(openApi, refStack, 10);
447
448
449 assertSame(item, result);
450 }
451
452 @Test void d06_resolveRefs_maxDepthDirect() {
453
454 var openApi = openApi()
455 .setComponents(components().setSchemas(map("MyItem", schemaInfo().setType("string"))));
456
457 var refStack = new ArrayDeque<String>();
458 refStack.add("dummy1");
459 refStack.add("dummy2");
460 refStack.add("dummy3");
461
462 var item = items()
463 .setType("object")
464 .set("properties", JsonMap.of("prop1", JsonMap.of("$ref", "#/components/schemas/MyItem")));
465 var result = item.resolveRefs(openApi, refStack, 3);
466
467
468 assertSame(item, result);
469 }
470
471 @Test void d07_resolveRefs_withJsonList() {
472
473 var openApi = openApi()
474 .setComponents(components().setSchemas(map("MyItem", schemaInfo().setType("string"))));
475
476 var item = items()
477 .setType("object")
478 .set("someList", JsonList.of(JsonMap.of("$ref", "#/components/schemas/MyItem")));
479 var result = item.resolveRefs(openApi, new ArrayDeque<>(), 10);
480
481
482 assertSame(item, result);
483 }
484
485 @Test void d08_resolveRefs_withPrimitiveValue() {
486
487 var openApi = openApi()
488 .setComponents(components().setSchemas(map("MyItem", schemaInfo().setType("string"))));
489
490 var item = items()
491 .setType("object")
492 .set("someString", "plain string value")
493 .set("someNumber", 42)
494 .set("someBoolean", true);
495 var result = item.resolveRefs(openApi, new ArrayDeque<>(), 10);
496
497
498 assertSame(item, result);
499 assertEquals("plain string value", item.get("someString", String.class));
500 assertEquals(42, item.get("someNumber", Integer.class));
501 assertEquals(true, item.get("someBoolean", Boolean.class));
502 }
503 }
504
505
506
507
508
509 private static Items bean() {
510 return items();
511 }
512 }