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