7a557c2aff6c2d53cddc50188344eea6d3400767
[policy/drools-applications.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 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.drools.apps.controller.usecases.step;
23
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;
38
39 import java.util.HashMap;
40 import java.util.List;
41 import java.util.Map;
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;
70
71 class Step2Test {
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";
80
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);
87
88     private Map<String, String> payload;
89     private VirtualControlLoopEvent event;
90     private BlockingQueue<OperationOutcome> starts;
91     private BlockingQueue<OperationOutcome> completions;
92     private ControlLoopOperationParams params;
93     private Step2 step;
94
95     /**
96      * Sets up.
97      */
98     @BeforeEach
99     public void setUp() {
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);
104
105         when(policyOperation.getPropertyNames()).thenReturn(List.of());
106
107         when(stepContext.getProperty(AaiCqResponse.CONTEXT_KEY)).thenReturn(aaicq);
108
109         payload = Map.of(PAYLOAD_KEY, PAYLOAD_VALUE);
110
111         event = new VirtualControlLoopEvent();
112         event.setRequestId(REQ_ID);
113
114         starts = new LinkedBlockingQueue<>();
115         completions = new LinkedBlockingQueue<>();
116
117         Map<String, String> entityIds = new HashMap<>();
118
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();
124
125         step = new Step2(stepContext, params, event);
126         step.init();
127     }
128
129     @Test
130     void testConstructor() {
131         assertSame(stepContext, step.stepContext);
132         assertSame(event, step.event);
133         assertSame(actors, step.getParams().getActorService());
134     }
135
136     @Test
137     void testConstructorStep2() {
138         step = new Step2(step, "actorB", "operationB");
139         assertSame(stepContext, step.stepContext);
140         assertSame(event, step.event);
141
142         assertEquals("actorB", step.getActorName());
143         assertEquals("operationB", step.getOperationName());
144         assertSame(actors, step.getParams().getActorService());
145     }
146
147     @Test
148     void testAcceptsEvent() {
149         // it's a policy step, thus it accepts events
150         assertTrue(step.acceptsEvent());
151
152         step = new Step2(step, "actorB", "operationB");
153
154         // it's not a policy step, thus it does not accept events
155         assertFalse(step.acceptsEvent());
156     }
157
158     @Test
159     void testSuccess() {
160         assertThatCode(() -> step.success(null)).doesNotThrowAnyException();
161     }
162
163     @Test
164     void testGetPropertyNames() {
165         // empty property list
166         assertThat(step.getPropertyNames()).isEmpty();
167
168         // try with non-empty list
169         when(policyOperation.getPropertyNames()).thenReturn(List.of("propA", "propB"));
170         assertThat(step.getPropertyNames()).isEqualTo(List.of("propA", "propB"));
171     }
172
173     @Test
174     void testSetProperties() {
175         var cloudRegion = new CloudRegion();
176         when(aaicq.getDefaultCloudRegion()).thenReturn(cloudRegion);
177
178         var tenant = new Tenant();
179         when(aaicq.getDefaultTenant()).thenReturn(tenant);
180
181         when(policyOperation.getPropertyNames()).thenReturn(
182                         List.of(OperationProperties.AAI_DEFAULT_CLOUD_REGION, OperationProperties.AAI_DEFAULT_TENANT));
183
184         step.setProperties();
185
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);
190     }
191
192     /**
193      * Tests setProperties() when the property is unknown.
194      */
195     @Test
196     void testSetPropertiesUnknown() {
197         when(policyOperation.getPropertyNames()).thenReturn(List.of("unknown-property"));
198
199         assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
200                         .withMessage("unknown property unknown-property needed by my-actor.my-operation");
201     }
202
203     @Test
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));
208
209         step.setProperties();
210         verify(policyOperation).setProperty(OperationProperties.AAI_DEFAULT_CLOUD_REGION, data);
211
212         when(aaicq.getDefaultCloudRegion()).thenReturn(null);
213         assertThatIllegalArgumentException().isThrownBy(() -> step.getCloudRegion())
214                         .withMessageContaining("missing default cloud region in A&AI response");
215     }
216
217     @Test
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));
222
223         step.setProperties();
224         verify(policyOperation).setProperty(OperationProperties.AAI_DEFAULT_TENANT, data);
225
226         when(aaicq.getDefaultTenant()).thenReturn(null);
227         assertThatIllegalArgumentException().isThrownBy(() -> step.getTenant())
228                         .withMessageContaining("missing default tenant in A&AI response");
229     }
230
231     @Test
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));
237
238         step.setProperties();
239         verify(policyOperation).setProperty(OperationProperties.AAI_PNF, data);
240
241         when(stepContext.getProperty(AaiGetPnfOperation.getKey(MY_TARGET))).thenReturn(null);
242         assertThatIllegalArgumentException().isThrownBy(() -> step.getPnf())
243                         .withMessageContaining("missing PNF for my-target");
244     }
245
246     @Test
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));
252
253         step.setProperties();
254         verify(policyOperation).setProperty(OperationProperties.AAI_RESOURCE_VNF, data);
255
256         when(aaicq.getGenericVnfByModelInvariantId("my-resource")).thenReturn(null);
257         assertThatIllegalArgumentException().isThrownBy(() -> step.getResourceVnf())
258                         .withMessageContaining("missing VNF for my-resource");
259
260         // missing resource ID
261         params.getTargetEntityIds().put(Step2.TARGET_RESOURCE_ID, null);
262         assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
263                         .withMessageContaining("missing Target resource ID");
264
265         // missing target entity IDs
266         params = params.toBuilder().targetEntityIds(null).build();
267         step = new Step2(stepContext, params, event);
268         step.init();
269         assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
270                         .withMessageContaining(Step2.TARGET_INFO_MSG);
271     }
272
273     @Test
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));
278
279         step.setProperties();
280         verify(policyOperation).setProperty(OperationProperties.AAI_SERVICE, data);
281
282         when(aaicq.getServiceInstance()).thenReturn(null);
283         assertThatIllegalArgumentException().isThrownBy(() -> step.getService())
284                         .withMessageContaining("missing service instance in A&AI response");
285     }
286
287     @Test
288     void testLoadServiceModel_testGetServiceModel() {
289         var service = new ServiceInstance();
290         service.setModelVersionId("my-service-version");
291         when(aaicq.getServiceInstance()).thenReturn(service);
292
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));
296
297         step.setProperties();
298         verify(policyOperation).setProperty(OperationProperties.AAI_SERVICE_MODEL, data);
299
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");
303
304         service.setModelVersionId(null);
305         assertThatIllegalArgumentException().isThrownBy(() -> step.getServiceModel())
306                         .withMessageContaining("missing service model version ID in A&AI response");
307
308         when(aaicq.getServiceInstance()).thenReturn(null);
309         assertThatIllegalArgumentException().isThrownBy(() -> step.getServiceModel())
310                         .withMessageContaining("missing service instance in A&AI response");
311     }
312
313     @Test
314     void testGetVserver() {
315         var vserver = new Vserver();
316         when(aaicq.getVserver()).thenReturn(vserver);
317
318         assertSame(vserver, step.getVServer());
319
320         when(aaicq.getVserver()).thenReturn(null);
321         assertThatIllegalArgumentException().isThrownBy(() -> step.getVServer())
322                         .withMessageContaining("missing vserver in A&AI response");
323     }
324
325     @Test
326     void testGetTargetEntity() {
327         when(stepContext.getProperty(OperationProperties.AAI_TARGET_ENTITY)).thenReturn(MY_TARGET);
328
329         assertEquals(MY_TARGET, step.getTargetEntity());
330
331         when(stepContext.getProperty(OperationProperties.AAI_TARGET_ENTITY)).thenReturn(null);
332         assertThatIllegalArgumentException().isThrownBy(() -> step.getTargetEntity())
333                         .withMessageContaining("missing A&AI target entity");
334     }
335
336     @Test
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));
342
343         step.setProperties();
344         verify(policyOperation).setProperty(OperationProperties.AAI_VNF, data);
345
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");
349
350         // missing model invariant ID
351         params.getTargetEntityIds().put(Step2.TARGET_MODEL_INVARIANT_ID, null);
352         assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
353                         .withMessageContaining("missing modelInvariantId");
354
355         // missing target
356         params = params.toBuilder().targetEntityIds(null).build();
357         step = new Step2(stepContext, params, event);
358         step.init();
359         assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
360                         .withMessageContaining(Step2.TARGET_INFO_MSG);
361     }
362
363     @Test
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);
368
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));
373
374         step.setProperties();
375         verify(policyOperation).setProperty(OperationProperties.AAI_VNF_MODEL, data);
376
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");
380
381         vnf.setModelVersionId(null);
382         assertThatIllegalArgumentException().isThrownBy(() -> step.getVnfModel())
383                         .withMessageContaining("missing model version ID for generic VNF in A&AI response");
384     }
385
386     @Test
387     void testLoadVserverLink_testGetVserverLink() {
388         event.setAai(Map.of(Step2.VSERVER_VSERVER_NAME, "vserverB"));
389
390         var tenant = mock(StandardCoderObject.class);
391         when(stepContext.getProperty(AaiGetTenantOperation.getKey("vserverB"))).thenReturn(tenant);
392
393         when(tenant.getString("result-data", 0, "resource-link")).thenReturn("/aai/v7/some/link/bbb");
394
395         when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.AAI_VSERVER_LINK));
396
397         step.setProperties();
398         verify(policyOperation).setProperty(OperationProperties.AAI_VSERVER_LINK, "/some/link/bbb");
399
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");
404
405         // missing tenant data
406         when(stepContext.getProperty(AaiGetTenantOperation.getKey("vserverB"))).thenReturn(null);
407         assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
408                         .withMessageContaining("missing tenant data for");
409
410         // empty vserver name
411         event.setAai(Map.of(Step2.VSERVER_VSERVER_NAME, ""));
412         assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
413                         .withMessageContaining("missing vserver.vserver-name");
414
415         // missing vserver name
416         event.setAai(Map.of());
417         assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
418                         .withMessageContaining("missing vserver.vserver-name");
419     }
420
421     @Test
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));
428
429         step.setProperties();
430         verify(policyOperation).setProperty(OperationProperties.DATA_VF_COUNT, 11);
431
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");
436
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");
441
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");
446
447         // missing target
448         params = params.toBuilder().targetEntityIds(null).build();
449         step = new Step2(stepContext, params, event);
450         step.init();
451         assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
452                         .withMessageContaining(Step2.TARGET_INFO_MSG);
453
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);
459     }
460
461     @Test
462     void testLoadEnrichment_testGetEnrichment() {
463         event.setAai(Map.of("bandwidth", "bandwidth-value"));
464         when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.ENRICHMENT_BANDWIDTH));
465
466         step.setProperties();
467         verify(policyOperation).setProperty(OperationProperties.ENRICHMENT_BANDWIDTH, "bandwidth-value");
468
469         // missing enrichment data
470         event.setAai(Map.of());
471         assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties());
472     }
473
474     @Test
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));
479
480         step.setProperties();
481         verify(policyOperation).setProperty(OperationProperties.EVENT_ADDITIONAL_PARAMS, data);
482     }
483
484     @Test
485     void testLoadEventPayload_testGetEventPayload() {
486         event.setPayload("some-event-payload");
487         when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.EVENT_PAYLOAD));
488
489         step.setProperties();
490         verify(policyOperation).setProperty(OperationProperties.EVENT_PAYLOAD, "some-event-payload");
491     }
492
493     @Test
494     void testLoadOptCdsGrpcAaiProperties() {
495         when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.OPT_CDS_GRPC_AAI_PROPERTIES));
496
497         step.setProperties();
498         verify(policyOperation, never()).setProperty(any(), anyString());
499     }
500
501     @Test
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));
506
507         step.setProperties();
508         verify(policyOperation).setProperty(UsecasesConstants.AAI_DEFAULT_GENERIC_VNF, data);
509
510         when(aaicq.getDefaultGenericVnf()).thenReturn(null);
511         assertThatIllegalArgumentException().isThrownBy(() -> step.getDefaultGenericVnf())
512                         .withMessageContaining("missing generic VNF in A&AI response");
513     }
514
515     @Test
516     void testGetCustomQueryData() {
517         assertSame(aaicq, step.getCustomQueryData());
518
519         when(stepContext.getProperty(AaiCqResponse.CONTEXT_KEY)).thenReturn(null);
520
521         assertThatIllegalArgumentException().isThrownBy(() -> step.getCustomQueryData())
522                         .withMessage("missing custom query data for my-actor.my-operation");
523     }
524
525     @Test
526     void testVerifyNotNull() {
527         assertThatCode(() -> step.verifyNotNull("verifyA", "verify-value-A")).doesNotThrowAnyException();
528
529         assertThatIllegalArgumentException().isThrownBy(() -> step.verifyNotNull("verifyB", null))
530                         .withMessage("missing verifyB for my-actor.my-operation");
531     }
532
533     @Test
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));
538
539         assertEquals(ONE_SLASH, Step2.stripPrefix(ONE_SLASH, 1));
540         assertEquals(ONE_SLASH, Step2.stripPrefix(ONE_SLASH, 2));
541
542         assertEquals("/slashes", Step2.stripPrefix("/two/slashes", 2));
543         assertEquals("/slashes", Step2.stripPrefix("/two/slashes", 3));
544
545         assertEquals("/and/more", Step2.stripPrefix("/three/slashes/and/more", 3));
546
547         assertEquals("/and/more", Step2.stripPrefix("prefix/three/slashes/and/more", 3));
548     }
549 }