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