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