Default ActorImpl should have an "operations" property
[policy/models.git] / models-interactions / model-actors / actorServiceProvider / src / test / java / org / onap / policy / controlloop / actorserviceprovider / ActorServiceTest.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;
22
23 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
24 import static org.junit.Assert.assertEquals;
25 import static org.junit.Assert.assertFalse;
26 import static org.junit.Assert.assertNotNull;
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.Arrays;
38 import java.util.Collection;
39 import java.util.Collections;
40 import java.util.Iterator;
41 import java.util.Map;
42 import java.util.TreeMap;
43 import java.util.function.Consumer;
44 import java.util.stream.Collectors;
45 import org.junit.Before;
46 import org.junit.Test;
47 import org.onap.policy.common.parameters.ObjectValidationResult;
48 import org.onap.policy.common.parameters.ValidationStatus;
49 import org.onap.policy.controlloop.actorserviceprovider.impl.ActorImpl;
50 import org.onap.policy.controlloop.actorserviceprovider.parameters.ActorParams;
51 import org.onap.policy.controlloop.actorserviceprovider.parameters.ParameterValidationRuntimeException;
52 import org.onap.policy.controlloop.actorserviceprovider.spi.Actor;
53
54 public class ActorServiceTest {
55     private static final String EXPECTED_EXCEPTION = "expected exception";
56     private static final String ACTOR1 = "actor A";
57     private static final String ACTOR2 = "actor B";
58     private static final String ACTOR3 = "actor C";
59     private static final String ACTOR4 = "actor D";
60
61     private Actor actor1;
62     private Actor actor2;
63     private Actor actor3;
64     private Actor actor4;
65
66     private Map<String, Object> sub1;
67     private Map<String, Object> sub2;
68     private Map<String, Object> sub3;
69     private Map<String, Object> sub4;
70     private Map<String, Object> params;
71
72     private ActorService service;
73
74
75     /**
76      * Initializes the fields, including a fully populated {@link #service}.
77      */
78     @Before
79     public void setUp() {
80         actor1 = spy(new ActorImpl(ACTOR1));
81         actor2 = spy(new ActorImpl(ACTOR2));
82         actor3 = spy(new ActorImpl(ACTOR3));
83         actor4 = spy(new ActorImpl(ACTOR4));
84
85         sub1 = Map.of("sub A", "value A", ActorParams.OPERATIONS_FIELD, Collections.emptyMap());
86         sub2 = Map.of("sub B", "value B", ActorParams.OPERATIONS_FIELD, Collections.emptyMap());
87         sub3 = Map.of("sub C", "value C", ActorParams.OPERATIONS_FIELD, Collections.emptyMap());
88         sub4 = Map.of("sub D", "value D", ActorParams.OPERATIONS_FIELD, Collections.emptyMap());
89
90         params = Map.of(ACTOR1, sub1, ACTOR2, sub2, ACTOR3, sub3, ACTOR4, sub4);
91
92         service = makeService(actor1, actor2, actor3, actor4);
93     }
94
95     @Test
96     public void testActorService_testBuildList() {
97         /*
98          * make a service where actors two and four have names that are duplicates of the
99          * others
100          */
101
102         /*
103          * actor0 has a higher sequence number than actor1, so it should be discarded,
104          * even though it will be examined first
105          */
106         Actor actor0 = spy(new ActorImpl(ACTOR1) {
107             @Override
108             public int getSequenceNumber() {
109                 return 10000;
110             }
111         });
112
113         actor2 = spy(new ActorImpl(ACTOR1));
114         actor4 = spy(new ActorImpl(ACTOR3));
115
116         service = makeService(actor0, actor1, actor2, actor3, actor4);
117
118         assertEquals(2, service.getActorNames().size());
119
120         assertSame(actor1, service.getActor(ACTOR1));
121         assertSame(actor3, service.getActor(ACTOR3));
122     }
123
124     @Test
125     public void testDoStart() {
126         service.configure(params);
127
128         setUpOp("testDoStart", actor -> when(actor.isConfigured()).thenReturn(false), Actor::start);
129
130         /*
131          * Start the service.
132          */
133         service.start();
134         assertTrue(service.isAlive());
135
136         Iterator<Actor> iter = service.getActors().iterator();
137         verify(iter.next()).start();
138         verify(iter.next(), never()).start();
139         verify(iter.next()).start();
140         verify(iter.next()).start();
141
142         // no additional types of operations
143         verify(actor1).configure(any());
144
145         // no other types of operations
146         verify(actor1, never()).stop();
147         verify(actor1, never()).shutdown();
148     }
149
150     @Test
151     public void testDoStop() {
152         service.configure(params);
153         service.start();
154
155         setUpOp("testDoStop", Actor::stop, Actor::stop);
156
157         /*
158          * Stop the service.
159          */
160         service.stop();
161         assertFalse(service.isAlive());
162
163         Iterator<Actor> iter = service.getActors().iterator();
164         verify(iter.next()).stop();
165         verify(iter.next(), times(2)).stop();
166         verify(iter.next()).stop();
167         verify(iter.next()).stop();
168
169         // no additional types of operations
170         verify(actor1).configure(any());
171         verify(actor1).start();
172
173         // no other types of operation
174         verify(actor1, never()).shutdown();
175     }
176
177     @Test
178     public void testDoShutdown() {
179         service.configure(params);
180         service.start();
181
182         setUpOp("testDoShutdown", Actor::shutdown, Actor::shutdown);
183
184         /*
185          * Shut down the service.
186          */
187         service.shutdown();
188         assertFalse(service.isAlive());
189
190         Iterator<Actor> iter = service.getActors().iterator();
191         verify(iter.next()).shutdown();
192         verify(iter.next(), times(2)).shutdown();
193         verify(iter.next()).shutdown();
194         verify(iter.next()).shutdown();
195
196         // no additional types of operations
197         verify(actor1).configure(any());
198         verify(actor1).start();
199
200         // no other types of operation
201         verify(actor1, never()).stop();
202     }
203
204     /**
205      * Applies an operation to the second actor, and then arranges for the third actor to
206      * throw an exception when its operation is performed.
207      *
208      * @param testName test name
209      * @param oper2 operation to apply to the second actor
210      * @param oper3 operation to apply to the third actor
211      */
212     private void setUpOp(String testName, Consumer<Actor> oper2, Consumer<Actor> oper3) {
213         Collection<Actor> actors = service.getActors();
214         assertEquals(testName, 4, actors.size());
215
216         Iterator<Actor> iter = actors.iterator();
217
218         // leave the first alone
219         iter.next();
220
221         // apply oper2 to the second actor
222         oper2.accept(iter.next());
223
224         // throw an exception in the third
225         oper3.accept(doThrow(new IllegalStateException(EXPECTED_EXCEPTION)).when(iter.next()));
226
227         // leave the fourth alone
228         iter.next();
229     }
230
231     @Test
232     public void testGetActor() {
233         assertSame(actor1, service.getActor(ACTOR1));
234         assertSame(actor3, service.getActor(ACTOR3));
235
236         assertThatIllegalArgumentException().isThrownBy(() -> service.getActor("unknown actor"));
237     }
238
239     @Test
240     public void testGetActors() {
241         // @formatter:off
242         assertEquals("[actor A, actor B, actor C, actor D]",
243                         service.getActors().stream()
244                             .map(Actor::getName)
245                             .sorted()
246                             .collect(Collectors.toList())
247                             .toString());
248         // @formatter:on
249     }
250
251     @Test
252     public void testGetActorNames() {
253         // @formatter:off
254         assertEquals("[actor A, actor B, actor C, actor D]",
255                         service.getActorNames().stream()
256                             .sorted()
257                             .collect(Collectors.toList())
258                             .toString());
259         // @formatter:on
260     }
261
262     @Test
263     public void testDoConfigure() {
264         service.configure(params);
265         assertTrue(service.isConfigured());
266
267         verify(actor1).configure(sub1);
268         verify(actor2).configure(sub2);
269         verify(actor3).configure(sub3);
270         verify(actor4).configure(sub4);
271
272         // no other types of operations
273         verify(actor1, never()).start();
274         verify(actor1, never()).stop();
275         verify(actor1, never()).shutdown();
276     }
277
278     /**
279      * Tests doConfigure() where actors throw parameter validation and runtime exceptions.
280      */
281     @Test
282     public void testDoConfigureExceptions() {
283         makeValidException(actor1);
284         makeRuntimeException(actor2);
285         makeValidException(actor3);
286
287         service.configure(params);
288         assertTrue(service.isConfigured());
289     }
290
291     /**
292      * Tests doConfigure(). Arranges for the following:
293      * <ul>
294      * <li>one actor is configured, but has parameters</li>
295      * <li>another actor is configured, but has no parameters</li>
296      * <li>another actor has no parameters and is not configured</li>
297      * </ul>
298      */
299     @Test
300     public void testDoConfigureConfigure() {
301         // need mutable parameters
302         params = new TreeMap<>(params);
303
304         // configure one actor
305         actor1.configure(sub1);
306
307         // configure another and remove its parameters
308         actor2.configure(sub2);
309         params.remove(ACTOR2);
310
311         // create a new, unconfigured actor
312         ActorImpl actor5 = spy(new ActorImpl("UNCONFIGURED"));
313         service = makeService(actor1, actor2, actor3, actor4, actor5);
314
315         /*
316          * Configure it.
317          */
318         service.configure(params);
319         assertTrue(service.isConfigured());
320
321         // this should have been configured again
322         verify(actor1, times(2)).configure(sub1);
323
324         // no parameters, so this should not have been configured again
325         verify(actor2).configure(sub2);
326
327         // these were only configured once
328         verify(actor3).configure(sub3);
329         verify(actor4).configure(sub4);
330
331         // never configured
332         verify(actor5, never()).configure(any());
333         assertFalse(actor5.isConfigured());
334
335         // start and verify that all are started except for the last
336         service.start();
337         verify(actor1).start();
338         verify(actor2).start();
339         verify(actor3).start();
340         verify(actor4).start();
341         verify(actor5, never()).start();
342     }
343
344     /**
345      * Arranges for an actor to throw a validation exception when
346      * {@link Actor#configure(Map)} is invoked.
347      *
348      * @param actor actor of interest
349      */
350     private void makeValidException(Actor actor) {
351         ParameterValidationRuntimeException ex = new ParameterValidationRuntimeException(
352                         new ObjectValidationResult(actor.getName(), null, ValidationStatus.INVALID, "null"));
353         doThrow(ex).when(actor).configure(any());
354     }
355
356     /**
357      * Arranges for an actor to throw a runtime exception when
358      * {@link Actor#configure(Map)} is invoked.
359      *
360      * @param actor actor of interest
361      */
362     private void makeRuntimeException(Actor actor) {
363         IllegalStateException ex = new IllegalStateException(EXPECTED_EXCEPTION);
364         doThrow(ex).when(actor).configure(any());
365     }
366
367     @Test
368     public void testLoadActors() {
369         ActorService service = new ActorService();
370         assertFalse(service.getActors().isEmpty());
371         assertNotNull(service.getActor(DummyActor.class.getSimpleName()));
372     }
373
374     /**
375      * Makes an actor service whose {@link ActorService#loadActors()} method returns the
376      * given actors.
377      *
378      * @param actors actors to be returned
379      * @return a new actor service
380      */
381     private ActorService makeService(Actor... actors) {
382         return new ActorService() {
383             @Override
384             protected Iterable<Actor> loadActors() {
385                 return Arrays.asList(actors);
386             }
387         };
388     }
389 }