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