Cannot parse finishTime in SO responses
[policy/models.git] / models-interactions / model-actors / actor.so / src / test / java / org / onap / policy / controlloop / actor / so / SoOperationTest.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
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
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
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=========================================================
19  */
20
21 package org.onap.policy.controlloop.actor.so;
22
23 import static org.assertj.core.api.Assertions.assertThatCode;
24 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
25 import static org.junit.Assert.assertEquals;
26 import static org.junit.Assert.assertFalse;
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;
34
35 import java.time.LocalDateTime;
36 import java.time.Month;
37 import java.util.Collections;
38 import java.util.List;
39 import java.util.concurrent.CompletableFuture;
40 import java.util.concurrent.ForkJoinPool;
41 import java.util.concurrent.TimeUnit;
42 import java.util.function.Consumer;
43 import java.util.function.Supplier;
44 import org.junit.Before;
45 import org.junit.Test;
46 import org.onap.aai.domain.yang.CloudRegion;
47 import org.onap.aai.domain.yang.GenericVnf;
48 import org.onap.aai.domain.yang.ServiceInstance;
49 import org.onap.aai.domain.yang.Tenant;
50 import org.onap.policy.aai.AaiCqResponse;
51 import org.onap.policy.common.utils.coder.Coder;
52 import org.onap.policy.common.utils.coder.CoderException;
53 import org.onap.policy.controlloop.ControlLoopOperation;
54 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
55 import org.onap.policy.controlloop.policy.PolicyResult;
56 import org.onap.policy.so.SoModelInfo;
57 import org.onap.policy.so.SoRequest;
58 import org.onap.policy.so.SoRequestInfo;
59 import org.onap.policy.so.SoRequestReferences;
60 import org.onap.policy.so.SoRequestStatus;
61 import org.onap.policy.so.SoResponse;
62
63 public class SoOperationTest extends BasicSoOperation {
64
65     private static final String VF_COUNT_KEY = SoConstants.VF_COUNT_PREFIX
66                     + "[my-model-customization-id][my-model-invariant-id][my-model-version-id]";
67     private SoOperation oper;
68
69     /**
70      * Sets up.
71      */
72     @Before
73     public void setUp() throws Exception {
74         super.setUp();
75
76         initConfig();
77
78         oper = new SoOperation(params, config) {};
79     }
80
81     @Test
82     public void testConstructor_testGetWaitMsGet() {
83         assertEquals(DEFAULT_ACTOR, oper.getActorName());
84         assertEquals(DEFAULT_OPERATION, oper.getName());
85         assertSame(config, oper.getConfig());
86         assertEquals(1000 * WAIT_SEC_GETS, oper.getWaitMsGet());
87
88         // check when Target is null
89         params = params.toBuilder().target(null).build();
90         assertThatIllegalArgumentException().isThrownBy(() -> new SoOperation(params, config) {})
91                         .withMessageContaining("Target information");
92     }
93
94     @Test
95     public void testValidateTarget() {
96         // check when various fields are null
97         verifyNotNull("modelCustomizationId", target::getModelCustomizationId, target::setModelCustomizationId);
98         verifyNotNull("modelInvariantId", target::getModelInvariantId, target::setModelInvariantId);
99         verifyNotNull("modelVersionId", target::getModelVersionId, target::setModelVersionId);
100
101         // verify it's still valid
102         assertThatCode(() -> new VfModuleCreate(params, config)).doesNotThrowAnyException();
103     }
104
105     private void verifyNotNull(String expectedText, Supplier<String> getter, Consumer<String> setter) {
106         String originalValue = getter.get();
107
108         // try with null
109         setter.accept(null);
110         assertThatIllegalArgumentException().isThrownBy(() -> new VfModuleCreate(params, config))
111                         .withMessageContaining(expectedText);
112
113         setter.accept(originalValue);
114     }
115
116     @Test
117     public void testStartPreprocessorAsync() {
118         assertNotNull(oper.startPreprocessorAsync());
119     }
120
121     @Test
122     public void testObtainVfCount_testGetVfCount_testSetVfCount() throws Exception {
123         // insert CQ data so it's there for the check
124         context.setProperty(AaiCqResponse.CONTEXT_KEY, makeCqResponse());
125
126         // shouldn't actually need to do anything
127         assertNull(oper.obtainVfCount());
128
129         // verify that the count was stored
130         Integer vfcount = context.getProperty(VF_COUNT_KEY);
131         assertEquals(VF_COUNT, vfcount);
132         assertEquals(VF_COUNT.intValue(), oper.getVfCount());
133
134         // change the count and then verify that it isn't overwritten by another call
135         oper.setVfCount(VF_COUNT + 1);
136
137         assertNull(oper.obtainVfCount());
138         vfcount = context.getProperty(VF_COUNT_KEY);
139         assertEquals(VF_COUNT + 1, vfcount.intValue());
140         assertEquals(VF_COUNT + 1, oper.getVfCount());
141     }
142
143     /**
144      * Tests obtainVfCount() when it actually has to query.
145      */
146     @Test
147     public void testObtainVfCountQuery() throws Exception {
148         CompletableFuture<OperationOutcome> future2 = oper.obtainVfCount();
149         assertNotNull(future2);
150         assertTrue(executor.runAll(100));
151
152         // not done yet
153         assertFalse(future2.isDone());
154
155         provideCqResponse(makeCqResponse());
156
157         assertTrue(executor.runAll(100));
158         assertTrue(future2.isDone());
159         assertEquals(PolicyResult.SUCCESS, future2.get().getResult());
160
161         // verify that the count was stored
162         Integer vfcount = context.getProperty(VF_COUNT_KEY);
163         assertEquals(VF_COUNT, vfcount);
164
165         // repeat - shouldn't need to do anything now
166         assertNull(oper.obtainVfCount());
167     }
168
169     @Test
170     public void testPostProcess() throws Exception {
171         // completed
172         oper.generateSubRequestId(2);
173         assertNull(oper.getSubRequestId());
174         CompletableFuture<OperationOutcome> future2 = oper.postProcessResponse(outcome, PATH, rawResponse, response);
175         assertTrue(future2.isDone());
176         assertSame(outcome, future2.get());
177         assertEquals(PolicyResult.SUCCESS, outcome.getResult());
178         assertNotNull(oper.getSubRequestId());
179
180         // failed
181         oper.generateSubRequestId(2);
182         assertNull(oper.getSubRequestId());
183         response.getRequest().getRequestStatus().setRequestState(SoOperation.FAILED);
184         future2 = oper.postProcessResponse(outcome, PATH, rawResponse, response);
185         assertTrue(future2.isDone());
186         assertSame(outcome, future2.get());
187         assertEquals(PolicyResult.FAILURE, outcome.getResult());
188         assertNotNull(oper.getSubRequestId());
189
190         // no request id in the response
191         oper.generateSubRequestId(2);
192         assertNull(oper.getSubRequestId());
193         response.getRequestReferences().setRequestId(null);
194         response.getRequest().getRequestStatus().setRequestState("unknown");
195         assertThatIllegalArgumentException()
196                         .isThrownBy(() -> oper.postProcessResponse(outcome, PATH, rawResponse, response))
197                         .withMessage("missing request ID in response");
198         response.getRequestReferences().setRequestId(REQ_ID.toString());
199
200         // status = 500
201         when(rawResponse.getStatus()).thenReturn(500);
202
203         // null request reference
204         SoRequestReferences ref = response.getRequestReferences();
205         response.setRequestReferences(null);
206         assertThatIllegalArgumentException()
207                         .isThrownBy(() -> oper.postProcessResponse(outcome, PATH, rawResponse, response))
208                         .withMessage("missing request ID in response");
209         response.setRequestReferences(ref);
210     }
211
212     /**
213      * Tests postProcess() when the "get" is repeated a couple of times.
214      */
215     @Test
216     public void testPostProcessRepeated_testResetGetCount() throws Exception {
217         /*
218          * Two failures and then a success - should result in two "get" calls.
219          *
220          * Note: getStatus() is invoked twice during each call, so have to double up the
221          * return values.
222          */
223         when(rawResponse.getStatus()).thenReturn(500, 500, 500, 500, 200, 200);
224
225         when(client.get(any(), any(), any())).thenAnswer(provideResponse(rawResponse));
226
227         // use a real executor
228         params = params.toBuilder().executor(ForkJoinPool.commonPool()).build();
229
230         oper = new SoOperation(params, config) {
231             @Override
232             public long getWaitMsGet() {
233                 return 1;
234             }
235         };
236
237         CompletableFuture<OperationOutcome> future2 = oper.postProcessResponse(outcome, PATH, rawResponse, response);
238
239         assertSame(outcome, future2.get(5, TimeUnit.SECONDS));
240         assertEquals(PolicyResult.SUCCESS, outcome.getResult());
241         assertEquals(2, oper.getGetCount());
242
243         /*
244          * repeat - this time, the "get" operations will be exhausted, so it should fail
245          */
246         when(rawResponse.getStatus()).thenReturn(500);
247
248         future2 = oper.postProcessResponse(outcome, PATH, rawResponse, response);
249
250         assertSame(outcome, future2.get(5, TimeUnit.SECONDS));
251         assertEquals(PolicyResult.FAILURE_TIMEOUT, outcome.getResult());
252         assertEquals(MAX_GETS + 1, oper.getGetCount());
253
254         oper.resetGetCount();
255         assertEquals(0, oper.getGetCount());
256         assertNull(oper.getSubRequestId());
257     }
258
259     @Test
260     public void testGetRequestState() {
261         SoResponse resp = new SoResponse();
262         assertNull(oper.getRequestState(resp));
263
264         SoRequest req = new SoRequest();
265         resp.setRequest(req);
266         assertNull(oper.getRequestState(resp));
267
268         SoRequestStatus status = new SoRequestStatus();
269         req.setRequestStatus(status);
270         assertNull(oper.getRequestState(resp));
271
272         status.setRequestState("my-state");
273         assertEquals("my-state", oper.getRequestState(resp));
274     }
275
276     @Test
277     public void testIsSuccess() {
278         // always true
279
280         assertTrue(oper.isSuccess(rawResponse, response));
281
282         when(rawResponse.getStatus()).thenReturn(500);
283         assertTrue(oper.isSuccess(rawResponse, response));
284     }
285
286     @Test
287     public void testSetOutcome() {
288         // success case
289         when(rawResponse.getStatus()).thenReturn(200);
290         assertSame(outcome, oper.setOutcome(outcome, PolicyResult.SUCCESS, rawResponse, response));
291
292         assertEquals(PolicyResult.SUCCESS, outcome.getResult());
293         assertEquals("200 " + ControlLoopOperation.SUCCESS_MSG, outcome.getMessage());
294
295         // failure case
296         when(rawResponse.getStatus()).thenReturn(500);
297         assertSame(outcome, oper.setOutcome(outcome, PolicyResult.FAILURE, rawResponse, response));
298
299         assertEquals(PolicyResult.FAILURE, outcome.getResult());
300         assertEquals("500 " + ControlLoopOperation.FAILED_MSG, outcome.getMessage());
301     }
302
303     @Test
304     public void testPrepareSoModelInfo() throws CoderException {
305         verifyMissingModelInfo(target::getModelCustomizationId, target::setModelCustomizationId);
306         verifyMissingModelInfo(target::getModelInvariantId, target::setModelInvariantId);
307         verifyMissingModelInfo(target::getModelName, target::setModelName);
308         verifyMissingModelInfo(target::getModelVersion, target::setModelVersion);
309         verifyMissingModelInfo(target::getModelVersionId, target::setModelVersionId);
310
311         // valid data
312         SoModelInfo info = oper.prepareSoModelInfo();
313         verifyRequest("model.json", info);
314
315         // try with null target
316         params = params.toBuilder().target(null).build();
317         assertThatIllegalArgumentException().isThrownBy(() -> new SoOperation(params, config) {})
318                         .withMessageContaining("missing Target");
319     }
320
321     private void verifyMissingModelInfo(Supplier<String> getter, Consumer<String> setter) {
322         String original = getter.get();
323
324         setter.accept(null);
325         assertThatIllegalArgumentException().isThrownBy(() -> oper.prepareSoModelInfo())
326                         .withMessage("missing VF Module model");
327
328         setter.accept(original);
329     }
330
331     @Test
332     public void testConstructRequestInfo() throws CoderException {
333         SoRequestInfo info = oper.constructRequestInfo();
334         verifyRequest("reqinfo.json", info);
335     }
336
337     @Test
338     public void testBuildRequestParameters() throws CoderException {
339         // valid data
340         verifyRequest("reqparams.json", oper.buildRequestParameters().get());
341
342         // invalid json
343         params.getPayload().put(SoOperation.REQ_PARAM_NM, "{invalid json");
344         assertThatIllegalArgumentException().isThrownBy(() -> oper.buildRequestParameters())
345                         .withMessage("invalid payload value: " + SoOperation.REQ_PARAM_NM);
346
347         // missing data
348         params.getPayload().remove(SoOperation.REQ_PARAM_NM);
349         assertTrue(oper.buildRequestParameters().isEmpty());
350
351         // null payload
352         params = params.toBuilder().payload(null).build();
353         oper = new SoOperation(params, config) {};
354         assertTrue(oper.buildRequestParameters().isEmpty());
355     }
356
357     @Test
358     public void testBuildConfigurationParameters() {
359         // valid data
360         assertEquals(List.of(Collections.emptyMap()), oper.buildConfigurationParameters().get());
361
362         // invalid json
363         params.getPayload().put(SoOperation.CONFIG_PARAM_NM, "{invalid json");
364         assertThatIllegalArgumentException().isThrownBy(() -> oper.buildConfigurationParameters())
365                         .withMessage("invalid payload value: " + SoOperation.CONFIG_PARAM_NM);
366
367         // missing data
368         params.getPayload().remove(SoOperation.CONFIG_PARAM_NM);
369         assertTrue(oper.buildConfigurationParameters().isEmpty());
370
371         // null payload
372         params = params.toBuilder().payload(null).build();
373         oper = new SoOperation(params, config) {};
374         assertTrue(oper.buildConfigurationParameters().isEmpty());
375     }
376
377     @Test
378     public void testGetVnfItem() {
379         // missing data
380         AaiCqResponse cq = mock(AaiCqResponse.class);
381         assertThatIllegalArgumentException().isThrownBy(() -> oper.getVnfItem(cq, oper.prepareSoModelInfo()))
382                         .withMessage("missing generic VNF");
383
384         // valid data
385         GenericVnf vnf = new GenericVnf();
386         when(cq.getGenericVnfByVfModuleModelInvariantId(MODEL_INVAR_ID)).thenReturn(vnf);
387         assertSame(vnf, oper.getVnfItem(cq, oper.prepareSoModelInfo()));
388     }
389
390     @Test
391     public void testGetServiceInstance() {
392         // missing data
393         AaiCqResponse cq = mock(AaiCqResponse.class);
394         assertThatIllegalArgumentException().isThrownBy(() -> oper.getServiceInstance(cq))
395                         .withMessage("missing VNF Service Item");
396
397         // valid data
398         ServiceInstance instance = new ServiceInstance();
399         when(cq.getServiceInstance()).thenReturn(instance);
400         assertSame(instance, oper.getServiceInstance(cq));
401     }
402
403     @Test
404     public void testGetDefaultTenant() {
405         // missing data
406         AaiCqResponse cq = mock(AaiCqResponse.class);
407         assertThatIllegalArgumentException().isThrownBy(() -> oper.getDefaultTenant(cq))
408                         .withMessage("missing Tenant Item");
409
410         // valid data
411         Tenant tenant = new Tenant();
412         when(cq.getDefaultTenant()).thenReturn(tenant);
413         assertSame(tenant, oper.getDefaultTenant(cq));
414     }
415
416     @Test
417     public void testGetDefaultCloudRegion() {
418         // missing data
419         AaiCqResponse cq = mock(AaiCqResponse.class);
420         assertThatIllegalArgumentException().isThrownBy(() -> oper.getDefaultCloudRegion(cq))
421                         .withMessage("missing Cloud Region");
422
423         // valid data
424         CloudRegion region = new CloudRegion();
425         when(cq.getDefaultCloudRegion()).thenReturn(region);
426         assertSame(region, oper.getDefaultCloudRegion(cq));
427     }
428
429     @Test
430     public void testMakeCoder() throws CoderException {
431         Coder opcoder = oper.makeCoder();
432
433         // ensure we can decode an SO timestamp
434         String json = "{'request':{'finishTime':'Fri, 15 May 2020 12:14:21 GMT'}}";
435         SoResponse resp = opcoder.decode(json.replace('\'', '"'), SoResponse.class);
436
437         LocalDateTime tfinish = resp.getRequest().getFinishTime();
438         assertNotNull(tfinish);
439         assertEquals(2020, tfinish.getYear());
440         assertEquals(Month.MAY, tfinish.getMonth());
441     }
442 }