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.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  
27  import org.apache.juneau.*;
28  import org.junit.jupiter.api.*;
29  
30  /**
31   * Testcase for {@link Operation}.
32   */
33  class Operation_Test extends TestBase {
34  
35  	@Nested class A_basicTests extends TestBase {
36  
37  		private static final BeanTester<Operation> TESTER =
38  			testBean(
39  				bean()
40  					.setDescription("a")
41  					.setOperationId("b")
42  					.setParameters(parameter().setIn("c1").setName("c2"))
43  					.setRequestBody(requestBodyInfo().setDescription("d"))
44  					.setResponses(map("200", response().setDescription("e")))
45  					.setSecurity(securityRequirement().setRequirements(map("f1",l("f2"))))
46  					.setServers(server().setUrl(java.net.URI.create("http://example.com")))
47  					.setSummary("g")
48  					.setTags("h")
49  			)
50  			.props("description,operationId,parameters{0{in,name}},requestBody{description},responses{200{description}},security{0{requirements{f1}}},servers{0{url}},summary,tags")
51  			.vals("a,b,{{c1,c2}},{d},{{e}},{{{[f2]}}},{{http://example.com}},g,[h]")
52  			.json("{description:'a',operationId:'b',parameters:[{'in':'c1',name:'c2'}],requestBody:{description:'d'},responses:{'200':{description:'e'}},security:[{requirements:{f1:['f2']}}],servers:[{url:'http://example.com'}],summary:'g',tags:['h']}")
53  			.string("{'description':'a','operationId':'b','parameters':[{'in':'c1','name':'c2'}],'requestBody':{'description':'d'},'responses':{'200':{'description':'e'}},'security':[{'requirements':{'f1':['f2']}}],'servers':[{'url':'http://example.com'}],'summary':'g','tags':['h']}".replace('\'','"'))
54  		;
55  
56  		@Test void a01_gettersAndSetters() {
57  			TESTER.assertGettersAndSetters();
58  		}
59  
60  		@Test void a02_copy() {
61  			TESTER.assertCopy();
62  		}
63  
64  		@Test void a03_toJson() {
65  			TESTER.assertToJson();
66  		}
67  
68  		@Test void a04_fromJson() {
69  			TESTER.assertFromJson();
70  		}
71  
72  		@Test void a05_roundTrip() {
73  			TESTER.assertRoundTrip();
74  		}
75  
76  		@Test void a06_toString() {
77  			TESTER.assertToString();
78  		}
79  
80  		@Test void a07_keySet() {
81  			assertList(TESTER.bean().keySet(), "description", "operationId", "parameters", "requestBody", "responses", "security", "servers", "summary", "tags");
82  		}
83  
84  		@Test void a08_otherGettersAndSetters() {
85  			// Test special getters
86  			var x = bean()
87  				.setParameters(parameter().setIn("a1").setName("a2"))
88  				.setResponses(map("b1", response().setDescription("b2"), "200", response().setDescription("b3")));
89  
90  			assertBean(x.getParameter("a1", "a2"), "in,name", "a1,a2");
91  			assertBean(x.getResponse("b1"), "description", "b2");
92  			assertBean(x.getResponse(200), "description", "b3");
93  		}
94  
95  		@Test void a09_nullParameters() {
96  			var x = bean();
97  
98  			assertThrows(IllegalArgumentException.class, ()->x.getParameter(null, "a"));
99  			assertThrows(IllegalArgumentException.class, ()->x.getParameter("a", null));
100 			assertThrows(IllegalArgumentException.class, ()->x.getResponse(null));
101 			assertThrows(IllegalArgumentException.class, () -> x.get(null, String.class));
102 			assertThrows(IllegalArgumentException.class, () -> x.set(null, "value"));
103 			assertThrows(IllegalArgumentException.class, () -> x.addResponse(null, response()));
104 			assertThrows(IllegalArgumentException.class, () -> x.addResponse("200", null));
105 			assertThrows(IllegalArgumentException.class, () -> x.addCallback(null, callback()));
106 			assertThrows(IllegalArgumentException.class, () -> x.addCallback("test", null));
107 		}
108 
109 		@Test void a10_collectionSetters() {
110 			// Test Collection variants of setters
111 			var x = bean()
112 				.setParameters(l(
113 					parameter().setIn("a1").setName("a2"),
114 					parameter().setIn("a3").setName("a4")
115 				))
116 				.setSecurity(l(
117 					securityRequirement().setRequirements(map("b1", l("b2"))),
118 					securityRequirement().setRequirements(map("b3", l("b4")))
119 				))
120 				.setTags(l("c1", "c2"));
121 
122 			assertBean(x,
123 				"parameters{#{in,name}},security{0{requirements{b1}},1{requirements{b3}}},tags",
124 				"{[{a1,a2},{a3,a4}]},{{{[b2]}},{{[b4]}}},[c1,c2]"
125 			);
126 		}
127 
128 		@Test void a11_asMap() {
129 			assertBean(
130 				bean()
131 					.setSummary("a")
132 					.set("x1", "x1a")
133 					.asMap(),
134 				"summary,x1",
135 				"a,x1a"
136 			);
137 		}
138 
139 		@Test void a12_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 a13_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 a14_addMethods() {
155 			var x = bean()
156 				.addTags("a1", "a2")
157 				.addTags(l("a3"))
158 				.addParameters(parameter().setIn("b1").setName("b2"))
159 				.addParameters(l(parameter().setIn("b3").setName("b4")))
160 				.addResponse("200", response().setDescription("c1"))
161 				.addCallback("d1", callback())
162 				.addSecurity(securityRequirement().setRequirements(map("e1", l("e2"))))
163 				.addSecurity(l(securityRequirement().setRequirements(map("e3", l("e4")))))
164 				.addServers(server().setUrl(URI.create("http://f1.com")))
165 				.addServers(l(server().setUrl(URI.create("http://f2.com"))));
166 
167 			assertBean(x,
168 				"tags,parameters{#{in,name}},responses{200{description}},callbacks{d1},security{#{requirements}},servers{#{url}}",
169 				"[a1,a2,a3],{[{b1,b2},{b3,b4}]},{{c1}},{{}},{[{{e1=[e2]}},{{e3=[e4]}}]},{[{http://f1.com},{http://f2.com}]}"
170 			);
171 		}
172 
173 		@Test void a15_collectionSetters() {
174 			var x = bean()
175 				.setParameters(l(parameter().setIn("a1").setName("a2"), parameter().setIn("b1").setName("b2")))
176 				.setSecurity(l(securityRequirement().setRequirements(map("c1", l("c2"))), securityRequirement().setRequirements(map("d1", l("d2")))))
177 				.setServers(l(server().setUrl(java.net.URI.create("http://example1.com")), server().setUrl(java.net.URI.create("http://example2.com"))))
178 				.setTags(l("f1", "f2"));
179 
180 			assertBean(x,
181 				"parameters{#{in,name}},security{#{requirements}},servers{#{url}},tags",
182 				"{[{a1,a2},{b1,b2}]},{[{{c1=[c2]}},{{d1=[d2]}}]},{[{http://example1.com},{http://example2.com}]},[f1,f2]"
183 			);
184 		}
185 
186 		@Test void a15_getParameter() {
187 			// Test getParameter with null parameters list (covers the null check branch)
188 			var y = bean();
189 			assertNull(y.getParameter("query", "param1"));
190 
191 			// Test with parameters set
192 			var x = bean()
193 				.setParameters(l(
194 					parameter().setIn("query").setName("param1"),
195 					parameter().setIn("path").setName("param2")
196 				));
197 
198 			// Test normal parameter lookup
199 			var param1 = x.getParameter("query", "param1");
200 			assertNotNull(param1);
201 			assertEquals("param1", param1.getName());
202 			assertEquals("query", param1.getIn());
203 
204 			var param2 = x.getParameter("path", "param2");
205 			assertNotNull(param2);
206 			assertEquals("param2", param2.getName());
207 			assertEquals("path", param2.getIn());
208 
209 			// Test non-existent parameter
210 			assertNull(x.getParameter("query", "nonexistent"));
211 			assertNull(x.getParameter("header", "param1"));
212 		}
213 
214 		@Test void a16_getResponseBranchCoverage() {
215 			// Test getResponse with null responses map (covers the null check branch)
216 			var y = bean();
217 			assertNull(y.getResponse("200"));
218 
219 			// Test with responses set
220 			var x = bean()
221 				.setResponses(map("200", response().setDescription("OK"), "404", response().setDescription("Not Found")));
222 
223 			var response200 = x.getResponse("200");
224 			assertNotNull(response200);
225 			assertEquals("OK", response200.getDescription());
226 
227 			var response404 = x.getResponse("404");
228 			assertNotNull(response404);
229 			assertEquals("Not Found", response404.getDescription());
230 
231 			assertNull(x.getResponse("500"));
232 		}
233 	}
234 
235 	@Nested class B_emptyTests extends TestBase {
236 
237 		private static final BeanTester<Operation> TESTER =
238 			testBean(bean())
239 			.props("summary,description,operationId,tags,parameters,requestBody,responses,security")
240 			.vals("<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>")
241 			.json("{}")
242 			.string("{}")
243 		;
244 
245 		@Test void b01_gettersAndSetters() {
246 			TESTER.assertGettersAndSetters();
247 		}
248 
249 		@Test void b02_copy() {
250 			TESTER.assertCopy();
251 		}
252 
253 		@Test void b03_toJson() {
254 			TESTER.assertToJson();
255 		}
256 
257 		@Test void b04_fromJson() {
258 			TESTER.assertFromJson();
259 		}
260 
261 		@Test void b05_roundTrip() {
262 			TESTER.assertRoundTrip();
263 		}
264 
265 		@Test void b06_toString() {
266 			TESTER.assertToString();
267 		}
268 
269 		@Test void b07_keySet() {
270 			assertEmpty(TESTER.bean().keySet());
271 		}
272 	}
273 
274 	@Nested class C_extraProperties extends TestBase {
275 		private static final BeanTester<Operation> TESTER =
276 			testBean(
277 				bean()
278 					.set("callbacks", m("a1", callback()))
279 					.set("deprecated", true)
280 					.set("description", "b")
281 					.set("externalDocs", externalDocumentation().setUrl(URI.create("c")))
282 					.set("operationId", "d")
283 					.set("parameters", l(parameter("e1", "e2")))
284 					.set("requestBody", requestBodyInfo().setDescription("f"))
285 					.set("responses", m("g1", response("g2")))
286 					.set("security", l(securityRequirement().set("h1", l("h2"))))
287 					.set("servers", l(server().setUrl(URI.create("i"))))
288 					.set("summary", "j")
289 					.set("tags", l("k"))
290 					.set("x1", "x1a")
291 					.set("x2", null)
292 			)
293 			.props("callbacks{a1},deprecated,description,externalDocs{url},operationId,parameters{#{in,name}},requestBody{description},responses{g1{description}},security{#{toString}},servers{#{url}},summary,tags{#{toString},x1,x2")
294 			.vals("{{}},true,b,{c},d,{[{e1,e2}]},{f},{{g2}},{[{<null>}]},{[{i}]},j,[k]")
295 			.json("{callbacks:{a1:{}},deprecated:true,description:'b',externalDocs:{url:'c'},operationId:'d',parameters:[{'in':'e1',name:'e2'}],requestBody:{description:'f'},responses:{g1:{description:'g2'}},security:[{h1:['h2']}],servers:[{url:'i'}],summary:'j',tags:['k'],x1:'x1a'}")
296 			.string("{'callbacks':{'a1':{}},'deprecated':true,'description':'b','externalDocs':{'url':'c'},'operationId':'d','parameters':[{'in':'e1','name':'e2'}],'requestBody':{'description':'f'},'responses':{'g1':{'description':'g2'}},'security':[{'h1':['h2']}],'servers':[{'url':'i'}],'summary':'j','tags':['k'],'x1':'x1a'}".replace('\'', '"'))
297 		;
298 
299 		@Test void c01_gettersAndSetters() {
300 			TESTER.assertGettersAndSetters();
301 		}
302 
303 		@Test void c02_copy() {
304 			TESTER.assertCopy();
305 		}
306 
307 		@Test void c03_toJson() {
308 			TESTER.assertToJson();
309 		}
310 
311 		@Test void c04_fromJson() {
312 			TESTER.assertFromJson();
313 		}
314 
315 		@Test void c05_roundTrip() {
316 			TESTER.assertRoundTrip();
317 		}
318 
319 		@Test void c06_toString() {
320 			TESTER.assertToString();
321 		}
322 
323 		@Test void c07_keySet() {
324 			assertList(TESTER.bean().keySet(), "callbacks", "deprecated", "description", "externalDocs", "operationId", "parameters", "requestBody", "responses", "security", "servers", "summary", "tags", "x1", "x2");
325 		}
326 
327 		@Test void c08_get() {
328 			assertMapped(
329 				TESTER.bean(), (obj,prop) -> obj.get(prop, Object.class),
330 				"callbacks{a1},deprecated,description,externalDocs{url},operationId,parameters{#{in,name}},requestBody{description},responses{g1{description}},security{#{h1{#{toString}}}},servers{#{url}},summary,tags{#{toString}},x1,x2",
331 				"{{}},true,b,{c},d,{[{e1,e2}]},{f},{{g2}},{[{{[{h2}]}}]},{[{i}]},j,{[{k}]},x1a,<null>"
332 			);
333 		}
334 
335 		@Test void c09_getTypes() {
336 			assertMapped(
337 				TESTER.bean(), (obj,prop) -> cns(obj.get(prop, Object.class)),
338 				"callbacks,deprecated,description,externalDocs,operationId,parameters,requestBody,responses,security,servers,summary,tags,x1,x2",
339 				"LinkedHashMap,Boolean,String,ExternalDocumentation,String,ArrayList,RequestBodyInfo,LinkedHashMap,ArrayList,ArrayList,String,ArrayList,String,<null>"
340 			);
341 		}
342 
343 		@Test void c10_nullPropertyValue() {
344 			assertThrows(IllegalArgumentException.class, ()->bean().get(null));
345 			assertThrows(IllegalArgumentException.class, ()->bean().get(null, String.class));
346 			assertThrows(IllegalArgumentException.class, ()->bean().set(null, "a"));
347 		}
348 	}
349 
350 	//---------------------------------------------------------------------------------------------
351 	// Helper methods
352 	//---------------------------------------------------------------------------------------------
353 
354 	private static Operation bean() {
355 		return operation();
356 	}
357 }