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