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.drools.apps.controller.usecases.step;
24 import static org.assertj.core.api.Assertions.assertThat;
25 import static org.assertj.core.api.Assertions.assertThatCode;
26 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
27 import static org.junit.jupiter.api.Assertions.assertEquals;
28 import static org.junit.jupiter.api.Assertions.assertFalse;
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.ArgumentMatchers.anyString;
33 import static org.mockito.Mockito.mock;
34 import static org.mockito.Mockito.never;
35 import static org.mockito.Mockito.times;
36 import static org.mockito.Mockito.verify;
37 import static org.mockito.Mockito.when;
39 import java.util.HashMap;
40 import java.util.List;
42 import java.util.TreeMap;
43 import java.util.UUID;
44 import java.util.concurrent.BlockingQueue;
45 import java.util.concurrent.ForkJoinPool;
46 import java.util.concurrent.LinkedBlockingQueue;
47 import org.junit.jupiter.api.BeforeEach;
48 import org.junit.jupiter.api.Test;
49 import org.onap.aai.domain.yang.CloudRegion;
50 import org.onap.aai.domain.yang.GenericVnf;
51 import org.onap.aai.domain.yang.ModelVer;
52 import org.onap.aai.domain.yang.ServiceInstance;
53 import org.onap.aai.domain.yang.Tenant;
54 import org.onap.aai.domain.yang.Vserver;
55 import org.onap.policy.aai.AaiCqResponse;
56 import org.onap.policy.common.utils.coder.StandardCoderObject;
57 import org.onap.policy.controlloop.VirtualControlLoopEvent;
58 import org.onap.policy.controlloop.actor.aai.AaiGetPnfOperation;
59 import org.onap.policy.controlloop.actor.aai.AaiGetTenantOperation;
60 import org.onap.policy.controlloop.actorserviceprovider.ActorService;
61 import org.onap.policy.controlloop.actorserviceprovider.Operation;
62 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
63 import org.onap.policy.controlloop.actorserviceprovider.OperationProperties;
64 import org.onap.policy.controlloop.actorserviceprovider.Operator;
65 import org.onap.policy.controlloop.actorserviceprovider.TargetType;
66 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
67 import org.onap.policy.controlloop.actorserviceprovider.spi.Actor;
68 import org.onap.policy.controlloop.eventmanager.StepContext;
69 import org.onap.policy.drools.apps.controller.usecases.UsecasesConstants;
72 private static final UUID REQ_ID = UUID.randomUUID();
73 private static final String POLICY_ACTOR = "my-actor";
74 private static final String POLICY_OPERATION = "my-operation";
75 private static final String MY_TARGET = "my-target";
76 private static final String PAYLOAD_KEY = "payload-key";
77 private static final String PAYLOAD_VALUE = "payload-value";
78 private static final String NO_SLASH = "noslash";
79 private static final String ONE_SLASH = "/one";
81 private final Operator policyOperator = mock(Operator.class);
82 private final Operation policyOperation = mock(Operation.class);
83 private final Actor policyActor = mock(Actor.class);
84 private final ActorService actors = mock(ActorService.class);
85 private final StepContext stepContext = mock(StepContext.class);
86 private final AaiCqResponse aaicq = mock(AaiCqResponse.class);
88 private Map<String, String> payload;
89 private VirtualControlLoopEvent event;
90 private BlockingQueue<OperationOutcome> starts;
91 private BlockingQueue<OperationOutcome> completions;
92 private ControlLoopOperationParams params;
100 // configure policy operation
101 when(actors.getActor(POLICY_ACTOR)).thenReturn(policyActor);
102 when(policyActor.getOperator(POLICY_OPERATION)).thenReturn(policyOperator);
103 when(policyOperator.buildOperation(any())).thenReturn(policyOperation);
105 when(policyOperation.getPropertyNames()).thenReturn(List.of());
107 when(stepContext.getProperty(AaiCqResponse.CONTEXT_KEY)).thenReturn(aaicq);
109 payload = Map.of(PAYLOAD_KEY, PAYLOAD_VALUE);
111 event = new VirtualControlLoopEvent();
112 event.setRequestId(REQ_ID);
114 starts = new LinkedBlockingQueue<>();
115 completions = new LinkedBlockingQueue<>();
117 Map<String, String> entityIds = new HashMap<>();
119 params = ControlLoopOperationParams.builder().actor(POLICY_ACTOR).actorService(actors)
120 .completeCallback(completions::add).executor(ForkJoinPool.commonPool())
121 .operation(POLICY_OPERATION).payload(new TreeMap<>(payload)).startCallback(starts::add)
122 .targetType(TargetType.VM).targetEntityIds(entityIds)
123 .requestId(REQ_ID).build();
125 step = new Step2(stepContext, params, event);
130 void testConstructor() {
131 assertSame(stepContext, step.stepContext);
132 assertSame(event, step.event);
133 assertSame(actors, step.getParams().getActorService());
137 void testConstructorStep2() {
138 step = new Step2(step, "actorB", "operationB");
139 assertSame(stepContext, step.stepContext);
140 assertSame(event, step.event);
142 assertEquals("actorB", step.getActorName());
143 assertEquals("operationB", step.getOperationName());
144 assertSame(actors, step.getParams().getActorService());
148 void testAcceptsEvent() {
149 // it's a policy step, thus it accepts events
150 assertTrue(step.acceptsEvent());
152 step = new Step2(step, "actorB", "operationB");
154 // it's not a policy step, thus it does not accept events
155 assertFalse(step.acceptsEvent());
160 assertThatCode(() -> step.success(null)).doesNotThrowAnyException();
164 void testGetPropertyNames() {
165 // empty property list
166 assertThat(step.getPropertyNames()).isEmpty();
168 // try with non-empty list
169 when(policyOperation.getPropertyNames()).thenReturn(List.of("propA", "propB"));
170 assertThat(step.getPropertyNames()).isEqualTo(List.of("propA", "propB"));
174 void testSetProperties() {
175 var cloudRegion = new CloudRegion();
176 when(aaicq.getDefaultCloudRegion()).thenReturn(cloudRegion);
178 var tenant = new Tenant();
179 when(aaicq.getDefaultTenant()).thenReturn(tenant);
181 when(policyOperation.getPropertyNames()).thenReturn(
182 List.of(OperationProperties.AAI_DEFAULT_CLOUD_REGION, OperationProperties.AAI_DEFAULT_TENANT));
184 step.setProperties();
186 // should have been exactly two properties set
187 verify(policyOperation, times(2)).setProperty(any(), any());
188 verify(policyOperation).setProperty(OperationProperties.AAI_DEFAULT_CLOUD_REGION, cloudRegion);
189 verify(policyOperation).setProperty(OperationProperties.AAI_DEFAULT_TENANT, tenant);
193 * Tests setProperties() when the property is unknown.
196 void testSetPropertiesUnknown() {
197 when(policyOperation.getPropertyNames()).thenReturn(List.of("unknown-property"));
199 assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
200 .withMessage("unknown property unknown-property needed by my-actor.my-operation");
204 void testLoadCloudRegion_testGetCloudRegion() {
205 var data = new CloudRegion();
206 when(aaicq.getDefaultCloudRegion()).thenReturn(data);
207 when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.AAI_DEFAULT_CLOUD_REGION));
209 step.setProperties();
210 verify(policyOperation).setProperty(OperationProperties.AAI_DEFAULT_CLOUD_REGION, data);
212 when(aaicq.getDefaultCloudRegion()).thenReturn(null);
213 assertThatIllegalArgumentException().isThrownBy(() -> step.getCloudRegion())
214 .withMessageContaining("missing default cloud region in A&AI response");
218 void testLoadTenant_testGetTenant() {
219 var data = new Tenant();
220 when(aaicq.getDefaultTenant()).thenReturn(data);
221 when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.AAI_DEFAULT_TENANT));
223 step.setProperties();
224 verify(policyOperation).setProperty(OperationProperties.AAI_DEFAULT_TENANT, data);
226 when(aaicq.getDefaultTenant()).thenReturn(null);
227 assertThatIllegalArgumentException().isThrownBy(() -> step.getTenant())
228 .withMessageContaining("missing default tenant in A&AI response");
232 void testLoadPnf_testGetPnf() {
233 var data = new StandardCoderObject();
234 when(stepContext.getProperty(OperationProperties.AAI_TARGET_ENTITY)).thenReturn(MY_TARGET);
235 when(stepContext.getProperty(AaiGetPnfOperation.getKey(MY_TARGET))).thenReturn(data);
236 when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.AAI_PNF));
238 step.setProperties();
239 verify(policyOperation).setProperty(OperationProperties.AAI_PNF, data);
241 when(stepContext.getProperty(AaiGetPnfOperation.getKey(MY_TARGET))).thenReturn(null);
242 assertThatIllegalArgumentException().isThrownBy(() -> step.getPnf())
243 .withMessageContaining("missing PNF for my-target");
247 void testLoadResourceVnf_testGetResourceVnf() {
248 params.getTargetEntityIds().put(Step2.TARGET_RESOURCE_ID, "my-resource");
249 var data = new GenericVnf();
250 when(aaicq.getGenericVnfByModelInvariantId("my-resource")).thenReturn(data);
251 when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.AAI_RESOURCE_VNF));
253 step.setProperties();
254 verify(policyOperation).setProperty(OperationProperties.AAI_RESOURCE_VNF, data);
256 when(aaicq.getGenericVnfByModelInvariantId("my-resource")).thenReturn(null);
257 assertThatIllegalArgumentException().isThrownBy(() -> step.getResourceVnf())
258 .withMessageContaining("missing VNF for my-resource");
260 // missing resource ID
261 params.getTargetEntityIds().put(Step2.TARGET_RESOURCE_ID, null);
262 assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
263 .withMessageContaining("missing Target resource ID");
265 // missing target entity IDs
266 params = params.toBuilder().targetEntityIds(null).build();
267 step = new Step2(stepContext, params, event);
269 assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
270 .withMessageContaining(Step2.TARGET_INFO_MSG);
274 void testLoadService_testGetService() {
275 var data = new ServiceInstance();
276 when(aaicq.getServiceInstance()).thenReturn(data);
277 when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.AAI_SERVICE));
279 step.setProperties();
280 verify(policyOperation).setProperty(OperationProperties.AAI_SERVICE, data);
282 when(aaicq.getServiceInstance()).thenReturn(null);
283 assertThatIllegalArgumentException().isThrownBy(() -> step.getService())
284 .withMessageContaining("missing service instance in A&AI response");
288 void testLoadServiceModel_testGetServiceModel() {
289 var service = new ServiceInstance();
290 service.setModelVersionId("my-service-version");
291 when(aaicq.getServiceInstance()).thenReturn(service);
293 var data = new ModelVer();
294 when(aaicq.getModelVerByVersionId("my-service-version")).thenReturn(data);
295 when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.AAI_SERVICE_MODEL));
297 step.setProperties();
298 verify(policyOperation).setProperty(OperationProperties.AAI_SERVICE_MODEL, data);
300 when(aaicq.getModelVerByVersionId("my-service-version")).thenReturn(null);
301 assertThatIllegalArgumentException().isThrownBy(() -> step.getServiceModel())
302 .withMessageContaining("missing model version for service in A&AI response");
304 service.setModelVersionId(null);
305 assertThatIllegalArgumentException().isThrownBy(() -> step.getServiceModel())
306 .withMessageContaining("missing service model version ID in A&AI response");
308 when(aaicq.getServiceInstance()).thenReturn(null);
309 assertThatIllegalArgumentException().isThrownBy(() -> step.getServiceModel())
310 .withMessageContaining("missing service instance in A&AI response");
314 void testGetVserver() {
315 var vserver = new Vserver();
316 when(aaicq.getVserver()).thenReturn(vserver);
318 assertSame(vserver, step.getVServer());
320 when(aaicq.getVserver()).thenReturn(null);
321 assertThatIllegalArgumentException().isThrownBy(() -> step.getVServer())
322 .withMessageContaining("missing vserver in A&AI response");
326 void testGetTargetEntity() {
327 when(stepContext.getProperty(OperationProperties.AAI_TARGET_ENTITY)).thenReturn(MY_TARGET);
329 assertEquals(MY_TARGET, step.getTargetEntity());
331 when(stepContext.getProperty(OperationProperties.AAI_TARGET_ENTITY)).thenReturn(null);
332 assertThatIllegalArgumentException().isThrownBy(() -> step.getTargetEntity())
333 .withMessageContaining("missing A&AI target entity");
337 void testLoadVnf_testGetVnf() {
338 params.getTargetEntityIds().put(Step2.TARGET_MODEL_INVARIANT_ID, "my-model-invariant");
339 var data = new GenericVnf();
340 when(aaicq.getGenericVnfByVfModuleModelInvariantId("my-model-invariant")).thenReturn(data);
341 when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.AAI_VNF));
343 step.setProperties();
344 verify(policyOperation).setProperty(OperationProperties.AAI_VNF, data);
346 when(aaicq.getGenericVnfByVfModuleModelInvariantId("my-model-invariant")).thenReturn(null);
347 assertThatIllegalArgumentException().isThrownBy(() -> step.getVnf())
348 .withMessageContaining("missing generic VNF in A&AI response for my-model-invariant");
350 // missing model invariant ID
351 params.getTargetEntityIds().put(Step2.TARGET_MODEL_INVARIANT_ID, null);
352 assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
353 .withMessageContaining("missing modelInvariantId");
356 params = params.toBuilder().targetEntityIds(null).build();
357 step = new Step2(stepContext, params, event);
359 assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
360 .withMessageContaining(Step2.TARGET_INFO_MSG);
364 void testLoadVnfModel_testGetVnfModel() {
365 params.getTargetEntityIds().put(Step2.TARGET_MODEL_INVARIANT_ID, "my-model-invariant");
366 var vnf = new GenericVnf();
367 when(aaicq.getGenericVnfByVfModuleModelInvariantId("my-model-invariant")).thenReturn(vnf);
369 vnf.setModelVersionId("my-vnf-model-version-id");
370 var data = new ModelVer();
371 when(aaicq.getModelVerByVersionId("my-vnf-model-version-id")).thenReturn(data);
372 when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.AAI_VNF_MODEL));
374 step.setProperties();
375 verify(policyOperation).setProperty(OperationProperties.AAI_VNF_MODEL, data);
377 when(aaicq.getModelVerByVersionId("my-vnf-model-version-id")).thenReturn(null);
378 assertThatIllegalArgumentException().isThrownBy(() -> step.getVnfModel())
379 .withMessageContaining("missing model version for generic VNF in A&AI response");
381 vnf.setModelVersionId(null);
382 assertThatIllegalArgumentException().isThrownBy(() -> step.getVnfModel())
383 .withMessageContaining("missing model version ID for generic VNF in A&AI response");
387 void testLoadVserverLink_testGetVserverLink() {
388 event.setAai(Map.of(Step2.VSERVER_VSERVER_NAME, "vserverB"));
390 var tenant = mock(StandardCoderObject.class);
391 when(stepContext.getProperty(AaiGetTenantOperation.getKey("vserverB"))).thenReturn(tenant);
393 when(tenant.getString("result-data", 0, "resource-link")).thenReturn("/aai/v7/some/link/bbb");
395 when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.AAI_VSERVER_LINK));
397 step.setProperties();
398 verify(policyOperation).setProperty(OperationProperties.AAI_VSERVER_LINK, "/some/link/bbb");
400 // missing resource link
401 when(tenant.getString("result-data", 0, "resource-link")).thenReturn(null);
402 assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
403 .withMessageContaining("missing tenant data resource-link");
405 // missing tenant data
406 when(stepContext.getProperty(AaiGetTenantOperation.getKey("vserverB"))).thenReturn(null);
407 assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
408 .withMessageContaining("missing tenant data for");
410 // empty vserver name
411 event.setAai(Map.of(Step2.VSERVER_VSERVER_NAME, ""));
412 assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
413 .withMessageContaining("missing vserver.vserver-name");
415 // missing vserver name
416 event.setAai(Map.of());
417 assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
418 .withMessageContaining("missing vserver.vserver-name");
422 void testLoadVfCount_testGetVfCount() {
423 params.getTargetEntityIds().put(Step2.TARGET_MODEL_CUSTOMIZATION_ID, "vf-count-customization");
424 params.getTargetEntityIds().put(Step2.TARGET_MODEL_INVARIANT_ID, "vf-count-invariant");
425 params.getTargetEntityIds().put(Step2.TARGET_MODEL_VERSION_ID, "vf-count-version");
426 when(aaicq.getVfModuleCount("vf-count-customization", "vf-count-invariant", "vf-count-version")).thenReturn(11);
427 when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.DATA_VF_COUNT));
429 step.setProperties();
430 verify(policyOperation).setProperty(OperationProperties.DATA_VF_COUNT, 11);
432 // missing model version id
433 params.getTargetEntityIds().put(Step2.TARGET_MODEL_VERSION_ID, null);
434 assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
435 .withMessageContaining("missing target modelVersionId");
437 // missing model invariant id
438 params.getTargetEntityIds().put(Step2.TARGET_MODEL_INVARIANT_ID, null);
439 assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
440 .withMessageContaining("missing target modelInvariantId");
442 // missing model customization id
443 params.getTargetEntityIds().put(Step2.TARGET_MODEL_CUSTOMIZATION_ID, null);
444 assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
445 .withMessageContaining("missing target modelCustomizationId");
448 params = params.toBuilder().targetEntityIds(null).build();
449 step = new Step2(stepContext, params, event);
451 assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
452 .withMessageContaining(Step2.TARGET_INFO_MSG);
454 // get it from the step context
455 when(stepContext.contains(OperationProperties.DATA_VF_COUNT)).thenReturn(true);
456 when(stepContext.getProperty(OperationProperties.DATA_VF_COUNT)).thenReturn(22);
457 step.setProperties();
458 verify(policyOperation).setProperty(OperationProperties.DATA_VF_COUNT, 22);
462 void testLoadEnrichment_testGetEnrichment() {
463 event.setAai(Map.of("bandwidth", "bandwidth-value"));
464 when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.ENRICHMENT_BANDWIDTH));
466 step.setProperties();
467 verify(policyOperation).setProperty(OperationProperties.ENRICHMENT_BANDWIDTH, "bandwidth-value");
469 // missing enrichment data
470 event.setAai(Map.of());
471 assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties());
475 void testLoadAdditionalEventParams_testGetAdditionalEventParams() {
476 Map<String, String> data = Map.of("addA", "add-valueA", "addB", "add-valueB");
477 event.setAdditionalEventParams(data);
478 when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.EVENT_ADDITIONAL_PARAMS));
480 step.setProperties();
481 verify(policyOperation).setProperty(OperationProperties.EVENT_ADDITIONAL_PARAMS, data);
485 void testLoadEventPayload_testGetEventPayload() {
486 event.setPayload("some-event-payload");
487 when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.EVENT_PAYLOAD));
489 step.setProperties();
490 verify(policyOperation).setProperty(OperationProperties.EVENT_PAYLOAD, "some-event-payload");
494 void testLoadOptCdsGrpcAaiProperties() {
495 when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.OPT_CDS_GRPC_AAI_PROPERTIES));
497 step.setProperties();
498 verify(policyOperation, never()).setProperty(any(), anyString());
502 void testLoadDefaultGenericVnf_testGetDefaultGenericVnf() {
503 var data = new GenericVnf();
504 when(aaicq.getDefaultGenericVnf()).thenReturn(data);
505 when(policyOperation.getPropertyNames()).thenReturn(List.of(UsecasesConstants.AAI_DEFAULT_GENERIC_VNF));
507 step.setProperties();
508 verify(policyOperation).setProperty(UsecasesConstants.AAI_DEFAULT_GENERIC_VNF, data);
510 when(aaicq.getDefaultGenericVnf()).thenReturn(null);
511 assertThatIllegalArgumentException().isThrownBy(() -> step.getDefaultGenericVnf())
512 .withMessageContaining("missing generic VNF in A&AI response");
516 void testGetCustomQueryData() {
517 assertSame(aaicq, step.getCustomQueryData());
519 when(stepContext.getProperty(AaiCqResponse.CONTEXT_KEY)).thenReturn(null);
521 assertThatIllegalArgumentException().isThrownBy(() -> step.getCustomQueryData())
522 .withMessage("missing custom query data for my-actor.my-operation");
526 void testVerifyNotNull() {
527 assertThatCode(() -> step.verifyNotNull("verifyA", "verify-value-A")).doesNotThrowAnyException();
529 assertThatIllegalArgumentException().isThrownBy(() -> step.verifyNotNull("verifyB", null))
530 .withMessage("missing verifyB for my-actor.my-operation");
534 void testStripPrefix() {
535 assertEquals(NO_SLASH, Step2.stripPrefix(NO_SLASH, 0));
536 assertEquals(NO_SLASH, Step2.stripPrefix(NO_SLASH, 1));
537 assertEquals(NO_SLASH, Step2.stripPrefix(NO_SLASH, 2));
539 assertEquals(ONE_SLASH, Step2.stripPrefix(ONE_SLASH, 1));
540 assertEquals(ONE_SLASH, Step2.stripPrefix(ONE_SLASH, 2));
542 assertEquals("/slashes", Step2.stripPrefix("/two/slashes", 2));
543 assertEquals("/slashes", Step2.stripPrefix("/two/slashes", 3));
545 assertEquals("/and/more", Step2.stripPrefix("/three/slashes/and/more", 3));
547 assertEquals("/and/more", Step2.stripPrefix("prefix/three/slashes/and/more", 3));