2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2019-2021 AT&T Intellectual Property. All rights reserved.
6 * Modifications Copyright (C) 2021 Bell Canada. All rights reserved.
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END=========================================================
22 package org.onap.policy.common.endpoints.http.server.test;
24 import static org.assertj.core.api.Assertions.assertThat;
25 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
26 import static org.junit.Assert.assertEquals;
27 import static org.junit.Assert.assertNotNull;
28 import static org.junit.Assert.assertTrue;
29 import static org.mockito.ArgumentMatchers.any;
30 import static org.mockito.Mockito.mock;
31 import static org.mockito.Mockito.never;
32 import static org.mockito.Mockito.verify;
33 import static org.mockito.Mockito.when;
35 import io.prometheus.client.exporter.MetricsServlet;
36 import java.io.IOException;
37 import java.io.PrintWriter;
38 import java.net.HttpURLConnection;
40 import java.nio.charset.StandardCharsets;
41 import java.util.Arrays;
42 import java.util.Base64;
43 import java.util.List;
44 import java.util.Properties;
45 import javax.servlet.FilterChain;
46 import javax.servlet.ServletException;
47 import javax.servlet.ServletRequest;
48 import javax.servlet.ServletResponse;
49 import javax.servlet.http.HttpServletRequest;
50 import javax.ws.rs.Consumes;
51 import javax.ws.rs.POST;
52 import javax.ws.rs.Path;
53 import javax.ws.rs.Produces;
54 import javax.ws.rs.core.MediaType;
55 import javax.ws.rs.core.Response;
57 import org.apache.commons.io.IOUtils;
58 import org.junit.AfterClass;
59 import org.junit.Before;
60 import org.junit.BeforeClass;
61 import org.junit.Test;
62 import org.mockito.ArgumentCaptor;
63 import org.onap.policy.common.endpoints.http.server.HttpServletServer;
64 import org.onap.policy.common.endpoints.http.server.HttpServletServerFactory;
65 import org.onap.policy.common.endpoints.http.server.JsonExceptionMapper;
66 import org.onap.policy.common.endpoints.http.server.RestServer;
67 import org.onap.policy.common.endpoints.http.server.RestServer.Factory;
68 import org.onap.policy.common.endpoints.http.server.YamlExceptionMapper;
69 import org.onap.policy.common.endpoints.http.server.YamlMessageBodyHandler;
70 import org.onap.policy.common.endpoints.http.server.aaf.AafAuthFilter;
71 import org.onap.policy.common.endpoints.parameters.RestServerParameters;
72 import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties;
73 import org.onap.policy.common.gson.GsonMessageBodyHandler;
74 import org.onap.policy.common.utils.coder.StandardCoder;
75 import org.onap.policy.common.utils.network.NetworkUtil;
76 import org.powermock.reflect.Whitebox;
78 public class RestServerTest {
79 private static final String METRICS_URI = "/metrics";
80 private static final String SERVER1 = "my-server-A";
81 private static final String SERVER2 = "my-server-B";
82 private static final String FACTORY_FIELD = "factory";
83 private static final String HOST = "my-host";
84 private static final String PARAM_NAME = "my-param";
85 private static final String PASS = "my-pass";
86 private static final Integer PORT = 9876;
87 private static final String USER = "my-user";
89 private static Factory saveFactory;
90 private static RestServer realRest;
91 private static int realPort;
92 private static RestServerParameters params;
94 private RestServer rest;
95 private HttpServletServer server1;
96 private HttpServletServer server2;
97 private Factory factory;
98 private HttpServletServerFactory serverFactory;
99 private String errorMsg;
102 * Starts the REST server.
103 * @throws Exception if an error occurs
106 public static void setUpBeforeClass() throws Exception {
107 saveFactory = Whitebox.getInternalState(RestServer.class, FACTORY_FIELD);
109 realPort = NetworkUtil.allocPort();
113 realRest = new RestServer(params, (Class<AafAuthFilter>) null, RealProvider.class) {
115 protected Properties getServerProperties(RestServerParameters restServerParameters, String names) {
116 Properties props = super.getServerProperties(restServerParameters, names);
118 String svcpfx = PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "."
119 + restServerParameters.getName();
120 props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SWAGGER_SUFFIX, "false");
127 assertTrue(NetworkUtil.isTcpPortOpen(params.getHost(), params.getPort(), 100, 100));
131 * Restores the factory and stops the REST server.
134 public static void tearDownAfterClass() {
135 Whitebox.setInternalState(RestServer.class, FACTORY_FIELD, saveFactory);
144 public void setUp() {
145 server1 = mock(HttpServletServer.class);
146 server2 = mock(HttpServletServer.class);
147 factory = mock(Factory.class);
148 serverFactory = mock(HttpServletServerFactory.class);
152 when(factory.getServerFactory()).thenReturn(serverFactory);
153 when(serverFactory.build(any())).thenReturn(Arrays.asList(server1, server2));
155 when(server1.getName()).thenReturn(SERVER1);
156 when(server2.getName()).thenReturn(SERVER2);
158 Whitebox.setInternalState(RestServer.class, FACTORY_FIELD, factory);
162 public void testRestServer() {
163 rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
166 verify(server1).start();
167 verify(server2).start();
170 verify(server1).stop();
171 verify(server2).stop();
175 public void testRestServerListList() {
176 rest = new RestServer(params, List.of(Filter.class, Filter2.class), List.of(Provider1.class, Provider2.class));
179 verify(server1).start();
180 verify(server2).start();
183 verify(server1).stop();
184 verify(server2).stop();
188 public void testRestServer_NoAaf() {
189 rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
190 verify(server1, never()).addFilterClass(any(), any());
191 verify(server2, never()).addFilterClass(any(), any());
195 public void testRestServer_OnlyOneAaf() {
196 when(server2.isAaf()).thenReturn(true);
198 rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
200 verify(server1, never()).addFilterClass(any(), any());
201 verify(server2).addFilterClass(null, Filter.class.getName());
205 public void testRestServer_BothAaf() {
206 when(server1.isAaf()).thenReturn(true);
207 when(server2.isAaf()).thenReturn(true);
209 rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
211 verify(server1).addFilterClass(null, Filter.class.getName());
212 verify(server2).addFilterClass(null, Filter.class.getName());
216 public void testRestServer_BothAaf_NoFilter() {
217 when(server1.isAaf()).thenReturn(true);
218 when(server2.isAaf()).thenReturn(true);
220 rest = new RestServer(params, (Class<AafAuthFilter>) null, Provider1.class, Provider2.class);
222 verify(server1, never()).addFilterClass(any(), any());
223 verify(server2, never()).addFilterClass(any(), any());
227 public void testRestServer_MissingProviders() {
228 assertThatIllegalArgumentException().isThrownBy(() -> new RestServer(params, Filter.class));
232 public void testGetServerProperties_testGetProviderNames() {
233 rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
235 ArgumentCaptor<Properties> cap = ArgumentCaptor.forClass(Properties.class);
236 verify(serverFactory).build(cap.capture());
238 Properties props = cap.getValue();
239 String svcpfx = PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + PARAM_NAME;
241 assertEquals(HOST, props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_HOST_SUFFIX));
242 assertEquals(String.valueOf(PORT),
243 props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_PORT_SUFFIX));
244 assertEquals(Provider1.class.getName() + "," + Provider2.class.getName(),
245 props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_REST_CLASSES_SUFFIX));
246 assertEquals("false", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_MANAGED_SUFFIX));
247 assertEquals("true", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SWAGGER_SUFFIX));
248 assertEquals(USER, props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_AUTH_USERNAME_SUFFIX));
249 assertEquals(PASS, props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_AUTH_PASSWORD_SUFFIX));
250 assertEquals("true", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_HTTPS_SUFFIX));
251 assertEquals("true", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_AAF_SUFFIX));
252 assertEquals(String.join(",", GsonMessageBodyHandler.class.getName(), YamlMessageBodyHandler.class.getName(),
253 JsonExceptionMapper.class.getName(), YamlExceptionMapper.class.getName()),
254 props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERIALIZATION_PROVIDER));
255 assertEquals("false", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_PROMETHEUS_SUFFIX));
259 public void testExplicitPrometheusAddedToProperty() {
260 when(params.isPrometheus()).thenReturn(true);
261 rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
262 ArgumentCaptor<Properties> cap = ArgumentCaptor.forClass(Properties.class);
263 verify(serverFactory).build(cap.capture());
265 Properties props = cap.getValue();
266 String svcpfx = PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + PARAM_NAME;
268 assertEquals("true", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_PROMETHEUS_SUFFIX));
269 assertThat(props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERVLET_URIPATH_SUFFIX)).isBlank();
270 assertThat(props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERVLET_CLASS_SUFFIX)).isBlank();
274 public void testStandardSevletAddedToProperty() {
275 when(params.getServletUriPath()).thenReturn("/metrics");
276 when(params.getServletClass()).thenReturn(MetricsServlet.class.getName());
277 rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
278 ArgumentCaptor<Properties> cap = ArgumentCaptor.forClass(Properties.class);
279 verify(serverFactory).build(cap.capture());
281 Properties props = cap.getValue();
282 String svcpfx = PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + PARAM_NAME;
284 assertEquals("false", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_PROMETHEUS_SUFFIX));
285 assertEquals(METRICS_URI,
286 props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERVLET_URIPATH_SUFFIX));
287 assertEquals(MetricsServlet.class.getName(),
288 props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERVLET_CLASS_SUFFIX));
292 public void testInvalidJson() throws Exception {
295 assertEquals(200, roundTrip(new StandardCoder().encode(new MyRequest())));
296 assertEquals(400, roundTrip("{'bogus-json'"));
297 assertThat(errorMsg).contains("Invalid request");
301 public void testInvalidYaml() throws Exception {
304 assertEquals(200, roundTrip(new StandardCoder().encode(new MyRequest()),
305 YamlMessageBodyHandler.APPLICATION_YAML));
306 assertEquals(400, roundTrip("<bogus yaml", YamlMessageBodyHandler.APPLICATION_YAML));
307 assertThat(errorMsg).contains("Invalid request");
310 private int roundTrip(String request) throws IOException {
311 return roundTrip(request, MediaType.APPLICATION_JSON);
314 private int roundTrip(String request, String mediaType) throws IOException {
315 URL url = new URL("http://" + params.getHost() + ":" + params.getPort() + "/request");
316 HttpURLConnection conn = (HttpURLConnection) url.openConnection();
317 conn.setDoInput(true);
318 conn.setDoOutput(true);
319 conn.setRequestMethod("POST");
320 String auth = params.getUserName() + ":" + params.getPassword();
321 conn.setRequestProperty("Authorization", "Basic " + Base64.getEncoder().encodeToString(auth.getBytes()));
322 conn.setRequestProperty("Content-type", mediaType);
323 conn.setRequestProperty("Accept", mediaType);
326 try (PrintWriter wtr = new PrintWriter(conn.getOutputStream())) {
330 int code = conn.getResponseCode();
335 errorMsg = IOUtils.toString(conn.getErrorStream(), StandardCharsets.UTF_8);
342 public void testToString() {
343 rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
344 assertNotNull(rest.toString());
348 public void testFactory() {
349 assertNotNull(saveFactory);
350 assertNotNull(saveFactory.getServerFactory());
353 private static void initRealParams() {
356 when(params.getHost()).thenReturn("localhost");
357 when(params.getPort()).thenReturn(realPort);
358 when(params.isHttps()).thenReturn(false);
359 when(params.isAaf()).thenReturn(false);
362 private static void initParams() {
363 params = mock(RestServerParameters.class);
365 when(params.getHost()).thenReturn(HOST);
366 when(params.getName()).thenReturn(PARAM_NAME);
367 when(params.getPassword()).thenReturn(PASS);
368 when(params.getPort()).thenReturn(PORT);
369 when(params.getUserName()).thenReturn(USER);
370 when(params.isAaf()).thenReturn(true);
371 when(params.isHttps()).thenReturn(true);
374 private static class Filter extends AafAuthFilter {
376 protected String getPermissionType(HttpServletRequest request) {
381 protected String getPermissionInstance(HttpServletRequest request) {
386 private static class Filter2 implements javax.servlet.Filter {
388 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
389 throws IOException, ServletException {
394 private static class Provider1 {
395 private Provider1() {
400 private static class Provider2 {
401 private Provider2() {
407 @Produces({MediaType.APPLICATION_JSON, YamlMessageBodyHandler.APPLICATION_YAML})
408 @Consumes({MediaType.APPLICATION_JSON, YamlMessageBodyHandler.APPLICATION_YAML})
409 public static class RealProvider {
412 public Response decision(MyRequest body) {
413 return Response.status(Response.Status.OK).entity(new MyResponse()).build();
418 public static class MyRequest {
423 public static class MyResponse {
424 private String text = "hello";