2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2020-2021 AT&T Intellectual Property. All rights reserved.
6 * Modifications Copyright (C) 2023 Nordix Foundation.
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.controlloop.actorserviceprovider.impl;
24 import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
25 import static org.assertj.core.api.Assertions.assertThatThrownBy;
26 import static org.junit.Assert.assertEquals;
27 import static org.junit.Assert.assertNotNull;
28 import static org.junit.Assert.assertNull;
29 import static org.junit.Assert.assertSame;
30 import static org.junit.Assert.assertTrue;
31 import static org.mockito.ArgumentMatchers.any;
32 import static org.mockito.Mockito.mock;
33 import static org.mockito.Mockito.when;
35 import jakarta.ws.rs.client.InvocationCallback;
36 import jakarta.ws.rs.core.Response;
37 import java.util.Collections;
38 import java.util.concurrent.CompletableFuture;
39 import java.util.concurrent.ForkJoinPool;
40 import java.util.concurrent.TimeUnit;
41 import org.junit.Before;
42 import org.junit.Test;
43 import org.junit.runner.RunWith;
44 import org.mockito.Mock;
45 import org.mockito.junit.MockitoJUnitRunner;
46 import org.mockito.stubbing.Answer;
47 import org.onap.policy.common.endpoints.http.client.HttpClient;
48 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
49 import org.onap.policy.controlloop.actorserviceprovider.OperationResult;
50 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
51 import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpConfig;
52 import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpPollingConfig;
55 * Tests HttpOperation when polling is enabled.
57 @RunWith(MockitoJUnitRunner.class)
58 public class HttpPollingOperationTest {
59 private static final String BASE_URI = "http://my-host:6969/base-uri/";
60 private static final String MY_PATH = "my-path";
61 private static final String FULL_PATH = BASE_URI + MY_PATH;
62 private static final int MAX_POLLS = 3;
63 private static final int POLL_WAIT_SEC = 20;
64 private static final String POLL_PATH = "my-poll-path";
65 private static final String MY_ACTOR = "my-actor";
66 private static final String MY_OPERATION = "my-operation";
67 private static final String MY_RESPONSE = "my-response";
68 private static final int RESPONSE_ACCEPT = 100;
69 private static final int RESPONSE_SUCCESS = 200;
70 private static final int RESPONSE_FAILURE = 500;
73 private HttpPollingConfig config;
75 private HttpClient client;
77 private Response rawResponse;
79 protected ControlLoopOperationParams params;
80 private String response;
81 private OperationOutcome outcome;
83 private HttpOperation<String> oper;
89 public void setUp() throws Exception {
90 when(client.getBaseUrl()).thenReturn(BASE_URI);
92 when(config.getClient()).thenReturn(client);
93 when(config.getMaxPolls()).thenReturn(MAX_POLLS);
94 when(config.getPollPath()).thenReturn(POLL_PATH);
95 when(config.getPollWaitSec()).thenReturn(POLL_WAIT_SEC);
97 response = MY_RESPONSE;
99 when(rawResponse.getStatus()).thenReturn(RESPONSE_SUCCESS);
100 when(rawResponse.readEntity(String.class)).thenReturn(response);
102 params = ControlLoopOperationParams.builder().actor(MY_ACTOR).operation(MY_OPERATION).build();
103 outcome = params.makeOutcome();
105 oper = new MyOper(params, config);
109 public void testConstructor_testGetWaitMsGet() {
110 assertEquals(MY_ACTOR, oper.getActorName());
111 assertEquals(MY_OPERATION, oper.getName());
112 assertSame(config, oper.getConfig());
113 assertEquals(1000 * POLL_WAIT_SEC, oper.getPollWaitMs());
117 public void testSetUsePollExceptions() {
118 // should be no exception
119 oper.setUsePolling();
121 // should throw an exception if we pass a plain HttpConfig
122 HttpConfig config2 = mock(HttpConfig.class);
124 assertThatIllegalStateException().isThrownBy(() -> new MyOper(params, config2).setUsePolling());
128 public void testPostProcess() throws Exception {
130 oper.generateSubRequestId(2);
131 CompletableFuture<OperationOutcome> future2 =
132 oper.postProcessResponse(outcome, FULL_PATH, rawResponse, response);
133 assertTrue(future2.isDone());
134 assertSame(outcome, future2.get());
135 assertEquals(OperationResult.SUCCESS, outcome.getResult());
136 assertNotNull(oper.getSubRequestId());
137 assertSame(response, outcome.getResponse());
140 oper.generateSubRequestId(2);
141 when(rawResponse.getStatus()).thenReturn(RESPONSE_FAILURE);
142 future2 = oper.postProcessResponse(outcome, FULL_PATH, rawResponse, response);
143 assertTrue(future2.isDone());
144 assertSame(outcome, future2.get());
145 assertEquals(OperationResult.FAILURE, outcome.getResult());
146 assertNotNull(oper.getSubRequestId());
147 assertSame(response, outcome.getResponse());
151 * Tests postProcess() when the poll is repeated a couple of times.
154 public void testPostProcessRepeated_testResetGetCount() throws Exception {
156 * Two accepts and then a success - should result in two polls.
158 when(rawResponse.getStatus()).thenReturn(RESPONSE_ACCEPT, RESPONSE_ACCEPT, RESPONSE_SUCCESS, RESPONSE_SUCCESS);
160 when(client.get(any(), any(), any())).thenAnswer(provideResponse(rawResponse));
162 // use a real executor
163 params = params.toBuilder().executor(ForkJoinPool.commonPool()).build();
165 oper = new MyOper(params, config) {
167 public long getPollWaitMs() {
172 CompletableFuture<OperationOutcome> future2 =
173 oper.postProcessResponse(outcome, FULL_PATH, rawResponse, response);
175 assertSame(outcome, future2.get(5, TimeUnit.SECONDS));
176 assertEquals(OperationResult.SUCCESS, outcome.getResult());
177 assertEquals(2, oper.getPollCount());
180 * repeat - this time, the "poll" count will be exhausted, so it should fail
182 when(rawResponse.getStatus()).thenReturn(RESPONSE_ACCEPT);
184 future2 = oper.postProcessResponse(outcome, FULL_PATH, rawResponse, response);
186 assertSame(outcome, future2.get(5, TimeUnit.SECONDS));
187 assertEquals(OperationResult.FAILURE_TIMEOUT, outcome.getResult());
188 assertEquals(MAX_POLLS + 1, oper.getPollCount());
190 oper.resetPollCount();
191 assertEquals(0, oper.getPollCount());
192 assertNull(oper.getSubRequestId());
196 public void testDetmStatus() {
197 // make an operation that does NOT override detmStatus()
198 oper = new HttpOperation<String>(params, config, String.class, Collections.emptyList()) {};
200 assertThatThrownBy(() -> oper.detmStatus(rawResponse, response))
201 .isInstanceOf(UnsupportedOperationException.class);
205 * Provides a response to an asynchronous HttpClient call.
207 * @param response response to be provided to the call
208 * @return a function that provides the response to the call
210 protected Answer<CompletableFuture<Response>> provideResponse(Response response) {
211 return provideResponse(response, 0);
215 * Provides a response to an asynchronous HttpClient call.
217 * @param response response to be provided to the call
218 * @param index index of the callback within the arguments
219 * @return a function that provides the response to the call
221 protected Answer<CompletableFuture<Response>> provideResponse(Response response, int index) {
223 InvocationCallback<Response> cb = args.getArgument(index);
224 cb.completed(response);
225 return CompletableFuture.completedFuture(response);
229 private static class MyOper extends HttpOperation<String> {
231 public MyOper(ControlLoopOperationParams params, HttpConfig config) {
232 super(params, config, String.class, Collections.emptyList());
238 protected Status detmStatus(Response rawResponse, String response) {
239 switch (rawResponse.getStatus()) {
240 case RESPONSE_ACCEPT:
241 return Status.STILL_WAITING;
242 case RESPONSE_SUCCESS:
243 return Status.SUCCESS;
245 return Status.FAILURE;
250 protected boolean isSuccess(Response rawResponse, String response) {