0850bb3585c59726b9a58f782e1ee9ac46ff115b
[policy/models.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2020-2021 AT&T Intellectual Property. All rights reserved.
6  * Modifications Copyright (C) 2023, 2024 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
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
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=========================================================
20  */
21
22 package org.onap.policy.controlloop.actorserviceprovider.impl;
23
24 import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
25 import static org.assertj.core.api.Assertions.assertThatThrownBy;
26 import static org.junit.jupiter.api.Assertions.assertEquals;
27 import static org.junit.jupiter.api.Assertions.assertNotNull;
28 import static org.junit.jupiter.api.Assertions.assertNull;
29 import static org.junit.jupiter.api.Assertions.assertSame;
30 import static org.junit.jupiter.api.Assertions.assertTrue;
31 import static org.mockito.ArgumentMatchers.any;
32 import static org.mockito.Mockito.mock;
33 import static org.mockito.Mockito.when;
34
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.jupiter.api.BeforeEach;
42 import org.junit.jupiter.api.Test;
43 import org.junit.jupiter.api.TestInstance;
44 import org.junit.jupiter.api.extension.ExtendWith;
45 import org.mockito.Mock;
46 import org.mockito.Mockito;
47 import org.mockito.junit.jupiter.MockitoExtension;
48 import org.mockito.stubbing.Answer;
49 import org.onap.policy.common.endpoints.http.client.HttpClient;
50 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
51 import org.onap.policy.controlloop.actorserviceprovider.OperationResult;
52 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
53 import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpConfig;
54 import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpPollingConfig;
55
56 /**
57  * Tests HttpOperation when polling is enabled.
58  */
59 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
60 @ExtendWith(MockitoExtension.class)
61 class HttpPollingOperationTest {
62     private static final String BASE_URI = "http://my-host:6969/base-uri/";
63     private static final String MY_PATH = "my-path";
64     private static final String FULL_PATH = BASE_URI + MY_PATH;
65     private static final int MAX_POLLS = 3;
66     private static final int POLL_WAIT_SEC = 20;
67     private static final String POLL_PATH = "my-poll-path";
68     private static final String MY_ACTOR = "my-actor";
69     private static final String MY_OPERATION = "my-operation";
70     private static final String MY_RESPONSE = "my-response";
71     private static final int RESPONSE_ACCEPT = 100;
72     private static final int RESPONSE_SUCCESS = 200;
73     private static final int RESPONSE_FAILURE = 500;
74
75     @Mock
76     private HttpPollingConfig config;
77     @Mock
78     private HttpClient client;
79     @Mock
80     private Response rawResponse;
81
82     protected ControlLoopOperationParams params;
83     private String response;
84     private OperationOutcome outcome;
85
86     private HttpOperation<String> oper;
87
88     /**
89      * Sets up.
90      */
91     @BeforeEach
92     void setUp() throws Exception {
93         Mockito.lenient().when(client.getBaseUrl()).thenReturn(BASE_URI);
94
95         Mockito.lenient().when(config.getClient()).thenReturn(client);
96         Mockito.lenient().when(config.getMaxPolls()).thenReturn(MAX_POLLS);
97         Mockito.lenient().when(config.getPollPath()).thenReturn(POLL_PATH);
98         Mockito.lenient().when(config.getPollWaitSec()).thenReturn(POLL_WAIT_SEC);
99
100         response = MY_RESPONSE;
101
102         Mockito.lenient().when(rawResponse.getStatus()).thenReturn(RESPONSE_SUCCESS);
103         Mockito.lenient().when(rawResponse.readEntity(String.class)).thenReturn(response);
104
105         params = ControlLoopOperationParams.builder().actor(MY_ACTOR).operation(MY_OPERATION).build();
106         outcome = params.makeOutcome();
107
108         oper = new MyOper(params, config);
109     }
110
111     @Test
112     void testConstructor_testGetWaitMsGet() {
113         assertEquals(MY_ACTOR, oper.getActorName());
114         assertEquals(MY_OPERATION, oper.getName());
115         assertSame(config, oper.getConfig());
116         assertEquals(1000 * POLL_WAIT_SEC, oper.getPollWaitMs());
117     }
118
119     @Test
120     void testSetUsePollExceptions() {
121         // should be no exception
122         oper.setUsePolling();
123
124         // should throw an exception if we pass a plain HttpConfig
125         HttpConfig config2 = mock(HttpConfig.class);
126
127         assertThatIllegalStateException().isThrownBy(() -> new MyOper(params, config2).setUsePolling());
128     }
129
130     @Test
131     void testPostProcess() throws Exception {
132         // completed
133         oper.generateSubRequestId(2);
134         CompletableFuture<OperationOutcome> future2 =
135                         oper.postProcessResponse(outcome, FULL_PATH, rawResponse, response);
136         assertTrue(future2.isDone());
137         assertSame(outcome, future2.get());
138         assertEquals(OperationResult.SUCCESS, outcome.getResult());
139         assertNotNull(oper.getSubRequestId());
140         assertSame(response, outcome.getResponse());
141
142         // failed
143         oper.generateSubRequestId(2);
144         when(rawResponse.getStatus()).thenReturn(RESPONSE_FAILURE);
145         future2 = oper.postProcessResponse(outcome, FULL_PATH, rawResponse, response);
146         assertTrue(future2.isDone());
147         assertSame(outcome, future2.get());
148         assertEquals(OperationResult.FAILURE, outcome.getResult());
149         assertNotNull(oper.getSubRequestId());
150         assertSame(response, outcome.getResponse());
151     }
152
153     /**
154      * Tests postProcess() when the poll is repeated a couple of times.
155      */
156     @Test
157     void testPostProcessRepeated_testResetGetCount() throws Exception {
158         /*
159          * Two accepts and then a success - should result in two polls.
160          */
161         when(rawResponse.getStatus()).thenReturn(RESPONSE_ACCEPT, RESPONSE_ACCEPT, RESPONSE_SUCCESS, RESPONSE_SUCCESS);
162
163         when(client.get(any(), any(), any())).thenAnswer(provideResponse(rawResponse));
164
165         // use a real executor
166         params = params.toBuilder().executor(ForkJoinPool.commonPool()).build();
167
168         oper = new MyOper(params, config) {
169             @Override
170             public long getPollWaitMs() {
171                 return 1;
172             }
173         };
174
175         CompletableFuture<OperationOutcome> future2 =
176                         oper.postProcessResponse(outcome, FULL_PATH, rawResponse, response);
177
178         assertSame(outcome, future2.get(5, TimeUnit.SECONDS));
179         assertEquals(OperationResult.SUCCESS, outcome.getResult());
180         assertEquals(2, oper.getPollCount());
181
182         /*
183          * repeat - this time, the "poll" count will be exhausted, so it should fail
184          */
185         when(rawResponse.getStatus()).thenReturn(RESPONSE_ACCEPT);
186
187         future2 = oper.postProcessResponse(outcome, FULL_PATH, rawResponse, response);
188
189         assertSame(outcome, future2.get(5, TimeUnit.SECONDS));
190         assertEquals(OperationResult.FAILURE_TIMEOUT, outcome.getResult());
191         assertEquals(MAX_POLLS + 1, oper.getPollCount());
192
193         oper.resetPollCount();
194         assertEquals(0, oper.getPollCount());
195         assertNull(oper.getSubRequestId());
196     }
197
198     @Test
199     void testDetmStatus() {
200         // make an operation that does NOT override detmStatus()
201         oper = new HttpOperation<String>(params, config, String.class, Collections.emptyList()) {};
202
203         assertThatThrownBy(() -> oper.detmStatus(rawResponse, response))
204                         .isInstanceOf(UnsupportedOperationException.class);
205     }
206
207     /**
208      * Provides a response to an asynchronous HttpClient call.
209      *
210      * @param response response to be provided to the call
211      * @return a function that provides the response to the call
212      */
213     protected Answer<CompletableFuture<Response>> provideResponse(Response response) {
214         return provideResponse(response, 0);
215     }
216
217     /**
218      * Provides a response to an asynchronous HttpClient call.
219      *
220      * @param response response to be provided to the call
221      * @param index index of the callback within the arguments
222      * @return a function that provides the response to the call
223      */
224     protected Answer<CompletableFuture<Response>> provideResponse(Response response, int index) {
225         return args -> {
226             InvocationCallback<Response> cb = args.getArgument(index);
227             cb.completed(response);
228             return CompletableFuture.completedFuture(response);
229         };
230     }
231
232     private static class MyOper extends HttpOperation<String> {
233
234         MyOper(ControlLoopOperationParams params, HttpConfig config) {
235             super(params, config, String.class, Collections.emptyList());
236
237             setUsePolling();
238         }
239
240         @Override
241         protected Status detmStatus(Response rawResponse, String response) {
242             switch (rawResponse.getStatus()) {
243                 case RESPONSE_ACCEPT:
244                     return Status.STILL_WAITING;
245                 case RESPONSE_SUCCESS:
246                     return Status.SUCCESS;
247                 default:
248                     return Status.FAILURE;
249             }
250         }
251
252         @Override
253         protected boolean isSuccess(Response rawResponse, String response) {
254             return true;
255         }
256     }
257 }