2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.onap.policy.controlloop.actorserviceprovider.impl;
23 import static org.assertj.core.api.Assertions.assertThat;
24 import static org.assertj.core.api.Assertions.assertThatCode;
25 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
26 import static org.assertj.core.api.Assertions.assertThatThrownBy;
27 import static org.junit.Assert.assertEquals;
28 import static org.junit.Assert.assertFalse;
29 import static org.junit.Assert.assertNotNull;
30 import static org.junit.Assert.assertSame;
31 import static org.junit.Assert.assertTrue;
32 import static org.mockito.ArgumentMatchers.any;
33 import static org.mockito.Mockito.when;
35 import java.util.Collections;
37 import java.util.Properties;
38 import java.util.UUID;
39 import java.util.concurrent.CancellationException;
40 import java.util.concurrent.CompletableFuture;
41 import java.util.concurrent.ExecutionException;
42 import java.util.concurrent.Executor;
43 import java.util.concurrent.Future;
44 import java.util.concurrent.TimeUnit;
45 import java.util.concurrent.TimeoutException;
46 import java.util.concurrent.atomic.AtomicReference;
47 import javax.ws.rs.Consumes;
48 import javax.ws.rs.DELETE;
49 import javax.ws.rs.GET;
50 import javax.ws.rs.POST;
51 import javax.ws.rs.PUT;
52 import javax.ws.rs.Path;
53 import javax.ws.rs.Produces;
54 import javax.ws.rs.client.Entity;
55 import javax.ws.rs.client.InvocationCallback;
56 import javax.ws.rs.core.MediaType;
57 import javax.ws.rs.core.Response;
58 import javax.ws.rs.core.Response.Status;
61 import org.junit.AfterClass;
62 import org.junit.Before;
63 import org.junit.BeforeClass;
64 import org.junit.Test;
65 import org.mockito.Mock;
66 import org.mockito.MockitoAnnotations;
67 import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
68 import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams;
69 import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams.TopicParamsBuilder;
70 import org.onap.policy.common.endpoints.http.client.HttpClient;
71 import org.onap.policy.common.endpoints.http.client.HttpClientFactory;
72 import org.onap.policy.common.endpoints.http.client.HttpClientFactoryInstance;
73 import org.onap.policy.common.endpoints.http.server.HttpServletServer;
74 import org.onap.policy.common.endpoints.http.server.HttpServletServerFactoryInstance;
75 import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties;
76 import org.onap.policy.common.endpoints.utils.NetLoggerUtil.EventType;
77 import org.onap.policy.common.gson.GsonMessageBodyHandler;
78 import org.onap.policy.common.utils.coder.CoderException;
79 import org.onap.policy.common.utils.network.NetworkUtil;
80 import org.onap.policy.controlloop.VirtualControlLoopEvent;
81 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
82 import org.onap.policy.controlloop.actorserviceprovider.controlloop.ControlLoopEventContext;
83 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
84 import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpConfig;
85 import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpParams;
86 import org.onap.policy.controlloop.policy.PolicyResult;
88 public class HttpOperationTest {
90 private static final IllegalStateException EXPECTED_EXCEPTION = new IllegalStateException("expected exception");
91 private static final String ACTOR = "my-actor";
92 private static final String OPERATION = "my-name";
93 private static final String HTTP_CLIENT = "my-client";
94 private static final String HTTP_NO_SERVER = "my-http-no-server-client";
95 private static final String MEDIA_TYPE_APPLICATION_JSON = "application/json";
96 private static final String BASE_URI = "oper";
97 private static final String PATH = "/my-path";
98 private static final String TEXT = "my-text";
99 private static final UUID REQ_ID = UUID.randomUUID();
102 * {@code True} if the server should reject the request, {@code false} otherwise.
104 private static boolean rejectRequest;
106 // call counts of each method type in the server
107 private static int nget;
108 private static int npost;
109 private static int nput;
110 private static int ndelete;
113 private HttpClient client;
115 private HttpClientFactory clientFactory;
117 private Response response;
119 private Executor executor;
121 private VirtualControlLoopEvent event;
122 private ControlLoopEventContext context;
123 private ControlLoopOperationParams params;
124 private OperationOutcome outcome;
125 private AtomicReference<InvocationCallback<Response>> callback;
126 private Future<Response> future;
127 private HttpConfig config;
128 private MyGetOperation<String> oper;
131 * Starts the simulator.
134 public static void setUpBeforeClass() throws Exception {
136 int port = NetworkUtil.allocPort();
139 * Start the simulator. Must use "Properties" to configure it, otherwise the
140 * server will use the wrong serialization provider.
142 Properties svrprops = getServerProperties("my-server", port);
143 HttpServletServerFactoryInstance.getServerFactory().build(svrprops).forEach(HttpServletServer::start);
145 if (!NetworkUtil.isTcpPortOpen("localhost", port, 100, 100)) {
146 HttpServletServerFactoryInstance.getServerFactory().destroy();
147 throw new IllegalStateException("server is not running");
151 * Start the clients, one to the server, and one to a non-existent server.
153 TopicParamsBuilder builder = BusTopicParams.builder().managed(true).hostname("localhost").basePath(BASE_URI)
154 .serializationProvider(GsonMessageBodyHandler.class.getName());
156 HttpClientFactoryInstance.getClientFactory().build(builder.clientName(HTTP_CLIENT).port(port).build());
158 HttpClientFactoryInstance.getClientFactory()
159 .build(builder.clientName(HTTP_NO_SERVER).port(NetworkUtil.allocPort()).build());
163 * Destroys the Http factories and stops the appender.
166 public static void tearDownAfterClass() {
167 HttpClientFactoryInstance.getClientFactory().destroy();
168 HttpServletServerFactoryInstance.getServerFactory().destroy();
172 * Initializes fields, including {@link #oper}, and resets the static fields used by
176 public void setUp() {
177 MockitoAnnotations.initMocks(this);
179 rejectRequest = false;
185 when(response.readEntity(String.class)).thenReturn(TEXT);
186 when(response.getStatus()).thenReturn(200);
188 event = new VirtualControlLoopEvent();
189 event.setRequestId(REQ_ID);
191 context = new ControlLoopEventContext(event);
192 params = ControlLoopOperationParams.builder().actor(ACTOR).operation(OPERATION).context(context).build();
194 outcome = params.makeOutcome();
196 callback = new AtomicReference<>();
197 future = new CompletableFuture<>();
199 when(clientFactory.get(any())).thenReturn(client);
201 initConfig(HTTP_CLIENT);
203 oper = new MyGetOperation<>(String.class);
207 public void testHttpOperator() {
208 assertEquals(ACTOR, oper.getActorName());
209 assertEquals(OPERATION, oper.getName());
210 assertEquals(ACTOR + "." + OPERATION, oper.getFullName());
214 public void testMakeHeaders() {
215 assertEquals(Collections.emptyMap(), oper.makeHeaders());
219 public void testGetPath() {
220 assertEquals(PATH, oper.getPath());
224 public void testMakeUrl() {
226 initRealConfig(HTTP_CLIENT);
228 oper = new MyGetOperation<>(String.class);
230 assertThat(oper.getUrl()).endsWith("/" + BASE_URI + PATH);
234 public void testDoConfigureMapOfStringObject_testGetClient_testGetPath_testGetTimeoutMs() {
236 // use value from operator
237 assertEquals(1000L, oper.getTimeoutMs(null));
238 assertEquals(1000L, oper.getTimeoutMs(0));
240 // should use given value
241 assertEquals(20 * 1000L, oper.getTimeoutMs(20));
245 * Tests handleResponse() when it completes.
248 public void testHandleResponseComplete() throws Exception {
249 CompletableFuture<OperationOutcome> future2 = oper.handleResponse(outcome, PATH, cb -> {
254 assertFalse(future2.isDone());
255 assertNotNull(callback.get());
256 callback.get().completed(response);
258 assertSame(outcome, future2.get(5, TimeUnit.SECONDS));
259 assertSame(TEXT, outcome.getResponse());
261 assertEquals(PolicyResult.SUCCESS, outcome.getResult());
265 * Tests handleResponse() when it fails.
268 public void testHandleResponseFailed() throws Exception {
269 CompletableFuture<OperationOutcome> future2 = oper.handleResponse(outcome, PATH, cb -> {
274 assertFalse(future2.isDone());
275 assertNotNull(callback.get());
276 callback.get().failed(EXPECTED_EXCEPTION);
278 assertThatThrownBy(() -> future2.get(5, TimeUnit.SECONDS)).hasCause(EXPECTED_EXCEPTION);
280 // future and future2 may be completed in parallel so we must wait again
281 assertThatThrownBy(() -> future.get(5, TimeUnit.SECONDS)).isInstanceOf(CancellationException.class);
282 assertTrue(future.isCancelled());
286 * Tests processResponse() when it's a success and the response type is a String.
289 public void testProcessResponseSuccessString() throws Exception {
290 CompletableFuture<OperationOutcome> result = oper.processResponse(outcome, PATH, response);
291 assertTrue(result.isDone());
292 assertSame(outcome, result.get());
293 assertEquals(PolicyResult.SUCCESS, outcome.getResult());
294 assertSame(TEXT, outcome.getResponse());
298 * Tests processResponse() when it's a failure.
301 public void testProcessResponseFailure() throws Exception {
302 when(response.getStatus()).thenReturn(555);
303 CompletableFuture<OperationOutcome> result = oper.processResponse(outcome, PATH, response);
304 assertTrue(result.isDone());
305 assertSame(outcome, result.get());
306 assertEquals(PolicyResult.FAILURE, outcome.getResult());
307 assertSame(TEXT, outcome.getResponse());
311 * Tests processResponse() when the decoder succeeds.
314 public void testProcessResponseDecodeOk() throws Exception {
315 when(response.readEntity(String.class)).thenReturn("10");
317 MyGetOperation<Integer> oper2 = new MyGetOperation<>(Integer.class);
319 CompletableFuture<OperationOutcome> result = oper2.processResponse(outcome, PATH, response);
320 assertTrue(result.isDone());
321 assertSame(outcome, result.get());
322 assertEquals(PolicyResult.SUCCESS, outcome.getResult());
323 assertEquals(Integer.valueOf(10), outcome.getResponse());
327 * Tests processResponse() when the decoder throws an exception.
330 public void testProcessResponseDecodeExcept() throws CoderException {
331 MyGetOperation<Integer> oper2 = new MyGetOperation<>(Integer.class);
333 assertThatIllegalArgumentException().isThrownBy(() -> oper2.processResponse(outcome, PATH, response));
337 public void testPostProcessResponse() {
338 assertThatCode(() -> oper.postProcessResponse(outcome, PATH, null, null)).doesNotThrowAnyException();
342 public void testIsSuccess() {
343 when(response.getStatus()).thenReturn(200);
344 assertTrue(oper.isSuccess(response, null));
346 when(response.getStatus()).thenReturn(555);
347 assertFalse(oper.isSuccess(response, null));
354 public void testGet() throws Exception {
356 initRealConfig(HTTP_CLIENT);
358 MyGetOperation<MyResponse> oper2 = new MyGetOperation<>(MyResponse.class);
360 OperationOutcome outcome = runOperation(oper2);
361 assertNotNull(outcome);
362 assertEquals(1, nget);
363 assertEquals(PolicyResult.SUCCESS, outcome.getResult());
364 assertTrue(outcome.getResponse() instanceof MyResponse);
371 public void testDelete() throws Exception {
373 initRealConfig(HTTP_CLIENT);
375 MyDeleteOperation oper2 = new MyDeleteOperation();
377 OperationOutcome outcome = runOperation(oper2);
378 assertNotNull(outcome);
379 assertEquals(1, ndelete);
380 assertEquals(PolicyResult.SUCCESS, outcome.getResult());
381 assertTrue(outcome.getResponse() instanceof String);
388 public void testPost() throws Exception {
390 initRealConfig(HTTP_CLIENT);
391 MyPostOperation oper2 = new MyPostOperation();
393 OperationOutcome outcome = runOperation(oper2);
394 assertNotNull(outcome);
395 assertEquals(1, npost);
396 assertEquals(PolicyResult.SUCCESS, outcome.getResult());
397 assertTrue(outcome.getResponse() instanceof MyResponse);
404 public void testPut() throws Exception {
406 initRealConfig(HTTP_CLIENT);
408 MyPutOperation oper2 = new MyPutOperation();
410 OperationOutcome outcome = runOperation(oper2);
411 assertNotNull(outcome);
412 assertEquals(1, nput);
413 assertEquals(PolicyResult.SUCCESS, outcome.getResult());
414 assertTrue(outcome.getResponse() instanceof MyResponse);
418 public void testMakeDecoder() {
419 assertNotNull(oper.makeCoder());
423 * Gets server properties.
425 * @param name server name
426 * @param port server port
427 * @return server properties
429 private static Properties getServerProperties(String name, int port) {
430 final Properties props = new Properties();
431 props.setProperty(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES, name);
433 final String svcpfx = PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + name;
435 props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_REST_CLASSES_SUFFIX, Server.class.getName());
436 props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_HOST_SUFFIX, "localhost");
437 props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_PORT_SUFFIX, String.valueOf(port));
438 props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_MANAGED_SUFFIX, "true");
439 props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SWAGGER_SUFFIX, "false");
441 props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERIALIZATION_PROVIDER,
442 GsonMessageBodyHandler.class.getName());
447 * Initializes the configuration.
449 * @param operator operator to be initialized
450 * @param clientName name of the client which it should use
452 private void initConfig(String clientName) {
453 initConfig(clientName, clientFactory);
457 * Initializes the configuration with a real client.
459 * @param operator operator to be initialized
460 * @param clientName name of the client which it should use
462 private void initConfig(String clientName, HttpClientFactory factory) {
463 HttpParams params = HttpParams.builder().clientName(clientName).path(PATH).timeoutSec(1).build();
464 config = new HttpConfig(executor, params, factory);
468 * Initializes the configuration with a real client.
470 * @param operator operator to be initialized
471 * @param clientName name of the client which it should use
473 private void initRealConfig(String clientName) {
474 initConfig(clientName, HttpClientFactoryInstance.getClientFactory());
478 * Runs the operation.
480 * @param operator operator on which to start the operation
481 * @return the outcome of the operation, or {@code null} if it does not complete in
484 private <T> OperationOutcome runOperation(HttpOperation<T> operator)
485 throws InterruptedException, ExecutionException, TimeoutException {
487 CompletableFuture<OperationOutcome> future = operator.start();
489 return future.get(5, TimeUnit.SECONDS);
494 public static class MyRequest {
495 private String input = "some input";
500 public static class MyResponse {
501 private String output = "some output";
504 private class MyGetOperation<T> extends HttpOperation<T> {
505 public MyGetOperation(Class<T> responseClass) {
506 super(HttpOperationTest.this.params, HttpOperationTest.this.config, responseClass);
510 protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) {
511 Map<String, Object> headers = makeHeaders();
513 headers.put("Accept", MediaType.APPLICATION_JSON);
514 String url = getUrl();
516 logMessage(EventType.OUT, CommInfrastructure.REST, url, null);
519 return handleResponse(outcome, url,
520 callback -> getClient().get(callback, getPath(), headers));
525 private class MyPostOperation extends HttpOperation<MyResponse> {
526 public MyPostOperation() {
527 super(HttpOperationTest.this.params, HttpOperationTest.this.config, MyResponse.class);
531 protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) {
533 MyRequest request = new MyRequest();
535 Entity<MyRequest> entity = Entity.entity(request, MediaType.APPLICATION_JSON);
537 Map<String, Object> headers = makeHeaders();
539 headers.put("Accept", MediaType.APPLICATION_JSON);
540 String url = getUrl();
542 logMessage(EventType.OUT, CommInfrastructure.REST, url, request);
545 return handleResponse(outcome, url,
546 callback -> getClient().post(callback, getPath(), entity, headers));
551 private class MyPutOperation extends HttpOperation<MyResponse> {
552 public MyPutOperation() {
553 super(HttpOperationTest.this.params, HttpOperationTest.this.config, MyResponse.class);
557 protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) {
559 MyRequest request = new MyRequest();
561 Entity<MyRequest> entity = Entity.entity(request, MediaType.APPLICATION_JSON);
563 Map<String, Object> headers = makeHeaders();
565 headers.put("Accept", MediaType.APPLICATION_JSON);
566 String url = getUrl();
568 logMessage(EventType.OUT, CommInfrastructure.REST, url, request);
571 return handleResponse(outcome, url,
572 callback -> getClient().put(callback, getPath(), entity, headers));
577 private class MyDeleteOperation extends HttpOperation<String> {
578 public MyDeleteOperation() {
579 super(HttpOperationTest.this.params, HttpOperationTest.this.config, String.class);
583 protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) {
584 Map<String, Object> headers = makeHeaders();
586 headers.put("Accept", MediaType.APPLICATION_JSON);
587 String url = getUrl();
589 logMessage(EventType.OUT, CommInfrastructure.REST, url, null);
592 return handleResponse(outcome, url,
593 callback -> getClient().delete(callback, getPath(), headers));
601 @Path("/" + BASE_URI)
602 @Produces(MEDIA_TYPE_APPLICATION_JSON)
603 @Consumes(value = {MEDIA_TYPE_APPLICATION_JSON})
604 public static class Server {
607 * Generates a response to a GET.
609 * @return resulting response
613 public Response getRequest() {
617 return Response.status(Status.BAD_REQUEST).build();
620 return Response.status(Status.OK).entity(new MyResponse()).build();
625 * Generates a response to a POST.
627 * @param request incoming request
628 * @return resulting response
632 public Response postRequest(MyRequest request) {
636 return Response.status(Status.BAD_REQUEST).build();
639 return Response.status(Status.OK).entity(new MyResponse()).build();
644 * Generates a response to a PUT.
646 * @param request incoming request
647 * @return resulting response
651 public Response putRequest(MyRequest request) {
655 return Response.status(Status.BAD_REQUEST).build();
658 return Response.status(Status.OK).entity(new MyResponse()).build();
663 * Generates a response to a DELETE.
665 * @return resulting response
669 public Response deleteRequest() {
673 return Response.status(Status.BAD_REQUEST).build();
676 return Response.status(Status.OK).entity(new MyResponse()).build();