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.Mockito.spy;
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.Future;
43 import java.util.concurrent.TimeUnit;
44 import java.util.concurrent.TimeoutException;
45 import java.util.concurrent.atomic.AtomicReference;
46 import javax.ws.rs.Consumes;
47 import javax.ws.rs.DELETE;
48 import javax.ws.rs.GET;
49 import javax.ws.rs.POST;
50 import javax.ws.rs.PUT;
51 import javax.ws.rs.Path;
52 import javax.ws.rs.Produces;
53 import javax.ws.rs.client.Entity;
54 import javax.ws.rs.client.InvocationCallback;
55 import javax.ws.rs.core.MediaType;
56 import javax.ws.rs.core.Response;
57 import javax.ws.rs.core.Response.Status;
60 import org.junit.AfterClass;
61 import org.junit.Before;
62 import org.junit.BeforeClass;
63 import org.junit.Test;
64 import org.mockito.Mock;
65 import org.mockito.MockitoAnnotations;
66 import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
67 import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams;
68 import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams.TopicParamsBuilder;
69 import org.onap.policy.common.endpoints.http.client.HttpClient;
70 import org.onap.policy.common.endpoints.http.client.HttpClientFactoryInstance;
71 import org.onap.policy.common.endpoints.http.server.HttpServletServer;
72 import org.onap.policy.common.endpoints.http.server.HttpServletServerFactoryInstance;
73 import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties;
74 import org.onap.policy.common.endpoints.utils.NetLoggerUtil.EventType;
75 import org.onap.policy.common.gson.GsonMessageBodyHandler;
76 import org.onap.policy.common.utils.coder.CoderException;
77 import org.onap.policy.common.utils.network.NetworkUtil;
78 import org.onap.policy.controlloop.VirtualControlLoopEvent;
79 import org.onap.policy.controlloop.actorserviceprovider.Operation;
80 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
81 import org.onap.policy.controlloop.actorserviceprovider.Util;
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.HttpParams;
85 import org.onap.policy.controlloop.policy.PolicyResult;
87 public class HttpOperationTest {
89 private static final IllegalStateException EXPECTED_EXCEPTION = new IllegalStateException("expected exception");
90 private static final String ACTOR = "my-actor";
91 private static final String OPERATION = "my-name";
92 private static final String HTTP_CLIENT = "my-client";
93 private static final String HTTP_NO_SERVER = "my-http-no-server-client";
94 private static final String MEDIA_TYPE_APPLICATION_JSON = "application/json";
95 private static final String BASE_URI = "oper";
96 private static final String PATH = "/my-path";
97 private static final String TEXT = "my-text";
98 private static final UUID REQ_ID = UUID.randomUUID();
101 * {@code True} if the server should reject the request, {@code false} otherwise.
103 private static boolean rejectRequest;
105 // call counts of each method type in the server
106 private static int nget;
107 private static int npost;
108 private static int nput;
109 private static int ndelete;
112 private HttpClient client;
115 private Response response;
117 private VirtualControlLoopEvent event;
118 private ControlLoopEventContext context;
119 private ControlLoopOperationParams params;
120 private OperationOutcome outcome;
121 private AtomicReference<InvocationCallback<Response>> callback;
122 private Future<Response> future;
123 private HttpOperator operator;
124 private MyGetOperation<String> oper;
127 * Starts the simulator.
130 public static void setUpBeforeClass() throws Exception {
132 int port = NetworkUtil.allocPort();
135 * Start the simulator. Must use "Properties" to configure it, otherwise the
136 * server will use the wrong serialization provider.
138 Properties svrprops = getServerProperties("my-server", port);
139 HttpServletServerFactoryInstance.getServerFactory().build(svrprops).forEach(HttpServletServer::start);
141 if (!NetworkUtil.isTcpPortOpen("localhost", port, 100, 100)) {
142 HttpServletServerFactoryInstance.getServerFactory().destroy();
143 throw new IllegalStateException("server is not running");
147 * Start the clients, one to the server, and one to a non-existent server.
149 TopicParamsBuilder builder = BusTopicParams.builder().managed(true).hostname("localhost").basePath(BASE_URI)
150 .serializationProvider(GsonMessageBodyHandler.class.getName());
152 HttpClientFactoryInstance.getClientFactory().build(builder.clientName(HTTP_CLIENT).port(port).build());
154 HttpClientFactoryInstance.getClientFactory()
155 .build(builder.clientName(HTTP_NO_SERVER).port(NetworkUtil.allocPort()).build());
159 * Destroys the Http factories and stops the appender.
162 public static void tearDownAfterClass() {
163 HttpClientFactoryInstance.getClientFactory().destroy();
164 HttpServletServerFactoryInstance.getServerFactory().destroy();
168 * Initializes fields, including {@link #oper}, and resets the static fields used by
172 public void setUp() {
173 MockitoAnnotations.initMocks(this);
175 rejectRequest = false;
181 when(response.readEntity(String.class)).thenReturn(TEXT);
182 when(response.getStatus()).thenReturn(200);
184 event = new VirtualControlLoopEvent();
185 event.setRequestId(REQ_ID);
187 context = new ControlLoopEventContext(event);
188 params = ControlLoopOperationParams.builder().actor(ACTOR).operation(OPERATION).context(context).build();
190 outcome = params.makeOutcome();
192 callback = new AtomicReference<>();
193 future = new CompletableFuture<>();
195 operator = new HttpOperator(ACTOR, OPERATION) {
197 public Operation buildOperation(ControlLoopOperationParams params) {
202 public HttpClient getClient() {
207 initOper(operator, HTTP_CLIENT);
209 oper = new MyGetOperation<>(String.class);
213 public void testHttpOperator() {
214 assertEquals(ACTOR, oper.getActorName());
215 assertEquals(OPERATION, oper.getName());
216 assertEquals(ACTOR + "." + OPERATION, oper.getFullName());
220 public void testMakeHeaders() {
221 assertEquals(Collections.emptyMap(), oper.makeHeaders());
225 public void testMakePath() {
226 assertEquals(PATH, oper.makePath());
230 public void testMakeUrl() {
232 client = HttpClientFactoryInstance.getClientFactory().get(HTTP_CLIENT);
234 assertThat(oper.makeUrl()).endsWith("/" + BASE_URI + PATH);
238 public void testDoConfigureMapOfStringObject_testGetClient_testGetPath_testGetTimeoutMs() {
240 // use value from operator
241 assertEquals(1000L, oper.getTimeoutMs(null));
242 assertEquals(1000L, oper.getTimeoutMs(0));
244 // should use given value
245 assertEquals(20 * 1000L, oper.getTimeoutMs(20));
247 // indicate we have a timeout value
248 operator = spy(operator);
249 when(operator.getTimeoutMs()).thenReturn(30L);
251 oper = new MyGetOperation<String>(String.class);
253 // should use default
254 assertEquals(30L, oper.getTimeoutMs(null));
255 assertEquals(30L, oper.getTimeoutMs(0));
257 // should use given value
258 assertEquals(40 * 1000L, oper.getTimeoutMs(40));
262 * Tests handleResponse() when it completes.
265 public void testHandleResponseComplete() throws Exception {
266 CompletableFuture<OperationOutcome> future2 = oper.handleResponse(outcome, PATH, cb -> {
271 assertFalse(future2.isDone());
272 assertNotNull(callback.get());
273 callback.get().completed(response);
275 assertSame(outcome, future2.get(5, TimeUnit.SECONDS));
277 assertEquals(PolicyResult.SUCCESS, outcome.getResult());
281 * Tests handleResponse() when it fails.
284 public void testHandleResponseFailed() throws Exception {
285 CompletableFuture<OperationOutcome> future2 = oper.handleResponse(outcome, PATH, cb -> {
290 assertFalse(future2.isDone());
291 assertNotNull(callback.get());
292 callback.get().failed(EXPECTED_EXCEPTION);
294 assertThatThrownBy(() -> future2.get(5, TimeUnit.SECONDS)).hasCause(EXPECTED_EXCEPTION);
296 // future and future2 may be completed in parallel so we must wait again
297 assertThatThrownBy(() -> future.get(5, TimeUnit.SECONDS)).isInstanceOf(CancellationException.class);
298 assertTrue(future.isCancelled());
302 * Tests processResponse() when it's a success and the response type is a String.
305 public void testProcessResponseSuccessString() throws Exception {
306 CompletableFuture<OperationOutcome> result = oper.processResponse(outcome, PATH, response);
307 assertTrue(result.isDone());
308 assertSame(outcome, result.get());
309 assertEquals(PolicyResult.SUCCESS, outcome.getResult());
313 * Tests processResponse() when it's a failure.
316 public void testProcessResponseFailure() throws Exception {
317 when(response.getStatus()).thenReturn(555);
318 CompletableFuture<OperationOutcome> result = oper.processResponse(outcome, PATH, response);
319 assertTrue(result.isDone());
320 assertSame(outcome, result.get());
321 assertEquals(PolicyResult.FAILURE, outcome.getResult());
325 * Tests processResponse() when the decoder succeeds.
328 public void testProcessResponseDecodeOk() throws Exception {
329 when(response.readEntity(String.class)).thenReturn("10");
331 MyGetOperation<Integer> oper2 = new MyGetOperation<>(Integer.class);
333 CompletableFuture<OperationOutcome> result = oper2.processResponse(outcome, PATH, response);
334 assertTrue(result.isDone());
335 assertSame(outcome, result.get());
336 assertEquals(PolicyResult.SUCCESS, outcome.getResult());
340 * Tests processResponse() when the decoder throws an exception.
343 public void testProcessResponseDecodeExcept() throws CoderException {
344 MyGetOperation<Integer> oper2 = new MyGetOperation<>(Integer.class);
346 assertThatIllegalArgumentException().isThrownBy(() -> oper2.processResponse(outcome, PATH, response));
350 public void testPostProcessResponse() {
351 assertThatCode(() -> oper.postProcessResponse(outcome, PATH, null, null)).doesNotThrowAnyException();
355 public void testIsSuccess() {
356 when(response.getStatus()).thenReturn(200);
357 assertTrue(oper.isSuccess(response, null));
359 when(response.getStatus()).thenReturn(555);
360 assertFalse(oper.isSuccess(response, null));
367 public void testGet() throws Exception {
369 client = HttpClientFactoryInstance.getClientFactory().get(HTTP_CLIENT);
371 MyGetOperation<MyResponse> oper2 = new MyGetOperation<>(MyResponse.class);
373 OperationOutcome outcome = runOperation(oper2);
374 assertNotNull(outcome);
375 assertEquals(1, nget);
376 assertEquals(PolicyResult.SUCCESS, outcome.getResult());
383 public void testDelete() throws Exception {
385 client = HttpClientFactoryInstance.getClientFactory().get(HTTP_CLIENT);
387 MyDeleteOperation oper2 = new MyDeleteOperation();
389 OperationOutcome outcome = runOperation(oper2);
390 assertNotNull(outcome);
391 assertEquals(1, ndelete);
392 assertEquals(PolicyResult.SUCCESS, outcome.getResult());
399 public void testPost() throws Exception {
401 client = HttpClientFactoryInstance.getClientFactory().get(HTTP_CLIENT);
403 MyPostOperation oper2 = new MyPostOperation();
405 OperationOutcome outcome = runOperation(oper2);
406 assertNotNull(outcome);
407 assertEquals(1, npost);
408 assertEquals(PolicyResult.SUCCESS, outcome.getResult());
415 public void testPut() throws Exception {
417 client = HttpClientFactoryInstance.getClientFactory().get(HTTP_CLIENT);
419 MyPutOperation oper2 = new MyPutOperation();
421 OperationOutcome outcome = runOperation(oper2);
422 assertNotNull(outcome);
423 assertEquals(1, nput);
424 assertEquals(PolicyResult.SUCCESS, outcome.getResult());
428 public void testMakeDecoder() {
429 assertNotNull(oper.makeCoder());
433 * Gets server properties.
435 * @param name server name
436 * @param port server port
437 * @return server properties
439 private static Properties getServerProperties(String name, int port) {
440 final Properties props = new Properties();
441 props.setProperty(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES, name);
443 final String svcpfx = PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + name;
445 props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_REST_CLASSES_SUFFIX, Server.class.getName());
446 props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_HOST_SUFFIX, "localhost");
447 props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_PORT_SUFFIX, String.valueOf(port));
448 props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_MANAGED_SUFFIX, "true");
449 props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SWAGGER_SUFFIX, "false");
451 props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERIALIZATION_PROVIDER,
452 GsonMessageBodyHandler.class.getName());
457 * Initializes the given operator.
459 * @param operator operator to be initialized
460 * @param clientName name of the client which it should use
462 private void initOper(HttpOperator operator, String clientName) {
465 HttpParams params = HttpParams.builder().clientName(clientName).path(PATH).timeoutSec(1).build();
466 Map<String, Object> mapParams = Util.translateToMap(OPERATION, params);
467 operator.configure(mapParams);
472 * Runs the operation.
474 * @param operator operator on which to start the operation
475 * @return the outcome of the operation, or {@code null} if it does not complete in
478 private <T> OperationOutcome runOperation(HttpOperation<T> operator)
479 throws InterruptedException, ExecutionException, TimeoutException {
481 CompletableFuture<OperationOutcome> future = operator.start();
483 return future.get(5, TimeUnit.SECONDS);
488 public static class MyRequest {
489 private String input = "some input";
494 public static class MyResponse {
495 private String output = "some output";
498 private class MyGetOperation<T> extends HttpOperation<T> {
499 public MyGetOperation(Class<T> responseClass) {
500 super(HttpOperationTest.this.params, HttpOperationTest.this.operator, responseClass);
504 protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) {
505 Map<String, Object> headers = makeHeaders();
507 headers.put("Accept", MediaType.APPLICATION_JSON);
508 String url = makeUrl();
510 logMessage(EventType.OUT, CommInfrastructure.REST, url, null);
513 return handleResponse(outcome, url,
514 callback -> operator.getClient().get(callback, makePath(), headers));
519 private class MyPostOperation extends HttpOperation<MyResponse> {
520 public MyPostOperation() {
521 super(HttpOperationTest.this.params, HttpOperationTest.this.operator, MyResponse.class);
525 protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) {
527 MyRequest request = new MyRequest();
529 Entity<MyRequest> entity = Entity.entity(request, MediaType.APPLICATION_JSON);
531 Map<String, Object> headers = makeHeaders();
533 headers.put("Accept", MediaType.APPLICATION_JSON);
534 String url = makeUrl();
536 logMessage(EventType.OUT, CommInfrastructure.REST, url, request);
539 return handleResponse(outcome, url,
540 callback -> operator.getClient().post(callback, makePath(), entity, headers));
545 private class MyPutOperation extends HttpOperation<MyResponse> {
546 public MyPutOperation() {
547 super(HttpOperationTest.this.params, HttpOperationTest.this.operator, MyResponse.class);
551 protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) {
553 MyRequest request = new MyRequest();
555 Entity<MyRequest> entity = Entity.entity(request, MediaType.APPLICATION_JSON);
557 Map<String, Object> headers = makeHeaders();
559 headers.put("Accept", MediaType.APPLICATION_JSON);
560 String url = makeUrl();
562 logMessage(EventType.OUT, CommInfrastructure.REST, url, request);
565 return handleResponse(outcome, url,
566 callback -> operator.getClient().put(callback, makePath(), entity, headers));
571 private class MyDeleteOperation extends HttpOperation<String> {
572 public MyDeleteOperation() {
573 super(HttpOperationTest.this.params, HttpOperationTest.this.operator, String.class);
577 protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) {
578 Map<String, Object> headers = makeHeaders();
580 headers.put("Accept", MediaType.APPLICATION_JSON);
581 String url = makeUrl();
583 logMessage(EventType.OUT, CommInfrastructure.REST, url, null);
586 return handleResponse(outcome, url,
587 callback -> operator.getClient().delete(callback, makePath(), headers));
595 @Path("/" + BASE_URI)
596 @Produces(MEDIA_TYPE_APPLICATION_JSON)
597 @Consumes(value = {MEDIA_TYPE_APPLICATION_JSON})
598 public static class Server {
601 * Generates a response to a GET.
603 * @return resulting response
607 public Response getRequest() {
611 return Response.status(Status.BAD_REQUEST).build();
614 return Response.status(Status.OK).entity(new MyResponse()).build();
619 * Generates a response to a POST.
621 * @param request incoming request
622 * @return resulting response
626 public Response postRequest(MyRequest request) {
630 return Response.status(Status.BAD_REQUEST).build();
633 return Response.status(Status.OK).entity(new MyResponse()).build();
638 * Generates a response to a PUT.
640 * @param request incoming request
641 * @return resulting response
645 public Response putRequest(MyRequest request) {
649 return Response.status(Status.BAD_REQUEST).build();
652 return Response.status(Status.OK).entity(new MyResponse()).build();
657 * Generates a response to a DELETE.
659 * @return resulting response
663 public Response deleteRequest() {
667 return Response.status(Status.BAD_REQUEST).build();
670 return Response.status(Status.OK).entity(new MyResponse()).build();