diff options
6 files changed, 1258 insertions, 98 deletions
diff --git a/feature-active-standby-management/src/main/java/org/onap/policy/drools/activestandby/DroolsPdpEntity.java b/feature-active-standby-management/src/main/java/org/onap/policy/drools/activestandby/DroolsPdpEntity.java index 12d716a6..0055b9cb 100644 --- a/feature-active-standby-management/src/main/java/org/onap/policy/drools/activestandby/DroolsPdpEntity.java +++ b/feature-active-standby-management/src/main/java/org/onap/policy/drools/activestandby/DroolsPdpEntity.java @@ -2,14 +2,14 @@ * ============LICENSE_START======================================================= * feature-active-standby-management * ================================================================================ - * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -72,7 +72,7 @@ public class DroolsPdpEntity extends DroolsPdpObject implements Serializable { updatedDate = new Date(); //When this is translated to a TimeStamp in MySQL, it assumes the date is relative //to the local timezone. So, a value of Date(0) is actually Dec 31 18:00:00 CST 1969 - //which is an invalid value for the MySql TimeStamp + //which is an invalid value for the MySql TimeStamp designatedDate = new Date(864000000); } @@ -135,25 +135,4 @@ public class DroolsPdpEntity extends DroolsPdpObject implements Serializable { public void setDesignatedDate(Date designatedDate) { this.designatedDate = designatedDate; } - - @Override - public boolean equals(Object obj) { - - if (obj instanceof DroolsPdp) { - DroolsPdpEntity entity = (DroolsPdpEntity) obj; - return this.pdpId.equals(entity.getPdpId()); - } else { - return false; - } - - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (this.pdpId == null ? 0 : this.pdpId.hashCode()); - return result; - } - } diff --git a/feature-active-standby-management/src/main/java/org/onap/policy/drools/activestandby/DroolsPdpImpl.java b/feature-active-standby-management/src/main/java/org/onap/policy/drools/activestandby/DroolsPdpImpl.java index 31728bc2..0c50f82d 100644 --- a/feature-active-standby-management/src/main/java/org/onap/policy/drools/activestandby/DroolsPdpImpl.java +++ b/feature-active-standby-management/src/main/java/org/onap/policy/drools/activestandby/DroolsPdpImpl.java @@ -2,14 +2,14 @@ * ============LICENSE_START======================================================= * feature-active-standby-management * ================================================================================ - * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -33,7 +33,7 @@ public class DroolsPdpImpl extends DroolsPdpObject { /** * Contructor. - * + * * @param pdpId ID for the PDP * @param designated is designated * @param priority priority @@ -46,11 +46,11 @@ public class DroolsPdpImpl extends DroolsPdpObject { this.updatedDate = updatedDate; //When this is translated to a TimeStamp in MySQL, it assumes the date is relative //to the local timezone. So, a value of Date(0) is actually Dec 31 18:00:00 CST 1969 - //which is an invalid value for the MySql TimeStamp + //which is an invalid value for the MySql TimeStamp this.designatedDate = new Date(864000000); } - + @Override public boolean isDesignated() { @@ -61,12 +61,12 @@ public class DroolsPdpImpl extends DroolsPdpObject { public int getPriority() { return priority; } - + @Override public void setUpdatedDate(Date date) { this.updatedDate = date; } - + @Override public Date getUpdatedDate() { return updatedDate; @@ -76,7 +76,7 @@ public class DroolsPdpImpl extends DroolsPdpObject { public String getPdpId() { return pdpId; } - + @Override public void setDesignated(boolean isDesignated) { this.designated = isDesignated; @@ -87,41 +87,21 @@ public class DroolsPdpImpl extends DroolsPdpObject { public String getSiteName() { return site; } - + @Override public void setSiteName(String siteName) { this.site = siteName; } - + @Override public Date getDesignatedDate() { return designatedDate; } - + @Override public void setDesignatedDate(Date designatedDate) { this.designatedDate = designatedDate; } - - @Override - public boolean equals(Object obj) { - - - if (obj instanceof DroolsPdp) { - DroolsPdpImpl temp = (DroolsPdpImpl) obj; - return this.pdpId.equals(temp.getPdpId()); - } else { - return false; - } - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (this.pdpId == null ? 0 : this.pdpId.hashCode()); - return result; - } } diff --git a/feature-active-standby-management/src/main/java/org/onap/policy/drools/activestandby/DroolsPdpObject.java b/feature-active-standby-management/src/main/java/org/onap/policy/drools/activestandby/DroolsPdpObject.java index 8ac14f0e..75157bbe 100644 --- a/feature-active-standby-management/src/main/java/org/onap/policy/drools/activestandby/DroolsPdpObject.java +++ b/feature-active-standby-management/src/main/java/org/onap/policy/drools/activestandby/DroolsPdpObject.java @@ -32,6 +32,14 @@ public abstract class DroolsPdpObject implements DroolsPdp { } } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (this.getPdpId() == null ? 0 : this.getPdpId().hashCode()); + return result; + } + private int nullSafeCompare(String one, String two) { if (one != null) { if (two != null) { diff --git a/feature-active-standby-management/src/test/java/org/onap/policy/drools/activestandby/DroolsPdpObjectTest.java b/feature-active-standby-management/src/test/java/org/onap/policy/drools/activestandby/DroolsPdpObjectTest.java index 310f9110..842d6c7f 100644 --- a/feature-active-standby-management/src/test/java/org/onap/policy/drools/activestandby/DroolsPdpObjectTest.java +++ b/feature-active-standby-management/src/test/java/org/onap/policy/drools/activestandby/DroolsPdpObjectTest.java @@ -66,6 +66,25 @@ public class DroolsPdpObjectTest { } @Test + public void testHashCode() { + int hc = pdp.hashCode(); + + // same data should yield same hash code + assertEquals(hc, pdp.hashCode()); + assertEquals(hc, makePdp(PDP_ID, SITE, PRIORITY).hashCode()); + + // different data should yield different hash code + assertTrue(makePdp(PDP_ID2, SITE, PRIORITY).hashCode() != hc); + + // these fields have no impact on hash code + assertEquals(hc, makePdp(PDP_ID, SITE, PRIORITY2).hashCode()); + assertEquals(hc, makePdp(PDP_ID, SITE2, PRIORITY).hashCode()); + + // should not throw an exception + new MyPdp().hashCode(); + } + + @Test public void testNullSafeCompare() { // self, when null pdp.setSiteName(null); diff --git a/policy-management/src/main/java/org/onap/policy/drools/controller/internal/MavenDroolsController.java b/policy-management/src/main/java/org/onap/policy/drools/controller/internal/MavenDroolsController.java index 818cae80..b09ae85c 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/controller/internal/MavenDroolsController.java +++ b/policy-management/src/main/java/org/onap/policy/drools/controller/internal/MavenDroolsController.java @@ -41,6 +41,7 @@ import org.kie.api.runtime.rule.QueryResultsRow; import org.onap.policy.common.endpoints.event.comm.TopicSink; import org.onap.policy.common.gson.annotation.GsonJsonIgnore; import org.onap.policy.common.gson.annotation.GsonJsonProperty; +import org.onap.policy.common.utils.services.OrderedServiceImpl; import org.onap.policy.drools.controller.DroolsController; import org.onap.policy.drools.core.PolicyContainer; import org.onap.policy.drools.core.PolicySession; @@ -148,7 +149,7 @@ public class MavenDroolsController implements DroolsController { throw new IllegalArgumentException("Missing maven version coordinate"); } - this.policyContainer = new PolicyContainer(groupId, artifactId, version); + this.policyContainer = makePolicyContainer(groupId, artifactId, version); this.init(decoderConfigurations, encoderConfigurations); logger.debug("{}: instantiation completed ", this); @@ -203,7 +204,7 @@ public class MavenDroolsController implements DroolsController { if (newGroupId.equalsIgnoreCase(this.getGroupId()) && newArtifactId.equalsIgnoreCase(this.getArtifactId()) && newVersion.equalsIgnoreCase(this.getVersion())) { - logger.warn("Al in the right version: " + newGroupId + ":" + logger.warn("All in the right version: " + newGroupId + ":" + newArtifactId + ":" + newVersion + " vs. " + this); return; } @@ -254,18 +255,15 @@ public class MavenDroolsController implements DroolsController { String topic = coderConfig.getTopic(); CustomGsonCoder customGsonCoder = coderConfig.getCustomGsonCoder(); - if (coderConfig.getCustomGsonCoder() != null - && coderConfig.getCustomGsonCoder().getClassContainer() != null - && !coderConfig.getCustomGsonCoder().getClassContainer().isEmpty()) { + if (customGsonCoder != null + && customGsonCoder.getClassContainer() != null + && !customGsonCoder.getClassContainer().isEmpty()) { - String customGsonCoderClass = coderConfig.getCustomGsonCoder().getClassContainer(); - if (!ReflectionUtil.isClass(this.policyContainer.getClassLoader(), - customGsonCoderClass)) { + String customGsonCoderClass = customGsonCoder.getClassContainer(); + if (!isClass(customGsonCoderClass)) { throw makeRetrieveEx(customGsonCoderClass); } else { - if (logger.isInfoEnabled()) { - logClassFetched(customGsonCoderClass); - } + logClassFetched(customGsonCoderClass); } } @@ -278,17 +276,14 @@ public class MavenDroolsController implements DroolsController { String potentialCodedClass = coderFilter.getCodedClass(); JsonProtocolFilter protocolFilter = coderFilter.getFilter(); - if (!ReflectionUtil.isClass(this.policyContainer.getClassLoader(), - potentialCodedClass)) { + if (!isClass(potentialCodedClass)) { throw makeRetrieveEx(potentialCodedClass); } else { - if (logger.isInfoEnabled()) { - logClassFetched(potentialCodedClass); - } + logClassFetched(potentialCodedClass); } if (decoder) { - EventProtocolCoder.manager.addDecoder(EventProtocolParams.builder() + getCoderManager().addDecoder(EventProtocolParams.builder() .groupId(this.getGroupId()) .artifactId(this.getArtifactId()) .topic(topic) @@ -297,7 +292,7 @@ public class MavenDroolsController implements DroolsController { .customGsonCoder(customGsonCoder) .modelClassLoaderHash(this.policyContainer.getClassLoader().hashCode())); } else { - EventProtocolCoder.manager.addEncoder( + getCoderManager().addEncoder( EventProtocolParams.builder().groupId(this.getGroupId()) .artifactId(this.getArtifactId()).topic(topic) .eventClass(potentialCodedClass).protocolFilter(protocolFilter) @@ -340,7 +335,7 @@ public class MavenDroolsController implements DroolsController { for (TopicCoderFilterConfiguration coderConfig: decoderConfigurations) { String topic = coderConfig.getTopic(); - EventProtocolCoder.manager.removeDecoders(this.getGroupId(), this.getArtifactId(), topic); + getCoderManager().removeDecoders(this.getGroupId(), this.getArtifactId(), topic); } } @@ -357,14 +352,14 @@ public class MavenDroolsController implements DroolsController { for (TopicCoderFilterConfiguration coderConfig: encoderConfigurations) { String topic = coderConfig.getTopic(); - EventProtocolCoder.manager.removeEncoders(this.getGroupId(), this.getArtifactId(), topic); + getCoderManager().removeEncoders(this.getGroupId(), this.getArtifactId(), topic); } } @Override public boolean ownsCoder(Class<? extends Object> coderClass, int modelHash) { - if (!ReflectionUtil.isClass(this.policyContainer.getClassLoader(), coderClass.getName())) { + if (!isClass(coderClass.getName())) { logger.error("{}{} cannot be retrieved. ", this, coderClass.getName()); return false; } @@ -473,7 +468,7 @@ public class MavenDroolsController implements DroolsController { // 1. Now, check if this topic has a decoder: - if (!EventProtocolCoder.manager.isDecodingSupported(this.getGroupId(), + if (!getCoderManager().isDecodingSupported(this.getGroupId(), this.getArtifactId(), topic)) { @@ -486,7 +481,7 @@ public class MavenDroolsController implements DroolsController { Object anEvent; try { - anEvent = EventProtocolCoder.manager.decode(this.getGroupId(), + anEvent = getCoderManager().decode(this.getGroupId(), this.getArtifactId(), topic, event); @@ -520,7 +515,7 @@ public class MavenDroolsController implements DroolsController { // Broadcast - for (DroolsControllerFeatureApi feature : DroolsControllerFeatureApi.providers.getList()) { + for (DroolsControllerFeatureApi feature : getDroolsProviders().getList()) { try { if (feature.beforeInsert(this, event)) { return true; @@ -536,7 +531,7 @@ public class MavenDroolsController implements DroolsController { logger.warn(this + "Failed to inject into PolicyContainer {}", this.getSessionNames()); } - for (DroolsControllerFeatureApi feature : DroolsControllerFeatureApi.providers.getList()) { + for (DroolsControllerFeatureApi feature : getDroolsProviders().getList()) { try { if (feature.afterInsert(this, event, successInject)) { return true; @@ -556,7 +551,7 @@ public class MavenDroolsController implements DroolsController { logger.info("{}DELIVER: {} FROM {} TO {}", this, event, this, sink); - for (DroolsControllerFeatureApi feature : DroolsControllerFeatureApi.providers.getList()) { + for (DroolsControllerFeatureApi feature : getDroolsProviders().getList()) { try { if (feature.beforeDeliver(this, sink, event)) { return true; @@ -585,7 +580,7 @@ public class MavenDroolsController implements DroolsController { } String json = - EventProtocolCoder.manager.encode(sink.getTopic(), event, this); + getCoderManager().encode(sink.getTopic(), event, this); synchronized (this.recentSinkEvents) { this.recentSinkEvents.add(json); @@ -593,7 +588,7 @@ public class MavenDroolsController implements DroolsController { boolean success = sink.send(json); - for (DroolsControllerFeatureApi feature : DroolsControllerFeatureApi.providers.getList()) { + for (DroolsControllerFeatureApi feature : getDroolsProviders().getList()) { try { if (feature.afterDeliver(this, sink, event, json, success)) { return true; @@ -743,16 +738,14 @@ public class MavenDroolsController implements DroolsController { @Override public Map<String,Integer> factClassNames(String sessionName) { - if (sessionName == null || sessionName.isEmpty()) { - throw invalidSessNameEx(sessionName); - } + validateSessionName(sessionName); Map<String,Integer> classNames = new HashMap<>(); PolicySession session = getSession(sessionName); KieSession kieSession = session.getKieSession(); - Collection<FactHandle> facts = session.getKieSession().getFactHandles(); + Collection<FactHandle> facts = kieSession.getFactHandles(); for (FactHandle fact : facts) { try { String className = kieSession.getObject(fact).getClass().getName(); @@ -769,11 +762,15 @@ public class MavenDroolsController implements DroolsController { return classNames; } - @Override - public long factCount(String sessionName) { + private void validateSessionName(String sessionName) { if (sessionName == null || sessionName.isEmpty()) { throw invalidSessNameEx(sessionName); } + } + + @Override + public long factCount(String sessionName) { + validateSessionName(sessionName); PolicySession session = getSession(sessionName); return session.getKieSession().getFactCount(); @@ -781,9 +778,7 @@ public class MavenDroolsController implements DroolsController { @Override public List<Object> facts(String sessionName, String className, boolean delete) { - if (sessionName == null || sessionName.isEmpty()) { - throw invalidSessNameEx(sessionName); - } + validateSessionName(sessionName); if (className == null || className.isEmpty()) { throw new IllegalArgumentException("Invalid Class Name: " + className); @@ -827,9 +822,7 @@ public class MavenDroolsController implements DroolsController { @Override public List<Object> factQuery(String sessionName, String queryName, String queriedEntity, boolean delete, Object... queryParams) { - if (sessionName == null || sessionName.isEmpty()) { - throw invalidSessNameEx(sessionName); - } + validateSessionName(sessionName); if (queryName == null || queryName.isEmpty()) { throw new IllegalArgumentException("Invalid Query Name: " + queryName); @@ -965,7 +958,7 @@ public class MavenDroolsController implements DroolsController { StringBuilder builder = new StringBuilder(); builder .append("MavenDroolsController [policyContainer=") - .append((policyContainer != null) ? policyContainer.getName() : "NULL") + .append(policyContainer.getName()) .append(":") .append(", alive=") .append(alive) @@ -975,4 +968,22 @@ public class MavenDroolsController implements DroolsController { .append("]"); return builder.toString(); } + + // these may be overridden by junit tests + + protected EventProtocolCoder getCoderManager() { + return EventProtocolCoder.manager; + } + + protected OrderedServiceImpl<DroolsControllerFeatureApi> getDroolsProviders() { + return DroolsControllerFeatureApi.providers; + } + + protected PolicyContainer makePolicyContainer(String groupId, String artifactId, String version) { + return new PolicyContainer(groupId, artifactId, version); + } + + protected boolean isClass(String className) { + return ReflectionUtil.isClass(this.policyContainer.getClassLoader(), className); + } } diff --git a/policy-management/src/test/java/org/onap/policy/drools/controller/internal/MavenDroolsControllerTest2.java b/policy-management/src/test/java/org/onap/policy/drools/controller/internal/MavenDroolsControllerTest2.java new file mode 100644 index 00000000..cd99eef0 --- /dev/null +++ b/policy-management/src/test/java/org/onap/policy/drools/controller/internal/MavenDroolsControllerTest2.java @@ -0,0 +1,1163 @@ +/* + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.drools.controller.internal; + +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import org.junit.Before; +import org.junit.Test; +import org.kie.api.KieBase; +import org.kie.api.definition.KiePackage; +import org.kie.api.definition.rule.Query; +import org.kie.api.runtime.KieContainer; +import org.kie.api.runtime.KieSession; +import org.kie.api.runtime.rule.FactHandle; +import org.kie.api.runtime.rule.QueryResults; +import org.kie.api.runtime.rule.QueryResultsRow; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.policy.common.endpoints.event.comm.TopicSink; +import org.onap.policy.common.utils.services.OrderedServiceImpl; +import org.onap.policy.drools.core.PolicyContainer; +import org.onap.policy.drools.core.PolicySession; +import org.onap.policy.drools.features.DroolsControllerFeatureApi; +import org.onap.policy.drools.protocol.coders.EventProtocolCoder; +import org.onap.policy.drools.protocol.coders.EventProtocolParams; +import org.onap.policy.drools.protocol.coders.JsonProtocolFilter; +import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration; +import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomGsonCoder; +import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.PotentialCoderFilter; + +public class MavenDroolsControllerTest2 { + private static final int FACT1_OBJECT = 1000; + private static final int FACT3_OBJECT = 1001; + + private static final long FACT_COUNT = 200L; + + private static final String EXPECTED_EXCEPTION = "expected exception"; + private static final RuntimeException RUNTIME_EX = new RuntimeException(EXPECTED_EXCEPTION); + private static final IllegalArgumentException ARG_EX = new IllegalArgumentException(EXPECTED_EXCEPTION); + + private static final String UNKNOWN_CLASS = "unknown class"; + + private static final String GROUP = "my-group"; + private static final String ARTIFACT = "my-artifact"; + private static final String VERSION = "my-version"; + + private static final String GROUP2 = "my-groupB"; + private static final String ARTIFACT2 = "my-artifactB"; + private static final String VERSION2 = "my-versionB"; + + private static final String TOPIC = "my-topic"; + private static final String TOPIC2 = "my-topic"; + + private static final ClassLoader CLASS_LOADER = MavenDroolsControllerTest2.class.getClassLoader(); + private static final int CLASS_LOADER_HASHCODE = CLASS_LOADER.hashCode(); + + private static final String SESSION1 = "session-A"; + private static final String SESSION2 = "session-B"; + private static final String FULL_SESSION1 = "full-A"; + private static final String FULL_SESSION2 = "full-B"; + + private static final String EVENT_TEXT = "my-event-text"; + private static final Object EVENT = new Object(); + + private static final String QUERY = "my-query"; + private static final String QUERY2 = "my-query-B"; + private static final String ENTITY = "my-entity"; + private static final Object PARM1 = "parmA"; + private static final Object PARM2 = "parmB"; + + @Mock + private EventProtocolCoder coderMgr; + @Mock + private DroolsControllerFeatureApi prov1; + @Mock + private DroolsControllerFeatureApi prov2; + @Mock + private OrderedServiceImpl<DroolsControllerFeatureApi> droolsProviders; + @Mock + private TopicCoderFilterConfiguration decoder1; + @Mock + private TopicCoderFilterConfiguration decoder2; + @Mock + private TopicCoderFilterConfiguration encoder1; + @Mock + private TopicCoderFilterConfiguration encoder2; + @Mock + private PolicyContainer container; + @Mock + private CustomGsonCoder gson1; + @Mock + private CustomGsonCoder gson2; + @Mock + private PotentialCoderFilter filter1a; + @Mock + private JsonProtocolFilter jsonFilter1a; + @Mock + private PotentialCoderFilter filter1b; + @Mock + private JsonProtocolFilter jsonFilter1b; + @Mock + private PotentialCoderFilter filter2; + @Mock + private JsonProtocolFilter jsonFilter2; + @Mock + private PolicySession sess1; + @Mock + private PolicySession sess2; + @Mock + private KieSession kieSess; + @Mock + private KieSession kieSess2; + @Mock + private TopicSink sink; + @Mock + private FactHandle fact1; + @Mock + private FactHandle fact2; + @Mock + private FactHandle fact3; + @Mock + private FactHandle factex; + @Mock + private KieBase kieBase; + @Mock + private KiePackage pkg1; + @Mock + private KiePackage pkg2; + @Mock + private Query query1; + @Mock + private Query query2; + @Mock + private Query query3; + @Mock + private QueryResults queryResults; + @Mock + private QueryResultsRow row1; + @Mock + private QueryResultsRow row2; + + private List<TopicCoderFilterConfiguration> decoders; + private List<TopicCoderFilterConfiguration> encoders; + + private MavenDroolsController drools; + + /** + * Initializes objects, including the drools controller. + */ + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(droolsProviders.getList()).thenReturn(Arrays.asList(prov1, prov2)); + + when(coderMgr.isDecodingSupported(GROUP, ARTIFACT, TOPIC)).thenReturn(true); + when(coderMgr.decode(GROUP, ARTIFACT, TOPIC, EVENT_TEXT)).thenReturn(EVENT); + + when(kieSess.getFactCount()).thenReturn(FACT_COUNT); + when(kieSess.getFactHandles()).thenReturn(Arrays.asList(fact1, fact2, factex, fact3)); + when(kieSess.getFactHandles(any())).thenReturn(Arrays.asList(fact1, fact3)); + when(kieSess.getKieBase()).thenReturn(kieBase); + when(kieSess.getQueryResults(QUERY, PARM1, PARM2)).thenReturn(queryResults); + + when(kieSess.getObject(fact1)).thenReturn(FACT1_OBJECT); + when(kieSess.getObject(fact2)).thenReturn(""); + when(kieSess.getObject(fact3)).thenReturn(FACT3_OBJECT); + when(kieSess.getObject(factex)).thenThrow(RUNTIME_EX); + + when(kieSess2.getFactHandles()).thenReturn(Collections.emptyList()); + + when(kieBase.getKiePackages()).thenReturn(Arrays.asList(pkg1, pkg2)); + + when(pkg1.getQueries()).thenReturn(Arrays.asList(query3)); + when(pkg2.getQueries()).thenReturn(Arrays.asList(query2, query1)); + + when(query1.getName()).thenReturn(QUERY); + when(query2.getName()).thenReturn(QUERY2); + + when(queryResults.iterator()).thenReturn(Arrays.asList(row1, row2).iterator()); + + when(row1.get(ENTITY)).thenReturn(FACT1_OBJECT); + when(row2.get(ENTITY)).thenReturn(FACT3_OBJECT); + + when(row1.getFactHandle(ENTITY)).thenReturn(fact1); + when(row2.getFactHandle(ENTITY)).thenReturn(fact3); + + when(sess1.getKieSession()).thenReturn(kieSess); + when(sess2.getKieSession()).thenReturn(kieSess2); + + when(sess1.getName()).thenReturn(SESSION1); + when(sess2.getName()).thenReturn(SESSION2); + + when(sess1.getFullName()).thenReturn(FULL_SESSION1); + when(sess2.getFullName()).thenReturn(FULL_SESSION2); + + when(container.getClassLoader()).thenReturn(CLASS_LOADER); + when(container.getPolicySessions()).thenReturn(Arrays.asList(sess1, sess2)); + when(container.insertAll(EVENT)).thenReturn(true); + + when(decoder1.getTopic()).thenReturn(TOPIC); + when(decoder2.getTopic()).thenReturn(TOPIC2); + + when(encoder1.getTopic()).thenReturn(TOPIC); + when(encoder2.getTopic()).thenReturn(TOPIC2); + + decoders = Arrays.asList(decoder1, decoder2); + encoders = Arrays.asList(encoder1, encoder2); + + when(decoder1.getCustomGsonCoder()).thenReturn(gson1); + when(encoder2.getCustomGsonCoder()).thenReturn(gson2); + + when(filter1a.getCodedClass()).thenReturn(Object.class.getName()); + when(filter1a.getFilter()).thenReturn(jsonFilter1a); + + when(filter1b.getCodedClass()).thenReturn(String.class.getName()); + when(filter1b.getFilter()).thenReturn(jsonFilter1b); + + when(filter2.getCodedClass()).thenReturn(Integer.class.getName()); + when(filter2.getFilter()).thenReturn(jsonFilter2); + + when(decoder1.getCoderFilters()).thenReturn(Arrays.asList(filter1a, filter1b)); + when(decoder2.getCoderFilters()).thenReturn(Collections.emptyList()); + + when(encoder1.getCoderFilters()).thenReturn(Collections.emptyList()); + when(encoder2.getCoderFilters()).thenReturn(Arrays.asList(filter2)); + + when(sink.getTopic()).thenReturn(TOPIC); + when(sink.send(EVENT_TEXT)).thenReturn(true); + + drools = new MyDrools(GROUP, ARTIFACT, VERSION, null, null); + + when(coderMgr.encode(TOPIC, EVENT, drools)).thenReturn(EVENT_TEXT); + } + + @Test + public void testMavenDroolsController_InvalidArgs() { + assertThatIllegalArgumentException().isThrownBy(() -> new MyDrools(null, ARTIFACT, VERSION, null, null)) + .withMessageContaining("group"); + assertThatIllegalArgumentException().isThrownBy(() -> new MyDrools("", ARTIFACT, VERSION, null, null)) + .withMessageContaining("group"); + + assertThatIllegalArgumentException().isThrownBy(() -> new MyDrools(GROUP, null, VERSION, null, null)) + .withMessageContaining("artifact"); + assertThatIllegalArgumentException().isThrownBy(() -> new MyDrools(GROUP, "", VERSION, null, null)) + .withMessageContaining("artifact"); + + assertThatIllegalArgumentException().isThrownBy(() -> new MyDrools(GROUP, ARTIFACT, null, null, null)) + .withMessageContaining("version"); + assertThatIllegalArgumentException().isThrownBy(() -> new MyDrools(GROUP, ARTIFACT, "", null, null)) + .withMessageContaining("version"); + } + + @Test + public void testUpdateToVersion() { + // add coders + drools.updateToVersion(GROUP, ARTIFACT, VERSION2, decoders, encoders); + + verify(container).updateToVersion(VERSION2); + + // nothing removed the first time + verify(coderMgr, never()).removeDecoders(GROUP, ARTIFACT, TOPIC2); + verify(coderMgr, never()).removeEncoders(GROUP, ARTIFACT, TOPIC); + + verify(coderMgr, times(2)).addDecoder(any()); + verify(coderMgr, times(1)).addEncoder(any()); + + // remove coders + when(container.getVersion()).thenReturn(VERSION2); + drools.updateToVersion(GROUP, ARTIFACT, VERSION, null, null); + + verify(container).updateToVersion(VERSION); + + verify(coderMgr, times(2)).removeDecoders(GROUP, ARTIFACT, TOPIC2); + verify(coderMgr, times(2)).removeEncoders(GROUP, ARTIFACT, TOPIC); + + // not added again + verify(coderMgr, times(2)).addDecoder(any()); + verify(coderMgr, times(1)).addEncoder(any()); + } + + @Test + public void testUpdateToVersion_Unchanged() { + drools.updateToVersion(GROUP, ARTIFACT, VERSION, decoders, encoders); + + verify(coderMgr, never()).addDecoder(any()); + verify(coderMgr, never()).addEncoder(any()); + } + + @Test + public void testUpdateToVersion_InvalidArgs() { + assertThatIllegalArgumentException() + .isThrownBy(() -> drools.updateToVersion(null, ARTIFACT, VERSION, null, null)) + .withMessageContaining("group"); + assertThatIllegalArgumentException().isThrownBy(() -> drools.updateToVersion("", ARTIFACT, VERSION, null, null)) + .withMessageContaining("group"); + + assertThatIllegalArgumentException().isThrownBy(() -> drools.updateToVersion(GROUP, null, VERSION, null, null)) + .withMessageContaining("artifact"); + assertThatIllegalArgumentException().isThrownBy(() -> drools.updateToVersion(GROUP, "", VERSION, null, null)) + .withMessageContaining("artifact"); + + assertThatIllegalArgumentException().isThrownBy(() -> drools.updateToVersion(GROUP, ARTIFACT, null, null, null)) + .withMessageContaining("version"); + assertThatIllegalArgumentException().isThrownBy(() -> drools.updateToVersion(GROUP, ARTIFACT, "", null, null)) + .withMessageContaining("version"); + + assertThatIllegalArgumentException() + .isThrownBy(() -> drools.updateToVersion("no-group-id", ARTIFACT, VERSION, null, null)) + .withMessageContaining("BRAINLESS"); + + assertThatIllegalArgumentException() + .isThrownBy(() -> drools.updateToVersion(GROUP, "no-artifact-id", VERSION, null, null)) + .withMessageContaining("BRAINLESS"); + + assertThatIllegalArgumentException() + .isThrownBy(() -> drools.updateToVersion(GROUP, ARTIFACT, "no-version", null, null)) + .withMessageContaining("BRAINLESS"); + + assertThatIllegalArgumentException() + .isThrownBy(() -> drools.updateToVersion(GROUP2, ARTIFACT, VERSION, null, null)) + .withMessageContaining("coordinates must be identical"); + + assertThatIllegalArgumentException() + .isThrownBy(() -> drools.updateToVersion(GROUP, ARTIFACT2, VERSION, null, null)) + .withMessageContaining("coordinates must be identical"); + } + + @Test + public void testInitCoders_NullCoders() { + // already constructed with null coders + verify(coderMgr, never()).addDecoder(any()); + verify(coderMgr, never()).addEncoder(any()); + } + + @Test + public void testInitCoders_NullOrEmptyFilters() { + when(decoder1.getCoderFilters()).thenReturn(Collections.emptyList()); + when(decoder2.getCoderFilters()).thenReturn(null); + + when(encoder1.getCoderFilters()).thenReturn(null); + when(encoder2.getCoderFilters()).thenReturn(Collections.emptyList()); + + drools = new MyDrools(GROUP, ARTIFACT, VERSION, decoders, encoders); + + verify(coderMgr, never()).addDecoder(any()); + verify(coderMgr, never()).addEncoder(any()); + } + + @Test + public void testInitCoders_GsonClass() { + when(gson1.getClassContainer()).thenReturn(""); + when(gson2.getClassContainer()).thenReturn(Long.class.getName()); + + drools = new MyDrools(GROUP, ARTIFACT, VERSION, decoders, encoders); + + // all should be added + verify(coderMgr, times(2)).addDecoder(any()); + verify(coderMgr, times(1)).addEncoder(any()); + } + + @Test + public void testInitCoders_InvalidGsonClass() { + when(gson1.getClassContainer()).thenReturn(UNKNOWN_CLASS); + + assertThatIllegalArgumentException() + .isThrownBy(() -> new MyDrools(GROUP, ARTIFACT, VERSION, decoders, encoders)) + .withMessageContaining("cannot be retrieved"); + } + + @Test + public void testInitCoders_InvalidFilterClass() { + when(filter2.getCodedClass()).thenReturn(UNKNOWN_CLASS); + + assertThatIllegalArgumentException() + .isThrownBy(() -> new MyDrools(GROUP, ARTIFACT, VERSION, decoders, encoders)) + .withMessageContaining("cannot be retrieved"); + } + + @Test + public void testInitCoders_Filters() { + + drools = new MyDrools(GROUP, ARTIFACT, VERSION, decoders, encoders); + + ArgumentCaptor<EventProtocolParams> dec = ArgumentCaptor.forClass(EventProtocolParams.class); + verify(coderMgr, times(2)).addDecoder(dec.capture()); + + ArgumentCaptor<EventProtocolParams> enc = ArgumentCaptor.forClass(EventProtocolParams.class); + verify(coderMgr, times(1)).addEncoder(enc.capture()); + + // validate parameters + EventProtocolParams params = dec.getAllValues().get(0); + assertEquals(ARTIFACT, params.getArtifactId()); + assertEquals(gson1, params.getCustomCoder()); + assertEquals(Object.class.getName(), params.getEventClass()); + assertEquals(GROUP, params.getGroupId()); + assertEquals(CLASS_LOADER_HASHCODE, params.getModelClassLoaderHash()); + assertEquals(jsonFilter1a, params.getProtocolFilter()); + assertEquals(TOPIC, params.getTopic()); + + params = dec.getAllValues().get(1); + assertEquals(ARTIFACT, params.getArtifactId()); + assertEquals(gson1, params.getCustomCoder()); + assertEquals(String.class.getName(), params.getEventClass()); + assertEquals(GROUP, params.getGroupId()); + assertEquals(CLASS_LOADER_HASHCODE, params.getModelClassLoaderHash()); + assertEquals(jsonFilter1b, params.getProtocolFilter()); + assertEquals(TOPIC, params.getTopic()); + + params = enc.getAllValues().get(0); + assertEquals(ARTIFACT, params.getArtifactId()); + assertEquals(gson2, params.getCustomCoder()); + assertEquals(Integer.class.getName(), params.getEventClass()); + assertEquals(GROUP, params.getGroupId()); + assertEquals(CLASS_LOADER_HASHCODE, params.getModelClassLoaderHash()); + assertEquals(jsonFilter2, params.getProtocolFilter()); + assertEquals(TOPIC, params.getTopic()); + } + + @Test + public void testOwnsCoder() { + int hc = CLASS_LOADER_HASHCODE; + + // wrong hash code + assertFalse(drools.ownsCoder(String.class, hc + 1)); + + // correct hash code + assertTrue(drools.ownsCoder(String.class, hc)); + + // unknown class + drools = new MyDrools(GROUP, ARTIFACT, VERSION, null, null) { + @Override + protected boolean isClass(String className) { + return false; + } + }; + assertFalse(drools.ownsCoder(String.class, hc)); + } + + @Test + public void testStart_testStop_testIsAlive() { + drools = new MyDrools(GROUP, ARTIFACT, VERSION, decoders, encoders); + + when(container.start()).thenReturn(true); + when(container.stop()).thenReturn(true); + + assertFalse(drools.isAlive()); + + // start it + assertTrue(drools.start()); + verify(container).start(); + assertTrue(drools.isAlive()); + + // repeat - no changes + assertTrue(drools.start()); + verify(container).start(); + assertTrue(drools.isAlive()); + + // stop it + assertTrue(drools.stop()); + verify(container).stop(); + assertFalse(drools.isAlive()); + + // repeat - no changes + assertTrue(drools.stop()); + verify(container).stop(); + assertFalse(drools.isAlive()); + + // now check with container returning false - should still be invoked + when(container.start()).thenReturn(false); + when(container.stop()).thenReturn(false); + assertFalse(drools.start()); + assertTrue(drools.isAlive()); + assertFalse(drools.stop()); + assertFalse(drools.isAlive()); + verify(container, times(2)).start(); + verify(container, times(2)).stop(); + + // coders should still be intact + verify(coderMgr, never()).removeDecoders(any(), any(), any()); + verify(coderMgr, never()).removeEncoders(any(), any(), any()); + + verify(container, never()).shutdown(); + verify(container, never()).destroy(); + } + + @Test + public void testShutdown() { + drools = new MyDrools(GROUP, ARTIFACT, VERSION, decoders, encoders); + + // start it + drools.start(); + + // shut down + drools.shutdown(); + + verify(container).stop(); + assertFalse(drools.isAlive()); + + // coders should have been removed + verify(coderMgr, times(2)).removeDecoders(any(), any(), any()); + verify(coderMgr, times(2)).removeEncoders(any(), any(), any()); + + verify(container).shutdown(); + verify(container, never()).destroy(); + } + + @Test + public void testShutdown_Ex() { + drools = new MyDrools(GROUP, ARTIFACT, VERSION, decoders, encoders); + + // start it + drools.start(); + + when(container.stop()).thenThrow(RUNTIME_EX); + + // shut down + drools.shutdown(); + + assertFalse(drools.isAlive()); + + verify(container).shutdown(); + verify(container, never()).destroy(); + } + + @Test + public void testHalt() { + drools = new MyDrools(GROUP, ARTIFACT, VERSION, decoders, encoders); + + // start it + drools.start(); + + // halt + drools.halt(); + + verify(container).stop(); + assertFalse(drools.isAlive()); + + // coders should have been removed + verify(coderMgr, times(2)).removeDecoders(any(), any(), any()); + verify(coderMgr, times(2)).removeEncoders(any(), any(), any()); + + verify(container).destroy(); + } + + @Test + public void testHalt_Ex() { + drools = new MyDrools(GROUP, ARTIFACT, VERSION, decoders, encoders); + + // start it + drools.start(); + + when(container.stop()).thenThrow(RUNTIME_EX); + + // halt + drools.halt(); + + assertFalse(drools.isAlive()); + + verify(container).destroy(); + } + + @Test + public void testRemoveCoders_Ex() { + drools = new MyDrools(GROUP, ARTIFACT, VERSION, decoders, encoders) { + @Override + protected void removeDecoders() { + throw ARG_EX; + } + + @Override + protected void removeEncoders() { + throw ARG_EX; + } + }; + + drools.updateToVersion(GROUP, ARTIFACT, VERSION2, null, null); + } + + @Test + public void testOfferStringString() { + drools.start(); + assertTrue(drools.offer(TOPIC, EVENT_TEXT)); + + verify(container).insertAll(EVENT); + } + + @Test + public void testOfferStringString_NoDecode() { + when(coderMgr.isDecodingSupported(GROUP, ARTIFACT, TOPIC)).thenReturn(false); + + drools.start(); + assertTrue(drools.offer(TOPIC, EVENT_TEXT)); + + verify(container, never()).insertAll(EVENT); + } + + @Test + public void testOfferStringString_DecodeUnsupported() { + when(coderMgr.decode(GROUP, ARTIFACT, TOPIC, EVENT_TEXT)) + .thenThrow(new UnsupportedOperationException(EXPECTED_EXCEPTION)); + + drools.start(); + assertTrue(drools.offer(TOPIC, EVENT_TEXT)); + + verify(container, never()).insertAll(EVENT); + } + + @Test + public void testOfferStringString_DecodeEx() { + when(coderMgr.decode(GROUP, ARTIFACT, TOPIC, EVENT_TEXT)).thenThrow(RUNTIME_EX); + + drools.start(); + assertTrue(drools.offer(TOPIC, EVENT_TEXT)); + + verify(container, never()).insertAll(EVENT); + } + + @Test + public void testOfferStringString_Ignored() { + drools.start(); + + drools.lock(); + assertTrue(drools.offer(TOPIC, EVENT_TEXT)); + assertEquals(0, drools.getRecentSourceEvents().length); + drools.unlock(); + + drools.stop(); + assertTrue(drools.offer(TOPIC, EVENT_TEXT)); + assertEquals(0, drools.getRecentSourceEvents().length); + drools.start(); + + // no sessions + when(container.getPolicySessions()).thenReturn(Collections.emptyList()); + assertTrue(drools.offer(TOPIC, EVENT_TEXT)); + assertEquals(0, drools.getRecentSourceEvents().length); + } + + @Test + public void testOfferT() { + drools.start(); + assertTrue(drools.offer(EVENT)); + assertEquals(1, drools.getRecentSourceEvents().length); + assertEquals(EVENT, drools.getRecentSourceEvents()[0]); + verify(container).insertAll(EVENT); + + verify(prov1).beforeInsert(drools, EVENT); + verify(prov2).beforeInsert(drools, EVENT); + + verify(prov1).afterInsert(drools, EVENT, true); + verify(prov2).afterInsert(drools, EVENT, true); + } + + @Test + public void testOfferT_Ex() { + when(prov1.beforeInsert(drools, EVENT)).thenThrow(RUNTIME_EX); + when(prov1.afterInsert(drools, EVENT, true)).thenThrow(RUNTIME_EX); + + drools.start(); + assertTrue(drools.offer(EVENT)); + assertEquals(1, drools.getRecentSourceEvents().length); + assertEquals(EVENT, drools.getRecentSourceEvents()[0]); + verify(container).insertAll(EVENT); + + // should still invoke prov2 + verify(prov2).beforeInsert(drools, EVENT); + + verify(prov2).afterInsert(drools, EVENT, true); + } + + @Test + public void testOfferT_NotInserted() { + when(container.insertAll(EVENT)).thenReturn(false); + + drools.start(); + assertTrue(drools.offer(EVENT)); + assertEquals(1, drools.getRecentSourceEvents().length); + assertEquals(EVENT, drools.getRecentSourceEvents()[0]); + verify(container).insertAll(EVENT); + + verify(prov1).beforeInsert(drools, EVENT); + verify(prov2).beforeInsert(drools, EVENT); + + verify(prov1).afterInsert(drools, EVENT, false); + verify(prov2).afterInsert(drools, EVENT, false); + } + + @Test + public void testOfferT_BeforeInsertIntercept() { + drools.start(); + when(prov1.beforeInsert(drools, EVENT)).thenReturn(true); + + assertTrue(drools.offer(EVENT)); + assertEquals(1, drools.getRecentSourceEvents().length); + assertEquals(EVENT, drools.getRecentSourceEvents()[0]); + verify(container, never()).insertAll(EVENT); + + verify(prov1).beforeInsert(drools, EVENT); + + // nothing else invoked + verify(prov2, never()).beforeInsert(drools, EVENT); + verify(prov1, never()).afterInsert(drools, EVENT, true); + verify(prov2, never()).afterInsert(drools, EVENT, true); + } + + @Test + public void testOfferT_AfterInsertIntercept() { + drools.start(); + + when(prov1.afterInsert(drools, EVENT, true)).thenReturn(true); + + assertTrue(drools.offer(EVENT)); + assertEquals(1, drools.getRecentSourceEvents().length); + assertEquals(EVENT, drools.getRecentSourceEvents()[0]); + verify(container).insertAll(EVENT); + + verify(prov1).beforeInsert(drools, EVENT); + verify(prov2).beforeInsert(drools, EVENT); + + verify(prov1).afterInsert(drools, EVENT, true); + + // prov2 is never called + verify(prov2, never()).afterInsert(drools, EVENT, true); + } + + @Test + public void testOfferT_Ignored() { + drools.start(); + + drools.lock(); + assertTrue(drools.offer(EVENT)); + assertEquals(0, drools.getRecentSourceEvents().length); + drools.unlock(); + + drools.stop(); + assertTrue(drools.offer(EVENT)); + assertEquals(0, drools.getRecentSourceEvents().length); + drools.start(); + + // no sessions + when(container.getPolicySessions()).thenReturn(Collections.emptyList()); + assertTrue(drools.offer(EVENT)); + assertEquals(0, drools.getRecentSourceEvents().length); + } + + @Test + public void testDeliver() { + drools.start(); + assertTrue(drools.deliver(sink, EVENT)); + assertEquals(1, drools.getRecentSinkEvents().length); + assertEquals(EVENT_TEXT, drools.getRecentSinkEvents()[0]); + + verify(sink).send(EVENT_TEXT); + + verify(prov1).beforeDeliver(drools, sink, EVENT); + verify(prov2).beforeDeliver(drools, sink, EVENT); + + verify(prov1).afterDeliver(drools, sink, EVENT, EVENT_TEXT, true); + verify(prov2).afterDeliver(drools, sink, EVENT, EVENT_TEXT, true); + } + + @Test + public void testDeliver_InvalidArgs() { + drools.start(); + + assertThatIllegalArgumentException().isThrownBy(() -> drools.deliver(null, EVENT)) + .withMessageContaining("sink"); + + assertThatIllegalArgumentException().isThrownBy(() -> drools.deliver(sink, null)) + .withMessageContaining("event"); + + drools.lock(); + assertThatIllegalStateException().isThrownBy(() -> drools.deliver(sink, EVENT)).withMessageContaining("locked"); + drools.unlock(); + + drools.stop(); + assertThatIllegalStateException().isThrownBy(() -> drools.deliver(sink, EVENT)) + .withMessageContaining("stopped"); + drools.start(); + + assertEquals(0, drools.getRecentSinkEvents().length); + } + + @Test + public void testDeliver_BeforeIntercept() { + when(prov1.beforeDeliver(drools, sink, EVENT)).thenReturn(true); + + drools.start(); + assertTrue(drools.deliver(sink, EVENT)); + assertEquals(0, drools.getRecentSinkEvents().length); + + verify(prov1).beforeDeliver(drools, sink, EVENT); + + // nothing else should have been invoked + verify(sink, never()).send(EVENT_TEXT); + verify(prov2, never()).beforeDeliver(drools, sink, EVENT); + verify(prov1, never()).afterDeliver(drools, sink, EVENT, EVENT_TEXT, true); + verify(prov2, never()).afterDeliver(drools, sink, EVENT, EVENT_TEXT, true); + } + + @Test + public void testDeliver_AfterIntercept() { + when(prov1.afterDeliver(drools, sink, EVENT, EVENT_TEXT, true)).thenReturn(true); + + drools.start(); + assertTrue(drools.deliver(sink, EVENT)); + assertEquals(1, drools.getRecentSinkEvents().length); + assertEquals(EVENT_TEXT, drools.getRecentSinkEvents()[0]); + + verify(prov1).beforeDeliver(drools, sink, EVENT); + verify(prov2).beforeDeliver(drools, sink, EVENT); + + verify(sink).send(EVENT_TEXT); + + verify(prov1).afterDeliver(drools, sink, EVENT, EVENT_TEXT, true); + + // nothing else should have been invoked + verify(prov2, never()).afterDeliver(drools, sink, EVENT, EVENT_TEXT, true); + } + + @Test + public void testDeliver_InterceptEx() { + when(prov1.beforeDeliver(drools, sink, EVENT)).thenThrow(RUNTIME_EX); + when(prov1.afterDeliver(drools, sink, EVENT, EVENT_TEXT, true)).thenThrow(RUNTIME_EX); + + drools.start(); + assertTrue(drools.deliver(sink, EVENT)); + + verify(sink).send(EVENT_TEXT); + + // should still invoke prov2 + verify(prov2).beforeDeliver(drools, sink, EVENT); + verify(prov2).afterDeliver(drools, sink, EVENT, EVENT_TEXT, true); + } + + @Test + public void testGetXxx() { + assertEquals(VERSION, drools.getVersion()); + assertEquals(ARTIFACT, drools.getArtifactId()); + assertEquals(GROUP, drools.getGroupId()); + assertEquals(CLASS_LOADER_HASHCODE, drools.getModelClassLoaderHash()); + assertSame(container, drools.getContainer()); + assertEquals(Arrays.asList(sess1, sess2), drools.getSessions()); + + // test junit methods - need a controller with fewer overrides + drools = new MavenDroolsController(GROUP, ARTIFACT, VERSION, null, null) { + @Override + protected PolicyContainer makePolicyContainer(String groupId, String artifactId, String version) { + return container; + } + }; + + assertSame(EventProtocolCoder.manager, drools.getCoderManager()); + assertSame(DroolsControllerFeatureApi.providers, drools.getDroolsProviders()); + } + + @Test + public void testLock_testUnlock_testIsLocked() { + assertFalse(drools.isLocked()); + + assertTrue(drools.lock()); + assertTrue(drools.isLocked()); + + assertTrue(drools.unlock()); + assertFalse(drools.isLocked()); + + // repeat + assertTrue(drools.lock()); + assertTrue(drools.isLocked()); + + assertTrue(drools.unlock()); + assertFalse(drools.isLocked()); + } + + @Test + public void testGetSessionNames_testGetCanonicalSessionNames() { + assertEquals("[session-A, session-B]", drools.getSessionNames(true).toString()); + assertEquals("[full-A, full-B]", drools.getSessionNames(false).toString()); + + assertEquals("[session-A, session-B]", drools.getSessionNames().toString()); + + assertEquals("[full-A, full-B]", drools.getCanonicalSessionNames().toString()); + + // exception case + when(container.getPolicySessions()).thenThrow(RUNTIME_EX); + assertEquals("[expected exception]", drools.getSessionNames().toString()); + } + + @Test + public void testGetBaseDomainNames() { + KieContainer kiecont = mock(KieContainer.class); + when(kiecont.getKieBaseNames()).thenReturn(Arrays.asList("kieA", "kieB")); + when(container.getKieContainer()).thenReturn(kiecont); + + assertEquals("[kieA, kieB]", drools.getBaseDomainNames().toString()); + } + + @Test + public void testGetSession() { + assertThatIllegalArgumentException().isThrownBy(() -> drools.getSession(null)) + .withMessageContaining("must be provided"); + + assertThatIllegalArgumentException().isThrownBy(() -> drools.getSession("")) + .withMessageContaining("must be provided"); + + assertSame(sess1, drools.getSession(SESSION1)); + assertSame(sess1, drools.getSession(FULL_SESSION1)); + + assertSame(sess2, drools.getSession(SESSION2)); + + assertThatIllegalArgumentException().isThrownBy(() -> drools.getSession("unknown session")) + .withMessageContaining("Invalid Session Name"); + } + + @Test + public void testFactClassNames() { + // copy to a sorted map so the order remains unchanged + Map<String, Integer> map = new TreeMap<>(drools.factClassNames(SESSION1)); + assertEquals("{java.lang.Integer=2, java.lang.String=1}", map.toString()); + + assertThatIllegalArgumentException().isThrownBy(() -> drools.factClassNames(null)) + .withMessageContaining("Invalid Session Name"); + + assertThatIllegalArgumentException().isThrownBy(() -> drools.factClassNames("")) + .withMessageContaining("Invalid Session Name"); + } + + @Test + public void testFactCount() { + assertEquals(FACT_COUNT, drools.factCount(SESSION1)); + + assertThatIllegalArgumentException().isThrownBy(() -> drools.factCount(null)) + .withMessageContaining("Invalid Session Name"); + + assertThatIllegalArgumentException().isThrownBy(() -> drools.factCount("")) + .withMessageContaining("Invalid Session Name"); + } + + @Test + public void testFactsStringStringBoolean() { + assertEquals("[1000, 1001]", drools.facts(SESSION1, Integer.class.getName(), false).toString()); + verify(kieSess, never()).delete(fact1); + verify(kieSess, never()).delete(fact2); + verify(kieSess, never()).delete(fact3); + verify(kieSess, never()).delete(factex); + + // now delete - but should only delete 1 & 3 + assertEquals("[1000, 1001]", drools.facts(SESSION1, Integer.class.getName(), true).toString()); + verify(kieSess).delete(fact1); + verify(kieSess, never()).delete(fact2); + verify(kieSess).delete(fact3); + verify(kieSess, never()).delete(factex); + + assertThatIllegalArgumentException().isThrownBy(() -> drools.facts(null, Integer.class.getName(), false)) + .withMessageContaining("Invalid Session Name"); + + assertThatIllegalArgumentException().isThrownBy(() -> drools.facts("", Integer.class.getName(), false)) + .withMessageContaining("Invalid Session Name"); + + assertThatIllegalArgumentException().isThrownBy(() -> drools.facts(SESSION1, null, false)) + .withMessageContaining("Invalid Class Name"); + + assertThatIllegalArgumentException().isThrownBy(() -> drools.facts(SESSION1, "", false)) + .withMessageContaining("Invalid Class Name"); + + assertThatIllegalArgumentException().isThrownBy(() -> drools.facts(SESSION1, UNKNOWN_CLASS, false)) + .withMessageContaining("classloader"); + } + + @Test + public void testFactsStringStringBoolean_DeleteEx() { + doThrow(RUNTIME_EX).when(kieSess).delete(fact1); + + assertEquals("[1000, 1001]", drools.facts(SESSION1, Integer.class.getName(), true).toString()); + + // should still have deleted #3 + verify(kieSess).delete(fact3); + } + + @Test + public void testFactsStringClassOfT() { + assertEquals("[1000, 1001]", drools.facts(SESSION1, Integer.class).toString()); + } + + @Test + public void testFactQuery() { + assertEquals("[1000, 1001]", drools.factQuery(SESSION1, QUERY, ENTITY, false, PARM1, PARM2).toString()); + + verify(kieSess, never()).delete(fact1); + verify(kieSess, never()).delete(fact3); + + assertThatIllegalArgumentException() + .isThrownBy(() -> drools.factQuery(null, QUERY, ENTITY, false, PARM1, PARM2)) + .withMessageContaining("Invalid Session Name"); + + assertThatIllegalArgumentException().isThrownBy(() -> drools.factQuery("", QUERY, ENTITY, false, PARM1, PARM2)) + .withMessageContaining("Invalid Session Name"); + + assertThatIllegalArgumentException() + .isThrownBy(() -> drools.factQuery(SESSION1, null, ENTITY, false, PARM1, PARM2)) + .withMessageContaining("Invalid Query Name"); + + assertThatIllegalArgumentException() + .isThrownBy(() -> drools.factQuery(SESSION1, "", ENTITY, false, PARM1, PARM2)) + .withMessageContaining("Invalid Query Name"); + + assertThatIllegalArgumentException() + .isThrownBy(() -> drools.factQuery(SESSION1, QUERY, null, false, PARM1, PARM2)) + .withMessageContaining("Invalid Queried Entity"); + + assertThatIllegalArgumentException() + .isThrownBy(() -> drools.factQuery(SESSION1, QUERY, "", false, PARM1, PARM2)) + .withMessageContaining("Invalid Queried Entity"); + + assertThatIllegalArgumentException().isThrownBy( + () -> drools.factQuery(SESSION1, QUERY + "-unknown-query", ENTITY, false, PARM1, PARM2)) + .withMessageContaining("Invalid Query Name"); + } + + @Test + public void testFactQuery_Delete() { + doThrow(RUNTIME_EX).when(kieSess).delete(fact1); + + assertEquals("[1000, 1001]", drools.factQuery(SESSION1, QUERY, ENTITY, true, PARM1, PARM2).toString()); + + // should still delete fact #3 + verify(kieSess).delete(fact3); + } + + @Test + public void testDeleteStringT() { + assertTrue(drools.delete(SESSION1, FACT3_OBJECT)); + + verify(kieSess, never()).delete(fact1); + verify(kieSess).delete(fact3); + + // not found + assertFalse(drools.delete(SESSION1, "hello")); + + // repeat, but generate exception while getting the first object + when(kieSess.getObject(fact1)).thenThrow(RUNTIME_EX); + assertTrue(drools.delete(SESSION1, FACT3_OBJECT)); + + verify(kieSess, never()).delete(fact1); + + // should still delete fact #3 + verify(kieSess, times(2)).delete(fact3); + } + + @Test + public void testDeleteT() { + assertTrue(drools.delete(FACT3_OBJECT)); + + verify(kieSess).delete(fact3); + } + + @Test + public void testDeleteStringClassOfT() { + assertTrue(drools.delete(SESSION1, Integer.class)); + + verify(kieSess).delete(fact1); + verify(kieSess).delete(fact3); + } + + @Test + public void testDeleteStringClassOfT_Ex() { + doThrow(RUNTIME_EX).when(kieSess).delete(fact1); + + assertFalse(drools.delete(SESSION1, Integer.class)); + + // should still delete fact #3 + verify(kieSess).delete(fact3); + } + + @Test + public void testDeleteClassOfT() { + assertTrue(drools.delete(Integer.class)); + + verify(kieSess).delete(fact1); + verify(kieSess).delete(fact3); + } + + @Test + public void testFetchModelClass() { + assertSame(Long.class, drools.fetchModelClass(Long.class.getName())); + } + + @Test + public void testIsBrained() { + assertTrue(drools.isBrained()); + } + + @Test + public void testToString() { + assertNotNull(drools.toString()); + } + + private class MyDrools extends MavenDroolsController { + + public MyDrools(String groupId, String artifactId, String version, + List<TopicCoderFilterConfiguration> decoderConfigurations, + List<TopicCoderFilterConfiguration> encoderConfigurations) { + + super(groupId, artifactId, version, decoderConfigurations, encoderConfigurations); + } + + @Override + protected EventProtocolCoder getCoderManager() { + return coderMgr; + } + + @Override + protected OrderedServiceImpl<DroolsControllerFeatureApi> getDroolsProviders() { + return droolsProviders; + } + + @Override + protected PolicyContainer makePolicyContainer(String groupId, String artifactId, String version) { + when(container.getGroupId()).thenReturn(groupId); + when(container.getArtifactId()).thenReturn(artifactId); + when(container.getVersion()).thenReturn(version); + + return container; + } + } +} |