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