diff options
author | Jim Hahn <jrh3@att.com> | 2018-10-02 16:46:57 -0400 |
---|---|---|
committer | Jim Hahn <jrh3@att.com> | 2018-10-03 17:34:09 -0400 |
commit | 08dc9c33a66b1bdf7c35696576fcb44e7e072eb6 (patch) | |
tree | 817b73046ca2701528ddf42f4ca3244f47fbf2cf /policy-management | |
parent | a0e0c7840f72392152a970d88a5a66d394c4a793 (diff) |
Add coverage for policy-management
Added coverage for PolicyControllerFactory and AggregatedPolicyController.
Fixed some typos in comments.
Reformatted some code.
Change-Id: I33aea8e1e7dde29bd51218d0ecad7b34047b33e5
Issue-ID: POLICY-1148
Signed-off-by: Jim Hahn <jrh3@att.com>
Diffstat (limited to 'policy-management')
-rw-r--r-- | policy-management/pom.xml | 13 | ||||
-rw-r--r-- | policy-management/src/main/java/org/onap/policy/drools/system/PolicyControllerFactory.java | 27 | ||||
-rw-r--r-- | policy-management/src/main/java/org/onap/policy/drools/system/internal/AggregatedPolicyController.java | 83 | ||||
-rw-r--r-- | policy-management/src/test/java/org/onap/policy/drools/system/PolicyControllerFactoryTest.java | 421 | ||||
-rw-r--r-- | policy-management/src/test/java/org/onap/policy/drools/system/PolicyEngineTest.java (renamed from policy-management/src/test/java/org/onap/policy/drools/system/test/PolicyEngineTest.java) | 2 | ||||
-rw-r--r-- | policy-management/src/test/java/org/onap/policy/drools/system/internal/AggregatedPolicyControllerTest.java | 948 |
6 files changed, 1452 insertions, 42 deletions
diff --git a/policy-management/pom.xml b/policy-management/pom.xml index e988b4a5..002575f7 100644 --- a/policy-management/pom.xml +++ b/policy-management/pom.xml @@ -277,6 +277,19 @@ <artifactId>junit</artifactId> <scope>test</scope> </dependency> + + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-api-mockito</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.onap.policy.common</groupId> + <artifactId>utils-test</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> <!-- The following dependencies are for features and drools diff --git a/policy-management/src/main/java/org/onap/policy/drools/system/PolicyControllerFactory.java b/policy-management/src/main/java/org/onap/policy/drools/system/PolicyControllerFactory.java index 1241acad..c0749790 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/system/PolicyControllerFactory.java +++ b/policy-management/src/main/java/org/onap/policy/drools/system/PolicyControllerFactory.java @@ -218,8 +218,7 @@ class IndexedPolicyControllerFactory implements PolicyControllerFactory { /* A PolicyController does not exist */ - PolicyController controller = - new AggregatedPolicyController(name, properties); + PolicyController controller = newPolicyController(name, properties); String coordinates = toKey(controller.getDrools().getGroupId(), controller.getDrools().getArtifactId()); @@ -254,9 +253,7 @@ class IndexedPolicyControllerFactory implements PolicyControllerFactory { this.patch(controller, droolsConfig); - if (logger.isInfoEnabled()) { - logger.info("UPDATED drools configuration: {} on {}", droolsConfig, this); - } + logger.info("UPDATED drools configuration: {} on {}", droolsConfig, this); return controller; } @@ -281,9 +278,7 @@ class IndexedPolicyControllerFactory implements PolicyControllerFactory { throw new IllegalArgumentException("Cannot update drools configuration Drools Configuration"); } - if (logger.isInfoEnabled()) { - logger.info("UPDATED drools configuration: {} on {}", droolsConfig, this); - } + logger.info("UPDATED drools configuration: {} on {}", droolsConfig, this); String coordinates = toKey(controller.getDrools().getGroupId(), controller.getDrools().getArtifactId()); @@ -484,7 +479,7 @@ class IndexedPolicyControllerFactory implements PolicyControllerFactory { @Override public List<String> getFeatures() { List<String> features = new ArrayList<>(); - for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) { + for (PolicyControllerFeatureAPI feature : getProviders()) { features.add(feature.getName()); } return features; @@ -496,7 +491,7 @@ class IndexedPolicyControllerFactory implements PolicyControllerFactory { @JsonIgnore @Override public List<PolicyControllerFeatureAPI> getFeatureProviders() { - return PolicyControllerFeatureAPI.providers.getList(); + return getProviders(); } /** @@ -508,7 +503,7 @@ class IndexedPolicyControllerFactory implements PolicyControllerFactory { throw new IllegalArgumentException("A feature name must be provided"); } - for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) { + for (PolicyControllerFeatureAPI feature : getProviders()) { if (feature.getName().equals(featureName)) { return feature; } @@ -520,4 +515,14 @@ class IndexedPolicyControllerFactory implements PolicyControllerFactory { private IllegalArgumentException makeArgEx(String argName) { return new IllegalArgumentException("Invalid " + argName); } + + // these methods can be overridden by junit tests + + protected PolicyController newPolicyController(String name, Properties properties) { + return new AggregatedPolicyController(name, properties); + } + + protected List<PolicyControllerFeatureAPI> getProviders() { + return PolicyControllerFeatureAPI.providers.getList(); + } } diff --git a/policy-management/src/main/java/org/onap/policy/drools/system/internal/AggregatedPolicyController.java b/policy-management/src/main/java/org/onap/policy/drools/system/internal/AggregatedPolicyController.java index 8d290674..581184ee 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/system/internal/AggregatedPolicyController.java +++ b/policy-management/src/main/java/org/onap/policy/drools/system/internal/AggregatedPolicyController.java @@ -32,6 +32,7 @@ import org.onap.policy.common.endpoints.event.comm.TopicListener; import org.onap.policy.common.endpoints.event.comm.TopicSink; import org.onap.policy.common.endpoints.event.comm.TopicSource; import org.onap.policy.drools.controller.DroolsController; +import org.onap.policy.drools.controller.DroolsControllerFactory; import org.onap.policy.drools.features.PolicyControllerFeatureAPI; import org.onap.policy.drools.persistence.SystemPersistence; import org.onap.policy.drools.properties.DroolsProperties; @@ -52,6 +53,11 @@ public class AggregatedPolicyController implements PolicyController, TopicListen private static final Logger logger = LoggerFactory.getLogger(AggregatedPolicyController.class); /** + * Used to access various objects. Can be overridden by junit tests. + */ + private static Factory factory = new Factory(); + + /** * identifier for this policy controller. */ private final String name; @@ -115,14 +121,14 @@ public class AggregatedPolicyController implements PolicyController, TopicListen // Create/Reuse Readers/Writers for all event sources endpoints - this.sources = TopicEndpoint.manager.addTopicSources(properties); - this.sinks = TopicEndpoint.manager.addTopicSinks(properties); + this.sources = factory.getEndpointManager().addTopicSources(properties); + this.sinks = factory.getEndpointManager().addTopicSinks(properties); initDrools(properties); initSinks(); /* persist new properties */ - SystemPersistence.manager.storeController(name, properties); + factory.getPersistenceManager().storeController(name, properties); this.properties = properties; } @@ -134,7 +140,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen private void initDrools(Properties properties) { try { // Register with drools infrastructure - this.droolsController = DroolsController.factory.build(properties, sources, sinks); + this.droolsController = factory.getDroolsFactory().build(properties, sources, sinks); } catch (Exception | LinkageError e) { logger.error("{}: cannot init-drools because of {}", this, e.getMessage(), e); throw new IllegalArgumentException(e); @@ -177,7 +183,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen this.properties.setProperty(DroolsProperties.RULES_ARTIFACTID, newDroolsConfiguration.getArtifactId()); this.properties.setProperty(DroolsProperties.RULES_VERSION, newDroolsConfiguration.getVersion()); - SystemPersistence.manager.storeController(name, this.properties); + factory.getPersistenceManager().storeController(name, this.properties); this.initDrools(this.properties); @@ -220,7 +226,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen public boolean start() { logger.info("{}: start", this); - for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) { + for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) { try { if (feature.beforeStart(this)) { return true; @@ -259,7 +265,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen } } - for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) { + for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) { try { if (feature.afterStart(this)) { return true; @@ -280,7 +286,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen public boolean stop() { logger.info("{}: stop", this); - for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) { + for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) { try { if (feature.beforeStop(this)) { return true; @@ -309,7 +315,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen boolean success = this.droolsController.stop(); - for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) { + for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) { try { if (feature.afterStop(this)) { return true; @@ -330,7 +336,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen public void shutdown() { logger.info("{}: shutdown", this); - for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) { + for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) { try { if (feature.beforeShutdown(this)) { return; @@ -343,9 +349,9 @@ public class AggregatedPolicyController implements PolicyController, TopicListen this.stop(); - DroolsController.factory.shutdown(this.droolsController); + factory.getDroolsFactory().shutdown(this.droolsController); - for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) { + for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) { try { if (feature.afterShutdown(this)) { return; @@ -364,7 +370,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen public void halt() { logger.info("{}: halt", this); - for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) { + for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) { try { if (feature.beforeHalt(this)) { return; @@ -376,10 +382,10 @@ public class AggregatedPolicyController implements PolicyController, TopicListen } this.stop(); - DroolsController.factory.destroy(this.droolsController); - SystemPersistence.manager.deleteController(this.name); + factory.getDroolsFactory().destroy(this.droolsController); + factory.getPersistenceManager().deleteController(this.name); - for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) { + for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) { try { if (feature.afterHalt(this)) { return; @@ -397,11 +403,9 @@ public class AggregatedPolicyController implements PolicyController, TopicListen @Override public void onTopicEvent(Topic.CommInfrastructure commType, String topic, String event) { - if (logger.isDebugEnabled()) { - logger.debug("{}: event offered from {}:{}: {}", this, commType, topic, event); - } + logger.debug("{}: event offered from {}:{}: {}", this, commType, topic, event); - for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) { + for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) { try { if (feature.beforeOffer(this, commType, topic, event)) { return; @@ -422,7 +426,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen boolean success = this.droolsController.offer(topic, event); - for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) { + for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) { try { if (feature.afterOffer(this, commType, topic, event, success)) { return; @@ -440,11 +444,9 @@ public class AggregatedPolicyController implements PolicyController, TopicListen @Override public boolean deliver(Topic.CommInfrastructure commType, String topic, Object event) { - if (logger.isDebugEnabled()) { - logger.debug("{}: deliver event to {}:{}: {}", this, commType, topic, event); - } + logger.debug("{}: deliver event to {}:{}: {}", this, commType, topic, event); - for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) { + for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) { try { if (feature.beforeDeliver(this, commType, topic, event)) { return true; @@ -479,7 +481,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen boolean success = this.droolsController.deliver(this.topic2Sinks.get(topic), event); - for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) { + for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) { try { if (feature.afterDeliver(this, commType, topic, event, success)) { return success; @@ -508,7 +510,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen public boolean lock() { logger.info("{}: lock", this); - for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) { + for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) { try { if (feature.beforeLock(this)) { return true; @@ -532,7 +534,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen boolean success = this.droolsController.lock(); - for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) { + for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) { try { if (feature.afterLock(this)) { return true; @@ -554,7 +556,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen logger.info("{}: unlock", this); - for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) { + for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) { try { if (feature.beforeUnlock(this)) { return true; @@ -575,7 +577,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen boolean success = this.droolsController.unlock(); - for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) { + for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) { try { if (feature.afterUnlock(this)) { return true; @@ -637,5 +639,26 @@ public class AggregatedPolicyController implements PolicyController, TopicListen + ", locked=" + locked + ", droolsController=" + droolsController + "]"; } + /** + * Factory to access various objects. Can be overridden by junit tests. + */ + public static class Factory { + + public SystemPersistence getPersistenceManager() { + return SystemPersistence.manager; + } + + public TopicEndpoint getEndpointManager() { + return TopicEndpoint.manager; + } + + public DroolsControllerFactory getDroolsFactory() { + return DroolsController.factory; + } + + public List<PolicyControllerFeatureAPI> getFeatureProviders() { + return PolicyControllerFeatureAPI.providers.getList(); + } + } } diff --git a/policy-management/src/test/java/org/onap/policy/drools/system/PolicyControllerFactoryTest.java b/policy-management/src/test/java/org/onap/policy/drools/system/PolicyControllerFactoryTest.java new file mode 100644 index 00000000..f5c71064 --- /dev/null +++ b/policy-management/src/test/java/org/onap/policy/drools/system/PolicyControllerFactoryTest.java @@ -0,0 +1,421 @@ +/* + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2018 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.system; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Matchers.any; +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 static org.onap.policy.common.utils.test.PolicyAssert.assertThrows; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Properties; +import org.junit.Before; +import org.junit.Test; +import org.onap.policy.drools.controller.DroolsController; +import org.onap.policy.drools.features.PolicyControllerFeatureAPI; +import org.onap.policy.drools.protocol.configuration.DroolsConfiguration; + +public class PolicyControllerFactoryTest { + + private static final String MY_NAME = "my-name-a"; + private static final String MY_NAME2 = "my-name-b"; + + private static final String ARTIFACT1 = "artifact-a"; + private static final String GROUP1 = "group-a"; + private static final String VERSION1 = "version-a"; + + private static final String ARTIFACT2 = "artifact-b"; + private static final String GROUP2 = "group-b"; + private static final String VERSION2 = "version-b"; + + private static final String FEATURE1 = "feature-a"; + private static final String FEATURE2 = "feature-b"; + + private PolicyController controller; + private PolicyController controller2; + private Properties properties; + private DroolsController drools; + private DroolsController drools2; + private DroolsConfiguration config; + private PolicyControllerFeatureAPI feature1; + private PolicyControllerFeatureAPI feature2; + private List<PolicyControllerFeatureAPI> providers; + private IndexedPolicyControllerFactory ipc; + + /** + * Initializes the object to be tested. + */ + @Before + public void setUp() { + controller = mock(PolicyController.class); + controller2 = mock(PolicyController.class); + properties = new Properties(); + drools = mock(DroolsController.class); + drools2 = mock(DroolsController.class); + config = mock(DroolsConfiguration.class); + feature1 = mock(PolicyControllerFeatureAPI.class); + feature2 = mock(PolicyControllerFeatureAPI.class); + providers = Arrays.asList(feature1, feature2); + + when(feature1.getName()).thenReturn(FEATURE1); + when(feature2.getName()).thenReturn(FEATURE2); + + when(drools.getArtifactId()).thenReturn(ARTIFACT1); + when(drools.getGroupId()).thenReturn(GROUP1); + when(drools.getVersion()).thenReturn(VERSION1); + + when(drools2.getArtifactId()).thenReturn(ARTIFACT2); + when(drools2.getGroupId()).thenReturn(GROUP2); + when(drools2.getVersion()).thenReturn(VERSION2); + + when(controller.getName()).thenReturn(MY_NAME); + when(controller.getDrools()).thenReturn(drools); + when(controller.updateDrools(any())).thenReturn(true); + + when(controller2.getName()).thenReturn(MY_NAME2); + when(controller2.getDrools()).thenReturn(drools2); + when(controller2.updateDrools(any())).thenReturn(true); + + ipc = new IndexedPolicyControllerFactoryImpl(); + } + + @Test + public void testFactory() { + // use a REAL object instead of an Impl + ipc = new IndexedPolicyControllerFactory(); + assertNotNull(ipc.getProviders()); + } + + @Test + public void testBuild() { + assertEquals(controller, ipc.build(MY_NAME, properties)); + + // re-build - should not create another one + assertEquals(controller, ipc.build(MY_NAME, properties)); + + // brained + setUp(); + when(drools.isBrained()).thenReturn(true); + ipc.build(MY_NAME, properties); + } + + @Test + public void testPatchStringDroolsConfiguration() { + // unknown controller + assertThrows(IllegalArgumentException.class, () -> ipc.patch(MY_NAME, config)); + + /* + * Build controller to be used by remaining tests. + */ + ipc.build(MY_NAME, properties); + + // null name + String nullName = null; + assertThrows(IllegalArgumentException.class, () -> ipc.patch(nullName, config)); + + // empty name + assertThrows(IllegalArgumentException.class, () -> ipc.patch("", config)); + + // success + ipc.patch(MY_NAME, config); + verify(controller).updateDrools(config); + + // create a factory whose get() method returns null + ipc = new IndexedPolicyControllerFactory() { + @Override + public PolicyController get(String name) { + return null; + } + }; + ipc.build(MY_NAME, properties); + assertThrows(IllegalArgumentException.class, () -> ipc.patch(MY_NAME, config)); + } + + @Test + public void testPatchPolicyControllerDroolsConfiguration() { + ipc.patch(controller, config); + verify(controller).updateDrools(config); + + // null controller + PolicyController nullCtlr = null; + assertThrows(IllegalArgumentException.class, () -> ipc.patch(nullCtlr, config)); + + // null config + assertThrows(IllegalArgumentException.class, () -> ipc.patch(controller, null)); + + // brained + when(drools.isBrained()).thenReturn(true); + ipc.patch(controller, config); + + // update failed + when(controller.updateDrools(config)).thenReturn(false); + assertThrows(IllegalArgumentException.class, () -> ipc.patch(controller, config)); + } + + @Test + public void testShutdownString() { + // null name + String nullName = null; + assertThrows(IllegalArgumentException.class, () -> ipc.shutdown(nullName)); + + // empty name + assertThrows(IllegalArgumentException.class, () -> ipc.shutdown("")); + + // unknown controller + ipc.shutdown(MY_NAME); + verify(controller, never()).shutdown(); + + // valid controller + ipc.build(MY_NAME, properties); + ipc.shutdown(MY_NAME); + verify(controller).shutdown(); + } + + @Test + public void testShutdownPolicyController() { + ipc.build(MY_NAME, properties); + + ipc.shutdown(controller); + + verify(controller).shutdown(); + + // should no longer be managed + assertThrows(IllegalArgumentException.class, () -> ipc.get(MY_NAME)); + } + + @Test + public void testShutdown() { + ipc.build(MY_NAME, properties); + ipc.build(MY_NAME2, properties); + + ipc.shutdown(); + + verify(controller).shutdown(); + verify(controller2).shutdown(); + + // should no longer be managed + assertThrows(IllegalArgumentException.class, () -> ipc.get(MY_NAME)); + assertThrows(IllegalArgumentException.class, () -> ipc.get(MY_NAME2)); + } + + @Test + public void testUnmanage() { + ipc.build(MY_NAME, properties); + ipc.build(MY_NAME2, properties); + + ipc.shutdown(MY_NAME); + + verify(controller).shutdown(); + verify(controller2, never()).shutdown(); + + // should no longer be managed + assertThrows(IllegalArgumentException.class, () -> ipc.get(MY_NAME)); + + // should still be managed + assertEquals(controller2, ipc.get(MY_NAME2)); + + // null controller + PolicyController nullCtlr = null; + assertThrows(IllegalArgumentException.class, () -> ipc.shutdown(nullCtlr)); + + // unknown controller + ipc.shutdown(controller); + verify(controller, times(2)).shutdown(); + } + + @Test + public void testDestroyString() { + // null name + String nullName = null; + assertThrows(IllegalArgumentException.class, () -> ipc.destroy(nullName)); + + // empty name + assertThrows(IllegalArgumentException.class, () -> ipc.destroy("")); + + // unknown controller + ipc.destroy(MY_NAME); + verify(controller, never()).halt(); + + // valid controller + ipc.build(MY_NAME, properties); + ipc.destroy(MY_NAME); + verify(controller).halt(); + } + + @Test + public void testDestroyPolicyController() { + ipc.build(MY_NAME, properties); + + ipc.destroy(controller); + + verify(controller).halt(); + + // should no longer be managed + assertThrows(IllegalArgumentException.class, () -> ipc.get(MY_NAME)); + } + + @Test + public void testDestroy() { + ipc.build(MY_NAME, properties); + ipc.build(MY_NAME2, properties); + + ipc.destroy(); + + verify(controller).halt(); + verify(controller2).halt(); + + // should no longer be managed + assertThrows(IllegalArgumentException.class, () -> ipc.get(MY_NAME)); + assertThrows(IllegalArgumentException.class, () -> ipc.get(MY_NAME2)); + } + + @Test + public void testGetString() { + // unknown name + assertThrows(IllegalArgumentException.class, () -> ipc.get(MY_NAME)); + + ipc.build(MY_NAME, properties); + ipc.build(MY_NAME2, properties); + + assertEquals(controller, ipc.get(MY_NAME)); + assertEquals(controller2, ipc.get(MY_NAME2)); + + // null name + String nullName = null; + assertThrows(IllegalArgumentException.class, () -> ipc.get(nullName)); + + // empty name + assertThrows(IllegalArgumentException.class, () -> ipc.get("")); + } + + @Test + public void testGetStringString_testToKey() { + // unknown controller + assertThrows(IllegalArgumentException.class, () -> ipc.get(GROUP1, ARTIFACT1)); + + when(drools.isBrained()).thenReturn(true); + when(drools2.isBrained()).thenReturn(true); + + ipc.build(MY_NAME, properties); + ipc.build(MY_NAME2, properties); + + assertEquals(controller, ipc.get(GROUP1, ARTIFACT1)); + assertEquals(controller2, ipc.get(GROUP2, ARTIFACT2)); + + // null group + assertThrows(IllegalArgumentException.class, () -> ipc.get(null, ARTIFACT1)); + + // empty group + assertThrows(IllegalArgumentException.class, () -> ipc.get("", ARTIFACT1)); + + // null artifact + assertThrows(IllegalArgumentException.class, () -> ipc.get(GROUP1, null)); + + // empty artifact + assertThrows(IllegalArgumentException.class, () -> ipc.get(GROUP1, "")); + } + + @Test + public void testGetDroolsController() { + // unknown controller + assertThrows(IllegalStateException.class, () -> ipc.get(drools)); + + when(drools.isBrained()).thenReturn(true); + when(drools2.isBrained()).thenReturn(true); + + ipc.build(MY_NAME, properties); + ipc.build(MY_NAME2, properties); + + assertEquals(controller, ipc.get(drools)); + assertEquals(controller2, ipc.get(drools2)); + + // null controller + DroolsController nullDrools = null; + assertThrows(IllegalArgumentException.class, () -> ipc.get(nullDrools)); + } + + @Test + public void testInventory() { + ipc.build(MY_NAME, properties); + ipc.build(MY_NAME2, properties); + + List<PolicyController> lst = ipc.inventory(); + Collections.sort(lst, (left, right) -> left.getName().compareTo(right.getName())); + assertEquals(Arrays.asList(controller, controller2), lst); + } + + @Test + public void testGetFeatures() { + assertEquals(Arrays.asList(FEATURE1, FEATURE2), ipc.getFeatures()); + } + + @Test + public void testGetFeatureProviders() { + assertEquals(providers, ipc.getFeatureProviders()); + } + + @Test + public void testGetFeatureProvider() { + // null name + assertThrows(IllegalArgumentException.class, () -> ipc.getFeatureProvider(null)); + + // empty name + assertThrows(IllegalArgumentException.class, () -> ipc.getFeatureProvider("")); + + // unknown name + assertThrows(IllegalArgumentException.class, () -> ipc.getFeatureProvider("unknown-feature")); + + assertEquals(feature1, ipc.getFeatureProvider(FEATURE1)); + assertEquals(feature2, ipc.getFeatureProvider(FEATURE2)); + } + + /** + * Factory with overrides. + */ + private class IndexedPolicyControllerFactoryImpl extends IndexedPolicyControllerFactory { + + @Override + protected PolicyController newPolicyController(String name, Properties properties) { + if (MY_NAME.equals(name)) { + return controller; + + } else if (MY_NAME2.equals(name)) { + return controller2; + + } else { + throw new IllegalArgumentException("unknown controller name: " + name); + + } + } + + @Override + protected List<PolicyControllerFeatureAPI> getProviders() { + return providers; + } + } +} diff --git a/policy-management/src/test/java/org/onap/policy/drools/system/test/PolicyEngineTest.java b/policy-management/src/test/java/org/onap/policy/drools/system/PolicyEngineTest.java index 7b02d755..cd93d94c 100644 --- a/policy-management/src/test/java/org/onap/policy/drools/system/test/PolicyEngineTest.java +++ b/policy-management/src/test/java/org/onap/policy/drools/system/PolicyEngineTest.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.policy.drools.system.test; +package org.onap.policy.drools.system; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; diff --git a/policy-management/src/test/java/org/onap/policy/drools/system/internal/AggregatedPolicyControllerTest.java b/policy-management/src/test/java/org/onap/policy/drools/system/internal/AggregatedPolicyControllerTest.java new file mode 100644 index 00000000..4f26419f --- /dev/null +++ b/policy-management/src/test/java/org/onap/policy/drools/system/internal/AggregatedPolicyControllerTest.java @@ -0,0 +1,948 @@ +/* + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2018 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.system.internal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +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 static org.onap.policy.common.utils.test.PolicyAssert.assertThrows; + +import java.util.Arrays; +import java.util.List; +import java.util.Properties; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure; +import org.onap.policy.common.endpoints.event.comm.TopicEndpoint; +import org.onap.policy.common.endpoints.event.comm.TopicSink; +import org.onap.policy.common.endpoints.event.comm.TopicSource; +import org.onap.policy.drools.controller.DroolsController; +import org.onap.policy.drools.controller.DroolsControllerFactory; +import org.onap.policy.drools.features.PolicyControllerFeatureAPI; +import org.onap.policy.drools.persistence.SystemPersistence; +import org.onap.policy.drools.protocol.configuration.DroolsConfiguration; +import org.onap.policy.drools.system.internal.AggregatedPolicyController.Factory; +import org.powermock.reflect.Whitebox; + +public class AggregatedPolicyControllerTest { + + /** + * Name of the "factory" field within the {@link AggregatedPolicyController} class. + */ + private static final String FACTORY_FIELD = "factory"; + + private static final String AGG_NAME = "agg-name"; + private static final String SINK_TOPIC1 = "sink-a"; + private static final String SINK_TOPIC2 = "sink-b"; + private static final String SOURCE_TOPIC1 = "source-a"; + private static final String SOURCE_TOPIC2 = "source-b"; + + private static final String EXPECTED = "expected exception"; + + private static final String MY_EVENT = "my-event"; + + private static final String ARTIFACT1 = "artifact-a"; + private static final String GROUP1 = "group-a"; + private static final String VERSION1 = "version-a"; + + private static final String ARTIFACT2 = "artifact-b"; + private static final String GROUP2 = "group-b"; + private static final String VERSION2 = "version-b"; + + private static Factory savedFactory; + + private Properties properties; + private Factory factory; + private TopicEndpoint endpointMgr; + private List<TopicSource> sources; + private TopicSource source1; + private TopicSource source2; + private List<TopicSink> sinks; + private TopicSink sink1; + private TopicSink sink2; + private SystemPersistence persist; + private DroolsControllerFactory droolsFactory; + private DroolsController drools; + private DroolsConfiguration config; + private List<PolicyControllerFeatureAPI> providers; + private PolicyControllerFeatureAPI prov1; + private PolicyControllerFeatureAPI prov2; + private AggregatedPolicyController apc; + + @BeforeClass + public static void setUpBeforeClass() { + savedFactory = Whitebox.getInternalState(AggregatedPolicyController.class, FACTORY_FIELD); + } + + @AfterClass + public static void tearDownAfterClass() { + Whitebox.setInternalState(AggregatedPolicyController.class, FACTORY_FIELD, savedFactory); + } + + /** + * Initializes the object to be tested. + */ + @Before + public void setUp() { + properties = new Properties(); + + source1 = mock(TopicSource.class); + source2 = mock(TopicSource.class); + when(source1.getTopic()).thenReturn(SOURCE_TOPIC1); + when(source2.getTopic()).thenReturn(SOURCE_TOPIC2); + + sink1 = mock(TopicSink.class); + sink2 = mock(TopicSink.class); + when(sink1.getTopic()).thenReturn(SINK_TOPIC1); + when(sink2.getTopic()).thenReturn(SINK_TOPIC2); + + sources = Arrays.asList(source1, source2); + sinks = Arrays.asList(sink1, sink2); + + endpointMgr = mock(TopicEndpoint.class); + when(endpointMgr.addTopicSources(any())).thenReturn(sources); + when(endpointMgr.addTopicSinks(any())).thenReturn(sinks); + + persist = mock(SystemPersistence.class); + + drools = mock(DroolsController.class); + when(drools.start()).thenReturn(true); + when(drools.stop()).thenReturn(true); + when(drools.offer(any(), any())).thenReturn(true); + when(drools.deliver(any(), any())).thenReturn(true); + when(drools.lock()).thenReturn(true); + when(drools.unlock()).thenReturn(true); + when(drools.getArtifactId()).thenReturn(ARTIFACT1); + when(drools.getGroupId()).thenReturn(GROUP1); + when(drools.getVersion()).thenReturn(VERSION1); + + config = mock(DroolsConfiguration.class); + when(config.getArtifactId()).thenReturn(ARTIFACT2); + when(config.getGroupId()).thenReturn(GROUP2); + when(config.getVersion()).thenReturn(VERSION2); + + droolsFactory = mock(DroolsControllerFactory.class); + when(droolsFactory.build(any(), any(), any())).thenReturn(drools); + + prov1 = mock(PolicyControllerFeatureAPI.class); + prov2 = mock(PolicyControllerFeatureAPI.class); + + providers = Arrays.asList(prov1, prov2); + + factory = mock(Factory.class); + Whitebox.setInternalState(AggregatedPolicyController.class, FACTORY_FIELD, factory); + + when(factory.getEndpointManager()).thenReturn(endpointMgr); + when(factory.getPersistenceManager()).thenReturn(persist); + when(factory.getDroolsFactory()).thenReturn(droolsFactory); + when(factory.getFeatureProviders()).thenReturn(providers); + + apc = new AggregatedPolicyController(AGG_NAME, properties); + } + + @Test + public void testFactory() { + assertNotNull(savedFactory); + + Factory factory = new Factory(); + assertNotNull(factory.getDroolsFactory()); + assertNotNull(factory.getEndpointManager()); + assertNotNull(factory.getFeatureProviders()); + assertNotNull(factory.getPersistenceManager()); + } + + @Test + public void testAggregatedPolicyController_() { + verify(persist).storeController(AGG_NAME, properties); + } + + @Test(expected = IllegalArgumentException.class) + public void testInitDrools_Ex() { + when(factory.getDroolsFactory()).thenThrow(new RuntimeException(EXPECTED)); + new AggregatedPolicyController(AGG_NAME, properties); + } + + @Test(expected = IllegalArgumentException.class) + public void testInitDrools_Error() { + when(factory.getDroolsFactory()).thenThrow(new LinkageError(EXPECTED)); + new AggregatedPolicyController(AGG_NAME, properties); + } + + @Test + public void testUpdateDrools_ConfigVariations() { + + // config should return same values as current controller + when(config.getArtifactId()).thenReturn(ARTIFACT1.toUpperCase()); + when(config.getGroupId()).thenReturn(GROUP1.toUpperCase()); + when(config.getVersion()).thenReturn(VERSION1.toUpperCase()); + + assertTrue(apc.updateDrools(config)); + + // number of times store should have been called + int count = 0; + + // invoked once during construction, but shouldn't be invoked during update + verify(persist, times(++count)).storeController(any(), any()); + + + // different artifact + when(config.getArtifactId()).thenReturn(ARTIFACT2); + + assertTrue(apc.updateDrools(config)); + + // should be invoked during update + verify(persist, times(++count)).storeController(any(), any()); + + + // different group + when(config.getArtifactId()).thenReturn(ARTIFACT1); + when(config.getGroupId()).thenReturn(GROUP2); + + assertTrue(apc.updateDrools(config)); + + // should be invoked during update + verify(persist, times(++count)).storeController(any(), any()); + + + // different version + when(config.getGroupId()).thenReturn(GROUP1); + when(config.getVersion()).thenReturn(VERSION2); + + assertTrue(apc.updateDrools(config)); + + // should be invoked during update + verify(persist, times(++count)).storeController(any(), any()); + + + /* + * Exception case. + */ + when(drools.lock()).thenThrow(new IllegalArgumentException(EXPECTED)); + when(drools.unlock()).thenThrow(new IllegalArgumentException(EXPECTED)); + + assertFalse(apc.updateDrools(config)); + } + + @Test + public void testUpdateDrools_LockVariations() { + // not locked + apc.updateDrools(config); + verify(drools, never()).lock(); + verify(drools).unlock(); + + // locked + setUp(); + apc.lock(); + apc.updateDrools(config); + verify(drools, times(2)).lock(); + verify(drools, never()).unlock(); + } + + @Test + public void testUpdateDrools_AliveVariations() { + // not started + apc.updateDrools(config); + verify(drools, never()).start(); + verify(drools).stop(); + + // started + setUp(); + apc.start(); + apc.updateDrools(config); + verify(drools, times(2)).start(); + verify(drools, never()).stop(); + } + + @Test + public void testGetName() { + assertEquals(AGG_NAME, apc.getName()); + } + + @Test + public void testStart() { + // arrange for first provider to throw exceptions + when(prov1.beforeStart(any())).thenThrow(new RuntimeException(EXPECTED)); + when(prov1.afterStart(any())).thenThrow(new RuntimeException(EXPECTED)); + + // arrange for first sink to throw exception + when(sink1.start()).thenThrow(new RuntimeException(EXPECTED)); + + // start it + assertTrue(apc.start()); + + assertTrue(apc.isAlive()); + + verify(prov1).beforeStart(apc); + verify(prov2).beforeStart(apc); + + verify(source1).register(apc); + verify(source2).register(apc); + + verify(sink1).start(); + verify(sink2).start(); + + verify(prov1).afterStart(apc); + verify(prov2).afterStart(apc); + + checkBeforeAfter( + (prov, flag) -> when(prov.beforeStart(apc)).thenReturn(flag), + (prov, flag) -> when(prov.afterStart(apc)).thenReturn(flag), + () -> apc.start(), + prov -> verify(prov).beforeStart(apc), + () -> verify(source1).register(apc), + prov -> verify(prov).afterStart(apc)); + } + + @Test + public void testStart_AlreadyStarted() { + apc.start(); + + // re-start it + assertTrue(apc.start()); + + assertTrue(apc.isAlive()); + + // these should now have been called twice + verify(prov1, times(2)).beforeStart(apc); + verify(prov2, times(2)).beforeStart(apc); + + // these should still only have been called once + verify(source1).register(apc); + verify(sink1).start(); + verify(prov1).afterStart(apc); + } + + @Test + public void testStart_Locked() { + apc.lock(); + + // start it + assertThrows(IllegalStateException.class, () -> apc.start()); + + assertFalse(apc.isAlive()); + + // should call beforeStart(), but stop after that + verify(prov1).beforeStart(apc); + verify(prov2).beforeStart(apc); + + verify(source1, never()).register(apc); + verify(sink1, never()).start(); + verify(prov1, never()).afterStart(apc); + } + + @Test + public void testStop() { + // arrange for first provider to throw exceptions + when(prov1.beforeStop(any())).thenThrow(new RuntimeException(EXPECTED)); + when(prov1.afterStop(any())).thenThrow(new RuntimeException(EXPECTED)); + + // start it + apc.start(); + + // now stop it + assertTrue(apc.stop()); + + assertFalse(apc.isAlive()); + + verify(prov1).beforeStop(apc); + verify(prov2).beforeStop(apc); + + verify(source1).unregister(apc); + verify(source2).unregister(apc); + + verify(prov1).afterStop(apc); + verify(prov2).afterStop(apc); + + // ensure no shutdown operations were called + verify(prov1, never()).beforeShutdown(apc); + verify(droolsFactory, never()).shutdown(drools); + verify(prov2, never()).afterShutdown(apc); + + checkBeforeAfter( + (prov, flag) -> when(prov.beforeStop(apc)).thenReturn(flag), + (prov, flag) -> when(prov.afterStop(apc)).thenReturn(flag), + () -> { + apc.start(); + apc.stop(); + }, + prov -> verify(prov).beforeStop(apc), + () -> verify(source1).unregister(apc), + prov -> verify(prov).afterStop(apc)); + } + + @Test + public void testStop_AlreadyStopped() { + apc.start(); + apc.stop(); + + // now re-stop it + assertTrue(apc.stop()); + + // called again + verify(prov1, times(2)).beforeStop(apc); + verify(prov2, times(2)).beforeStop(apc); + + // should NOT be called again + verify(source1).unregister(apc); + verify(prov1).afterStop(apc); + } + + @Test + public void testShutdown() { + // arrange for first provider to throw exceptions + when(prov1.beforeShutdown(any())).thenThrow(new RuntimeException(EXPECTED)); + when(prov1.afterShutdown(any())).thenThrow(new RuntimeException(EXPECTED)); + + // start it + apc.start(); + + // now shut it down + apc.shutdown(); + + verify(prov1).beforeShutdown(apc); + verify(prov2).beforeShutdown(apc); + + assertFalse(apc.isAlive()); + + verify(prov1).afterStop(apc); + verify(prov2).afterStop(apc); + + verify(droolsFactory).shutdown(drools); + + verify(prov1).afterShutdown(apc); + verify(prov2).afterShutdown(apc); + + // ensure no halt operation was called + verify(prov1, never()).beforeHalt(apc); + + checkBeforeAfter( + (prov, flag) -> when(prov.beforeShutdown(apc)).thenReturn(flag), + (prov, flag) -> when(prov.afterShutdown(apc)).thenReturn(flag), + () -> { + apc.start(); + apc.shutdown(); + }, + prov -> verify(prov).beforeShutdown(apc), + () -> verify(source1).unregister(apc), + prov -> verify(prov).afterShutdown(apc)); + } + + @Test + public void testHalt() { + // arrange for first provider to throw exceptions + when(prov1.beforeHalt(any())).thenThrow(new RuntimeException(EXPECTED)); + when(prov1.afterHalt(any())).thenThrow(new RuntimeException(EXPECTED)); + + // start it + apc.start(); + + // now halt it + apc.halt(); + + verify(prov1).beforeHalt(apc); + verify(prov2).beforeHalt(apc); + + assertFalse(apc.isAlive()); + + verify(prov1).beforeStop(apc); + verify(prov2).beforeStop(apc); + + verify(droolsFactory).destroy(drools); + verify(persist).deleteController(AGG_NAME); + + verify(prov1).afterHalt(apc); + verify(prov2).afterHalt(apc); + + // ensure no shutdown operation was called + verify(prov1, never()).beforeShutdown(apc); + + checkBeforeAfter( + (prov, flag) -> when(prov.beforeHalt(apc)).thenReturn(flag), + (prov, flag) -> when(prov.afterHalt(apc)).thenReturn(flag), + () -> { + apc.start(); + apc.halt(); + }, + prov -> verify(prov).beforeHalt(apc), + () -> verify(source1).unregister(apc), + prov -> verify(prov).afterHalt(apc)); + } + + @Test + public void testOnTopicEvent() { + // arrange for first provider to throw exceptions + when(prov1.beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT)) + .thenThrow(new RuntimeException(EXPECTED)); + when(prov1.afterOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT, true)) + .thenThrow(new RuntimeException(EXPECTED)); + + // start it + apc.start(); + + // now offer it + apc.onTopicEvent(CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT); + + verify(prov1).beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT); + verify(prov2).beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT); + + verify(drools).offer(SOURCE_TOPIC1, MY_EVENT); + + verify(prov1).afterOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT, true); + verify(prov2).afterOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT, true); + + checkBeforeAfter( + (prov, flag) -> when(prov.beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT)) + .thenReturn(flag), + (prov, flag) -> when( + prov.afterOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT, true)) + .thenReturn(flag), + () -> { + apc.start(); + apc.onTopicEvent(CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT); + }, + prov -> verify(prov).beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT), + () -> verify(drools).offer(SOURCE_TOPIC1, MY_EVENT), + prov -> verify(prov).afterOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT, true)); + } + + @Test + public void testOnTopicEvent_Locked() { + // start it + apc.start(); + + apc.lock(); + + // now offer it + apc.onTopicEvent(CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT); + + verify(prov1).beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT); + verify(prov2).beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT); + + // never gets this far + verify(drools, never()).offer(SOURCE_TOPIC1, MY_EVENT); + verify(prov1, never()).afterOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT, true); + } + + @Test + public void testOnTopicEvent_NotStarted() { + + // offer it + apc.onTopicEvent(CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT); + + verify(prov1).beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT); + verify(prov2).beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT); + + // never gets this far + verify(drools, never()).offer(SOURCE_TOPIC1, MY_EVENT); + verify(prov1, never()).afterOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT, true); + } + + @Test + public void testDeliver_testInitSinks() { + // arrange for first provider to throw exceptions + when(prov1.beforeDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT)) + .thenThrow(new RuntimeException(EXPECTED)); + when(prov1.afterDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT, true)) + .thenThrow(new RuntimeException(EXPECTED)); + + // start it + apc.start(); + + // now offer it + assertTrue(apc.deliver(CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT)); + + verify(prov1).beforeDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT); + verify(prov2).beforeDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT); + + verify(drools).deliver(sink1, MY_EVENT); + verify(drools, never()).deliver(sink2, MY_EVENT); + + verify(prov1).afterDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT, true); + verify(prov2).afterDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT, true); + + // offer to the other topic + assertTrue(apc.deliver(CommInfrastructure.NOOP, SINK_TOPIC2, MY_EVENT)); + + // now both topics should show one message delivered + verify(drools).deliver(sink1, MY_EVENT); + verify(drools).deliver(sink2, MY_EVENT); + + checkBeforeAfter( + (prov, flag) -> when(prov.beforeDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT)) + .thenReturn(flag), + (prov, flag) -> when( + prov.afterDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT, true)) + .thenReturn(flag), + () -> { + apc.start(); + apc.deliver(CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT); + }, + prov -> verify(prov).beforeDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT), + () -> verify(drools).deliver(sink1, MY_EVENT), + prov -> verify(prov).afterDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT, true)); + } + + @Test(expected = IllegalArgumentException.class) + public void testDeliver_NullTopic() { + apc.start(); + apc.deliver(CommInfrastructure.NOOP, null, MY_EVENT); + } + + @Test(expected = IllegalArgumentException.class) + public void testDeliver_EmptyTopic() { + apc.start(); + apc.deliver(CommInfrastructure.NOOP, "", MY_EVENT); + } + + @Test(expected = IllegalArgumentException.class) + public void testDeliver_NullEvent() { + apc.start(); + apc.deliver(CommInfrastructure.NOOP, SINK_TOPIC1, null); + } + + @Test(expected = IllegalStateException.class) + public void testDeliver_NotStarted() { + // do NOT start + apc.deliver(CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT); + } + + @Test(expected = IllegalStateException.class) + public void testDeliver_Locked() { + apc.start(); + apc.lock(); + apc.deliver(CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT); + } + + @Test(expected = IllegalArgumentException.class) + public void testDeliver_UnknownTopic() { + apc.start(); + apc.deliver(CommInfrastructure.NOOP, "unknown-topic", MY_EVENT); + } + + @Test + public void testIsAlive() { + assertFalse(apc.isAlive()); + + apc.start(); + assertTrue(apc.isAlive()); + + apc.stop(); + assertFalse(apc.isAlive()); + } + + @Test + public void testLock() { + // arrange for first provider to throw exceptions + when(prov1.beforeLock(any())).thenThrow(new RuntimeException(EXPECTED)); + when(prov1.afterLock(any())).thenThrow(new RuntimeException(EXPECTED)); + + // start it + apc.start(); + + // now lock it + assertTrue(apc.lock()); + + verify(prov1).beforeLock(apc); + verify(prov2).beforeLock(apc); + + assertTrue(apc.isLocked()); + + verify(drools).lock(); + + verify(prov1).afterLock(apc); + verify(prov2).afterLock(apc); + + checkBeforeAfter( + (prov, flag) -> when(prov.beforeLock(apc)).thenReturn(flag), + (prov, flag) -> when(prov.afterLock(apc)).thenReturn(flag), + () -> { + apc.start(); + apc.lock(); + }, + prov -> verify(prov).beforeLock(apc), + () -> verify(drools).lock(), + prov -> verify(prov).afterLock(apc)); + } + + @Test + public void testLock_AlreadyLocked() { + apc.start(); + apc.lock(); + + // now re-lock it + assertTrue(apc.lock()); + + // these should be invoked a second time + verify(prov1, times(2)).beforeLock(apc); + verify(prov2, times(2)).beforeLock(apc); + + assertTrue(apc.isLocked()); + + // these shouldn't be invoked a second time + verify(drools).lock(); + verify(prov1).afterLock(apc); + } + + @Test + public void testUnlock() { + // arrange for first provider to throw exceptions + when(prov1.beforeUnlock(any())).thenThrow(new RuntimeException(EXPECTED)); + when(prov1.afterUnlock(any())).thenThrow(new RuntimeException(EXPECTED)); + + // start it + apc.start(); + apc.lock(); + + // now unlock it + assertTrue(apc.unlock()); + + verify(prov1).beforeUnlock(apc); + verify(prov2).beforeUnlock(apc); + + assertFalse(apc.isLocked()); + + verify(drools).unlock(); + + verify(prov1).afterUnlock(apc); + verify(prov2).afterUnlock(apc); + + checkBeforeAfter( + (prov, flag) -> when(prov.beforeUnlock(apc)).thenReturn(flag), + (prov, flag) -> when(prov.afterUnlock(apc)).thenReturn(flag), + () -> { + apc.start(); + apc.lock(); + apc.unlock(); + }, + prov -> verify(prov).beforeUnlock(apc), + () -> verify(drools).unlock(), + prov -> verify(prov).afterUnlock(apc)); + } + + @Test + public void testUnlock_NotLocked() { + apc.start(); + + // now unlock it + assertTrue(apc.unlock()); + + verify(prov1).beforeUnlock(apc); + verify(prov2).beforeUnlock(apc); + + assertFalse(apc.isLocked()); + + // these shouldn't be invoked + verify(drools, never()).unlock(); + verify(prov1, never()).afterLock(apc); + } + + @Test + public void testIsLocked() { + assertFalse(apc.isLocked()); + + apc.lock(); + assertTrue(apc.isLocked()); + + apc.unlock(); + assertFalse(apc.isLocked()); + } + + @Test + public void testGetTopicSources() { + assertEquals(sources, apc.getTopicSources()); + } + + @Test + public void testGetTopicSinks() { + assertEquals(sinks, apc.getTopicSinks()); + } + + @Test + public void testGetDrools() { + assertEquals(drools, apc.getDrools()); + } + + @Test + public void testGetProperties() { + assertEquals(properties, apc.getProperties()); + } + + @Test + public void testToString() { + assertTrue(apc.toString().startsWith("AggregatedPolicyController [")); + } + + /** + * Performs an operation that has a beforeXxx method and an afterXxx method. Tries + * combinations where beforeXxx and afterXxx return {@code true} and {@code false}. + * + * @param setBefore function to set the return value of a provider's beforeXxx method + * @param setAfter function to set the return value of a provider's afterXxx method + * @param action invokes the operation + * @param verifyBefore verifies that a provider's beforeXxx method was invoked + * @param verifyMiddle verifies that the action occurring between the beforeXxx loop + * and the afterXxx loop was invoked + * @param verifyAfter verifies that a provider's afterXxx method was invoked + */ + private void checkBeforeAfter(BiConsumer<PolicyControllerFeatureAPI, Boolean> setBefore, + BiConsumer<PolicyControllerFeatureAPI, Boolean> setAfter, Runnable action, + Consumer<PolicyControllerFeatureAPI> verifyBefore, Runnable verifyMiddle, + Consumer<PolicyControllerFeatureAPI> verifyAfter) { + + checkBeforeAfter_FalseFalse(setBefore, setAfter, action, verifyBefore, verifyMiddle, verifyAfter); + checkBeforeAfter_FalseTrue(setBefore, setAfter, action, verifyBefore, verifyMiddle, verifyAfter); + checkBeforeAfter_TrueFalse(setBefore, setAfter, action, verifyBefore, verifyMiddle, verifyAfter); + + // don't need to test true-true, as it's behavior is a subset of true-false + } + + /** + * Performs an operation that has a beforeXxx method and an afterXxx method. Tries the + * case where both the beforeXxx and afterXxx methods return {@code false}. + * + * @param setBefore function to set the return value of a provider's beforeXxx method + * @param setAfter function to set the return value of a provider's afterXxx method + * @param action invokes the operation + * @param verifyBefore verifies that a provider's beforeXxx method was invoked + * @param verifyMiddle verifies that the action occurring between the beforeXxx loop + * and the afterXxx loop was invoked + * @param verifyAfter verifies that a provider's afterXxx method was invoked + */ + private void checkBeforeAfter_FalseFalse(BiConsumer<PolicyControllerFeatureAPI, Boolean> setBefore, + BiConsumer<PolicyControllerFeatureAPI, Boolean> setAfter, Runnable action, + Consumer<PolicyControllerFeatureAPI> verifyBefore, Runnable verifyMiddle, + Consumer<PolicyControllerFeatureAPI> verifyAfter) { + + setUp(); + + // configure for the test + setBefore.accept(prov1, false); + setBefore.accept(prov2, false); + + setAfter.accept(prov1, false); + setAfter.accept(prov2, false); + + // run the action + action.run(); + + // verify that various methods were invoked + verifyBefore.accept(prov1); + verifyBefore.accept(prov2); + + verifyMiddle.run(); + + verifyAfter.accept(prov1); + verifyAfter.accept(prov2); + } + + /** + * Performs an operation that has a beforeXxx method and an afterXxx method. Tries the + * case where the first provider's afterXxx returns {@code true}, while the others + * return {@code false}. + * + * @param setBefore function to set the return value of a provider's beforeXxx method + * @param setAfter function to set the return value of a provider's afterXxx method + * @param action invokes the operation + * @param verifyBefore verifies that a provider's beforeXxx method was invoked + * @param verifyMiddle verifies that the action occurring between the beforeXxx loop + * and the afterXxx loop was invoked + * @param verifyAfter verifies that a provider's afterXxx method was invoked + */ + private void checkBeforeAfter_FalseTrue(BiConsumer<PolicyControllerFeatureAPI, Boolean> setBefore, + BiConsumer<PolicyControllerFeatureAPI, Boolean> setAfter, Runnable action, + Consumer<PolicyControllerFeatureAPI> verifyBefore, Runnable verifyMiddle, + Consumer<PolicyControllerFeatureAPI> verifyAfter) { + + setUp(); + + // configure for the test + setBefore.accept(prov1, false); + setBefore.accept(prov2, false); + + setAfter.accept(prov1, true); + setAfter.accept(prov2, false); + + // run the action + action.run(); + + // verify that various methods were invoked + verifyBefore.accept(prov1); + verifyBefore.accept(prov2); + + verifyMiddle.run(); + + verifyAfter.accept(prov1); + assertThrows(AssertionError.class, () -> verifyAfter.accept(prov2)); + } + + /** + * Performs an operation that has a beforeXxx method and an afterXxx method. Tries the + * case where the first provider's beforeXxx returns {@code true}, while the others + * return {@code false}. + * + * @param setBefore function to set the return value of a provider's beforeXxx method + * @param setAfter function to set the return value of a provider's afterXxx method + * @param action invokes the operation + * @param verifyBefore verifies that a provider's beforeXxx method was invoked + * @param verifyMiddle verifies that the action occurring between the beforeXxx loop + * and the afterXxx loop was invoked + * @param verifyAfter verifies that a provider's afterXxx method was invoked + */ + private void checkBeforeAfter_TrueFalse(BiConsumer<PolicyControllerFeatureAPI, Boolean> setBefore, + BiConsumer<PolicyControllerFeatureAPI, Boolean> setAfter, Runnable action, + Consumer<PolicyControllerFeatureAPI> verifyBefore, Runnable verifyMiddle, + Consumer<PolicyControllerFeatureAPI> verifyAfter) { + + setUp(); + + // configure for the test + setBefore.accept(prov1, true); + setBefore.accept(prov2, false); + + setAfter.accept(prov1, false); + setAfter.accept(prov2, false); + + // run the action + action.run(); + + // verify that various methods were invoked + verifyBefore.accept(prov1); + + // remaining methods should not have been invoked + assertThrows(AssertionError.class, () -> verifyBefore.accept(prov2)); + + assertThrows(AssertionError.class, () -> verifyMiddle.run()); + + assertThrows(AssertionError.class, () -> verifyAfter.accept(prov1)); + assertThrows(AssertionError.class, () -> verifyAfter.accept(prov2)); + } +} |