Removing deprecated DMAAP library
[policy/drools-pdp.git] / policy-management / src / test / java / org / onap / policy / drools / system / internal / AggregatedPolicyControllerTest.java
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2018-2021 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.drools.system.internal;
23
24 import static org.assertj.core.api.Assertions.assertThatCode;
25 import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
26 import static org.assertj.core.api.Assertions.assertThatThrownBy;
27 import static org.junit.jupiter.api.Assertions.assertEquals;
28 import static org.junit.jupiter.api.Assertions.assertFalse;
29 import static org.junit.jupiter.api.Assertions.assertNotNull;
30 import static org.junit.jupiter.api.Assertions.assertThrows;
31 import static org.junit.jupiter.api.Assertions.assertTrue;
32 import static org.mockito.ArgumentMatchers.any;
33 import static org.mockito.Mockito.mock;
34 import static org.mockito.Mockito.never;
35 import static org.mockito.Mockito.times;
36 import static org.mockito.Mockito.verify;
37 import static org.mockito.Mockito.when;
38
39 import java.util.Arrays;
40 import java.util.List;
41 import java.util.Properties;
42 import java.util.function.BiConsumer;
43 import java.util.function.Consumer;
44 import org.junit.jupiter.api.BeforeEach;
45 import org.junit.jupiter.api.Test;
46 import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
47 import org.onap.policy.common.endpoints.event.comm.TopicEndpoint;
48 import org.onap.policy.common.endpoints.event.comm.TopicSink;
49 import org.onap.policy.common.endpoints.event.comm.TopicSource;
50 import org.onap.policy.common.utils.gson.GsonTestUtils;
51 import org.onap.policy.drools.controller.DroolsController;
52 import org.onap.policy.drools.controller.DroolsControllerFactory;
53 import org.onap.policy.drools.features.PolicyControllerFeatureApi;
54 import org.onap.policy.drools.persistence.SystemPersistence;
55 import org.onap.policy.drools.protocol.configuration.DroolsConfiguration;
56 import org.onap.policy.drools.system.GsonMgmtTestBuilder;
57
58 class AggregatedPolicyControllerTest {
59
60     private static final String AGG_NAME = "agg-name";
61     private static final String SINK_TOPIC1 = "sink-a";
62     private static final String SINK_TOPIC2 = "sink-b";
63     private static final String SOURCE_TOPIC1 = "source-a";
64     private static final String SOURCE_TOPIC2 = "source-b";
65
66     private static final String EXPECTED = "expected exception";
67
68     private static final String MY_EVENT = "my-event";
69
70     private static final String ARTIFACT1 = "artifact-a";
71     private static final String GROUP1 = "group-a";
72     private static final String VERSION1 = "version-a";
73
74     private static final String ARTIFACT2 = "artifact-b";
75     private static final String GROUP2 = "group-b";
76     private static final String VERSION2 = "version-b";
77
78     private Properties properties;
79     private TopicEndpoint endpointMgr;
80     private List<TopicSource> sources;
81     private TopicSource source1;
82     private TopicSource source2;
83     private List<TopicSink> sinks;
84     private TopicSink sink1;
85     private TopicSink sink2;
86     private SystemPersistence persist;
87     private DroolsControllerFactory droolsFactory;
88     private DroolsController drools;
89     private DroolsConfiguration config;
90     private List<PolicyControllerFeatureApi> providers;
91     private PolicyControllerFeatureApi prov1;
92     private PolicyControllerFeatureApi prov2;
93     private AggregatedPolicyController apc;
94
95     /**
96      * Initializes the object to be tested.
97      */
98     @BeforeEach
99     public void setUp() {
100         properties = new Properties();
101
102         source1 = mock(TopicSource.class);
103         source2 = mock(TopicSource.class);
104         when(source1.getTopic()).thenReturn(SOURCE_TOPIC1);
105         when(source2.getTopic()).thenReturn(SOURCE_TOPIC2);
106
107         sink1 = mock(TopicSink.class);
108         sink2 = mock(TopicSink.class);
109         when(sink1.getTopic()).thenReturn(SINK_TOPIC1);
110         when(sink2.getTopic()).thenReturn(SINK_TOPIC2);
111
112         sources = Arrays.asList(source1, source2);
113         sinks = Arrays.asList(sink1, sink2);
114
115         endpointMgr = mock(TopicEndpoint.class);
116         when(endpointMgr.addTopicSources(any(Properties.class))).thenReturn(sources);
117         when(endpointMgr.addTopicSinks(any(Properties.class))).thenReturn(sinks);
118
119         persist = mock(SystemPersistence.class);
120
121         drools = mock(DroolsController.class);
122         when(drools.start()).thenReturn(true);
123         when(drools.stop()).thenReturn(true);
124         when(drools.offer(any(), any())).thenReturn(true);
125         when(drools.deliver(any(), any())).thenReturn(true);
126         when(drools.lock()).thenReturn(true);
127         when(drools.unlock()).thenReturn(true);
128         when(drools.getArtifactId()).thenReturn(ARTIFACT1);
129         when(drools.getGroupId()).thenReturn(GROUP1);
130         when(drools.getVersion()).thenReturn(VERSION1);
131
132         config = mock(DroolsConfiguration.class);
133         when(config.getArtifactId()).thenReturn(ARTIFACT2);
134         when(config.getGroupId()).thenReturn(GROUP2);
135         when(config.getVersion()).thenReturn(VERSION2);
136
137         droolsFactory = mock(DroolsControllerFactory.class);
138         when(droolsFactory.build(any(), any(), any())).thenReturn(drools);
139
140         prov1 = mock(PolicyControllerFeatureApi.class);
141         prov2 = mock(PolicyControllerFeatureApi.class);
142
143         providers = Arrays.asList(prov1, prov2);
144
145         apc = new AggregatedPolicyControllerImpl(AGG_NAME, properties);
146     }
147
148     @Test
149     void testFactory() {
150         apc = new AggregatedPolicyController(AGG_NAME, properties);
151         assertNotNull(apc.getDroolsFactory());
152         assertNotNull(apc.getEndpointManager());
153         assertNotNull(apc.getProviders());
154         assertNotNull(apc.getPersistenceManager());
155     }
156
157     @Test
158     void testAggregatedPolicyController_() {
159         verify(persist).storeController(AGG_NAME, properties);
160     }
161
162     @Test
163     void testInitDrools_Ex() {
164         assertThrows(IllegalArgumentException.class, () ->
165         new AggregatedPolicyControllerImpl(AGG_NAME, properties) {
166             @Override
167             protected DroolsControllerFactory getDroolsFactory() {
168                 throw new RuntimeException(EXPECTED);
169             }
170         });
171     }
172
173     @Test
174     void testInitDrools_Error() {
175         assertThrows(IllegalArgumentException.class, () ->
176         new AggregatedPolicyControllerImpl(AGG_NAME, properties) {
177             @Override
178             protected DroolsControllerFactory getDroolsFactory() {
179                 throw new LinkageError(EXPECTED);
180             }
181         });
182     }
183
184     @Test
185     void testUpdateDrools_ConfigVariations() {
186
187         // config should return same values as current controller
188         when(config.getArtifactId()).thenReturn(ARTIFACT1.toUpperCase());
189         when(config.getGroupId()).thenReturn(GROUP1.toUpperCase());
190         when(config.getVersion()).thenReturn(VERSION1.toUpperCase());
191
192         assertTrue(apc.updateDrools(config));
193
194         // number of times store should have been called
195         int count = 0;
196
197         // invoked once during construction, but shouldn't be invoked during update
198         verify(persist, times(++count)).storeController(any(), any());
199
200
201         // different artifact
202         when(config.getArtifactId()).thenReturn(ARTIFACT2);
203
204         assertTrue(apc.updateDrools(config));
205
206         // should be invoked during update
207         verify(persist, times(++count)).storeController(any(), any());
208
209
210         // different group
211         when(config.getArtifactId()).thenReturn(ARTIFACT1);
212         when(config.getGroupId()).thenReturn(GROUP2);
213
214         assertTrue(apc.updateDrools(config));
215
216         // should be invoked during update
217         verify(persist, times(++count)).storeController(any(), any());
218
219
220         // different version
221         when(config.getGroupId()).thenReturn(GROUP1);
222         when(config.getVersion()).thenReturn(VERSION2);
223
224         assertTrue(apc.updateDrools(config));
225
226         // should be invoked during update
227         verify(persist, times(++count)).storeController(any(), any());
228
229
230         /*
231          * Exception case.
232          */
233         when(drools.lock()).thenThrow(new IllegalArgumentException(EXPECTED));
234         when(drools.unlock()).thenThrow(new IllegalArgumentException(EXPECTED));
235
236         assertFalse(apc.updateDrools(config));
237     }
238
239     @Test
240     void testUpdateDrools_LockVariations() {
241         // not locked
242         apc.updateDrools(config);
243         verify(drools, never()).lock();
244         verify(drools).unlock();
245
246         // locked
247         setUp();
248         apc.lock();
249         apc.updateDrools(config);
250         verify(drools, times(2)).lock();
251         verify(drools, never()).unlock();
252     }
253
254     @Test
255     void testUpdateDrools_AliveVariations() {
256         // not started
257         apc.updateDrools(config);
258         verify(drools, never()).start();
259         verify(drools).stop();
260
261         // started
262         setUp();
263         apc.start();
264         apc.updateDrools(config);
265         verify(drools, times(2)).start();
266         verify(drools, never()).stop();
267     }
268
269     @Test
270     void testSerialize() {
271         GsonTestUtils gson = new GsonMgmtTestBuilder().addDroolsControllerMock().addTopicSinkMock().addTopicSourceMock()
272                         .build();
273         assertThatCode(() -> gson.compareGson(apc, AggregatedPolicyControllerTest.class)).doesNotThrowAnyException();
274     }
275
276     @Test
277     void testGetName() {
278         assertEquals(AGG_NAME, apc.getName());
279     }
280
281     @Test
282     void testStart() {
283         // arrange for first provider to throw exceptions
284         when(prov1.beforeStart(any())).thenThrow(new RuntimeException(EXPECTED));
285         when(prov1.afterStart(any())).thenThrow(new RuntimeException(EXPECTED));
286
287         // arrange for first sink to throw exception
288         when(sink1.start()).thenThrow(new RuntimeException(EXPECTED));
289
290         // start it
291         assertTrue(apc.start());
292
293         assertTrue(apc.isAlive());
294
295         verify(prov1).beforeStart(apc);
296         verify(prov2).beforeStart(apc);
297
298         verify(source1).register(apc);
299         verify(source2).register(apc);
300
301         verify(sink1).start();
302         verify(sink2).start();
303
304         verify(prov1).afterStart(apc);
305         verify(prov2).afterStart(apc);
306
307         checkBeforeAfter(
308             (prov, flag) -> when(prov.beforeStart(apc)).thenReturn(flag),
309             (prov, flag) -> when(prov.afterStart(apc)).thenReturn(flag),
310             () -> apc.start(),
311             prov -> verify(prov).beforeStart(apc),
312             () -> verify(source1).register(apc),
313             prov -> verify(prov).afterStart(apc));
314     }
315
316     @Test
317     void testStart_AlreadyStarted() {
318         apc.start();
319
320         // re-start it
321         assertTrue(apc.start());
322
323         assertTrue(apc.isAlive());
324
325         // these should now have been called twice
326         verify(prov1, times(2)).beforeStart(apc);
327         verify(prov2, times(2)).beforeStart(apc);
328
329         // these should still only have been called once
330         verify(source1).register(apc);
331         verify(sink1).start();
332         verify(prov1).afterStart(apc);
333     }
334
335     @Test
336     void testStart_Locked() {
337         apc.lock();
338
339         // start it
340         assertThatIllegalStateException().isThrownBy(() -> apc.start());
341
342         assertFalse(apc.isAlive());
343
344         // should call beforeStart(), but stop after that
345         verify(prov1).beforeStart(apc);
346         verify(prov2).beforeStart(apc);
347
348         verify(source1, never()).register(apc);
349         verify(sink1, never()).start();
350         verify(prov1, never()).afterStart(apc);
351     }
352
353     @Test
354     void testStop() {
355         // arrange for first provider to throw exceptions
356         when(prov1.beforeStop(any())).thenThrow(new RuntimeException(EXPECTED));
357         when(prov1.afterStop(any())).thenThrow(new RuntimeException(EXPECTED));
358
359         // start it
360         apc.start();
361
362         // now stop it
363         assertTrue(apc.stop());
364
365         assertFalse(apc.isAlive());
366
367         verify(prov1).beforeStop(apc);
368         verify(prov2).beforeStop(apc);
369
370         verify(source1).unregister(apc);
371         verify(source2).unregister(apc);
372
373         verify(prov1).afterStop(apc);
374         verify(prov2).afterStop(apc);
375
376         // ensure no shutdown operations were called
377         verify(prov1, never()).beforeShutdown(apc);
378         verify(droolsFactory, never()).shutdown(drools);
379         verify(prov2, never()).afterShutdown(apc);
380
381         checkBeforeAfter(
382             (prov, flag) -> when(prov.beforeStop(apc)).thenReturn(flag),
383             (prov, flag) -> when(prov.afterStop(apc)).thenReturn(flag),
384             () -> {
385                 apc.start();
386                 apc.stop();
387             },
388             prov -> verify(prov).beforeStop(apc),
389             () -> verify(source1).unregister(apc),
390             prov -> verify(prov).afterStop(apc));
391     }
392
393     @Test
394     void testStop_AlreadyStopped() {
395         apc.start();
396         apc.stop();
397
398         // now re-stop it
399         assertTrue(apc.stop());
400
401         // called again
402         verify(prov1, times(2)).beforeStop(apc);
403         verify(prov2, times(2)).beforeStop(apc);
404
405         // should NOT be called again
406         verify(source1).unregister(apc);
407         verify(prov1).afterStop(apc);
408     }
409
410     @Test
411     void testShutdown() {
412         // arrange for first provider to throw exceptions
413         when(prov1.beforeShutdown(any())).thenThrow(new RuntimeException(EXPECTED));
414         when(prov1.afterShutdown(any())).thenThrow(new RuntimeException(EXPECTED));
415
416         // start it
417         apc.start();
418
419         // now shut it down
420         apc.shutdown();
421
422         verify(prov1).beforeShutdown(apc);
423         verify(prov2).beforeShutdown(apc);
424
425         assertFalse(apc.isAlive());
426
427         verify(prov1).afterStop(apc);
428         verify(prov2).afterStop(apc);
429
430         verify(droolsFactory).shutdown(drools);
431
432         verify(prov1).afterShutdown(apc);
433         verify(prov2).afterShutdown(apc);
434
435         // ensure no halt operation was called
436         verify(prov1, never()).beforeHalt(apc);
437
438         checkBeforeAfter(
439             (prov, flag) -> when(prov.beforeShutdown(apc)).thenReturn(flag),
440             (prov, flag) -> when(prov.afterShutdown(apc)).thenReturn(flag),
441             () -> {
442                 apc.start();
443                 apc.shutdown();
444             },
445             prov -> verify(prov).beforeShutdown(apc),
446             () -> verify(source1).unregister(apc),
447             prov -> verify(prov).afterShutdown(apc));
448     }
449
450     @Test
451     void testHalt() {
452         // arrange for first provider to throw exceptions
453         when(prov1.beforeHalt(any())).thenThrow(new RuntimeException(EXPECTED));
454         when(prov1.afterHalt(any())).thenThrow(new RuntimeException(EXPECTED));
455
456         // start it
457         apc.start();
458
459         // now halt it
460         apc.halt();
461
462         verify(prov1).beforeHalt(apc);
463         verify(prov2).beforeHalt(apc);
464
465         assertFalse(apc.isAlive());
466
467         verify(prov1).beforeStop(apc);
468         verify(prov2).beforeStop(apc);
469
470         verify(droolsFactory).destroy(drools);
471         verify(persist).deleteController(AGG_NAME);
472
473         verify(prov1).afterHalt(apc);
474         verify(prov2).afterHalt(apc);
475
476         // ensure no shutdown operation was called
477         verify(prov1, never()).beforeShutdown(apc);
478
479         checkBeforeAfter(
480             (prov, flag) -> when(prov.beforeHalt(apc)).thenReturn(flag),
481             (prov, flag) -> when(prov.afterHalt(apc)).thenReturn(flag),
482             () -> {
483                 apc.start();
484                 apc.halt();
485             },
486             prov -> verify(prov).beforeHalt(apc),
487             () -> verify(source1).unregister(apc),
488             prov -> verify(prov).afterHalt(apc));
489     }
490
491     @Test
492     void testOnTopicEvent() {
493         // arrange for first provider to throw exceptions
494         when(prov1.beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT))
495                         .thenThrow(new RuntimeException(EXPECTED));
496         when(prov1.afterOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT, true))
497                         .thenThrow(new RuntimeException(EXPECTED));
498
499         // start it
500         apc.start();
501
502         // now offer it
503         apc.onTopicEvent(CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT);
504
505         verify(prov1).beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT);
506         verify(prov2).beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT);
507
508         verify(drools).offer(SOURCE_TOPIC1, MY_EVENT);
509
510         verify(prov1).afterOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT, true);
511         verify(prov2).afterOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT, true);
512
513         checkBeforeAfter(
514             (prov, flag) -> when(prov.beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT))
515                             .thenReturn(flag),
516             (prov, flag) -> when(
517                             prov.afterOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT, true))
518                                             .thenReturn(flag),
519             () -> {
520                 apc.start();
521                 apc.onTopicEvent(CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT);
522             },
523             prov -> verify(prov).beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT),
524             () -> verify(drools).offer(SOURCE_TOPIC1, MY_EVENT),
525             prov -> verify(prov).afterOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT, true));
526     }
527
528     @Test
529     void testOnTopicEvent_Locked() {
530         // start it
531         apc.start();
532
533         apc.lock();
534
535         // now offer it
536         apc.onTopicEvent(CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT);
537
538         verify(prov1, never()).beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT);
539         verify(prov2, never()).beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT);
540
541         // never gets this far
542         verify(drools, never()).offer(SOURCE_TOPIC1, MY_EVENT);
543         verify(prov1, never()).afterOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT, true);
544     }
545
546     @Test
547     void testOnTopicEvent_NotStarted() {
548
549         // offer it
550         apc.onTopicEvent(CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT);
551
552         verify(prov1, never()).beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT);
553         verify(prov2, never()).beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT);
554
555         // never gets this far
556         verify(drools, never()).offer(SOURCE_TOPIC1, MY_EVENT);
557         verify(prov1, never()).afterOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT, true);
558     }
559
560     @Test
561     void testDeliver_testInitSinks() {
562         // arrange for first provider to throw exceptions
563         when(prov1.beforeDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT))
564                         .thenThrow(new RuntimeException(EXPECTED));
565         when(prov1.afterDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT, true))
566                         .thenThrow(new RuntimeException(EXPECTED));
567
568         // start it
569         apc.start();
570
571         // now offer it
572         assertTrue(apc.deliver(CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT));
573
574         verify(prov1).beforeDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT);
575         verify(prov2).beforeDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT);
576
577         verify(drools).deliver(sink1, MY_EVENT);
578         verify(drools, never()).deliver(sink2, MY_EVENT);
579
580         verify(prov1).afterDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT, true);
581         verify(prov2).afterDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT, true);
582
583         // offer to the other topic
584         assertTrue(apc.deliver(CommInfrastructure.NOOP, SINK_TOPIC2, MY_EVENT));
585
586         // now both topics should show one message delivered
587         verify(drools).deliver(sink1, MY_EVENT);
588         verify(drools).deliver(sink2, MY_EVENT);
589
590         checkBeforeAfter(
591             (prov, flag) -> when(prov.beforeDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT))
592                             .thenReturn(flag),
593             (prov, flag) -> when(
594                             prov.afterDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT, true))
595                                             .thenReturn(flag),
596             () -> {
597                 apc.start();
598                 apc.deliver(CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT);
599             },
600             prov -> verify(prov).beforeDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT),
601             () -> verify(drools).deliver(sink1, MY_EVENT),
602             prov -> verify(prov).afterDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT, true));
603     }
604
605     @Test
606     void testDeliver_NullTopic() {
607         assertThrows(IllegalArgumentException.class, () -> validateDeliverFailure(null, MY_EVENT));
608     }
609
610     @Test
611     void testDeliver_EmptyTopic() {
612         assertThrows(IllegalArgumentException.class, () -> validateDeliverFailure("", MY_EVENT));
613     }
614
615     @Test
616     void testDeliver_NullEvent() {
617         assertThrows(IllegalArgumentException.class, () -> validateDeliverFailure(SINK_TOPIC1, null));
618     }
619
620     @Test
621     void testDeliver_NotStarted() {
622         // do NOT start
623         assertThrows(IllegalStateException.class, () -> apc.deliver(CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT));
624     }
625
626     @Test
627     void testDeliver_Locked() {
628         apc.start();
629         apc.lock();
630         assertThrows(IllegalStateException.class, () -> apc.deliver(CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT));
631     }
632
633     @Test
634     void testDeliver_UnknownTopic() {
635         apc.start();
636         assertThrows(IllegalArgumentException.class,
637             () -> apc.deliver(CommInfrastructure.NOOP, "unknown-topic", MY_EVENT));
638     }
639
640     @Test
641     void testIsAlive() {
642         assertFalse(apc.isAlive());
643
644         apc.start();
645         assertTrue(apc.isAlive());
646
647         apc.stop();
648         assertFalse(apc.isAlive());
649     }
650
651     @Test
652     void testLock() {
653         // arrange for first provider to throw exceptions
654         when(prov1.beforeLock(any())).thenThrow(new RuntimeException(EXPECTED));
655         when(prov1.afterLock(any())).thenThrow(new RuntimeException(EXPECTED));
656
657         // start it
658         apc.start();
659
660         // now lock it
661         assertTrue(apc.lock());
662
663         verify(prov1).beforeLock(apc);
664         verify(prov2).beforeLock(apc);
665
666         assertTrue(apc.isLocked());
667
668         verify(drools).lock();
669
670         verify(prov1).afterLock(apc);
671         verify(prov2).afterLock(apc);
672
673         checkBeforeAfter(
674             (prov, flag) -> when(prov.beforeLock(apc)).thenReturn(flag),
675             (prov, flag) -> when(prov.afterLock(apc)).thenReturn(flag),
676             () -> {
677                 apc.start();
678                 apc.lock();
679             },
680             prov -> verify(prov).beforeLock(apc),
681             () -> verify(drools).lock(),
682             prov -> verify(prov).afterLock(apc));
683     }
684
685     @Test
686     void testLock_AlreadyLocked() {
687         apc.start();
688         apc.lock();
689
690         // now re-lock it
691         assertTrue(apc.lock());
692
693         // these should be invoked a second time
694         verify(prov1, times(2)).beforeLock(apc);
695         verify(prov2, times(2)).beforeLock(apc);
696
697         assertTrue(apc.isLocked());
698
699         // these shouldn't be invoked a second time
700         verify(drools).lock();
701         verify(prov1).afterLock(apc);
702     }
703
704     @Test
705     void testUnlock() {
706         // arrange for first provider to throw exceptions
707         when(prov1.beforeUnlock(any())).thenThrow(new RuntimeException(EXPECTED));
708         when(prov1.afterUnlock(any())).thenThrow(new RuntimeException(EXPECTED));
709
710         // start it
711         apc.start();
712         apc.lock();
713
714         // now unlock it
715         assertTrue(apc.unlock());
716
717         verify(prov1).beforeUnlock(apc);
718         verify(prov2).beforeUnlock(apc);
719
720         assertFalse(apc.isLocked());
721
722         verify(drools).unlock();
723
724         verify(prov1).afterUnlock(apc);
725         verify(prov2).afterUnlock(apc);
726
727         checkBeforeAfter(
728             (prov, flag) -> when(prov.beforeUnlock(apc)).thenReturn(flag),
729             (prov, flag) -> when(prov.afterUnlock(apc)).thenReturn(flag),
730             () -> {
731                 apc.start();
732                 apc.lock();
733                 apc.unlock();
734             },
735             prov -> verify(prov).beforeUnlock(apc),
736             () -> verify(drools).unlock(),
737             prov -> verify(prov).afterUnlock(apc));
738     }
739
740     @Test
741     void testUnlock_NotLocked() {
742         apc.start();
743
744         // now unlock it
745         assertTrue(apc.unlock());
746
747         verify(prov1).beforeUnlock(apc);
748         verify(prov2).beforeUnlock(apc);
749
750         assertFalse(apc.isLocked());
751
752         // these shouldn't be invoked
753         verify(drools, never()).unlock();
754         verify(prov1, never()).afterLock(apc);
755     }
756
757     @Test
758     void testIsLocked() {
759         assertFalse(apc.isLocked());
760
761         apc.lock();
762         assertTrue(apc.isLocked());
763
764         apc.unlock();
765         assertFalse(apc.isLocked());
766     }
767
768     @Test
769     void testGetTopicSources() {
770         assertEquals(sources, apc.getTopicSources());
771     }
772
773     @Test
774     void testGetTopicSinks() {
775         assertEquals(sinks, apc.getTopicSinks());
776     }
777
778     @Test
779     void testGetDrools() {
780         assertEquals(drools, apc.getDrools());
781     }
782
783     @Test
784     void testGetProperties() {
785         assertEquals(properties, apc.getProperties());
786     }
787
788     @Test
789     void testToString() {
790         assertTrue(apc.toString().startsWith("AggregatedPolicyController("));
791     }
792
793     private void validateDeliverFailure(String topic, String event) {
794         apc.start();
795         apc.deliver(CommInfrastructure.NOOP, topic, event);
796     }
797
798     /**
799      * Performs an operation that has a beforeXxx method and an afterXxx method. Tries
800      * combinations where beforeXxx and afterXxx return {@code true} and {@code false}.
801      *
802      * @param setBefore function to set the return value of a provider's beforeXxx method
803      * @param setAfter function to set the return value of a provider's afterXxx method
804      * @param action invokes the operation
805      * @param verifyBefore verifies that a provider's beforeXxx method was invoked
806      * @param verifyMiddle verifies that the action occurring between the beforeXxx loop
807      *        and the afterXxx loop was invoked
808      * @param verifyAfter verifies that a provider's afterXxx method was invoked
809      */
810     private void checkBeforeAfter(BiConsumer<PolicyControllerFeatureApi, Boolean> setBefore,
811                     BiConsumer<PolicyControllerFeatureApi, Boolean> setAfter, Runnable action,
812                     Consumer<PolicyControllerFeatureApi> verifyBefore, Runnable verifyMiddle,
813                     Consumer<PolicyControllerFeatureApi> verifyAfter) {
814
815         checkBeforeAfter_FalseFalse(setBefore, setAfter, action, verifyBefore, verifyMiddle, verifyAfter);
816         checkBeforeAfter_FalseTrue(setBefore, setAfter, action, verifyBefore, verifyMiddle, verifyAfter);
817         checkBeforeAfter_TrueFalse(setBefore, setAfter, action, verifyBefore, verifyMiddle, verifyAfter);
818
819         // don't need to test true-true, as it's behavior is a subset of true-false
820     }
821
822     /**
823      * Performs an operation that has a beforeXxx method and an afterXxx method. Tries the
824      * case where both the beforeXxx and afterXxx methods return {@code false}.
825      *
826      * @param setBefore function to set the return value of a provider's beforeXxx method
827      * @param setAfter function to set the return value of a provider's afterXxx method
828      * @param action invokes the operation
829      * @param verifyBefore verifies that a provider's beforeXxx method was invoked
830      * @param verifyMiddle verifies that the action occurring between the beforeXxx loop
831      *        and the afterXxx loop was invoked
832      * @param verifyAfter verifies that a provider's afterXxx method was invoked
833      */
834     private void checkBeforeAfter_FalseFalse(BiConsumer<PolicyControllerFeatureApi, Boolean> setBefore,
835                     BiConsumer<PolicyControllerFeatureApi, Boolean> setAfter, Runnable action,
836                     Consumer<PolicyControllerFeatureApi> verifyBefore, Runnable verifyMiddle,
837                     Consumer<PolicyControllerFeatureApi> verifyAfter) {
838
839         setUp();
840
841         // configure for the test
842         setBefore.accept(prov1, false);
843         setBefore.accept(prov2, false);
844
845         setAfter.accept(prov1, false);
846         setAfter.accept(prov2, false);
847
848         // run the action
849         action.run();
850
851         // verify that various methods were invoked
852         verifyBefore.accept(prov1);
853         verifyBefore.accept(prov2);
854
855         verifyMiddle.run();
856
857         verifyAfter.accept(prov1);
858         verifyAfter.accept(prov2);
859     }
860
861     /**
862      * Performs an operation that has a beforeXxx method and an afterXxx method. Tries the
863      * case where the first provider's afterXxx returns {@code true}, while the others
864      * return {@code false}.
865      *
866      * @param setBefore function to set the return value of a provider's beforeXxx method
867      * @param setAfter function to set the return value of a provider's afterXxx method
868      * @param action invokes the operation
869      * @param verifyBefore verifies that a provider's beforeXxx method was invoked
870      * @param verifyMiddle verifies that the action occurring between the beforeXxx loop
871      *        and the afterXxx loop was invoked
872      * @param verifyAfter verifies that a provider's afterXxx method was invoked
873      */
874     private void checkBeforeAfter_FalseTrue(BiConsumer<PolicyControllerFeatureApi, Boolean> setBefore,
875                     BiConsumer<PolicyControllerFeatureApi, Boolean> setAfter, Runnable action,
876                     Consumer<PolicyControllerFeatureApi> verifyBefore, Runnable verifyMiddle,
877                     Consumer<PolicyControllerFeatureApi> verifyAfter) {
878
879         setUp();
880
881         // configure for the test
882         setBefore.accept(prov1, false);
883         setBefore.accept(prov2, false);
884
885         setAfter.accept(prov1, true);
886         setAfter.accept(prov2, false);
887
888         // run the action
889         action.run();
890
891         // verify that various methods were invoked
892         verifyBefore.accept(prov1);
893         verifyBefore.accept(prov2);
894
895         verifyMiddle.run();
896
897         verifyAfter.accept(prov1);
898         assertThatThrownBy(() -> verifyAfter.accept(prov2)).isInstanceOf(AssertionError.class);
899     }
900
901     /**
902      * Performs an operation that has a beforeXxx method and an afterXxx method. Tries the
903      * case where the first provider's beforeXxx returns {@code true}, while the others
904      * return {@code false}.
905      *
906      * @param setBefore function to set the return value of a provider's beforeXxx method
907      * @param setAfter function to set the return value of a provider's afterXxx method
908      * @param action invokes the operation
909      * @param verifyBefore verifies that a provider's beforeXxx method was invoked
910      * @param verifyMiddle verifies that the action occurring between the beforeXxx loop
911      *        and the afterXxx loop was invoked
912      * @param verifyAfter verifies that a provider's afterXxx method was invoked
913      */
914     private void checkBeforeAfter_TrueFalse(BiConsumer<PolicyControllerFeatureApi, Boolean> setBefore,
915                     BiConsumer<PolicyControllerFeatureApi, Boolean> setAfter, Runnable action,
916                     Consumer<PolicyControllerFeatureApi> verifyBefore, Runnable verifyMiddle,
917                     Consumer<PolicyControllerFeatureApi> verifyAfter) {
918
919         setUp();
920
921         // configure for the test
922         setBefore.accept(prov1, true);
923         setBefore.accept(prov2, false);
924
925         setAfter.accept(prov1, false);
926         setAfter.accept(prov2, false);
927
928         // run the action
929         action.run();
930
931         // verify that various methods were invoked
932         verifyBefore.accept(prov1);
933
934         // remaining methods should not have been invoked
935         assertThatThrownBy(() -> verifyBefore.accept(prov2)).isInstanceOf(AssertionError.class);
936
937         assertThatThrownBy(verifyMiddle::run).isInstanceOf(AssertionError.class);
938
939         assertThatThrownBy(() -> verifyAfter.accept(prov1)).isInstanceOf(AssertionError.class);
940         assertThatThrownBy(() -> verifyAfter.accept(prov2)).isInstanceOf(AssertionError.class);
941     }
942
943     /**
944      * Controller with overrides.
945      */
946     private class AggregatedPolicyControllerImpl extends AggregatedPolicyController {
947
948         public AggregatedPolicyControllerImpl(String name, Properties properties) {
949             super(name, properties);
950         }
951
952         @Override
953         protected SystemPersistence getPersistenceManager() {
954             return persist;
955         }
956
957         @Override
958         protected TopicEndpoint getEndpointManager() {
959             return endpointMgr;
960         }
961
962         @Override
963         protected DroolsControllerFactory getDroolsFactory() {
964             return droolsFactory;
965         }
966
967         @Override
968         protected List<PolicyControllerFeatureApi> getProviders() {
969             return providers;
970         }
971     }
972 }