cd40f01259a093cf3a0414c64e416c13fd5f7a8b
[policy/common.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2019 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.Matchers.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 APPLICATION_YAML = "application/yaml";
73     private static final String SERVER1 = "my-server-A";
74     private static final String SERVER2 = "my-server-B";
75     private static final String FACTORY_FIELD = "factory";
76     private static final String HOST = "my-host";
77     private static final String PARAM_NAME = "my-param";
78     private static final String PASS = "my-pass";
79     private static final Integer PORT = 9876;
80     private static final String USER = "my-user";
81
82     private static Factory saveFactory;
83     private static RestServer realRest;
84     private static int realPort;
85     private static RestServerParameters params;
86
87     private RestServer rest;
88     private HttpServletServer server1;
89     private HttpServletServer server2;
90     private Factory factory;
91     private HttpServletServerFactory serverFactory;
92     private String errorMsg;
93
94     /**
95      * Starts the REST server.
96      * @throws Exception if an error occurs
97      */
98     @BeforeClass
99     public static void setUpBeforeClass() throws Exception {
100         saveFactory = Whitebox.getInternalState(RestServer.class, FACTORY_FIELD);
101
102         realPort = NetworkUtil.allocPort();
103
104         initRealParams();
105
106         realRest = new RestServer(params, null, RealProvider.class) {
107             @Override
108             protected Properties getServerProperties(RestServerParameters restServerParameters, String names) {
109                 Properties props = super.getServerProperties(restServerParameters, names);
110
111                 String svcpfx = PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "."
112                                 + restServerParameters.getName();
113                 props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SWAGGER_SUFFIX, "false");
114
115                 return props;
116             }
117         };
118
119         realRest.start();
120         assertTrue(NetworkUtil.isTcpPortOpen(params.getHost(), params.getPort(), 100, 100));
121     }
122
123     /**
124      * Restores the factory and stops the REST server.
125      */
126     @AfterClass
127     public static void tearDownAfterClass() {
128         Whitebox.setInternalState(RestServer.class, FACTORY_FIELD, saveFactory);
129
130         realRest.stop();
131     }
132
133     /**
134      * Initializes mocks.
135      */
136     @Before
137     public void setUp() {
138         server1 = mock(HttpServletServer.class);
139         server2 = mock(HttpServletServer.class);
140         factory = mock(Factory.class);
141         serverFactory = mock(HttpServletServerFactory.class);
142
143         initParams();
144
145         when(factory.getServerFactory()).thenReturn(serverFactory);
146         when(serverFactory.build(any())).thenReturn(Arrays.asList(server1, server2));
147
148         when(server1.getName()).thenReturn(SERVER1);
149         when(server2.getName()).thenReturn(SERVER2);
150
151         Whitebox.setInternalState(RestServer.class, FACTORY_FIELD, factory);
152     }
153
154     @Test
155     public void testRestServer() {
156         rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
157
158         rest.start();
159         verify(server1).start();
160         verify(server2).start();
161
162         rest.stop();
163         verify(server1).stop();
164         verify(server2).stop();
165     }
166
167     @Test
168     public void testRestServer_NoAaf() {
169         rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
170         verify(server1, never()).addFilterClass(any(), any());
171         verify(server2, never()).addFilterClass(any(), any());
172     }
173
174     @Test
175     public void testRestServer_OnlyOneAaf() {
176         when(server2.isAaf()).thenReturn(true);
177
178         rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
179
180         verify(server1, never()).addFilterClass(any(), any());
181         verify(server2).addFilterClass(null, Filter.class.getName());
182     }
183
184     @Test
185     public void testRestServer_BothAaf() {
186         when(server1.isAaf()).thenReturn(true);
187         when(server2.isAaf()).thenReturn(true);
188
189         rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
190
191         verify(server1).addFilterClass(null, Filter.class.getName());
192         verify(server2).addFilterClass(null, Filter.class.getName());
193     }
194
195     @Test
196     public void testRestServer_BothAaf_NoFilter() {
197         when(server1.isAaf()).thenReturn(true);
198         when(server2.isAaf()).thenReturn(true);
199
200         rest = new RestServer(params, null, Provider1.class, Provider2.class);
201
202         verify(server1, never()).addFilterClass(any(), any());
203         verify(server2, never()).addFilterClass(any(), any());
204     }
205
206     @Test
207     public void testRestServer_MissingProviders() {
208         assertThatIllegalArgumentException().isThrownBy(() -> new RestServer(params, Filter.class));
209     }
210
211     @Test
212     public void testGetServerProperties_testGetProviderNames() {
213         rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
214
215         ArgumentCaptor<Properties> cap = ArgumentCaptor.forClass(Properties.class);
216         verify(serverFactory).build(cap.capture());
217
218         Properties props = cap.getValue();
219         String svcpfx = PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + PARAM_NAME;
220
221         assertEquals(HOST, props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_HOST_SUFFIX));
222         assertEquals(String.valueOf(PORT),
223                         props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_PORT_SUFFIX));
224         assertEquals(Provider1.class.getName() + "," + Provider2.class.getName(),
225                         props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_REST_CLASSES_SUFFIX));
226         assertEquals("false", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_MANAGED_SUFFIX));
227         assertEquals("true", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SWAGGER_SUFFIX));
228         assertEquals(USER, props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_AUTH_USERNAME_SUFFIX));
229         assertEquals(PASS, props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_AUTH_PASSWORD_SUFFIX));
230         assertEquals("true", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_HTTPS_SUFFIX));
231         assertEquals("true", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_AAF_SUFFIX));
232         assertEquals(String.join(",", GsonMessageBodyHandler.class.getName(), YamlMessageBodyHandler.class.getName(),
233                         JsonExceptionMapper.class.getName(), YamlExceptionMapper.class.getName()),
234                         props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERIALIZATION_PROVIDER));
235     }
236
237     @Test
238     public void testInvalidJson() throws Exception {
239         initRealParams();
240
241         assertEquals(200, roundTrip(new StandardCoder().encode(new MyRequest())));
242         assertEquals(400, roundTrip("{'bogus-json'"));
243         assertThat(errorMsg).contains("Invalid request");
244     }
245
246     @Test
247     public void testInvalidYaml() throws Exception {
248         initRealParams();
249
250         assertEquals(200, roundTrip(new StandardCoder().encode(new MyRequest()), APPLICATION_YAML));
251         assertEquals(400, roundTrip("<bogus yaml", 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.connect();
269
270         try (PrintWriter wtr = new PrintWriter(conn.getOutputStream())) {
271             wtr.write(request);
272         }
273
274         int code = conn.getResponseCode();
275
276         if (code == 200) {
277             errorMsg = "";
278         } else {
279             errorMsg = IOUtils.toString(conn.getErrorStream(), StandardCharsets.UTF_8);
280         }
281
282         return code;
283     }
284
285     @Test
286     public void testToString() {
287         rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
288         assertNotNull(rest.toString());
289     }
290
291     @Test
292     public void testFactory() {
293         assertNotNull(saveFactory);
294         assertNotNull(saveFactory.getServerFactory());
295     }
296
297     private static void initRealParams() {
298         initParams();
299
300         when(params.getHost()).thenReturn("localhost");
301         when(params.getPort()).thenReturn(realPort);
302         when(params.isHttps()).thenReturn(false);
303         when(params.isAaf()).thenReturn(false);
304     }
305
306     private static void initParams() {
307         params = mock(RestServerParameters.class);
308
309         when(params.getHost()).thenReturn(HOST);
310         when(params.getName()).thenReturn(PARAM_NAME);
311         when(params.getPassword()).thenReturn(PASS);
312         when(params.getPort()).thenReturn(PORT);
313         when(params.getUserName()).thenReturn(USER);
314         when(params.isAaf()).thenReturn(true);
315         when(params.isHttps()).thenReturn(true);
316     }
317
318     private static class Filter extends AafAuthFilter {
319         @Override
320         protected String getPermissionType(HttpServletRequest request) {
321             return "";
322         }
323
324         @Override
325         protected String getPermissionInstance(HttpServletRequest request) {
326             return "";
327         }
328     }
329
330     private static class Provider1 {
331         private Provider1() {
332             // do nothing
333         }
334     }
335
336     private static class Provider2 {
337         private Provider2() {
338             // do nothing
339         }
340     }
341
342     @Path("/")
343     @Produces({MediaType.APPLICATION_JSON, APPLICATION_YAML})
344     @Consumes({MediaType.APPLICATION_JSON, APPLICATION_YAML})
345     public static class RealProvider {
346         @POST
347         @Path("/request")
348         public Response decision(MyRequest body) {
349             return Response.status(Response.Status.OK).entity(new MyResponse()).build();
350         }
351     }
352
353     @Getter
354     public static class MyRequest {
355         private String data;
356     }
357
358     @Getter
359     public static class MyResponse {
360         private String text = "hello";
361     }
362 }