2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2019-2020 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.Properties;
44 import javax.servlet.http.HttpServletRequest;
45 import javax.ws.rs.Consumes;
46 import javax.ws.rs.POST;
47 import javax.ws.rs.Path;
48 import javax.ws.rs.Produces;
49 import javax.ws.rs.core.MediaType;
50 import javax.ws.rs.core.Response;
52 import org.apache.commons.io.IOUtils;
53 import org.junit.AfterClass;
54 import org.junit.Before;
55 import org.junit.BeforeClass;
56 import org.junit.Test;
57 import org.mockito.ArgumentCaptor;
58 import org.onap.policy.common.endpoints.http.server.HttpServletServer;
59 import org.onap.policy.common.endpoints.http.server.HttpServletServerFactory;
60 import org.onap.policy.common.endpoints.http.server.JsonExceptionMapper;
61 import org.onap.policy.common.endpoints.http.server.RestServer;
62 import org.onap.policy.common.endpoints.http.server.RestServer.Factory;
63 import org.onap.policy.common.endpoints.http.server.YamlExceptionMapper;
64 import org.onap.policy.common.endpoints.http.server.YamlMessageBodyHandler;
65 import org.onap.policy.common.endpoints.http.server.aaf.AafAuthFilter;
66 import org.onap.policy.common.endpoints.parameters.RestServerParameters;
67 import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties;
68 import org.onap.policy.common.gson.GsonMessageBodyHandler;
69 import org.onap.policy.common.utils.coder.StandardCoder;
70 import org.onap.policy.common.utils.network.NetworkUtil;
71 import org.powermock.reflect.Whitebox;
73 public class RestServerTest {
74 private static final String METRICS_URI = "/metrics";
75 private static final String SERVER1 = "my-server-A";
76 private static final String SERVER2 = "my-server-B";
77 private static final String FACTORY_FIELD = "factory";
78 private static final String HOST = "my-host";
79 private static final String PARAM_NAME = "my-param";
80 private static final String PASS = "my-pass";
81 private static final Integer PORT = 9876;
82 private static final String USER = "my-user";
84 private static Factory saveFactory;
85 private static RestServer realRest;
86 private static int realPort;
87 private static RestServerParameters params;
89 private RestServer rest;
90 private HttpServletServer server1;
91 private HttpServletServer server2;
92 private Factory factory;
93 private HttpServletServerFactory serverFactory;
94 private String errorMsg;
97 * Starts the REST server.
98 * @throws Exception if an error occurs
101 public static void setUpBeforeClass() throws Exception {
102 saveFactory = Whitebox.getInternalState(RestServer.class, FACTORY_FIELD);
104 realPort = NetworkUtil.allocPort();
108 realRest = new RestServer(params, null, RealProvider.class) {
110 protected Properties getServerProperties(RestServerParameters restServerParameters, String names) {
111 Properties props = super.getServerProperties(restServerParameters, names);
113 String svcpfx = PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "."
114 + restServerParameters.getName();
115 props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SWAGGER_SUFFIX, "false");
122 assertTrue(NetworkUtil.isTcpPortOpen(params.getHost(), params.getPort(), 100, 100));
126 * Restores the factory and stops the REST server.
129 public static void tearDownAfterClass() {
130 Whitebox.setInternalState(RestServer.class, FACTORY_FIELD, saveFactory);
139 public void setUp() {
140 server1 = mock(HttpServletServer.class);
141 server2 = mock(HttpServletServer.class);
142 factory = mock(Factory.class);
143 serverFactory = mock(HttpServletServerFactory.class);
147 when(factory.getServerFactory()).thenReturn(serverFactory);
148 when(serverFactory.build(any())).thenReturn(Arrays.asList(server1, server2));
150 when(server1.getName()).thenReturn(SERVER1);
151 when(server2.getName()).thenReturn(SERVER2);
153 Whitebox.setInternalState(RestServer.class, FACTORY_FIELD, factory);
157 public void testRestServer() {
158 rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
161 verify(server1).start();
162 verify(server2).start();
165 verify(server1).stop();
166 verify(server2).stop();
170 public void testRestServer_NoAaf() {
171 rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
172 verify(server1, never()).addFilterClass(any(), any());
173 verify(server2, never()).addFilterClass(any(), any());
177 public void testRestServer_OnlyOneAaf() {
178 when(server2.isAaf()).thenReturn(true);
180 rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
182 verify(server1, never()).addFilterClass(any(), any());
183 verify(server2).addFilterClass(null, Filter.class.getName());
187 public void testRestServer_BothAaf() {
188 when(server1.isAaf()).thenReturn(true);
189 when(server2.isAaf()).thenReturn(true);
191 rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
193 verify(server1).addFilterClass(null, Filter.class.getName());
194 verify(server2).addFilterClass(null, Filter.class.getName());
198 public void testRestServer_BothAaf_NoFilter() {
199 when(server1.isAaf()).thenReturn(true);
200 when(server2.isAaf()).thenReturn(true);
202 rest = new RestServer(params, null, Provider1.class, Provider2.class);
204 verify(server1, never()).addFilterClass(any(), any());
205 verify(server2, never()).addFilterClass(any(), any());
209 public void testRestServer_MissingProviders() {
210 assertThatIllegalArgumentException().isThrownBy(() -> new RestServer(params, Filter.class));
214 public void testGetServerProperties_testGetProviderNames() {
215 rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
217 ArgumentCaptor<Properties> cap = ArgumentCaptor.forClass(Properties.class);
218 verify(serverFactory).build(cap.capture());
220 Properties props = cap.getValue();
221 String svcpfx = PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + PARAM_NAME;
223 assertEquals(HOST, props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_HOST_SUFFIX));
224 assertEquals(String.valueOf(PORT),
225 props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_PORT_SUFFIX));
226 assertEquals(Provider1.class.getName() + "," + Provider2.class.getName(),
227 props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_REST_CLASSES_SUFFIX));
228 assertEquals("false", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_MANAGED_SUFFIX));
229 assertEquals("true", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SWAGGER_SUFFIX));
230 assertEquals(USER, props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_AUTH_USERNAME_SUFFIX));
231 assertEquals(PASS, props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_AUTH_PASSWORD_SUFFIX));
232 assertEquals("true", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_HTTPS_SUFFIX));
233 assertEquals("true", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_AAF_SUFFIX));
234 assertEquals(String.join(",", GsonMessageBodyHandler.class.getName(), YamlMessageBodyHandler.class.getName(),
235 JsonExceptionMapper.class.getName(), YamlExceptionMapper.class.getName()),
236 props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERIALIZATION_PROVIDER));
237 assertEquals("false", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_PROMETHEUS_SUFFIX));
241 public void testExplicitPrometheusAddedToProperty() {
242 when(params.isPrometheus()).thenReturn(true);
243 rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
244 ArgumentCaptor<Properties> cap = ArgumentCaptor.forClass(Properties.class);
245 verify(serverFactory).build(cap.capture());
247 Properties props = cap.getValue();
248 String svcpfx = PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + PARAM_NAME;
250 assertEquals("true", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_PROMETHEUS_SUFFIX));
251 assertThat(props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERVLET_URIPATH_SUFFIX)).isBlank();
252 assertThat(props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERVLET_CLASS_SUFFIX)).isBlank();
256 public void testStandardSevletAddedToProperty() {
257 when(params.getServletUriPath()).thenReturn("/metrics");
258 when(params.getServletClass()).thenReturn(MetricsServlet.class.getName());
259 rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
260 ArgumentCaptor<Properties> cap = ArgumentCaptor.forClass(Properties.class);
261 verify(serverFactory).build(cap.capture());
263 Properties props = cap.getValue();
264 String svcpfx = PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + PARAM_NAME;
266 assertEquals("false", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_PROMETHEUS_SUFFIX));
267 assertEquals(METRICS_URI,
268 props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERVLET_URIPATH_SUFFIX));
269 assertEquals(MetricsServlet.class.getName(),
270 props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERVLET_CLASS_SUFFIX));
274 public void testInvalidJson() throws Exception {
277 assertEquals(200, roundTrip(new StandardCoder().encode(new MyRequest())));
278 assertEquals(400, roundTrip("{'bogus-json'"));
279 assertThat(errorMsg).contains("Invalid request");
283 public void testInvalidYaml() throws Exception {
286 assertEquals(200, roundTrip(new StandardCoder().encode(new MyRequest()),
287 YamlMessageBodyHandler.APPLICATION_YAML));
288 assertEquals(400, roundTrip("<bogus yaml", YamlMessageBodyHandler.APPLICATION_YAML));
289 assertThat(errorMsg).contains("Invalid request");
292 private int roundTrip(String request) throws IOException {
293 return roundTrip(request, MediaType.APPLICATION_JSON);
296 private int roundTrip(String request, String mediaType) throws IOException {
297 URL url = new URL("http://" + params.getHost() + ":" + params.getPort() + "/request");
298 HttpURLConnection conn = (HttpURLConnection) url.openConnection();
299 conn.setDoInput(true);
300 conn.setDoOutput(true);
301 conn.setRequestMethod("POST");
302 String auth = params.getUserName() + ":" + params.getPassword();
303 conn.setRequestProperty("Authorization", "Basic " + Base64.getEncoder().encodeToString(auth.getBytes()));
304 conn.setRequestProperty("Content-type", mediaType);
305 conn.setRequestProperty("Accept", mediaType);
308 try (PrintWriter wtr = new PrintWriter(conn.getOutputStream())) {
312 int code = conn.getResponseCode();
317 errorMsg = IOUtils.toString(conn.getErrorStream(), StandardCharsets.UTF_8);
324 public void testToString() {
325 rest = new RestServer(params, Filter.class, Provider1.class, Provider2.class);
326 assertNotNull(rest.toString());
330 public void testFactory() {
331 assertNotNull(saveFactory);
332 assertNotNull(saveFactory.getServerFactory());
335 private static void initRealParams() {
338 when(params.getHost()).thenReturn("localhost");
339 when(params.getPort()).thenReturn(realPort);
340 when(params.isHttps()).thenReturn(false);
341 when(params.isAaf()).thenReturn(false);
344 private static void initParams() {
345 params = mock(RestServerParameters.class);
347 when(params.getHost()).thenReturn(HOST);
348 when(params.getName()).thenReturn(PARAM_NAME);
349 when(params.getPassword()).thenReturn(PASS);
350 when(params.getPort()).thenReturn(PORT);
351 when(params.getUserName()).thenReturn(USER);
352 when(params.isAaf()).thenReturn(true);
353 when(params.isHttps()).thenReturn(true);
356 private static class Filter extends AafAuthFilter {
358 protected String getPermissionType(HttpServletRequest request) {
363 protected String getPermissionInstance(HttpServletRequest request) {
368 private static class Provider1 {
369 private Provider1() {
374 private static class Provider2 {
375 private Provider2() {
381 @Produces({MediaType.APPLICATION_JSON, YamlMessageBodyHandler.APPLICATION_YAML})
382 @Consumes({MediaType.APPLICATION_JSON, YamlMessageBodyHandler.APPLICATION_YAML})
383 public static class RealProvider {
386 public Response decision(MyRequest body) {
387 return Response.status(Response.Status.OK).entity(new MyResponse()).build();
392 public static class MyRequest {
397 public static class MyResponse {
398 private String text = "hello";