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