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 * Modifications Copyright (C) 2023-2024 Nordix Foundation.
8 * ================================================================================
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 * ============LICENSE_END=========================================================
23 package org.onap.policy.common.endpoints.http.server.test;
25 import static org.assertj.core.api.Assertions.assertThat;
26 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
27 import static org.junit.Assert.assertEquals;
28 import static org.junit.Assert.assertNotNull;
29 import static org.junit.Assert.assertTrue;
30 import static org.mockito.ArgumentMatchers.any;
31 import static org.mockito.Mockito.mock;
32 import static org.mockito.Mockito.never;
33 import static org.mockito.Mockito.verify;
34 import static org.mockito.Mockito.when;
36 import io.prometheus.client.servlet.jakarta.exporter.MetricsServlet;
37 import jakarta.servlet.FilterChain;
38 import jakarta.servlet.ServletRequest;
39 import jakarta.servlet.ServletResponse;
40 import jakarta.ws.rs.Consumes;
41 import jakarta.ws.rs.POST;
42 import jakarta.ws.rs.Path;
43 import jakarta.ws.rs.Produces;
44 import jakarta.ws.rs.core.MediaType;
45 import jakarta.ws.rs.core.Response;
46 import java.io.IOException;
47 import java.io.PrintWriter;
48 import java.net.HttpURLConnection;
50 import java.nio.charset.StandardCharsets;
51 import java.util.Arrays;
52 import java.util.Base64;
53 import java.util.List;
54 import java.util.Properties;
56 import org.apache.commons.io.IOUtils;
57 import org.junit.AfterClass;
58 import org.junit.Before;
59 import org.junit.BeforeClass;
60 import org.junit.Test;
61 import org.mockito.ArgumentCaptor;
62 import org.onap.policy.common.endpoints.http.server.HttpServletServer;
63 import org.onap.policy.common.endpoints.http.server.HttpServletServerFactory;
64 import org.onap.policy.common.endpoints.http.server.JsonExceptionMapper;
65 import org.onap.policy.common.endpoints.http.server.RestServer;
66 import org.onap.policy.common.endpoints.http.server.RestServer.Factory;
67 import org.onap.policy.common.endpoints.http.server.YamlExceptionMapper;
68 import org.onap.policy.common.endpoints.http.server.YamlMessageBodyHandler;
69 import org.onap.policy.common.endpoints.parameters.RestServerParameters;
70 import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties;
71 import org.onap.policy.common.gson.GsonMessageBodyHandler;
72 import org.onap.policy.common.utils.coder.StandardCoder;
73 import org.onap.policy.common.utils.network.NetworkUtil;
74 import org.springframework.test.util.ReflectionTestUtils;
76 public class RestServerTest {
77 private static final String METRICS_URI = "/metrics";
78 private static final String SERVER1 = "my-server-A";
79 private static final String SERVER2 = "my-server-B";
80 private static final String FACTORY_FIELD = "factory";
81 private static final String HOST = "my-host";
82 private static final String PARAM_NAME = "my-param";
83 private static final String PASS = "my-pass";
84 private static final Integer PORT = 9876;
85 private static final String USER = "my-user";
87 private static Factory saveFactory;
88 private static RestServer realRest;
89 private static int realPort;
90 private static RestServerParameters params;
92 private RestServer rest;
93 private HttpServletServer server1;
94 private HttpServletServer server2;
95 private Factory factory;
96 private HttpServletServerFactory serverFactory;
97 private String errorMsg;
100 * Starts the REST server.
101 * @throws Exception if an error occurs
104 public static void setUpBeforeClass() throws Exception {
105 saveFactory = (Factory) ReflectionTestUtils.getField(RestServer.class, FACTORY_FIELD);
107 realPort = NetworkUtil.allocPort();
111 realRest = new RestServer(params, RealProvider.class) {
113 protected Properties getServerProperties(RestServerParameters restServerParameters, String names) {
114 Properties props = super.getServerProperties(restServerParameters, names);
116 String svcpfx = PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "."
117 + restServerParameters.getName();
118 props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SWAGGER_SUFFIX, "false");
125 assertTrue(NetworkUtil.isTcpPortOpen(params.getHost(), params.getPort(), 100, 100));
129 * Restores the factory and stops the REST server.
132 public static void tearDownAfterClass() {
133 ReflectionTestUtils.setField(RestServer.class, FACTORY_FIELD, saveFactory);
142 public void setUp() {
143 server1 = mock(HttpServletServer.class);
144 server2 = mock(HttpServletServer.class);
145 factory = mock(Factory.class);
146 serverFactory = mock(HttpServletServerFactory.class);
150 when(factory.getServerFactory()).thenReturn(serverFactory);
151 when(serverFactory.build(any())).thenReturn(Arrays.asList(server1, server2));
153 when(server1.getName()).thenReturn(SERVER1);
154 when(server2.getName()).thenReturn(SERVER2);
156 ReflectionTestUtils.setField(RestServer.class, FACTORY_FIELD, factory);
160 public void testRestServer() {
161 rest = new RestServer(params, Filter2.class, Provider1.class, Provider2.class);
164 verify(server1).start();
165 verify(server2).start();
168 verify(server1).stop();
169 verify(server2).stop();
173 public void testRestServerListList() {
174 rest = new RestServer(params, List.of(Filter2.class), List.of(Provider1.class, Provider2.class));
177 verify(server1).start();
178 verify(server2).start();
181 verify(server1).stop();
182 verify(server2).stop();
186 public void testRestServer_MissingProviders() {
187 assertThatIllegalArgumentException().isThrownBy(() -> new RestServer(params, List.of(Filter2.class), null));
191 public void testGetServerProperties_testGetProviderNames() {
192 rest = new RestServer(params, Provider1.class, Provider2.class);
194 ArgumentCaptor<Properties> cap = ArgumentCaptor.forClass(Properties.class);
195 verify(serverFactory).build(cap.capture());
197 Properties props = cap.getValue();
198 String svcpfx = PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + PARAM_NAME;
200 assertEquals(HOST, props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_HOST_SUFFIX));
201 assertEquals(String.valueOf(PORT),
202 props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_PORT_SUFFIX));
203 assertEquals(Provider1.class.getName() + "," + Provider2.class.getName(),
204 props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_REST_CLASSES_SUFFIX));
205 assertEquals("false", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_MANAGED_SUFFIX));
206 assertEquals("true", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SWAGGER_SUFFIX));
207 assertEquals(USER, props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_AUTH_USERNAME_SUFFIX));
208 assertEquals(PASS, props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_AUTH_PASSWORD_SUFFIX));
209 assertEquals("true", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_HTTPS_SUFFIX));
210 assertEquals(String.join(",", GsonMessageBodyHandler.class.getName(), YamlMessageBodyHandler.class.getName(),
211 JsonExceptionMapper.class.getName(), YamlExceptionMapper.class.getName()),
212 props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERIALIZATION_PROVIDER));
213 assertEquals("false", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_PROMETHEUS_SUFFIX));
217 public void testExplicitPrometheusAddedToProperty() {
218 when(params.isPrometheus()).thenReturn(true);
219 rest = new RestServer(params, Filter2.class, Provider1.class, Provider2.class);
220 ArgumentCaptor<Properties> cap = ArgumentCaptor.forClass(Properties.class);
221 verify(serverFactory).build(cap.capture());
223 Properties props = cap.getValue();
224 String svcpfx = PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + PARAM_NAME;
226 assertEquals("true", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_PROMETHEUS_SUFFIX));
227 assertThat(props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERVLET_URIPATH_SUFFIX)).isBlank();
228 assertThat(props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERVLET_CLASS_SUFFIX)).isBlank();
232 public void testStandardServletAddedToProperty() {
233 when(params.getServletUriPath()).thenReturn("/metrics");
234 when(params.getServletClass()).thenReturn(MetricsServlet.class.getName());
235 rest = new RestServer(params, Filter2.class, Provider1.class, Provider2.class);
236 ArgumentCaptor<Properties> cap = ArgumentCaptor.forClass(Properties.class);
237 verify(serverFactory).build(cap.capture());
239 Properties props = cap.getValue();
240 String svcpfx = PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + PARAM_NAME;
242 assertEquals("false", props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_PROMETHEUS_SUFFIX));
243 assertEquals(METRICS_URI,
244 props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERVLET_URIPATH_SUFFIX));
245 assertEquals(MetricsServlet.class.getName(),
246 props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERVLET_CLASS_SUFFIX));
250 public void testInvalidJson() throws Exception {
253 assertEquals(200, roundTrip(new StandardCoder().encode(new MyRequest())));
254 assertEquals(400, roundTrip("{'bogus-json'"));
255 assertThat(errorMsg).contains("Invalid request");
259 public void testInvalidYaml() throws Exception {
262 assertEquals(200, roundTrip(new StandardCoder().encode(new MyRequest()),
263 YamlMessageBodyHandler.APPLICATION_YAML));
264 assertEquals(400, roundTrip("<bogus yaml", YamlMessageBodyHandler.APPLICATION_YAML));
265 assertThat(errorMsg).contains("Invalid request");
268 private int roundTrip(String request) throws IOException {
269 return roundTrip(request, MediaType.APPLICATION_JSON);
272 private int roundTrip(String request, String mediaType) throws IOException {
273 URL url = new URL("http://" + params.getHost() + ":" + params.getPort() + "/request");
274 HttpURLConnection conn = (HttpURLConnection) url.openConnection();
275 conn.setDoInput(true);
276 conn.setDoOutput(true);
277 conn.setRequestMethod("POST");
278 String auth = params.getUserName() + ":" + params.getPassword();
279 conn.setRequestProperty("Authorization", "Basic " + Base64.getEncoder().encodeToString(auth.getBytes()));
280 conn.setRequestProperty("Content-type", mediaType);
281 conn.setRequestProperty("Accept", mediaType);
284 try (PrintWriter wtr = new PrintWriter(conn.getOutputStream())) {
288 int code = conn.getResponseCode();
293 errorMsg = IOUtils.toString(conn.getErrorStream(), StandardCharsets.UTF_8);
300 public void testToString() {
301 rest = new RestServer(params, Filter2.class, Provider1.class, Provider2.class);
302 assertNotNull(rest.toString());
306 public void testFactory() {
307 assertNotNull(saveFactory);
308 assertNotNull(saveFactory.getServerFactory());
311 private static void initRealParams() {
314 when(params.getHost()).thenReturn("localhost");
315 when(params.getPort()).thenReturn(realPort);
316 when(params.isHttps()).thenReturn(false);
317 when(params.isAaf()).thenReturn(false);
320 private static void initParams() {
321 params = mock(RestServerParameters.class);
323 when(params.getHost()).thenReturn(HOST);
324 when(params.getName()).thenReturn(PARAM_NAME);
325 when(params.getPassword()).thenReturn(PASS);
326 when(params.getPort()).thenReturn(PORT);
327 when(params.getUserName()).thenReturn(USER);
328 when(params.isAaf()).thenReturn(true);
329 when(params.isHttps()).thenReturn(true);
332 private static class Filter2 implements jakarta.servlet.Filter {
334 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
339 private static class Provider1 {
340 private Provider1() {
345 private static class Provider2 {
346 private Provider2() {
352 @Produces({MediaType.APPLICATION_JSON, YamlMessageBodyHandler.APPLICATION_YAML})
353 @Consumes({MediaType.APPLICATION_JSON, YamlMessageBodyHandler.APPLICATION_YAML})
354 public static class RealProvider {
357 public Response decision(MyRequest body) {
358 return Response.status(Response.Status.OK).entity(new MyResponse()).build();
363 public static class MyRequest {
368 public static class MyResponse {
369 private String text = "hello";