16f1765229d9e5a89c8539fd9647e236fa41455b
[policy/common.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2019-2020 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.common.endpoints.http.server.test;
22
23 import static org.assertj.core.api.Assertions.assertThat;
24 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
25 import static org.junit.Assert.assertEquals;
26 import static org.junit.Assert.assertNotNull;
27 import static org.junit.Assert.assertTrue;
28 import static org.mockito.ArgumentMatchers.any;
29 import static org.mockito.Mockito.mock;
30 import static org.mockito.Mockito.never;
31 import static org.mockito.Mockito.verify;
32 import static org.mockito.Mockito.when;
33
34 import java.io.IOException;
35 import java.io.PrintWriter;
36 import java.net.HttpURLConnection;
37 import java.net.URL;
38 import java.nio.charset.StandardCharsets;
39 import java.util.Arrays;
40 import java.util.Base64;
41 import java.util.Properties;
42 import javax.servlet.http.HttpServletRequest;
43 import javax.ws.rs.Consumes;
44 import javax.ws.rs.POST;
45 import javax.ws.rs.Path;
46 import javax.ws.rs.Produces;
47 import javax.ws.rs.core.MediaType;
48 import javax.ws.rs.core.Response;
49 import lombok.Getter;
50 import org.apache.commons.io.IOUtils;
51 import org.junit.AfterClass;
52 import org.junit.Before;
53 import org.junit.BeforeClass;
54 import org.junit.Test;
55 import org.mockito.ArgumentCaptor;
56 import org.onap.policy.common.endpoints.http.server.HttpServletServer;
57 import org.onap.policy.common.endpoints.http.server.HttpServletServerFactory;
58 import org.onap.policy.common.endpoints.http.server.JsonExceptionMapper;
59 import org.onap.policy.common.endpoints.http.server.RestServer;
60 import org.onap.policy.common.endpoints.http.server.RestServer.Factory;
61 import org.onap.policy.common.endpoints.http.server.YamlExceptionMapper;
62 import org.onap.policy.common.endpoints.http.server.YamlMessageBodyHandler;
63 import org.onap.policy.common.endpoints.http.server.aaf.AafAuthFilter;
64 import org.onap.policy.common.endpoints.parameters.RestServerParameters;
65 import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties;
66 import org.onap.policy.common.gson.GsonMessageBodyHandler;
67 import org.onap.policy.common.utils.coder.StandardCoder;
68 import org.onap.policy.common.utils.network.NetworkUtil;
69 import org.powermock.reflect.Whitebox;
70
71 public class RestServerTest {
72     private static final String SERVER1 = "my-server-A";
73     private static final String SERVER2 = "my-server-B";
74     private static final String FACTORY_FIELD = "factory";
75     private static final String HOST = "my-host";
76     private static final String PARAM_NAME = "my-param";
77     private static final String PASS = "my-pass";
78     private static final Integer PORT = 9876;
79     private static final String USER = "my-user";
80
81     private static Factory saveFactory;
82     private static RestServer realRest;
83     private static int realPort;
84     private static RestServerParameters params;
85
86     private RestServer rest;
87     private HttpServletServer server1;
88     private HttpServletServer server2;
89     private Factory factory;
90     private HttpServletServerFactory serverFactory;
91     private String errorMsg;
92
93     /**
94      * Starts the REST server.
95      * @throws Exception if an error occurs
96      */
97     @BeforeClass
98     public static void setUpBeforeClass() throws Exception {
99         saveFactory = Whitebox.getInternalState(RestServer.class, FACTORY_FIELD);
100
101         realPort = NetworkUtil.allocPort();
102
103         initRealParams();
104
105         realRest = new RestServer(params, null, RealProvider.class) {
106             @Override
107             protected Properties getServerProperties(RestServerParameters restServerParameters, String names) {
108                 Properties props = super.getServerProperties(restServerParameters, names);
109
110                 String svcpfx = PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "."
111                                 + restServerParameters.getName();
112                 props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SWAGGER_SUFFIX, "false");
113
114                 return props;
115             }
116         };
117
118         realRest.start();
119         assertTrue(NetworkUtil.isTcpPortOpen(params.getHost(), params.getPort(), 100, 100));
120     }
121
122     /**
123      * Restores the factory and stops the REST server.
124      */
125     @AfterClass
126     public static void tearDownAfterClass() {
127         Whitebox.setInternalState(RestServer.class, FACTORY_FIELD, saveFactory);
128
129         realRest.stop();
130     }
131
132     /**
133      * Initializes mocks.
134      */
135     @Before
136     public void setUp() {
137         server1 = mock(HttpServletServer.class);
138         server2 = mock(HttpServletServer.class);
139         factory = mock(Factory.class);
140         serverFactory = mock(HttpServletServerFactory.class);
141
142         initParams();
143
144         when(factory.getServerFactory()).thenReturn(serverFactory);
145         when(serverFactory.build(any())).thenReturn(Arrays.asList(server1, server2));
146
147         when(server1.getName()).thenReturn(SERVER1);
148         when(server2.getName()).thenReturn(SERVER2);
149
150         Whitebox.setInternalState(RestServer.class, FACTORY_FIELD, factory);
151     }
152
153     @Test
154     public void testRestServer() {
155         rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
156
157         rest.start();
158         verify(server1).start();
159         verify(server2).start();
160
161         rest.stop();
162         verify(server1).stop();
163         verify(server2).stop();
164     }
165
166     @Test
167     public void testRestServer_NoAaf() {
168         rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
169         verify(server1, never()).addFilterClass(any(), any());
170         verify(server2, never()).addFilterClass(any(), any());
171     }
172
173     @Test
174     public void testRestServer_OnlyOneAaf() {
175         when(server2.isAaf()).thenReturn(true);
176
177         rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
178
179         verify(server1, never()).addFilterClass(any(), any());
180         verify(server2).addFilterClass(null, Filter.class.getName());
181     }
182
183     @Test
184     public void testRestServer_BothAaf() {
185         when(server1.isAaf()).thenReturn(true);
186         when(server2.isAaf()).thenReturn(true);
187
188         rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
189
190         verify(server1).addFilterClass(null, Filter.class.getName());
191         verify(server2).addFilterClass(null, Filter.class.getName());
192     }
193
194     @Test
195     public void testRestServer_BothAaf_NoFilter() {
196         when(server1.isAaf()).thenReturn(true);
197         when(server2.isAaf()).thenReturn(true);
198
199         rest = new RestServer(params, null, Provider1.class, Provider2.class);
200
201         verify(server1, never()).addFilterClass(any(), any());
202         verify(server2, never()).addFilterClass(any(), any());
203     }
204
205     @Test
206     public void testRestServer_MissingProviders() {
207         assertThatIllegalArgumentException().isThrownBy(() -> new RestServer(params, Filter.class));
208     }
209
210     @Test
211     public void testGetServerProperties_testGetProviderNames() {
212         rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
213
214         ArgumentCaptor<Properties> cap = ArgumentCaptor.forClass(Properties.class);
215         verify(serverFactory).build(cap.capture());
216
217         Properties props = cap.getValue();
218         String svcpfx = PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + PARAM_NAME;
219
220         assertEquals(HOST, props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_HOST_SUFFIX));
221         assertEquals(String.valueOf(PORT),
222                         props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_PORT_SUFFIX));
223         assertEquals(Provider1.class.getName() + "," + Provider2.class.getName(),
224                         props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_REST_CLASSES_SUFFIX));
225         assertEquals("false", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_MANAGED_SUFFIX));
226         assertEquals("true", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SWAGGER_SUFFIX));
227         assertEquals(USER, props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_AUTH_USERNAME_SUFFIX));
228         assertEquals(PASS, props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_AUTH_PASSWORD_SUFFIX));
229         assertEquals("true", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_HTTPS_SUFFIX));
230         assertEquals("true", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_AAF_SUFFIX));
231         assertEquals(String.join(",", GsonMessageBodyHandler.class.getName(), YamlMessageBodyHandler.class.getName(),
232                         JsonExceptionMapper.class.getName(), YamlExceptionMapper.class.getName()),
233                         props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERIALIZATION_PROVIDER));
234     }
235
236     @Test
237     public void testInvalidJson() throws Exception {
238         initRealParams();
239
240         assertEquals(200, roundTrip(new StandardCoder().encode(new MyRequest())));
241         assertEquals(400, roundTrip("{'bogus-json'"));
242         assertThat(errorMsg).contains("Invalid request");
243     }
244
245     @Test
246     public void testInvalidYaml() throws Exception {
247         initRealParams();
248
249         assertEquals(200, roundTrip(new StandardCoder().encode(new MyRequest()),
250                         YamlMessageBodyHandler.APPLICATION_YAML));
251         assertEquals(400, roundTrip("<bogus yaml", YamlMessageBodyHandler.APPLICATION_YAML));
252         assertThat(errorMsg).contains("Invalid request");
253     }
254
255     private int roundTrip(String request) throws IOException {
256         return roundTrip(request, MediaType.APPLICATION_JSON);
257     }
258
259     private int roundTrip(String request, String mediaType) throws IOException {
260         URL url = new URL("http://" + params.getHost() + ":" + params.getPort() + "/request");
261         HttpURLConnection conn = (HttpURLConnection) url.openConnection();
262         conn.setDoInput(true);
263         conn.setDoOutput(true);
264         conn.setRequestMethod("POST");
265         String auth = params.getUserName() + ":" + params.getPassword();
266         conn.setRequestProperty("Authorization", "Basic " + Base64.getEncoder().encodeToString(auth.getBytes()));
267         conn.setRequestProperty("Content-type", mediaType);
268         conn.setRequestProperty("Accept", mediaType);
269         conn.connect();
270
271         try (PrintWriter wtr = new PrintWriter(conn.getOutputStream())) {
272             wtr.write(request);
273         }
274
275         int code = conn.getResponseCode();
276
277         if (code == 200) {
278             errorMsg = "";
279         } else {
280             errorMsg = IOUtils.toString(conn.getErrorStream(), StandardCharsets.UTF_8);
281         }
282
283         return code;
284     }
285
286     @Test
287     public void testToString() {
288         rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
289         assertNotNull(rest.toString());
290     }
291
292     @Test
293     public void testFactory() {
294         assertNotNull(saveFactory);
295         assertNotNull(saveFactory.getServerFactory());
296     }
297
298     private static void initRealParams() {
299         initParams();
300
301         when(params.getHost()).thenReturn("localhost");
302         when(params.getPort()).thenReturn(realPort);
303         when(params.isHttps()).thenReturn(false);
304         when(params.isAaf()).thenReturn(false);
305     }
306
307     private static void initParams() {
308         params = mock(RestServerParameters.class);
309
310         when(params.getHost()).thenReturn(HOST);
311         when(params.getName()).thenReturn(PARAM_NAME);
312         when(params.getPassword()).thenReturn(PASS);
313         when(params.getPort()).thenReturn(PORT);
314         when(params.getUserName()).thenReturn(USER);
315         when(params.isAaf()).thenReturn(true);
316         when(params.isHttps()).thenReturn(true);
317     }
318
319     private static class Filter extends AafAuthFilter {
320         @Override
321         protected String getPermissionType(HttpServletRequest request) {
322             return "";
323         }
324
325         @Override
326         protected String getPermissionInstance(HttpServletRequest request) {
327             return "";
328         }
329     }
330
331     private static class Provider1 {
332         private Provider1() {
333             // do nothing
334         }
335     }
336
337     private static class Provider2 {
338         private Provider2() {
339             // do nothing
340         }
341     }
342
343     @Path("/")
344     @Produces({MediaType.APPLICATION_JSON, YamlMessageBodyHandler.APPLICATION_YAML})
345     @Consumes({MediaType.APPLICATION_JSON, YamlMessageBodyHandler.APPLICATION_YAML})
346     public static class RealProvider {
347         @POST
348         @Path("/request")
349         public Response decision(MyRequest body) {
350             return Response.status(Response.Status.OK).entity(new MyResponse()).build();
351         }
352     }
353
354     @Getter
355     public static class MyRequest {
356         private String data;
357     }
358
359     @Getter
360     public static class MyResponse {
361         private String text = "hello";
362     }
363 }