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