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.junit.jupiter.api.*;
30
31
32
33
34 class SchemaInfo_Test extends TestBase {
35
36 @Nested class A_basicTests extends TestBase {
37
38 private static final BeanTester<SchemaInfo> TESTER =
39 testBean(
40 bean()
41 .setDefault("a")
42 .setDeprecated(true)
43 .setDescription("b")
44 .setEnum(l("c1", "c2"))
45 .setExample("d")
46 .setExclusiveMaximum(true)
47 .setExclusiveMinimum(true)
48 .setFormat("e")
49 .setItems(items("f"))
50 .setMaxItems(1)
51 .setMaxLength(2)
52 .setMaxProperties(3)
53 .setMaximum(4)
54 .setMinItems(5)
55 .setMinLength(6)
56 .setMinProperties(7)
57 .setMinimum(8)
58 .setMultipleOf(9)
59 .setNullable(true)
60 .setPattern("g")
61 .setReadOnly(true)
62 .setRef("h")
63 .setRequired(l("i"))
64 .setTitle("j")
65 .setType("k")
66 .setUniqueItems(true)
67 .setWriteOnly(true)
68 )
69 .props("default,deprecated,description,enum,example,exclusiveMaximum,exclusiveMinimum,format,items{type},maxItems,maxLength,maxProperties,maximum,minItems,minLength,minProperties,minimum,multipleOf,nullable,pattern,readOnly,ref,required,title,type,uniqueItems,writeOnly")
70 .vals("a,true,b,[c1,c2],d,true,true,e,{f},1,2,3,4,5,6,7,8,9,true,g,true,h,[i],j,k,true,true")
71 .json("{'$ref':'h','default':'a',deprecated:true,description:'b','enum':['c1','c2'],example:'d',exclusiveMaximum:true,exclusiveMinimum:true,format:'e',items:{type:'f'},maxItems:1,maxLength:2,maxProperties:3,maximum:4,minItems:5,minLength:6,minProperties:7,minimum:8,multipleOf:9,nullable:true,pattern:'g',readOnly:true,required:['i'],title:'j',type:'k',uniqueItems:true,writeOnly:true}")
72 .string("{'$ref':'h','default':'a','deprecated':true,'description':'b','enum':['c1','c2'],'example':'d','exclusiveMaximum':true,'exclusiveMinimum':true,'format':'e','items':{'type':'f'},'maxItems':1,'maxLength':2,'maxProperties':3,'maximum':4,'minItems':5,'minLength':6,'minProperties':7,'minimum':8,'multipleOf':9,'nullable':true,'pattern':'g','readOnly':true,'required':['i'],'title':'j','type':'k','uniqueItems':true,'writeOnly':true}".replace('\'','"'))
73 ;
74
75 @Test void a01_gettersAndSetters() {
76 TESTER.assertGettersAndSetters();
77 }
78
79 @Test void a02_copy() {
80 TESTER.assertCopy();
81 }
82
83 @Test void a03_toJson() {
84 TESTER.assertToJson();
85 }
86
87 @Test void a04_fromJson() {
88 TESTER.assertFromJson();
89 }
90
91 @Test void a05_roundTrip() {
92 TESTER.assertRoundTrip();
93 }
94
95 @Test void a06_toString() {
96 TESTER.assertToString();
97 }
98
99 @Test void a07_keySet() {
100 assertList(TESTER.bean().keySet(), "$ref", "default", "deprecated", "description", "enum", "example", "exclusiveMaximum", "exclusiveMinimum", "format", "items", "maxItems", "maxLength", "maxProperties", "maximum", "minItems", "minLength", "minProperties", "minimum", "multipleOf", "nullable", "pattern", "readOnly", "required", "title", "type", "uniqueItems", "writeOnly");
101 }
102
103 @Test void a08_nullParameters() {
104 var x = bean();
105 assertThrows(IllegalArgumentException.class, () -> x.get(null, String.class));
106 assertThrows(IllegalArgumentException.class, () -> x.set(null, "value"));
107 }
108
109 @Test void a08b_getSetRef() {
110
111 var x = bean();
112 x.set("$ref", "#/components/schemas/MySchema");
113 assertEquals("#/components/schemas/MySchema", x.get("$ref", String.class));
114 assertEquals("#/components/schemas/MySchema", x.getRef());
115 }
116
117 @Test void a09_addMethods() {
118 assertBean(
119 bean()
120 .addEnum("a1", "a2")
121 .addRequired("b1", "b2")
122 .addAllOf(schemaInfo("c1"), schemaInfo("c2"))
123 .addAnyOf(schemaInfo("d1"), schemaInfo("d2"))
124 .addOneOf(schemaInfo("e1"), schemaInfo("e2")),
125 "enum,required,allOf{#{type}},anyOf{#{type}},oneOf{#{type}}",
126 "[a1,a2],[b1,b2],{[{c1},{c2}]},{[{d1},{d2}]},{[{e1},{e2}]}"
127 );
128 }
129
130 @Test void a10_asMap() {
131 assertBean(
132 bean()
133 .setType("a")
134 .set("x1", "x1a")
135 .asMap(),
136 "type,x1",
137 "a,x1a"
138 );
139 }
140
141 @Test void a11_extraKeys() {
142 var x = bean().set("x1", "x1a").set("x2", "x2a");
143 assertList(x.extraKeys(), "x1", "x2");
144 assertEmpty(bean().extraKeys());
145 }
146
147 @Test void a12_strictMode() {
148 assertThrows(RuntimeException.class, () -> bean().strict().set("foo", "bar"));
149 assertDoesNotThrow(() -> bean().set("foo", "bar"));
150
151 assertFalse(bean().isStrict());
152 assertTrue(bean().strict().isStrict());
153 assertFalse(bean().strict(false).isStrict());
154 }
155
156 @Test void a13_collectionSetters() {
157 var x = bean()
158 .setEnum(l("a1", "a2"))
159 .setRequired(l("b1", "b2"));
160
161 assertBean(x,
162 "enum,required",
163 "[a1,a2],[b1,b2]"
164 );
165 }
166 }
167
168 @Nested class B_emptyTests extends TestBase {
169
170 private static final BeanTester<SchemaInfo> TESTER =
171 testBean(bean())
172 .props("type,format,title,description,default,multipleOf,maximum,exclusiveMaximum,minimum,exclusiveMinimum,maxLength,minLength,pattern,maxItems,minItems,uniqueItems,maxProperties,minProperties,required,enum,items,example,nullable,readOnly,writeOnly,deprecated,ref")
173 .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>")
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<SchemaInfo> TESTER =
209 testBean(
210 bean()
211 .set("additionalItems", schemaInfo("a"))
212 .set("additionalProperties", schemaInfo("b"))
213 .set("allOf", l(schemaInfo("c1"), schemaInfo("c2")))
214 .set("anyOf", l(schemaInfo("d1"), schemaInfo("d2")))
215 .set("default", "e")
216 .set("description", "f")
217 .set("discriminator", discriminator("g"))
218 .set("enum", l("h1", "h2"))
219 .set("example", "i")
220 .set("exclusiveMaximum", true)
221 .set("exclusiveMinimum", true)
222 .set("externalDocs", externalDocumentation().setUrl(URI.create("j")))
223 .set("format", "k")
224 .set("items", schemaInfo("l"))
225 .set("maxItems", 1)
226 .set("maxLength", 2)
227 .set("maxProperties", 3)
228 .set("maximum", 4)
229 .set("minItems", 5)
230 .set("minLength", 6)
231 .set("minProperties", 7)
232 .set("minimum", 8)
233 .set("multipleOf", 9)
234 .set("not", schemaInfo("m"))
235 .set("nullable", true)
236 .set("oneOf", l(schemaInfo("n1"), schemaInfo("n2")))
237 .set("pattern", "o")
238 .set("properties", m("p1", schemaInfo("p2")))
239 .set("readOnly", true)
240 .set("required", l("q1", "q2"))
241 .set("title", "r")
242 .set("type", "s")
243 .set("uniqueItems", true)
244 .set("writeOnly", true)
245 .set("xml", xml().setName("t"))
246 .set("x1", "x1a")
247 .set("x2", null)
248 )
249 .props("additionalItems{type},additionalProperties{type},allOf{#{type}},anyOf{#{type}},default,description,discriminator{propertyName},enum{#{toString}},example,exclusiveMaximum,exclusiveMinimum,externalDocs{url},format,items{type},maxItems,maxLength,maxProperties,maximum,minItems,minLength,minProperties,minimum,multipleOf,not{type},nullable,oneOf{#{type}},pattern,properties{p1{type}},readOnly,required{#{toString}},title,type,uniqueItems,writeOnly,xml{name},x1,x2")
250 .vals("{a},{b},{[{c1},{c2}]},{[{d1},{d2}]},e,f,{g},{[{h1},{h2}]},i,true,true,{j},k,{l},1,2,3,4,5,6,7,8,9,{m},true,{[{n1},{n2}]},o,{{p2}},true,{[{q1},{q2}]},r,s,true,true,{t},x1a,<null>")
251 .json("{additionalItems:{type:'a'},additionalProperties:{type:'b'},allOf:[{type:'c1'},{type:'c2'}],anyOf:[{type:'d1'},{type:'d2'}],'default':'e',description:'f',discriminator:{propertyName:'g'},'enum':['h1','h2'],example:'i',exclusiveMaximum:true,exclusiveMinimum:true,externalDocs:{url:'j'},format:'k',items:{type:'l'},maxItems:1,maxLength:2,maxProperties:3,maximum:4,minItems:5,minLength:6,minProperties:7,minimum:8,multipleOf:9,not:{type:'m'},nullable:true,oneOf:[{type:'n1'},{type:'n2'}],pattern:'o',properties:{p1:{type:'p2'}},readOnly:true,required:['q1','q2'],title:'r',type:'s',uniqueItems:true,writeOnly:true,x1:'x1a',xml:{name:'t'}}")
252 .string("{'additionalItems':{'type':'a'},'additionalProperties':{'type':'b'},'allOf':[{'type':'c1'},{'type':'c2'}],'anyOf':[{'type':'d1'},{'type':'d2'}],'default':'e','description':'f','discriminator':{'propertyName':'g'},'enum':['h1','h2'],'example':'i','exclusiveMaximum':true,'exclusiveMinimum':true,'externalDocs':{'url':'j'},'format':'k','items':{'type':'l'},'maxItems':1,'maxLength':2,'maxProperties':3,'maximum':4,'minItems':5,'minLength':6,'minProperties':7,'minimum':8,'multipleOf':9,'not':{'type':'m'},'nullable':true,'oneOf':[{'type':'n1'},{'type':'n2'}],'pattern':'o','properties':{'p1':{'type':'p2'}},'readOnly':true,'required':['q1','q2'],'title':'r','type':'s','uniqueItems':true,'writeOnly':true,'x1':'x1a','xml':{'name':'t'}}".replace('\'', '"'))
253 ;
254
255 @Test void c01_gettersAndSetters() {
256 TESTER.assertGettersAndSetters();
257 }
258
259 @Test void c02_copy() {
260 TESTER.assertCopy();
261 }
262
263 @Test void c03_toJson() {
264 TESTER.assertToJson();
265 }
266
267 @Test void c04_fromJson() {
268 TESTER.assertFromJson();
269 }
270
271 @Test void c05_roundTrip() {
272 TESTER.assertRoundTrip();
273 }
274
275 @Test void c06_toString() {
276 TESTER.assertToString();
277 }
278
279 @Test void c07_keySet() {
280 assertList(TESTER.bean().keySet(), "additionalProperties", "allOf", "anyOf", "default", "description", "discriminator", "enum", "example", "exclusiveMaximum", "exclusiveMinimum", "externalDocs", "format", "items", "maxItems", "maxLength", "maxProperties", "maximum", "minItems", "minLength", "minProperties", "minimum", "multipleOf", "not", "nullable", "oneOf", "pattern", "properties", "readOnly", "required", "title", "type", "uniqueItems", "writeOnly", "xml", "additionalItems", "x1", "x2");
281 }
282
283 @Test void c08_get() {
284 assertMapped(
285 TESTER.bean(), (obj,prop) -> obj.get(prop, Object.class),
286 "additionalItems{type},additionalProperties{type},allOf{#{type}},anyOf{#{type}},default,description,discriminator{propertyName},enum{#{toString}},example,exclusiveMaximum,exclusiveMinimum,externalDocs{url},format,items{type},maxItems,maxLength,maxProperties,maximum,minItems,minLength,minProperties,minimum,multipleOf,not{type},nullable,oneOf{#{type}},pattern,properties{p1{type}},readOnly,required{#{toString}},title,type,uniqueItems,writeOnly,xml{name},x1,x2",
287 "{a},{b},{[{c1},{c2}]},{[{d1},{d2}]},e,f,{g},{[{h1},{h2}]},i,true,true,{j},k,{l},1,2,3,4,5,6,7,8,9,{m},true,{[{n1},{n2}]},o,{{p2}},true,{[{q1},{q2}]},r,s,true,true,{t},x1a,<null>"
288 );
289 }
290
291 @Test void c09_getTypes() {
292 assertMapped(
293 TESTER.bean(), (obj,prop) -> cns(obj.get(prop, Object.class)),
294 "additionalItems,additionalProperties,allOf,anyOf,default,description,discriminator,enum,example,exclusiveMaximum,exclusiveMinimum,externalDocs,format,items,maxItems,maxLength,maxProperties,maximum,minItems,minLength,minProperties,minimum,multipleOf,not,nullable,oneOf,pattern,properties,readOnly,required,title,type,uniqueItems,writeOnly,xml,x1,x2",
295 "SchemaInfo,SchemaInfo,ArrayList,ArrayList,String,String,Discriminator,ArrayList,String,Boolean,Boolean,ExternalDocumentation,String,Items,Integer,Integer,Integer,Integer,Integer,Integer,Integer,Integer,Integer,SchemaInfo,Boolean,ArrayList,String,LinkedHashMap,Boolean,ArrayList,String,String,Boolean,Boolean,Xml,String,<null>"
296 );
297 }
298
299 @Test void c10_nullPropertyValue() {
300 assertThrows(IllegalArgumentException.class, ()->bean().get(null));
301 assertThrows(IllegalArgumentException.class, ()->bean().get(null, String.class));
302 assertThrows(IllegalArgumentException.class, ()->bean().set(null, "a"));
303 }
304 }
305
306 @Nested class D_refs extends TestBase {
307
308 @Test void d01_resolveRefs_basic() {
309 var openApi = openApi()
310 .setComponents(components().setSchemas(m(
311 "Pet", schemaInfo().setType("object").setTitle("Pet")
312 )));
313
314 assertBean(
315 schemaInfo().setRef("#/components/schemas/Pet").resolveRefs(openApi, new ArrayDeque<>(), 10),
316 "type,title",
317 "object,Pet"
318 );
319 }
320
321 @Test void d02_resolveRefs_nested() {
322 var openApi = openApi()
323 .setComponents(components().setSchemas(m(
324 "Pet", schemaInfo().setType("object").setTitle("Pet"),
325 "Pets", schemaInfo().setType("array").setItems(items().setRef("#/components/schemas/Pet"))
326 )));
327
328 assertBean(
329 schemaInfo().setRef("#/components/schemas/Pets").resolveRefs(openApi, new ArrayDeque<>(), 10),
330 "type,items{type,title}",
331 "array,{object,Pet}"
332 );
333 }
334
335 @Test void d03_resolveRefs_maxDepth() {
336 var openApi = openApi()
337 .setComponents(components().setSchemas(m(
338 "Pet", schemaInfo().setType("object").setTitle("Pet"),
339 "Pets", schemaInfo().setType("array").setItems(items().setRef("#/components/schemas/Pet"))
340 )));
341
342 assertBean(
343 schemaInfo().setRef("#/components/schemas/Pets").resolveRefs(openApi, new ArrayDeque<>(), 1),
344 "type,items{ref}",
345 "array,{#/components/schemas/Pet}"
346 );
347 }
348
349 @Test void d04_resolveRefs_circular() {
350 var openApi = openApi()
351 .setComponents(components().setSchemas(m(
352 "A", schemaInfo().setType("object").setTitle("A").setProperties(m("b", schemaInfo().setRef("#/components/schemas/B"))),
353 "B", schemaInfo().setType("object").setTitle("B").setProperties(m("a", schemaInfo().setRef("#/components/schemas/A")))
354 )));
355
356 assertBean(
357 schemaInfo().setRef("#/components/schemas/A").resolveRefs(openApi, new ArrayDeque<>(), 10),
358 "type,title,properties{b{type,title,properties{a{ref}}}}",
359 "object,A,{{object,B,{{#/components/schemas/A}}}}"
360 );
361 }
362
363 @Test void d05_resolveRefs() {
364
365 var openApi = openApi()
366 .setComponents(components().setSchemas(map("MySchema", schemaInfo().setType("object").setTitle("My Schema"))));
367
368 var schema = bean()
369 .setType("string")
370 .setDescription("Test schema");
371
372 var result = schema.resolveRefs(openApi, new ArrayDeque<>(), 10);
373
374
375 assertSame(schema, result);
376 assertEquals("string", result.getType());
377 assertEquals("Test schema", result.getDescription());
378 }
379
380 @Test void d06_resolveRefs_maxDepthDirect() {
381
382 var openApi = openApi()
383 .setComponents(components().setSchemas(map("MySchema", schemaInfo().setType("string"))));
384
385 var refStack = new ArrayDeque<String>();
386 refStack.add("dummy1");
387 refStack.add("dummy2");
388 refStack.add("dummy3");
389
390 var schema = schemaInfo().setRef("#/components/schemas/MySchema");
391 var result = schema.resolveRefs(openApi, refStack, 3);
392
393
394 assertSame(schema, result);
395 assertEquals("#/components/schemas/MySchema", result.getRef());
396 }
397
398 @Test void d07_resolveRefs_additionalProperties() {
399
400 var openApi = openApi()
401 .setComponents(components().setSchemas(map(
402 "MyAdditional", schemaInfo().setType("string").setDescription("Additional property schema")
403 )));
404
405 var schema = schemaInfo()
406 .setType("object")
407 .setAdditionalProperties(schemaInfo().setRef("#/components/schemas/MyAdditional"));
408 var result = schema.resolveRefs(openApi, new ArrayDeque<>(), 10);
409
410
411 assertSame(schema, result);
412 assertNotNull(result.getAdditionalProperties());
413 assertEquals("string", result.getAdditionalProperties().getType());
414 assertNull(result.getAdditionalProperties().getRef());
415 }
416 }
417
418
419
420
421
422 private static SchemaInfo bean() {
423 return schemaInfo();
424 }
425 }