2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
6 * Modifications Copyright (C) 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
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.assertThatIllegalArgumentException;
25 import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
26 import static org.junit.jupiter.api.Assertions.assertEquals;
27 import static org.junit.jupiter.api.Assertions.assertFalse;
28 import static org.junit.jupiter.api.Assertions.assertSame;
29 import static org.junit.jupiter.api.Assertions.assertTrue;
30 import static org.mockito.ArgumentMatchers.any;
31 import static org.mockito.Mockito.doThrow;
32 import static org.mockito.Mockito.never;
33 import static org.mockito.Mockito.spy;
34 import static org.mockito.Mockito.times;
35 import static org.mockito.Mockito.verify;
36 import static org.mockito.Mockito.when;
38 import java.util.Iterator;
40 import java.util.stream.Collectors;
41 import org.junit.jupiter.api.BeforeEach;
42 import org.junit.jupiter.api.Test;
43 import org.onap.policy.common.parameters.ObjectValidationResult;
44 import org.onap.policy.common.parameters.ValidationStatus;
45 import org.onap.policy.controlloop.actorserviceprovider.Operation;
46 import org.onap.policy.controlloop.actorserviceprovider.Operator;
47 import org.onap.policy.controlloop.actorserviceprovider.parameters.ActorParams;
48 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
49 import org.onap.policy.controlloop.actorserviceprovider.parameters.ParameterValidationRuntimeException;
52 private static final String EXPECTED_EXCEPTION = "expected exception";
53 private static final String ACTOR_NAME = "my-actor";
54 private static final String OPER1 = "add";
55 private static final String OPER2 = "subtract";
56 private static final String OPER3 = "multiply";
57 private static final String OPER4 = "divide";
64 private Map<String, Object> sub1;
65 private Map<String, Object> sub2;
66 private Map<String, Object> sub3;
67 private Map<String, Object> sub4;
68 private Map<String, Object> params;
70 private ActorImpl actor;
74 * Initializes the fields, including a fully populated {@link #actor}.
78 oper1 = spy(new MyOper(OPER1));
79 oper2 = spy(new MyOper(OPER2));
80 oper3 = spy(new MyOper(OPER3));
81 oper4 = spy(new MyOper(OPER4));
83 sub1 = Map.of("sub A", "value A");
84 sub2 = Map.of("sub B", "value B");
85 sub3 = Map.of("sub C", "value C");
86 sub4 = Map.of("sub D", "value D");
88 params = Map.of(ActorParams.OPERATIONS_FIELD, Map.of(OPER1, sub1, OPER2, sub2, OPER3, sub3, OPER4, sub4));
90 actor = makeActor(oper1, oper2, oper3, oper4);
94 void testActorImpl_testGetName() {
95 assertEquals(ACTOR_NAME, actor.getName());
96 assertEquals(4, actor.getOperationNames().size());
97 assertEquals(0, actor.getSequenceNumber());
102 actor.configure(params);
103 assertEquals(4, actor.getOperationNames().size());
106 * arrange for second operator to be unconfigured and the third operator to throw
109 Iterator<Operator> iter = actor.getOperators().iterator();
111 when(iter.next().isConfigured()).thenReturn(false);
112 when(iter.next().start()).thenThrow(new IllegalStateException(EXPECTED_EXCEPTION));
118 assertTrue(actor.isAlive());
120 iter = actor.getOperators().iterator();
121 verify(iter.next()).start();
122 // this one isn't configured, so shouldn't attempt to start it
123 verify(iter.next(), never()).start();
124 // this one threw an exception
126 verify(iter.next()).start();
128 // no other types of operations
129 verify(oper1, never()).stop();
130 verify(oper1, never()).shutdown();
135 actor.configure(params);
137 assertEquals(4, actor.getOperationNames().size());
139 // arrange for second operator to throw an exception
140 Iterator<Operator> iter = actor.getOperators().iterator();
142 when(iter.next().stop()).thenThrow(new IllegalStateException(EXPECTED_EXCEPTION));
148 assertFalse(actor.isAlive());
150 iter = actor.getOperators().iterator();
151 verify(iter.next()).stop();
152 // this one threw an exception
154 verify(iter.next()).stop();
155 verify(iter.next()).stop();
157 // no additional types of operations
158 verify(oper1).configure(any());
159 verify(oper1).start();
161 // no other types of operation
162 verify(oper1, never()).shutdown();
166 void testDoShutdown() {
167 actor.configure(params);
169 assertEquals(4, actor.getOperationNames().size());
171 // arrange for second operator to throw an exception
172 Iterator<Operator> iter = actor.getOperators().iterator();
174 doThrow(new IllegalStateException(EXPECTED_EXCEPTION)).when(iter.next()).shutdown();
180 assertFalse(actor.isAlive());
182 iter = actor.getOperators().iterator();
183 verify(iter.next()).shutdown();
184 // this one threw an exception
186 verify(iter.next()).shutdown();
187 verify(iter.next()).shutdown();
189 // no additional types of operations
190 verify(oper1).configure(any());
191 verify(oper1).start();
193 // no other types of operation
194 verify(oper1, never()).stop();
198 void testAddOperator() {
199 // cannot add operators if already configured
200 actor.configure(params);
201 assertThatIllegalStateException().isThrownBy(() -> actor.addOperator(oper1));
204 * make an actor where operators two and four have names that are duplicates of
207 oper2 = spy(new MyOper(OPER1));
208 oper4 = spy(new MyOper(OPER3));
210 actor = makeActor(oper1, oper2, oper3, oper4);
212 assertEquals(2, actor.getOperationNames().size());
214 assertSame(oper1, actor.getOperator(OPER1));
215 assertSame(oper3, actor.getOperator(OPER3));
219 void testGetOperator() {
220 assertSame(oper1, actor.getOperator(OPER1));
221 assertSame(oper3, actor.getOperator(OPER3));
223 assertThatIllegalArgumentException().isThrownBy(() -> actor.getOperator("unknown name"));
227 void testGetOperators() {
229 assertEquals("[add, divide, multiply, subtract]",
230 actor.getOperators().stream()
231 .map(Operator::getName)
233 .collect(Collectors.toList())
239 void testGetOperationNames() {
241 assertEquals("[add, divide, multiply, subtract]",
242 actor.getOperationNames().stream()
244 .collect(Collectors.toList())
250 void testDoConfigure() {
251 actor.configure(params);
252 assertTrue(actor.isConfigured());
254 verify(oper1).configure(sub1);
255 verify(oper2).configure(sub2);
256 verify(oper3).configure(sub3);
257 verify(oper4).configure(sub4);
259 // no other types of operations
260 verify(oper1, never()).start();
261 verify(oper1, never()).stop();
262 verify(oper1, never()).shutdown();
266 * Tests doConfigure() where operators throw parameter validation and runtime
270 void testDoConfigureExceptions() {
271 makeValidException(oper1);
272 makeRuntimeException(oper2);
273 makeValidException(oper3);
275 actor.configure(params);
276 assertTrue(actor.isConfigured());
280 * Tests doConfigure(). Arranges for the following:
282 * <li>one operator is configured, but has parameters</li>
283 * <li>another operator is configured, but has no parameters</li>
284 * <li>another operator has no parameters and is not configured</li>
288 void testDoConfigureConfigure() {
289 // configure one operator
290 oper1.configure(sub1);
292 // configure another and remove its parameters
293 oper2.configure(sub2);
294 params = Map.of(ActorParams.OPERATIONS_FIELD, Map.of(OPER1, sub1, OPER3, sub3, OPER4, sub4));
296 // create a new, unconfigured actor
297 Operator oper5 = spy(new MyOper("UNCONFIGURED"));
298 actor = makeActor(oper1, oper2, oper3, oper4, oper5);
303 actor.configure(params);
304 assertTrue(actor.isConfigured());
306 // this should have been configured again
307 verify(oper1, times(2)).configure(sub1);
309 // no parameters, so this should not have been configured again
310 verify(oper2).configure(sub2);
312 // these were only configured once
313 verify(oper3).configure(sub3);
314 verify(oper4).configure(sub4);
317 verify(oper5, never()).configure(any());
318 assertFalse(oper5.isConfigured());
320 // start and verify that all are started except for the last
322 verify(oper1).start();
323 verify(oper2).start();
324 verify(oper3).start();
325 verify(oper4).start();
326 verify(oper5, never()).start();
330 * Arranges for an operator to throw a validation exception when
331 * {@link Operator#configure(Map)} is invoked.
333 * @param oper operator of interest
335 private void makeValidException(Operator oper) {
336 ParameterValidationRuntimeException ex = new ParameterValidationRuntimeException(
337 new ObjectValidationResult(actor.getName(), null, ValidationStatus.INVALID, "null"));
338 doThrow(ex).when(oper).configure(any());
342 * Arranges for an operator to throw a runtime exception when
343 * {@link Operator#configure(Map)} is invoked.
345 * @param oper operator of interest
347 private void makeRuntimeException(Operator oper) {
348 IllegalStateException ex = new IllegalStateException(EXPECTED_EXCEPTION);
349 doThrow(ex).when(oper).configure(any());
353 void testMakeOperatorParameters() {
354 actor.configure(params);
356 // each operator should have received its own parameters
357 verify(oper1).configure(sub1);
358 verify(oper2).configure(sub2);
359 verify(oper3).configure(sub3);
360 verify(oper4).configure(sub4);
364 * Makes an actor with the given operators.
366 * @param operators associated operators
367 * @return a new actor
369 private ActorImpl makeActor(Operator... operators) {
370 ActorImpl actor = new ActorImpl(ACTOR_NAME);
372 for (Operator oper : operators) {
373 actor.addOperator(oper);
379 private static class MyOper extends OperatorPartial {
381 public MyOper(String name) {
382 super(ACTOR_NAME, name);
386 public Operation buildOperation(ControlLoopOperationParams params) {