Actor redesign.
[policy/models.git] / models-interactions / model-actors / actorServiceProvider / src / test / java / org / onap / policy / controlloop / actorserviceprovider / impl / ActorImplTest.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.actorserviceprovider.impl;
22
23 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
24 import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
25 import static org.junit.Assert.assertEquals;
26 import static org.junit.Assert.assertFalse;
27 import static org.junit.Assert.assertSame;
28 import static org.junit.Assert.assertTrue;
29 import static org.mockito.ArgumentMatchers.any;
30 import static org.mockito.Mockito.doThrow;
31 import static org.mockito.Mockito.never;
32 import static org.mockito.Mockito.spy;
33 import static org.mockito.Mockito.times;
34 import static org.mockito.Mockito.verify;
35 import static org.mockito.Mockito.when;
36
37 import java.util.Collections;
38 import java.util.Iterator;
39 import java.util.Map;
40 import java.util.TreeMap;
41 import java.util.stream.Collectors;
42 import org.junit.Before;
43 import org.junit.Test;
44 import org.onap.policy.common.parameters.ObjectValidationResult;
45 import org.onap.policy.common.parameters.ValidationStatus;
46 import org.onap.policy.controlloop.actorserviceprovider.Operator;
47 import org.onap.policy.controlloop.actorserviceprovider.parameters.ParameterValidationRuntimeException;
48
49 public class ActorImplTest {
50     private static final String EXPECTED_EXCEPTION = "expected exception";
51     private static final String ACTOR_NAME = "my-actor";
52     private static final String OPER1 = "add";
53     private static final String OPER2 = "subtract";
54     private static final String OPER3 = "multiply";
55     private static final String OPER4 = "divide";
56
57     private MyOper oper1;
58     private MyOper oper2;
59     private MyOper oper3;
60     private MyOper oper4;
61
62     private Map<String, Object> sub1;
63     private Map<String, Object> sub2;
64     private Map<String, Object> sub3;
65     private Map<String, Object> sub4;
66     private Map<String, Object> params;
67
68     private ActorImpl actor;
69
70
71     /**
72      * Initializes the fields, including a fully populated {@link #actor}.
73      */
74     @Before
75     public void setUp() {
76         oper1 = spy(new MyOper(OPER1));
77         oper2 = spy(new MyOper(OPER2));
78         oper3 = spy(new MyOper(OPER3));
79         oper4 = spy(new MyOper(OPER4));
80
81         sub1 = Map.of("sub A", "value A");
82         sub2 = Map.of("sub B", "value B");
83         sub3 = Map.of("sub C", "value C");
84         sub4 = Map.of("sub D", "value D");
85
86         params = Map.of(OPER1, sub1, OPER2, sub2, OPER3, sub3, OPER4, sub4);
87
88         actor = makeActor(oper1, oper2, oper3, oper4);
89     }
90
91     @Test
92     public void testActorImpl_testGetName() {
93         assertEquals(ACTOR_NAME, actor.getName());
94         assertEquals(4, actor.getOperationNames().size());
95     }
96
97     @Test
98     public void testDoStart() {
99         actor.configure(params);
100         assertEquals(4, actor.getOperationNames().size());
101
102         /*
103          * arrange for second operator to be unconfigured and the third operator to throw
104          * an exception
105          */
106         Iterator<Operator> iter = actor.getOperators().iterator();
107         iter.next();
108         when(iter.next().isConfigured()).thenReturn(false);
109         when(iter.next().start()).thenThrow(new IllegalStateException(EXPECTED_EXCEPTION));
110
111         /*
112          * Start the actor.
113          */
114         actor.start();
115         assertTrue(actor.isAlive());
116
117         iter = actor.getOperators().iterator();
118         verify(iter.next()).start();
119         // this one isn't configured, so shouldn't attempt to start it
120         verify(iter.next(), never()).start();
121         // this one threw an exception
122         iter.next();
123         verify(iter.next()).start();
124
125         // no other types of operations
126         verify(oper1, never()).stop();
127         verify(oper1, never()).shutdown();
128     }
129
130     @Test
131     public void testDoStop() {
132         actor.configure(params);
133         actor.start();
134         assertEquals(4, actor.getOperationNames().size());
135
136         // arrange for second operator to throw an exception
137         Iterator<Operator> iter = actor.getOperators().iterator();
138         iter.next();
139         when(iter.next().stop()).thenThrow(new IllegalStateException(EXPECTED_EXCEPTION));
140
141         /*
142          * Stop the actor.
143          */
144         actor.stop();
145         assertFalse(actor.isAlive());
146
147         iter = actor.getOperators().iterator();
148         verify(iter.next()).stop();
149         // this one threw an exception
150         iter.next();
151         verify(iter.next()).stop();
152         verify(iter.next()).stop();
153
154         // no additional types of operations
155         verify(oper1).configure(any());
156         verify(oper1).start();
157
158         // no other types of operation
159         verify(oper1, never()).shutdown();
160     }
161
162     @Test
163     public void testDoShutdown() {
164         actor.configure(params);
165         actor.start();
166         assertEquals(4, actor.getOperationNames().size());
167
168         // arrange for second operator to throw an exception
169         Iterator<Operator> iter = actor.getOperators().iterator();
170         iter.next();
171         doThrow(new IllegalStateException(EXPECTED_EXCEPTION)).when(iter.next()).shutdown();
172
173         /*
174          * Stop the actor.
175          */
176         actor.shutdown();
177         assertFalse(actor.isAlive());
178
179         iter = actor.getOperators().iterator();
180         verify(iter.next()).shutdown();
181         // this one threw an exception
182         iter.next();
183         verify(iter.next()).shutdown();
184         verify(iter.next()).shutdown();
185
186         // no additional types of operations
187         verify(oper1).configure(any());
188         verify(oper1).start();
189
190         // no other types of operation
191         verify(oper1, never()).stop();
192     }
193
194     @Test
195     public void testSetOperators() {
196         // cannot set operators if already configured
197         actor.configure(params);
198         assertThatIllegalStateException().isThrownBy(() -> actor.setOperators(Collections.emptyList()));
199
200         /*
201          * make an actor where operators two and four have names that are duplicates of
202          * the others
203          */
204         oper2 = spy(new MyOper(OPER1));
205         oper4 = spy(new MyOper(OPER3));
206
207         actor = makeActor(oper1, oper2, oper3, oper4);
208
209         assertEquals(2, actor.getOperationNames().size());
210
211         assertSame(oper1, actor.getOperator(OPER1));
212         assertSame(oper3, actor.getOperator(OPER3));
213     }
214
215     @Test
216     public void testGetOperator() {
217         assertSame(oper1, actor.getOperator(OPER1));
218         assertSame(oper3, actor.getOperator(OPER3));
219
220         assertThatIllegalArgumentException().isThrownBy(() -> actor.getOperator("unknown name"));
221     }
222
223     @Test
224     public void testGetOperators() {
225         // @formatter:off
226         assertEquals("[add, divide, multiply, subtract]",
227                         actor.getOperators().stream()
228                             .map(Operator::getName)
229                             .sorted()
230                             .collect(Collectors.toList())
231                             .toString());
232         // @formatter:on
233     }
234
235     @Test
236     public void testGetOperationNames() {
237         // @formatter:off
238         assertEquals("[add, divide, multiply, subtract]",
239                         actor.getOperationNames().stream()
240                             .sorted()
241                             .collect(Collectors.toList())
242                             .toString());
243         // @formatter:on
244     }
245
246     @Test
247     public void testDoConfigure() {
248         actor.configure(params);
249         assertTrue(actor.isConfigured());
250
251         verify(oper1).configure(sub1);
252         verify(oper2).configure(sub2);
253         verify(oper3).configure(sub3);
254         verify(oper4).configure(sub4);
255
256         // no other types of operations
257         verify(oper1, never()).start();
258         verify(oper1, never()).stop();
259         verify(oper1, never()).shutdown();
260     }
261
262     /**
263      * Tests doConfigure() where operators throw parameter validation and runtime
264      * exceptions.
265      */
266     @Test
267     public void testDoConfigureExceptions() {
268         makeValidException(oper1);
269         makeRuntimeException(oper2);
270         makeValidException(oper3);
271
272         actor.configure(params);
273         assertTrue(actor.isConfigured());
274     }
275
276     /**
277      * Tests doConfigure(). Arranges for the following:
278      * <ul>
279      * <li>one operator is configured, but has parameters</li>
280      * <li>another operator is configured, but has no parameters</li>
281      * <li>another operator has no parameters and is not configured</li>
282      * </ul>
283      */
284     @Test
285     public void testDoConfigureConfigure() {
286         // need mutable parameters
287         params = new TreeMap<>(params);
288
289         // configure one operator
290         oper1.configure(sub1);
291
292         // configure another and remove its parameters
293         oper2.configure(sub2);
294         params.remove(OPER2);
295
296         // create a new, unconfigured actor
297         Operator oper5 = spy(new MyOper("UNCONFIGURED"));
298         actor = makeActor(oper1, oper2, oper3, oper4, oper5);
299
300         /*
301          * Configure it.
302          */
303         actor.configure(params);
304         assertTrue(actor.isConfigured());
305
306         // this should have been configured again
307         verify(oper1, times(2)).configure(sub1);
308
309         // no parameters, so this should not have been configured again
310         verify(oper2).configure(sub2);
311
312         // these were only configured once
313         verify(oper3).configure(sub3);
314         verify(oper4).configure(sub4);
315
316         // never configured
317         verify(oper5, never()).configure(any());
318         assertFalse(oper5.isConfigured());
319
320         // start and verify that all are started except for the last
321         actor.start();
322         verify(oper1).start();
323         verify(oper2).start();
324         verify(oper3).start();
325         verify(oper4).start();
326         verify(oper5, never()).start();
327     }
328
329     /**
330      * Arranges for an operator to throw a validation exception when
331      * {@link Operator#configure(Map)} is invoked.
332      *
333      * @param oper operator of interest
334      */
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());
339     }
340
341     /**
342      * Arranges for an operator to throw a runtime exception when
343      * {@link Operator#configure(Map)} is invoked.
344      *
345      * @param oper operator of interest
346      */
347     private void makeRuntimeException(Operator oper) {
348         IllegalStateException ex = new IllegalStateException(EXPECTED_EXCEPTION);
349         doThrow(ex).when(oper).configure(any());
350     }
351
352     @Test
353     public void testMakeOperatorParameters() {
354         actor.configure(params);
355
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);
361     }
362
363     /**
364      * Makes an actor with the given operators.
365      *
366      * @param operators associated operators
367      * @return a new actor
368      */
369     private ActorImpl makeActor(Operator... operators) {
370         return new ActorImpl(ACTOR_NAME, operators);
371     }
372
373     private static class MyOper extends OperatorPartial implements Operator {
374
375         public MyOper(String name) {
376             super(ACTOR_NAME, name);
377         }
378     }
379 }