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