20b79bde34ede9eb42f0b85f5e5c6a194c108478
[policy/drools-pdp.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2019 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.controller.internal;
22
23 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
24 import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
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.assertSame;
29 import static org.junit.Assert.assertTrue;
30 import static org.mockito.Matchers.any;
31 import static org.mockito.Mockito.doThrow;
32 import static org.mockito.Mockito.mock;
33 import static org.mockito.Mockito.never;
34 import static org.mockito.Mockito.times;
35 import static org.mockito.Mockito.verify;
36 import static org.mockito.Mockito.when;
37
38 import java.util.Arrays;
39 import java.util.Collections;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.TreeMap;
43 import org.junit.Before;
44 import org.junit.Test;
45 import org.kie.api.KieBase;
46 import org.kie.api.definition.KiePackage;
47 import org.kie.api.definition.rule.Query;
48 import org.kie.api.runtime.KieContainer;
49 import org.kie.api.runtime.KieSession;
50 import org.kie.api.runtime.rule.FactHandle;
51 import org.kie.api.runtime.rule.QueryResults;
52 import org.kie.api.runtime.rule.QueryResultsRow;
53 import org.mockito.ArgumentCaptor;
54 import org.mockito.Mock;
55 import org.mockito.MockitoAnnotations;
56 import org.onap.policy.common.endpoints.event.comm.TopicSink;
57 import org.onap.policy.common.utils.services.OrderedServiceImpl;
58 import org.onap.policy.drools.core.PolicyContainer;
59 import org.onap.policy.drools.core.PolicySession;
60 import org.onap.policy.drools.features.DroolsControllerFeatureApi;
61 import org.onap.policy.drools.protocol.coders.EventProtocolCoder;
62 import org.onap.policy.drools.protocol.coders.EventProtocolParams;
63 import org.onap.policy.drools.protocol.coders.JsonProtocolFilter;
64 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration;
65 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomGsonCoder;
66 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.PotentialCoderFilter;
67
68 public class MavenDroolsController2Test {
69     private static final int FACT1_OBJECT = 1000;
70     private static final int FACT3_OBJECT = 1001;
71
72     private static final long FACT_COUNT = 200L;
73
74     private static final String EXPECTED_EXCEPTION = "expected exception";
75     private static final RuntimeException RUNTIME_EX = new RuntimeException(EXPECTED_EXCEPTION);
76     private static final IllegalArgumentException ARG_EX = new IllegalArgumentException(EXPECTED_EXCEPTION);
77
78     private static final String UNKNOWN_CLASS = "unknown class";
79
80     private static final String GROUP = "my-group";
81     private static final String ARTIFACT = "my-artifact";
82     private static final String VERSION = "my-version";
83
84     private static final String GROUP2 = "my-groupB";
85     private static final String ARTIFACT2 = "my-artifactB";
86     private static final String VERSION2 = "my-versionB";
87
88     private static final String TOPIC = "my-topic";
89     private static final String TOPIC2 = "my-topic";
90
91     private static final ClassLoader CLASS_LOADER = MavenDroolsController2Test.class.getClassLoader();
92     private static final int CLASS_LOADER_HASHCODE = CLASS_LOADER.hashCode();
93
94     private static final String SESSION1 = "session-A";
95     private static final String SESSION2 = "session-B";
96     private static final String FULL_SESSION1 = "full-A";
97     private static final String FULL_SESSION2 = "full-B";
98
99     private static final String EVENT_TEXT = "my-event-text";
100     private static final Object EVENT = new Object();
101
102     private static final String QUERY = "my-query";
103     private static final String QUERY2 = "my-query-B";
104     private static final String ENTITY = "my-entity";
105     private static final Object PARM1 = "parmA";
106     private static final Object PARM2 = "parmB";
107
108     @Mock
109     private EventProtocolCoder coderMgr;
110     @Mock
111     private DroolsControllerFeatureApi prov1;
112     @Mock
113     private DroolsControllerFeatureApi prov2;
114     @Mock
115     private OrderedServiceImpl<DroolsControllerFeatureApi> droolsProviders;
116     @Mock
117     private TopicCoderFilterConfiguration decoder1;
118     @Mock
119     private TopicCoderFilterConfiguration decoder2;
120     @Mock
121     private TopicCoderFilterConfiguration encoder1;
122     @Mock
123     private TopicCoderFilterConfiguration encoder2;
124     @Mock
125     private PolicyContainer container;
126     @Mock
127     private CustomGsonCoder gson1;
128     @Mock
129     private CustomGsonCoder gson2;
130     @Mock
131     private PotentialCoderFilter filter1a;
132     @Mock
133     private JsonProtocolFilter jsonFilter1a;
134     @Mock
135     private PotentialCoderFilter filter1b;
136     @Mock
137     private JsonProtocolFilter jsonFilter1b;
138     @Mock
139     private PotentialCoderFilter filter2;
140     @Mock
141     private JsonProtocolFilter jsonFilter2;
142     @Mock
143     private PolicySession sess1;
144     @Mock
145     private PolicySession sess2;
146     @Mock
147     private KieSession kieSess;
148     @Mock
149     private KieSession kieSess2;
150     @Mock
151     private TopicSink sink;
152     @Mock
153     private FactHandle fact1;
154     @Mock
155     private FactHandle fact2;
156     @Mock
157     private FactHandle fact3;
158     @Mock
159     private FactHandle factex;
160     @Mock
161     private KieBase kieBase;
162     @Mock
163     private KiePackage pkg1;
164     @Mock
165     private KiePackage pkg2;
166     @Mock
167     private Query query1;
168     @Mock
169     private Query query2;
170     @Mock
171     private Query query3;
172     @Mock
173     private QueryResults queryResults;
174     @Mock
175     private QueryResultsRow row1;
176     @Mock
177     private QueryResultsRow row2;
178
179     private List<TopicCoderFilterConfiguration> decoders;
180     private List<TopicCoderFilterConfiguration> encoders;
181
182     private MavenDroolsController drools;
183
184     /**
185      * Initializes objects, including the drools controller.
186      */
187     @Before
188     public void setUp() {
189         MockitoAnnotations.initMocks(this);
190
191         when(droolsProviders.getList()).thenReturn(Arrays.asList(prov1, prov2));
192
193         when(coderMgr.isDecodingSupported(GROUP, ARTIFACT, TOPIC)).thenReturn(true);
194         when(coderMgr.decode(GROUP, ARTIFACT, TOPIC, EVENT_TEXT)).thenReturn(EVENT);
195
196         when(kieSess.getFactCount()).thenReturn(FACT_COUNT);
197         when(kieSess.getFactHandles()).thenReturn(Arrays.asList(fact1, fact2, factex, fact3));
198         when(kieSess.getFactHandles(any())).thenReturn(Arrays.asList(fact1, fact3));
199         when(kieSess.getKieBase()).thenReturn(kieBase);
200         when(kieSess.getQueryResults(QUERY, PARM1, PARM2)).thenReturn(queryResults);
201
202         when(kieSess.getObject(fact1)).thenReturn(FACT1_OBJECT);
203         when(kieSess.getObject(fact2)).thenReturn("");
204         when(kieSess.getObject(fact3)).thenReturn(FACT3_OBJECT);
205         when(kieSess.getObject(factex)).thenThrow(RUNTIME_EX);
206
207         when(kieSess2.getFactHandles()).thenReturn(Collections.emptyList());
208
209         when(kieBase.getKiePackages()).thenReturn(Arrays.asList(pkg1, pkg2));
210
211         when(pkg1.getQueries()).thenReturn(Arrays.asList(query3));
212         when(pkg2.getQueries()).thenReturn(Arrays.asList(query2, query1));
213
214         when(query1.getName()).thenReturn(QUERY);
215         when(query2.getName()).thenReturn(QUERY2);
216
217         when(queryResults.iterator()).thenReturn(Arrays.asList(row1, row2).iterator());
218
219         when(row1.get(ENTITY)).thenReturn(FACT1_OBJECT);
220         when(row2.get(ENTITY)).thenReturn(FACT3_OBJECT);
221
222         when(row1.getFactHandle(ENTITY)).thenReturn(fact1);
223         when(row2.getFactHandle(ENTITY)).thenReturn(fact3);
224
225         when(sess1.getKieSession()).thenReturn(kieSess);
226         when(sess2.getKieSession()).thenReturn(kieSess2);
227
228         when(sess1.getName()).thenReturn(SESSION1);
229         when(sess2.getName()).thenReturn(SESSION2);
230
231         when(sess1.getFullName()).thenReturn(FULL_SESSION1);
232         when(sess2.getFullName()).thenReturn(FULL_SESSION2);
233
234         when(container.getClassLoader()).thenReturn(CLASS_LOADER);
235         when(container.getPolicySessions()).thenReturn(Arrays.asList(sess1, sess2));
236         when(container.insertAll(EVENT)).thenReturn(true);
237
238         when(decoder1.getTopic()).thenReturn(TOPIC);
239         when(decoder2.getTopic()).thenReturn(TOPIC2);
240
241         when(encoder1.getTopic()).thenReturn(TOPIC);
242         when(encoder2.getTopic()).thenReturn(TOPIC2);
243
244         decoders = Arrays.asList(decoder1, decoder2);
245         encoders = Arrays.asList(encoder1, encoder2);
246
247         when(decoder1.getCustomGsonCoder()).thenReturn(gson1);
248         when(encoder2.getCustomGsonCoder()).thenReturn(gson2);
249
250         when(filter1a.getCodedClass()).thenReturn(Object.class.getName());
251         when(filter1a.getFilter()).thenReturn(jsonFilter1a);
252
253         when(filter1b.getCodedClass()).thenReturn(String.class.getName());
254         when(filter1b.getFilter()).thenReturn(jsonFilter1b);
255
256         when(filter2.getCodedClass()).thenReturn(Integer.class.getName());
257         when(filter2.getFilter()).thenReturn(jsonFilter2);
258
259         when(decoder1.getCoderFilters()).thenReturn(Arrays.asList(filter1a, filter1b));
260         when(decoder2.getCoderFilters()).thenReturn(Collections.emptyList());
261
262         when(encoder1.getCoderFilters()).thenReturn(Collections.emptyList());
263         when(encoder2.getCoderFilters()).thenReturn(Arrays.asList(filter2));
264
265         when(sink.getTopic()).thenReturn(TOPIC);
266         when(sink.send(EVENT_TEXT)).thenReturn(true);
267
268         drools = new MyDrools(GROUP, ARTIFACT, VERSION, null, null);
269
270         when(coderMgr.encode(TOPIC, EVENT, drools)).thenReturn(EVENT_TEXT);
271     }
272
273     @Test
274     public void testMavenDroolsController_InvalidArgs() {
275         assertThatIllegalArgumentException().isThrownBy(() -> new MyDrools(null, ARTIFACT, VERSION, null, null))
276                         .withMessageContaining("group");
277         assertThatIllegalArgumentException().isThrownBy(() -> new MyDrools("", ARTIFACT, VERSION, null, null))
278                         .withMessageContaining("group");
279
280         assertThatIllegalArgumentException().isThrownBy(() -> new MyDrools(GROUP, null, VERSION, null, null))
281                         .withMessageContaining("artifact");
282         assertThatIllegalArgumentException().isThrownBy(() -> new MyDrools(GROUP, "", VERSION, null, null))
283                         .withMessageContaining("artifact");
284
285         assertThatIllegalArgumentException().isThrownBy(() -> new MyDrools(GROUP, ARTIFACT, null, null, null))
286                         .withMessageContaining("version");
287         assertThatIllegalArgumentException().isThrownBy(() -> new MyDrools(GROUP, ARTIFACT, "", null, null))
288                         .withMessageContaining("version");
289     }
290
291     @Test
292     public void testUpdateToVersion() {
293         // add coders
294         drools.updateToVersion(GROUP, ARTIFACT, VERSION2, decoders, encoders);
295
296         verify(container).updateToVersion(VERSION2);
297
298         // nothing removed the first time
299         verify(coderMgr, never()).removeDecoders(GROUP, ARTIFACT, TOPIC2);
300         verify(coderMgr, never()).removeEncoders(GROUP, ARTIFACT, TOPIC);
301
302         verify(coderMgr, times(2)).addDecoder(any());
303         verify(coderMgr, times(1)).addEncoder(any());
304
305         // remove coders
306         when(container.getVersion()).thenReturn(VERSION2);
307         drools.updateToVersion(GROUP, ARTIFACT, VERSION, null, null);
308
309         verify(container).updateToVersion(VERSION);
310
311         verify(coderMgr, times(2)).removeDecoders(GROUP, ARTIFACT, TOPIC2);
312         verify(coderMgr, times(2)).removeEncoders(GROUP, ARTIFACT, TOPIC);
313
314         // not added again
315         verify(coderMgr, times(2)).addDecoder(any());
316         verify(coderMgr, times(1)).addEncoder(any());
317     }
318
319     @Test
320     public void testUpdateToVersion_Unchanged() {
321         drools.updateToVersion(GROUP, ARTIFACT, VERSION, decoders, encoders);
322
323         verify(coderMgr, never()).addDecoder(any());
324         verify(coderMgr, never()).addEncoder(any());
325     }
326
327     @Test
328     public void testUpdateToVersion_InvalidArgs() {
329         assertThatIllegalArgumentException()
330                         .isThrownBy(() -> drools.updateToVersion(null, ARTIFACT, VERSION, null, null))
331                         .withMessageContaining("group");
332         assertThatIllegalArgumentException().isThrownBy(() -> drools.updateToVersion("", ARTIFACT, VERSION, null, null))
333                         .withMessageContaining("group");
334
335         assertThatIllegalArgumentException().isThrownBy(() -> drools.updateToVersion(GROUP, null, VERSION, null, null))
336                         .withMessageContaining("artifact");
337         assertThatIllegalArgumentException().isThrownBy(() -> drools.updateToVersion(GROUP, "", VERSION, null, null))
338                         .withMessageContaining("artifact");
339
340         assertThatIllegalArgumentException().isThrownBy(() -> drools.updateToVersion(GROUP, ARTIFACT, null, null, null))
341                         .withMessageContaining("version");
342         assertThatIllegalArgumentException().isThrownBy(() -> drools.updateToVersion(GROUP, ARTIFACT, "", null, null))
343                         .withMessageContaining("version");
344
345         assertThatIllegalArgumentException()
346                         .isThrownBy(() -> drools.updateToVersion("no-group-id", ARTIFACT, VERSION, null, null))
347                         .withMessageContaining("BRAINLESS");
348
349         assertThatIllegalArgumentException()
350                         .isThrownBy(() -> drools.updateToVersion(GROUP, "no-artifact-id", VERSION, null, null))
351                         .withMessageContaining("BRAINLESS");
352
353         assertThatIllegalArgumentException()
354                         .isThrownBy(() -> drools.updateToVersion(GROUP, ARTIFACT, "no-version", null, null))
355                         .withMessageContaining("BRAINLESS");
356
357         assertThatIllegalArgumentException()
358                         .isThrownBy(() -> drools.updateToVersion(GROUP2, ARTIFACT, VERSION, null, null))
359                         .withMessageContaining("coordinates must be identical");
360
361         assertThatIllegalArgumentException()
362                         .isThrownBy(() -> drools.updateToVersion(GROUP, ARTIFACT2, VERSION, null, null))
363                         .withMessageContaining("coordinates must be identical");
364     }
365
366     @Test
367     public void testInitCoders_NullCoders() {
368         // already constructed with null coders
369         verify(coderMgr, never()).addDecoder(any());
370         verify(coderMgr, never()).addEncoder(any());
371     }
372
373     @Test
374     public void testInitCoders_NullOrEmptyFilters() {
375         when(decoder1.getCoderFilters()).thenReturn(Collections.emptyList());
376         when(decoder2.getCoderFilters()).thenReturn(null);
377
378         when(encoder1.getCoderFilters()).thenReturn(null);
379         when(encoder2.getCoderFilters()).thenReturn(Collections.emptyList());
380
381         drools = new MyDrools(GROUP, ARTIFACT, VERSION, decoders, encoders);
382
383         verify(coderMgr, never()).addDecoder(any());
384         verify(coderMgr, never()).addEncoder(any());
385     }
386
387     @Test
388     public void testInitCoders_GsonClass() {
389         when(gson1.getClassContainer()).thenReturn("");
390         when(gson2.getClassContainer()).thenReturn(Long.class.getName());
391
392         drools = new MyDrools(GROUP, ARTIFACT, VERSION, decoders, encoders);
393
394         // all should be added
395         verify(coderMgr, times(2)).addDecoder(any());
396         verify(coderMgr, times(1)).addEncoder(any());
397     }
398
399     @Test
400     public void testInitCoders_InvalidGsonClass() {
401         when(gson1.getClassContainer()).thenReturn(UNKNOWN_CLASS);
402
403         assertThatIllegalArgumentException()
404                         .isThrownBy(() -> new MyDrools(GROUP, ARTIFACT, VERSION, decoders, encoders))
405                         .withMessageContaining("cannot be retrieved");
406     }
407
408     @Test
409     public void testInitCoders_InvalidFilterClass() {
410         when(filter2.getCodedClass()).thenReturn(UNKNOWN_CLASS);
411
412         assertThatIllegalArgumentException()
413                         .isThrownBy(() -> new MyDrools(GROUP, ARTIFACT, VERSION, decoders, encoders))
414                         .withMessageContaining("cannot be retrieved");
415     }
416
417     @Test
418     public void testInitCoders_Filters() {
419
420         drools = new MyDrools(GROUP, ARTIFACT, VERSION, decoders, encoders);
421
422         ArgumentCaptor<EventProtocolParams> dec = ArgumentCaptor.forClass(EventProtocolParams.class);
423         verify(coderMgr, times(2)).addDecoder(dec.capture());
424
425         ArgumentCaptor<EventProtocolParams> enc = ArgumentCaptor.forClass(EventProtocolParams.class);
426         verify(coderMgr, times(1)).addEncoder(enc.capture());
427
428         // validate parameters
429         EventProtocolParams params = dec.getAllValues().get(0);
430         assertEquals(ARTIFACT, params.getArtifactId());
431         assertEquals(gson1, params.getCustomCoder());
432         assertEquals(Object.class.getName(), params.getEventClass());
433         assertEquals(GROUP, params.getGroupId());
434         assertEquals(CLASS_LOADER_HASHCODE, params.getModelClassLoaderHash());
435         assertEquals(jsonFilter1a, params.getProtocolFilter());
436         assertEquals(TOPIC, params.getTopic());
437
438         params = dec.getAllValues().get(1);
439         assertEquals(ARTIFACT, params.getArtifactId());
440         assertEquals(gson1, params.getCustomCoder());
441         assertEquals(String.class.getName(), params.getEventClass());
442         assertEquals(GROUP, params.getGroupId());
443         assertEquals(CLASS_LOADER_HASHCODE, params.getModelClassLoaderHash());
444         assertEquals(jsonFilter1b, params.getProtocolFilter());
445         assertEquals(TOPIC, params.getTopic());
446
447         params = enc.getAllValues().get(0);
448         assertEquals(ARTIFACT, params.getArtifactId());
449         assertEquals(gson2, params.getCustomCoder());
450         assertEquals(Integer.class.getName(), params.getEventClass());
451         assertEquals(GROUP, params.getGroupId());
452         assertEquals(CLASS_LOADER_HASHCODE, params.getModelClassLoaderHash());
453         assertEquals(jsonFilter2, params.getProtocolFilter());
454         assertEquals(TOPIC, params.getTopic());
455     }
456
457     @Test
458     public void testOwnsCoder() {
459         int hc = CLASS_LOADER_HASHCODE;
460
461         // wrong hash code
462         assertFalse(drools.ownsCoder(String.class, hc + 1));
463
464         // correct hash code
465         assertTrue(drools.ownsCoder(String.class, hc));
466
467         // unknown class
468         drools = new MyDrools(GROUP, ARTIFACT, VERSION, null, null) {
469             @Override
470             protected boolean isClass(String className) {
471                 return false;
472             }
473         };
474         assertFalse(drools.ownsCoder(String.class, hc));
475     }
476
477     @Test
478     public void testStart_testStop_testIsAlive() {
479         drools = new MyDrools(GROUP, ARTIFACT, VERSION, decoders, encoders);
480
481         when(container.start()).thenReturn(true);
482         when(container.stop()).thenReturn(true);
483
484         assertFalse(drools.isAlive());
485
486         // start it
487         assertTrue(drools.start());
488         verify(container).start();
489         assertTrue(drools.isAlive());
490
491         // repeat - no changes
492         assertTrue(drools.start());
493         verify(container).start();
494         assertTrue(drools.isAlive());
495
496         // stop it
497         assertTrue(drools.stop());
498         verify(container).stop();
499         assertFalse(drools.isAlive());
500
501         // repeat - no changes
502         assertTrue(drools.stop());
503         verify(container).stop();
504         assertFalse(drools.isAlive());
505
506         // now check with container returning false - should still be invoked
507         when(container.start()).thenReturn(false);
508         when(container.stop()).thenReturn(false);
509         assertFalse(drools.start());
510         assertTrue(drools.isAlive());
511         assertFalse(drools.stop());
512         assertFalse(drools.isAlive());
513         verify(container, times(2)).start();
514         verify(container, times(2)).stop();
515
516         // coders should still be intact
517         verify(coderMgr, never()).removeDecoders(any(), any(), any());
518         verify(coderMgr, never()).removeEncoders(any(), any(), any());
519
520         verify(container, never()).shutdown();
521         verify(container, never()).destroy();
522     }
523
524     @Test
525     public void testShutdown() {
526         drools = new MyDrools(GROUP, ARTIFACT, VERSION, decoders, encoders);
527
528         // start it
529         drools.start();
530
531         // shut down
532         drools.shutdown();
533
534         verify(container).stop();
535         assertFalse(drools.isAlive());
536
537         // coders should have been removed
538         verify(coderMgr, times(2)).removeDecoders(any(), any(), any());
539         verify(coderMgr, times(2)).removeEncoders(any(), any(), any());
540
541         verify(container).shutdown();
542         verify(container, never()).destroy();
543     }
544
545     @Test
546     public void testShutdown_Ex() {
547         drools = new MyDrools(GROUP, ARTIFACT, VERSION, decoders, encoders);
548
549         // start it
550         drools.start();
551
552         when(container.stop()).thenThrow(RUNTIME_EX);
553
554         // shut down
555         drools.shutdown();
556
557         assertFalse(drools.isAlive());
558
559         verify(container).shutdown();
560         verify(container, never()).destroy();
561     }
562
563     @Test
564     public void testHalt() {
565         drools = new MyDrools(GROUP, ARTIFACT, VERSION, decoders, encoders);
566
567         // start it
568         drools.start();
569
570         // halt
571         drools.halt();
572
573         verify(container).stop();
574         assertFalse(drools.isAlive());
575
576         // coders should have been removed
577         verify(coderMgr, times(2)).removeDecoders(any(), any(), any());
578         verify(coderMgr, times(2)).removeEncoders(any(), any(), any());
579
580         verify(container).destroy();
581     }
582
583     @Test
584     public void testHalt_Ex() {
585         drools = new MyDrools(GROUP, ARTIFACT, VERSION, decoders, encoders);
586
587         // start it
588         drools.start();
589
590         when(container.stop()).thenThrow(RUNTIME_EX);
591
592         // halt
593         drools.halt();
594
595         assertFalse(drools.isAlive());
596
597         verify(container).destroy();
598     }
599
600     @Test
601     public void testRemoveCoders_Ex() {
602         drools = new MyDrools(GROUP, ARTIFACT, VERSION, decoders, encoders) {
603             @Override
604             protected void removeDecoders() {
605                 throw ARG_EX;
606             }
607
608             @Override
609             protected void removeEncoders() {
610                 throw ARG_EX;
611             }
612         };
613
614         drools.updateToVersion(GROUP, ARTIFACT, VERSION2, null, null);
615     }
616
617     @Test
618     public void testOfferStringString() {
619         drools.start();
620         assertTrue(drools.offer(TOPIC, EVENT_TEXT));
621
622         verify(container).insertAll(EVENT);
623     }
624
625     @Test
626     public void testOfferStringString_NoDecode() {
627         when(coderMgr.isDecodingSupported(GROUP, ARTIFACT, TOPIC)).thenReturn(false);
628
629         drools.start();
630         assertTrue(drools.offer(TOPIC, EVENT_TEXT));
631
632         verify(container, never()).insertAll(EVENT);
633     }
634
635     @Test
636     public void testOfferStringString_DecodeUnsupported() {
637         when(coderMgr.decode(GROUP, ARTIFACT, TOPIC, EVENT_TEXT))
638                         .thenThrow(new UnsupportedOperationException(EXPECTED_EXCEPTION));
639
640         drools.start();
641         assertTrue(drools.offer(TOPIC, EVENT_TEXT));
642
643         verify(container, never()).insertAll(EVENT);
644     }
645
646     @Test
647     public void testOfferStringString_DecodeEx() {
648         when(coderMgr.decode(GROUP, ARTIFACT, TOPIC, EVENT_TEXT)).thenThrow(RUNTIME_EX);
649
650         drools.start();
651         assertTrue(drools.offer(TOPIC, EVENT_TEXT));
652
653         verify(container, never()).insertAll(EVENT);
654     }
655
656     @Test
657     public void testOfferStringString_Ignored() {
658         drools.start();
659
660         drools.lock();
661         assertTrue(drools.offer(TOPIC, EVENT_TEXT));
662         assertEquals(0, drools.getRecentSourceEvents().length);
663         drools.unlock();
664
665         drools.stop();
666         assertTrue(drools.offer(TOPIC, EVENT_TEXT));
667         assertEquals(0, drools.getRecentSourceEvents().length);
668         drools.start();
669
670         // no sessions
671         when(container.getPolicySessions()).thenReturn(Collections.emptyList());
672         assertTrue(drools.offer(TOPIC, EVENT_TEXT));
673         assertEquals(0, drools.getRecentSourceEvents().length);
674     }
675
676     @Test
677     public void testOfferT() {
678         drools.start();
679         assertTrue(drools.offer(EVENT));
680         assertEquals(1, drools.getRecentSourceEvents().length);
681         assertEquals(EVENT, drools.getRecentSourceEvents()[0]);
682         verify(container).insertAll(EVENT);
683
684         verify(prov1).beforeInsert(drools, EVENT);
685         verify(prov2).beforeInsert(drools, EVENT);
686
687         verify(prov1).afterInsert(drools, EVENT, true);
688         verify(prov2).afterInsert(drools, EVENT, true);
689     }
690
691     @Test
692     public void testOfferT_Ex() {
693         when(prov1.beforeInsert(drools, EVENT)).thenThrow(RUNTIME_EX);
694         when(prov1.afterInsert(drools, EVENT, true)).thenThrow(RUNTIME_EX);
695
696         drools.start();
697         assertTrue(drools.offer(EVENT));
698         assertEquals(1, drools.getRecentSourceEvents().length);
699         assertEquals(EVENT, drools.getRecentSourceEvents()[0]);
700         verify(container).insertAll(EVENT);
701
702         // should still invoke prov2
703         verify(prov2).beforeInsert(drools, EVENT);
704
705         verify(prov2).afterInsert(drools, EVENT, true);
706     }
707
708     @Test
709     public void testOfferT_NotInserted() {
710         when(container.insertAll(EVENT)).thenReturn(false);
711
712         drools.start();
713         assertTrue(drools.offer(EVENT));
714         assertEquals(1, drools.getRecentSourceEvents().length);
715         assertEquals(EVENT, drools.getRecentSourceEvents()[0]);
716         verify(container).insertAll(EVENT);
717
718         verify(prov1).beforeInsert(drools, EVENT);
719         verify(prov2).beforeInsert(drools, EVENT);
720
721         verify(prov1).afterInsert(drools, EVENT, false);
722         verify(prov2).afterInsert(drools, EVENT, false);
723     }
724
725     @Test
726     public void testOfferT_BeforeInsertIntercept() {
727         drools.start();
728         when(prov1.beforeInsert(drools, EVENT)).thenReturn(true);
729
730         assertTrue(drools.offer(EVENT));
731         assertEquals(1, drools.getRecentSourceEvents().length);
732         assertEquals(EVENT, drools.getRecentSourceEvents()[0]);
733         verify(container, never()).insertAll(EVENT);
734
735         verify(prov1).beforeInsert(drools, EVENT);
736
737         // nothing else invoked
738         verify(prov2, never()).beforeInsert(drools, EVENT);
739         verify(prov1, never()).afterInsert(drools, EVENT, true);
740         verify(prov2, never()).afterInsert(drools, EVENT, true);
741     }
742
743     @Test
744     public void testOfferT_AfterInsertIntercept() {
745         drools.start();
746
747         when(prov1.afterInsert(drools, EVENT, true)).thenReturn(true);
748
749         assertTrue(drools.offer(EVENT));
750         assertEquals(1, drools.getRecentSourceEvents().length);
751         assertEquals(EVENT, drools.getRecentSourceEvents()[0]);
752         verify(container).insertAll(EVENT);
753
754         verify(prov1).beforeInsert(drools, EVENT);
755         verify(prov2).beforeInsert(drools, EVENT);
756
757         verify(prov1).afterInsert(drools, EVENT, true);
758
759         // prov2 is never called
760         verify(prov2, never()).afterInsert(drools, EVENT, true);
761     }
762
763     @Test
764     public void testOfferT_Ignored() {
765         drools.start();
766
767         drools.lock();
768         assertTrue(drools.offer(EVENT));
769         assertEquals(0, drools.getRecentSourceEvents().length);
770         drools.unlock();
771
772         drools.stop();
773         assertTrue(drools.offer(EVENT));
774         assertEquals(0, drools.getRecentSourceEvents().length);
775         drools.start();
776
777         // no sessions
778         when(container.getPolicySessions()).thenReturn(Collections.emptyList());
779         assertTrue(drools.offer(EVENT));
780         assertEquals(0, drools.getRecentSourceEvents().length);
781     }
782
783     @Test
784     public void testDeliver() {
785         drools.start();
786         assertTrue(drools.deliver(sink, EVENT));
787         assertEquals(1, drools.getRecentSinkEvents().length);
788         assertEquals(EVENT_TEXT, drools.getRecentSinkEvents()[0]);
789
790         verify(sink).send(EVENT_TEXT);
791
792         verify(prov1).beforeDeliver(drools, sink, EVENT);
793         verify(prov2).beforeDeliver(drools, sink, EVENT);
794
795         verify(prov1).afterDeliver(drools, sink, EVENT, EVENT_TEXT, true);
796         verify(prov2).afterDeliver(drools, sink, EVENT, EVENT_TEXT, true);
797     }
798
799     @Test
800     public void testDeliver_InvalidArgs() {
801         drools.start();
802
803         assertThatIllegalArgumentException().isThrownBy(() -> drools.deliver(null, EVENT))
804                         .withMessageContaining("sink");
805
806         assertThatIllegalArgumentException().isThrownBy(() -> drools.deliver(sink, null))
807                         .withMessageContaining("event");
808
809         drools.lock();
810         assertThatIllegalStateException().isThrownBy(() -> drools.deliver(sink, EVENT)).withMessageContaining("locked");
811         drools.unlock();
812
813         drools.stop();
814         assertThatIllegalStateException().isThrownBy(() -> drools.deliver(sink, EVENT))
815                         .withMessageContaining("stopped");
816         drools.start();
817
818         assertEquals(0, drools.getRecentSinkEvents().length);
819     }
820
821     @Test
822     public void testDeliver_BeforeIntercept() {
823         when(prov1.beforeDeliver(drools, sink, EVENT)).thenReturn(true);
824
825         drools.start();
826         assertTrue(drools.deliver(sink, EVENT));
827         assertEquals(0, drools.getRecentSinkEvents().length);
828
829         verify(prov1).beforeDeliver(drools, sink, EVENT);
830
831         // nothing else should have been invoked
832         verify(sink, never()).send(EVENT_TEXT);
833         verify(prov2, never()).beforeDeliver(drools, sink, EVENT);
834         verify(prov1, never()).afterDeliver(drools, sink, EVENT, EVENT_TEXT, true);
835         verify(prov2, never()).afterDeliver(drools, sink, EVENT, EVENT_TEXT, true);
836     }
837
838     @Test
839     public void testDeliver_AfterIntercept() {
840         when(prov1.afterDeliver(drools, sink, EVENT, EVENT_TEXT, true)).thenReturn(true);
841
842         drools.start();
843         assertTrue(drools.deliver(sink, EVENT));
844         assertEquals(1, drools.getRecentSinkEvents().length);
845         assertEquals(EVENT_TEXT, drools.getRecentSinkEvents()[0]);
846
847         verify(prov1).beforeDeliver(drools, sink, EVENT);
848         verify(prov2).beforeDeliver(drools, sink, EVENT);
849
850         verify(sink).send(EVENT_TEXT);
851
852         verify(prov1).afterDeliver(drools, sink, EVENT, EVENT_TEXT, true);
853
854         // nothing else should have been invoked
855         verify(prov2, never()).afterDeliver(drools, sink, EVENT, EVENT_TEXT, true);
856     }
857
858     @Test
859     public void testDeliver_InterceptEx() {
860         when(prov1.beforeDeliver(drools, sink, EVENT)).thenThrow(RUNTIME_EX);
861         when(prov1.afterDeliver(drools, sink, EVENT, EVENT_TEXT, true)).thenThrow(RUNTIME_EX);
862
863         drools.start();
864         assertTrue(drools.deliver(sink, EVENT));
865
866         verify(sink).send(EVENT_TEXT);
867
868         // should still invoke prov2
869         verify(prov2).beforeDeliver(drools, sink, EVENT);
870         verify(prov2).afterDeliver(drools, sink, EVENT, EVENT_TEXT, true);
871     }
872
873     @Test
874     public void testGetXxx() {
875         assertEquals(VERSION, drools.getVersion());
876         assertEquals(ARTIFACT, drools.getArtifactId());
877         assertEquals(GROUP, drools.getGroupId());
878         assertEquals(CLASS_LOADER_HASHCODE, drools.getModelClassLoaderHash());
879         assertSame(container, drools.getContainer());
880         assertEquals(Arrays.asList(sess1, sess2), drools.getSessions());
881
882         // test junit methods - need a controller with fewer overrides
883         drools = new MavenDroolsController(GROUP, ARTIFACT, VERSION, null, null) {
884             @Override
885             protected PolicyContainer makePolicyContainer(String groupId, String artifactId, String version) {
886                 return container;
887             }
888         };
889
890         assertSame(EventProtocolCoder.manager, drools.getCoderManager());
891         assertSame(DroolsControllerFeatureApi.providers, drools.getDroolsProviders());
892     }
893
894     @Test
895     public void testLock_testUnlock_testIsLocked() {
896         assertFalse(drools.isLocked());
897
898         assertTrue(drools.lock());
899         assertTrue(drools.isLocked());
900
901         assertTrue(drools.unlock());
902         assertFalse(drools.isLocked());
903
904         // repeat
905         assertTrue(drools.lock());
906         assertTrue(drools.isLocked());
907
908         assertTrue(drools.unlock());
909         assertFalse(drools.isLocked());
910     }
911
912     @Test
913     public void testGetSessionNames_testGetCanonicalSessionNames() {
914         assertEquals("[session-A, session-B]", drools.getSessionNames(true).toString());
915         assertEquals("[full-A, full-B]", drools.getSessionNames(false).toString());
916
917         assertEquals("[session-A, session-B]", drools.getSessionNames().toString());
918
919         assertEquals("[full-A, full-B]", drools.getCanonicalSessionNames().toString());
920
921         // exception case
922         when(container.getPolicySessions()).thenThrow(RUNTIME_EX);
923         assertEquals("[expected exception]", drools.getSessionNames().toString());
924     }
925
926     @Test
927     public void testGetBaseDomainNames() {
928         KieContainer kiecont = mock(KieContainer.class);
929         when(kiecont.getKieBaseNames()).thenReturn(Arrays.asList("kieA", "kieB"));
930         when(container.getKieContainer()).thenReturn(kiecont);
931
932         assertEquals("[kieA, kieB]", drools.getBaseDomainNames().toString());
933     }
934
935     @Test
936     public void testGetSession() {
937         assertThatIllegalArgumentException().isThrownBy(() -> drools.getSession(null))
938                         .withMessageContaining("must be provided");
939
940         assertThatIllegalArgumentException().isThrownBy(() -> drools.getSession(""))
941                         .withMessageContaining("must be provided");
942
943         assertSame(sess1, drools.getSession(SESSION1));
944         assertSame(sess1, drools.getSession(FULL_SESSION1));
945
946         assertSame(sess2, drools.getSession(SESSION2));
947
948         assertThatIllegalArgumentException().isThrownBy(() -> drools.getSession("unknown session"))
949                         .withMessageContaining("Invalid Session Name");
950     }
951
952     @Test
953     public void testFactClassNames() {
954         // copy to a sorted map so the order remains unchanged
955         Map<String, Integer> map = new TreeMap<>(drools.factClassNames(SESSION1));
956         assertEquals("{java.lang.Integer=2, java.lang.String=1}", map.toString());
957
958         assertThatIllegalArgumentException().isThrownBy(() -> drools.factClassNames(null))
959                         .withMessageContaining("Invalid Session Name");
960
961         assertThatIllegalArgumentException().isThrownBy(() -> drools.factClassNames(""))
962                         .withMessageContaining("Invalid Session Name");
963     }
964
965     @Test
966     public void testFactCount() {
967         assertEquals(FACT_COUNT, drools.factCount(SESSION1));
968
969         assertThatIllegalArgumentException().isThrownBy(() -> drools.factCount(null))
970                         .withMessageContaining("Invalid Session Name");
971
972         assertThatIllegalArgumentException().isThrownBy(() -> drools.factCount(""))
973                         .withMessageContaining("Invalid Session Name");
974     }
975
976     @Test
977     public void testFactsStringStringBoolean() {
978         assertEquals("[1000, 1001]", drools.facts(SESSION1, Integer.class.getName(), false).toString());
979         verify(kieSess, never()).delete(fact1);
980         verify(kieSess, never()).delete(fact2);
981         verify(kieSess, never()).delete(fact3);
982         verify(kieSess, never()).delete(factex);
983
984         // now delete - but should only delete 1 & 3
985         assertEquals("[1000, 1001]", drools.facts(SESSION1, Integer.class.getName(), true).toString());
986         verify(kieSess).delete(fact1);
987         verify(kieSess, never()).delete(fact2);
988         verify(kieSess).delete(fact3);
989         verify(kieSess, never()).delete(factex);
990
991         assertThatIllegalArgumentException().isThrownBy(() -> drools.facts(null, Integer.class.getName(), false))
992                         .withMessageContaining("Invalid Session Name");
993
994         assertThatIllegalArgumentException().isThrownBy(() -> drools.facts("", Integer.class.getName(), false))
995                         .withMessageContaining("Invalid Session Name");
996
997         assertThatIllegalArgumentException().isThrownBy(() -> drools.facts(SESSION1, null, false))
998                         .withMessageContaining("Invalid Class Name");
999
1000         assertThatIllegalArgumentException().isThrownBy(() -> drools.facts(SESSION1, "", false))
1001                         .withMessageContaining("Invalid Class Name");
1002
1003         assertThatIllegalArgumentException().isThrownBy(() -> drools.facts(SESSION1, UNKNOWN_CLASS, false))
1004                         .withMessageContaining("classloader");
1005     }
1006
1007     @Test
1008     public void testFactsStringStringBoolean_DeleteEx() {
1009         doThrow(RUNTIME_EX).when(kieSess).delete(fact1);
1010
1011         assertEquals("[1000, 1001]", drools.facts(SESSION1, Integer.class.getName(), true).toString());
1012
1013         // should still have deleted #3
1014         verify(kieSess).delete(fact3);
1015     }
1016
1017     @Test
1018     public void testFactsStringClassOfT() {
1019         assertEquals("[1000, 1001]", drools.facts(SESSION1, Integer.class).toString());
1020     }
1021
1022     @Test
1023     public void testFactQuery() {
1024         assertEquals("[1000, 1001]", drools.factQuery(SESSION1, QUERY, ENTITY, false, PARM1, PARM2).toString());
1025
1026         verify(kieSess, never()).delete(fact1);
1027         verify(kieSess, never()).delete(fact3);
1028
1029         assertThatIllegalArgumentException()
1030                         .isThrownBy(() -> drools.factQuery(null, QUERY, ENTITY, false, PARM1, PARM2))
1031                         .withMessageContaining("Invalid Session Name");
1032
1033         assertThatIllegalArgumentException().isThrownBy(() -> drools.factQuery("", QUERY, ENTITY, false, PARM1, PARM2))
1034                         .withMessageContaining("Invalid Session Name");
1035
1036         assertThatIllegalArgumentException()
1037                         .isThrownBy(() -> drools.factQuery(SESSION1, null, ENTITY, false, PARM1, PARM2))
1038                         .withMessageContaining("Invalid Query Name");
1039
1040         assertThatIllegalArgumentException()
1041                         .isThrownBy(() -> drools.factQuery(SESSION1, "", ENTITY, false, PARM1, PARM2))
1042                         .withMessageContaining("Invalid Query Name");
1043
1044         assertThatIllegalArgumentException()
1045                         .isThrownBy(() -> drools.factQuery(SESSION1, QUERY, null, false, PARM1, PARM2))
1046                         .withMessageContaining("Invalid Queried Entity");
1047
1048         assertThatIllegalArgumentException()
1049                         .isThrownBy(() -> drools.factQuery(SESSION1, QUERY, "", false, PARM1, PARM2))
1050                         .withMessageContaining("Invalid Queried Entity");
1051
1052         assertThatIllegalArgumentException().isThrownBy(
1053             () -> drools.factQuery(SESSION1, QUERY + "-unknown-query", ENTITY, false, PARM1, PARM2))
1054             .withMessageContaining("Invalid Query Name");
1055     }
1056
1057     @Test
1058     public void testFactQuery_Delete() {
1059         doThrow(RUNTIME_EX).when(kieSess).delete(fact1);
1060
1061         assertEquals("[1000, 1001]", drools.factQuery(SESSION1, QUERY, ENTITY, true, PARM1, PARM2).toString());
1062
1063         // should still delete fact #3
1064         verify(kieSess).delete(fact3);
1065     }
1066
1067     @Test
1068     public void testDeleteStringT() {
1069         assertTrue(drools.delete(SESSION1, FACT3_OBJECT));
1070
1071         verify(kieSess, never()).delete(fact1);
1072         verify(kieSess).delete(fact3);
1073
1074         // not found
1075         assertFalse(drools.delete(SESSION1, "hello"));
1076
1077         // repeat, but generate exception while getting the first object
1078         when(kieSess.getObject(fact1)).thenThrow(RUNTIME_EX);
1079         assertTrue(drools.delete(SESSION1, FACT3_OBJECT));
1080
1081         verify(kieSess, never()).delete(fact1);
1082
1083         // should still delete fact #3
1084         verify(kieSess, times(2)).delete(fact3);
1085     }
1086
1087     @Test
1088     public void testDeleteT() {
1089         assertTrue(drools.delete(FACT3_OBJECT));
1090
1091         verify(kieSess).delete(fact3);
1092     }
1093
1094     @Test
1095     public void testDeleteStringClassOfT() {
1096         assertTrue(drools.delete(SESSION1, Integer.class));
1097
1098         verify(kieSess).delete(fact1);
1099         verify(kieSess).delete(fact3);
1100     }
1101
1102     @Test
1103     public void testDeleteStringClassOfT_Ex() {
1104         doThrow(RUNTIME_EX).when(kieSess).delete(fact1);
1105
1106         assertFalse(drools.delete(SESSION1, Integer.class));
1107
1108         // should still delete fact #3
1109         verify(kieSess).delete(fact3);
1110     }
1111
1112     @Test
1113     public void testDeleteClassOfT() {
1114         assertTrue(drools.delete(Integer.class));
1115
1116         verify(kieSess).delete(fact1);
1117         verify(kieSess).delete(fact3);
1118     }
1119
1120     @Test
1121     public void testFetchModelClass() {
1122         assertSame(Long.class, drools.fetchModelClass(Long.class.getName()));
1123     }
1124
1125     @Test
1126     public void testIsBrained() {
1127         assertTrue(drools.isBrained());
1128     }
1129
1130     @Test
1131     public void testToString() {
1132         assertNotNull(drools.toString());
1133     }
1134
1135     private class MyDrools extends MavenDroolsController {
1136
1137         public MyDrools(String groupId, String artifactId, String version,
1138                         List<TopicCoderFilterConfiguration> decoderConfigurations,
1139                         List<TopicCoderFilterConfiguration> encoderConfigurations) {
1140
1141             super(groupId, artifactId, version, decoderConfigurations, encoderConfigurations);
1142         }
1143
1144         @Override
1145         protected EventProtocolCoder getCoderManager() {
1146             return coderMgr;
1147         }
1148
1149         @Override
1150         protected OrderedServiceImpl<DroolsControllerFeatureApi> getDroolsProviders() {
1151             return droolsProviders;
1152         }
1153
1154         @Override
1155         protected PolicyContainer makePolicyContainer(String groupId, String artifactId, String version) {
1156             when(container.getGroupId()).thenReturn(groupId);
1157             when(container.getArtifactId()).thenReturn(artifactId);
1158             when(container.getVersion()).thenReturn(version);
1159
1160             return container;
1161         }
1162     }
1163 }