aboutsummaryrefslogtreecommitdiffstats
path: root/controlloop/common/rules-test/src
diff options
context:
space:
mode:
Diffstat (limited to 'controlloop/common/rules-test/src')
-rw-r--r--controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/BaseRuleTest.java496
-rw-r--r--controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/HttpClients.java67
-rw-r--r--controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Listener.java180
-rw-r--r--controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/NamedRunner.java85
-rw-r--r--controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Rules.java413
-rw-r--r--controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/SimulatorException.java44
-rw-r--r--controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Simulators.java85
-rw-r--r--controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/TestNames.java44
-rw-r--r--controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/TopicException.java44
-rw-r--r--controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Topics.java176
-rw-r--r--controlloop/common/rules-test/src/main/resources/duplicates/duplicates.appc.success.json22
-rw-r--r--controlloop/common/rules-test/src/main/resources/duplicates/duplicates.onset.1.json16
-rw-r--r--controlloop/common/rules-test/src/main/resources/duplicates/duplicates.onset.2.json16
-rw-r--r--controlloop/common/rules-test/src/main/resources/duplicates/tosca-compliant-duplicates.json37
-rw-r--r--controlloop/common/rules-test/src/main/resources/service123/service123.appc.migrate.success.json22
-rw-r--r--controlloop/common/rules-test/src/main/resources/service123/service123.appc.rebuild.failure.json22
-rw-r--r--controlloop/common/rules-test/src/main/resources/service123/service123.appc.restart.failure.json22
-rw-r--r--controlloop/common/rules-test/src/main/resources/service123/service123.onset.json17
-rw-r--r--controlloop/common/rules-test/src/main/resources/service123/tosca-compliant-service123.json75
-rw-r--r--controlloop/common/rules-test/src/main/resources/vcpe/tosca-compliant-vcpe.json37
-rw-r--r--controlloop/common/rules-test/src/main/resources/vcpe/tosca-legacy-vcpe.json9
-rw-r--r--controlloop/common/rules-test/src/main/resources/vcpe/vcpe.appc.success.json22
-rw-r--r--controlloop/common/rules-test/src/main/resources/vcpe/vcpe.onset.1.json16
-rw-r--r--controlloop/common/rules-test/src/main/resources/vcpe/vcpe.onset.2.json16
-rw-r--r--controlloop/common/rules-test/src/main/resources/vcpe/vcpe.onset.3.json17
-rw-r--r--controlloop/common/rules-test/src/main/resources/vdns/tosca-compliant-vdns.json48
-rw-r--r--controlloop/common/rules-test/src/main/resources/vdns/vdns.onset.json16
-rw-r--r--controlloop/common/rules-test/src/main/resources/vfw/tosca-compliant-vfw.json40
-rw-r--r--controlloop/common/rules-test/src/main/resources/vfw/tosca-vfw.json9
-rw-r--r--controlloop/common/rules-test/src/main/resources/vfw/vfw.appc.success.json17
-rw-r--r--controlloop/common/rules-test/src/main/resources/vfw/vfw.onset.json17
-rw-r--r--controlloop/common/rules-test/src/main/resources/vlb/tosca-compliant-vlb.json48
-rw-r--r--controlloop/common/rules-test/src/main/resources/vlb/tosca-vlb.json9
-rw-r--r--controlloop/common/rules-test/src/main/resources/vlb/vlb.onset.json16
-rw-r--r--controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/BaseRuleTestTest.java459
-rw-r--r--controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/ExceptionsTest.java38
-rw-r--r--controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/HttpClientsTest.java94
-rw-r--r--controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/ListenerTest.java202
-rw-r--r--controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/NamedRunnerTest.java87
-rw-r--r--controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/NamedRunnerTest2.java58
-rw-r--r--controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/RulesTest.java341
-rw-r--r--controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/SimulatorsTest.java105
-rw-r--r--controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/TopicsTest.java230
-rw-r--r--controlloop/common/rules-test/src/test/resources/META-INF/kmodule.xml27
-rw-r--r--controlloop/common/rules-test/src/test/resources/config/rulesTest-controller.properties25
-rw-r--r--controlloop/common/rules-test/src/test/resources/my-http-client.properties26
-rw-r--r--controlloop/common/rules-test/src/test/resources/rulesTest.drl96
-rw-r--r--controlloop/common/rules-test/src/test/resources/rulesTest.pom30
-rw-r--r--controlloop/common/rules-test/src/test/resources/topics.json5
-rw-r--r--controlloop/common/rules-test/src/test/resources/topicsReplaced.json5
-rw-r--r--controlloop/common/rules-test/src/test/resources/tosca-policy.json37
-rw-r--r--controlloop/common/rules-test/src/test/resources/tosca-template.json45
52 files changed, 4130 insertions, 0 deletions
diff --git a/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/BaseRuleTest.java b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/BaseRuleTest.java
new file mode 100644
index 000000000..711a61738
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/BaseRuleTest.java
@@ -0,0 +1,496 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.common.rules.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+import java.util.UUID;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import lombok.AccessLevel;
+import lombok.Getter;
+import org.junit.Test;
+import org.onap.policy.appc.Request;
+import org.onap.policy.appclcm.AppcLcmDmaapWrapper;
+import org.onap.policy.common.utils.coder.Coder;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.common.utils.coder.StandardCoderInstantAsMillis;
+import org.onap.policy.controlloop.ControlLoopNotificationType;
+import org.onap.policy.controlloop.VirtualControlLoopNotification;
+import org.onap.policy.drools.system.PolicyController;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
+
+/**
+ * Superclass used for rule tests.
+ */
+public abstract class BaseRuleTest {
+ /*
+ * Canonical Topic Names.
+ */
+ protected static final String DCAE_TOPIC = "DCAE_TOPIC";
+ protected static final String APPC_LCM_WRITE_TOPIC = "APPC-LCM-WRITE";
+ protected static final String POLICY_CL_MGT_TOPIC = "POLICY-CL-MGT";
+ protected static final String APPC_LCM_READ_TOPIC = "APPC-LCM-READ";
+ protected static final String APPC_CL_TOPIC = "APPC-CL";
+
+ /*
+ * Constants for each test case.
+ */
+
+ // service123 (i.e., multi-operation policy)
+ private static final String SERVICE123_TOSCA_COMPLIANT_POLICY = "service123/tosca-compliant-service123.json";
+ private static final String SERVICE123_ONSET = "service123/service123.onset.json";
+ private static final String SERVICE123_APPC_RESTART_FAILURE = "service123/service123.appc.restart.failure.json";
+ private static final String SERVICE123_APPC_REBUILD_FAILURE = "service123/service123.appc.rebuild.failure.json";
+ private static final String SERVICE123_APPC_MIGRATE_SUCCESS = "service123/service123.appc.migrate.success.json";
+
+ // duplicates (i.e., mutliple events in the engine at the same time)
+ private static final String DUPLICATES_TOSCA_COMPLIANT_POLICY = "duplicates/tosca-compliant-duplicates.json";
+ private static final String DUPLICATES_ONSET_1 = "duplicates/duplicates.onset.1.json";
+ private static final String DUPLICATES_ONSET_2 = "duplicates/duplicates.onset.2.json";
+ private static final String DUPLICATES_APPC_SUCCESS = "duplicates/duplicates.appc.success.json";
+
+ // VCPE
+ private static final String VCPE_TOSCA_LEGACY_POLICY = "vcpe/tosca-legacy-vcpe.json";
+ private static final String VCPE_TOSCA_COMPLIANT_POLICY = "vcpe/tosca-compliant-vcpe.json";
+ private static final String VCPE_ONSET_1 = "vcpe/vcpe.onset.1.json";
+ private static final String VCPE_ONSET_2 = "vcpe/vcpe.onset.2.json";
+ private static final String VCPE_ONSET_3 = "vcpe/vcpe.onset.3.json";
+ private static final String VCPE_APPC_SUCCESS = "vcpe/vcpe.appc.success.json";
+
+ // VDNS
+ private static final String VDNS_TOSCA_COMPLIANT_POLICY = "vdns/tosca-compliant-vdns.json";
+ private static final String VDNS_ONSET = "vdns/vdns.onset.json";
+
+ // VFW
+ private static final String VFW_TOSCA_LEGACY_POLICY = "vfw/tosca-vfw.json";
+ private static final String VFW_TOSCA_COMPLIANT_POLICY = "vfw/tosca-compliant-vfw.json";
+ private static final String VFW_ONSET = "vfw/vfw.onset.json";
+ private static final String VFW_APPC_SUCCESS = "vfw/vfw.appc.success.json";
+
+ // VLB
+ private static final String VLB_TOSCA_LEGACY_POLICY = "vlb/tosca-vlb.json";
+ private static final String VLB_TOSCA_COMPLIANT_POLICY = "vlb/tosca-compliant-vlb.json";
+ private static final String VLB_ONSET = "vlb/vlb.onset.json";
+
+ /*
+ * Coders used to decode requests and responses.
+ */
+ private static final Coder APPC_LEGACY_CODER = new StandardCoderInstantAsMillis();
+ private static final Coder APPC_LCM_CODER = new StandardCoder();
+
+ // these may be overridden by junit tests
+ private static Function<String, Rules> ruleMaker = Rules::new;
+ private static Supplier<HttpClients> httpClientMaker = HttpClients::new;
+ private static Supplier<Simulators> simMaker = Simulators::new;
+ private static Supplier<Topics> topicMaker = Topics::new;
+
+ protected static Rules rules;
+ protected static HttpClients httpClients;
+ protected static Simulators simulators;
+
+
+ // used to inject and wait for messages
+ @Getter(AccessLevel.PROTECTED)
+ private Topics topics;
+
+ // used to wait for messages on SINK topics
+ protected Listener<VirtualControlLoopNotification> policyClMgt;
+ protected Listener<Request> appcClSink;
+ protected Listener<AppcLcmDmaapWrapper> appcLcmRead;
+
+ protected PolicyController controller;
+
+ /*
+ * Tosca Policy that was loaded.
+ */
+ protected ToscaPolicy policy;
+
+
+ /**
+ * Initializes {@link #rules}, {@link #httpClients}, and {@link #simulators}.
+ *
+ * @param controllerName the rule controller name
+ */
+ public static void initStatics(String controllerName) {
+ rules = ruleMaker.apply(controllerName);
+ httpClients = httpClientMaker.get();
+ simulators = simMaker.get();
+ }
+
+ /**
+ * Destroys {@link #httpClients}, {@link #simulators}, and {@link #rules}.
+ */
+ public static void finishStatics() {
+ httpClients.destroy();
+ simulators.destroy();
+ rules.destroy();
+ }
+
+ /**
+ * Initializes {@link #topics} and {@link #controller}.
+ */
+ public void init() {
+ topics = topicMaker.get();
+ controller = rules.getController();
+ }
+
+ /**
+ * Destroys {@link #topics} and resets the rule facts.
+ */
+ public void finish() {
+ topics.destroy();
+ rules.resetFacts();
+ }
+
+ // Service123 (i.e., Policy with multiple operations)
+
+ /**
+ * Service123 with Tosca Compliant Policy.
+ */
+ @Test
+ public void testService123Compliant() {
+ policyClMgt = topics.createListener(POLICY_CL_MGT_TOPIC, VirtualControlLoopNotification.class, controller);
+ appcLcmRead = topics.createListener(APPC_LCM_READ_TOPIC, AppcLcmDmaapWrapper.class, APPC_LCM_CODER);
+
+ assertEquals(0, controller.getDrools().factCount(rules.getControllerName()));
+ policy = rules.setupPolicyFromFile(SERVICE123_TOSCA_COMPLIANT_POLICY);
+ assertEquals(2, controller.getDrools().factCount(rules.getControllerName()));
+
+ // inject an ONSET event over the DCAE topic
+ topics.inject(DCAE_TOPIC, SERVICE123_ONSET);
+
+ /* Wait to acquire a LOCK and a PDP-X PERMIT */
+ waitForLockAndPermit(policy, policyClMgt);
+
+ // restart request should be sent and fail four times (i.e., because retry=3)
+ for (int count = 0; count < 4; ++count) {
+ AppcLcmDmaapWrapper appcreq = appcLcmRead.await(req -> "restart".equals(req.getRpcName()));
+
+ topics.inject(APPC_LCM_WRITE_TOPIC, SERVICE123_APPC_RESTART_FAILURE,
+ appcreq.getBody().getInput().getCommonHeader().getSubRequestId());
+ }
+
+ // rebuild request should be sent and fail once
+ AppcLcmDmaapWrapper appcreq = appcLcmRead.await(req -> "rebuild".equals(req.getRpcName()));
+
+ topics.inject(APPC_LCM_WRITE_TOPIC, SERVICE123_APPC_REBUILD_FAILURE,
+ appcreq.getBody().getInput().getCommonHeader().getSubRequestId());
+
+ // migrate request should be sent and succeed
+ appcreq = appcLcmRead.await(req -> "migrate".equals(req.getRpcName()));
+
+ topics.inject(APPC_LCM_WRITE_TOPIC, SERVICE123_APPC_MIGRATE_SUCCESS,
+ appcreq.getBody().getInput().getCommonHeader().getSubRequestId());
+
+ /* --- Operation Completed --- */
+
+ waitForOperationSuccess();
+
+ /* --- Transaction Completed --- */
+ waitForFinalSuccess(policy, policyClMgt);
+ }
+
+ // Duplicate events
+
+ /**
+ * This test case tests the scenario where 3 events occur and 2 of the requests refer
+ * to the same target entity while the 3rd is for another entity. The expected result
+ * is that the event with the duplicate target entity will have a final success result
+ * for one of the events, and a rejected message for the one that was unable to obtain
+ * the lock. The event that is referring to a different target entity should be able
+ * to obtain a lock since it is a different target. After processing of all events
+ * there should only be the policy and params objects left in memory.
+ */
+ @Test
+ public void testDuplicatesEvents() {
+ policyClMgt = topics.createListener(POLICY_CL_MGT_TOPIC, VirtualControlLoopNotification.class, controller);
+ appcLcmRead = topics.createListener(APPC_LCM_READ_TOPIC, AppcLcmDmaapWrapper.class, APPC_LCM_CODER);
+
+ assertEquals(0, controller.getDrools().factCount(rules.getControllerName()));
+ policy = rules.setupPolicyFromFile(DUPLICATES_TOSCA_COMPLIANT_POLICY);
+ assertEquals(2, controller.getDrools().factCount(rules.getControllerName()));
+
+ /*
+ * Inject ONSET events over the DCAE topic. First and last have the same target
+ * entity, but different request IDs - only one should succeed. The middle one is
+ * for a different target entity, so it should succeed.
+ */
+ topics.inject(DCAE_TOPIC, DUPLICATES_ONSET_1, UUID.randomUUID().toString());
+ topics.inject(DCAE_TOPIC, DUPLICATES_ONSET_2);
+ topics.inject(DCAE_TOPIC, DUPLICATES_ONSET_1, UUID.randomUUID().toString());
+
+ // one should immediately generate a FINAL failure
+ waitForFinal(policy, policyClMgt, ControlLoopNotificationType.FINAL_FAILURE);
+
+ // should see two restarts
+ for (int count = 0; count < 2; ++count) {
+ AppcLcmDmaapWrapper appcreq = appcLcmRead.await(req -> "restart".equals(req.getRpcName()));
+
+ // indicate success
+ topics.inject(APPC_LCM_WRITE_TOPIC, DUPLICATES_APPC_SUCCESS,
+ appcreq.getBody().getInput().getCommonHeader().getSubRequestId());
+ }
+
+ // should see two FINAL successes
+ VirtualControlLoopNotification notif1 = waitForFinalSuccess(policy, policyClMgt);
+ VirtualControlLoopNotification notif2 = waitForFinalSuccess(policy, policyClMgt);
+
+ // get the list of target names so we can ensure there's one of each
+ List<String> actual = List.of(notif1, notif2).stream().map(notif -> notif.getAai().get("generic-vnf.vnf-id"))
+ .sorted().collect(Collectors.toList());
+
+ assertEquals(List.of("duplicate-VNF", "vCPE_Infrastructure_vGMUX_demo_app").toString(), actual.toString());
+ }
+
+ // VCPE
+
+ /**
+ * Sunny Day with Legacy Tosca Policy.
+ */
+ @Test
+ public void testVcpeSunnyDayLegacy() {
+ appcLcmSunnyDay(VCPE_TOSCA_LEGACY_POLICY, VCPE_ONSET_1, "restart");
+ }
+
+ /**
+ * Sunny Day with Tosca Compliant Policy.
+ */
+ @Test
+ public void testVcpeSunnyDayCompliant() {
+ appcLcmSunnyDay(VCPE_TOSCA_COMPLIANT_POLICY, VCPE_ONSET_1, "restart");
+ }
+
+ /**
+ * An ONSET flood prevention test that injects a few ONSETs at once. It attempts to
+ * simulate the flooding behavior of the DCAE TCA microservice. TCA could blast tens
+ * or hundreds of ONSETs within sub-second intervals.
+ */
+ @Test
+ public void testVcpeOnsetFloodPrevention() {
+ appcLcmSunnyDay(VCPE_TOSCA_COMPLIANT_POLICY, List.of(VCPE_ONSET_1, VCPE_ONSET_2, VCPE_ONSET_3), "restart");
+ }
+
+ // VDNS
+
+ /**
+ * Sunny Day with Tosca Compliant Policy.
+ */
+ @Test
+ public void testVdnsSunnyDayCompliant() {
+ httpSunnyDay(VDNS_TOSCA_COMPLIANT_POLICY, VDNS_ONSET);
+ }
+
+ // VFW
+
+ /**
+ * VFW Sunny Day with Legacy Tosca Policy.
+ */
+ @Test
+ public void testVfwSunnyDayLegacy() {
+ appcLegacySunnyDay(VFW_TOSCA_LEGACY_POLICY, VFW_ONSET, "ModifyConfig");
+ }
+
+ /**
+ * VFW Sunny Day with Tosca Compliant Policy.
+ */
+ @Test
+ public void testVfwSunnyDayCompliant() {
+ appcLegacySunnyDay(VFW_TOSCA_COMPLIANT_POLICY, VFW_ONSET, "ModifyConfig");
+ }
+
+ // VLB
+
+ /**
+ * Sunny Day with Legacy Tosca Policy.
+ */
+ @Test
+ public void testVlbSunnyDayLegacy() {
+ httpSunnyDay(VLB_TOSCA_LEGACY_POLICY, VLB_ONSET);
+ }
+
+ /**
+ * Sunny Day with Tosca Compliant Policy.
+ */
+ @Test
+ public void testVlbSunnyDayCompliant() {
+ httpSunnyDay(VLB_TOSCA_COMPLIANT_POLICY, VLB_ONSET);
+ }
+
+ /**
+ * Sunny day scenario for use cases that use APPC-LCM.
+ *
+ * @param policyFile file containing the ToscaPolicy to be loaded
+ * @param onsetFile file containing the ONSET to be injected
+ * @param operation expected APPC operation request
+ */
+ protected void appcLcmSunnyDay(String policyFile, String onsetFile, String operation) {
+ appcLcmSunnyDay(policyFile, List.of(onsetFile), operation);
+ }
+
+ /**
+ * Sunny day scenario for use cases that use APPC-LCM.
+ *
+ * @param policyFile file containing the ToscaPolicy to be loaded
+ * @param onsetFiles list of files containing the ONSET to be injected
+ * @param operation expected APPC operation request
+ */
+ protected void appcLcmSunnyDay(String policyFile, List<String> onsetFiles, String operation) {
+ policyClMgt = topics.createListener(POLICY_CL_MGT_TOPIC, VirtualControlLoopNotification.class, controller);
+ appcLcmRead = topics.createListener(APPC_LCM_READ_TOPIC, AppcLcmDmaapWrapper.class, APPC_LCM_CODER);
+
+ assertEquals(0, controller.getDrools().factCount(rules.getControllerName()));
+ policy = rules.setupPolicyFromFile(policyFile);
+ assertEquals(2, controller.getDrools().factCount(rules.getControllerName()));
+
+ // inject several ONSET events over the DCAE topic
+ for (String onsetFile : onsetFiles) {
+ topics.inject(DCAE_TOPIC, onsetFile);
+ }
+
+ /* Wait to acquire a LOCK and a PDP-X PERMIT */
+ waitForLockAndPermit(policy, policyClMgt);
+
+ /*
+ * Ensure that an APPC RESTART request was sent in response to the matching ONSET
+ */
+ AppcLcmDmaapWrapper appcreq = appcLcmRead.await(req -> operation.equals(req.getRpcName()));
+
+ /*
+ * Inject a 400 APPC Response Return over the APPC topic, with appropriate
+ * subRequestId
+ */
+ topics.inject(APPC_LCM_WRITE_TOPIC, VCPE_APPC_SUCCESS,
+ appcreq.getBody().getInput().getCommonHeader().getSubRequestId());
+
+ /* --- Operation Completed --- */
+
+ waitForOperationSuccess();
+
+ /* --- Transaction Completed --- */
+ waitForFinalSuccess(policy, policyClMgt);
+ }
+
+ /**
+ * Sunny day scenario for use cases that use Legacy APPC.
+ *
+ * @param policyFile file containing the ToscaPolicy to be loaded
+ * @param onsetFile file containing the ONSET to be injected
+ * @param operation expected APPC operation request
+ */
+ protected void appcLegacySunnyDay(String policyFile, String onsetFile, String operation) {
+ policyClMgt = topics.createListener(POLICY_CL_MGT_TOPIC, VirtualControlLoopNotification.class, controller);
+ appcClSink = topics.createListener(APPC_CL_TOPIC, Request.class, APPC_LEGACY_CODER);
+
+ assertEquals(0, controller.getDrools().factCount(rules.getControllerName()));
+ policy = rules.setupPolicyFromFile(policyFile);
+ assertEquals(2, controller.getDrools().factCount(rules.getControllerName()));
+
+ /* Inject an ONSET event over the DCAE topic */
+ topics.inject(DCAE_TOPIC, onsetFile);
+
+ /* Wait to acquire a LOCK and a PDP-X PERMIT */
+ waitForLockAndPermit(policy, policyClMgt);
+
+ /*
+ * Ensure that an APPC RESTART request was sent in response to the matching ONSET
+ */
+ Request appcreq = appcClSink.await(req -> operation.equals(req.getAction()));
+
+ /*
+ * Inject a 400 APPC Response Return over the APPC topic, with appropriate
+ * subRequestId
+ */
+ topics.inject(APPC_CL_TOPIC, VFW_APPC_SUCCESS, appcreq.getCommonHeader().getSubRequestId());
+
+ /* --- Operation Completed --- */
+
+ waitForOperationSuccess();
+
+ /* --- Transaction Completed --- */
+ waitForFinalSuccess(policy, policyClMgt);
+ }
+
+ /**
+ * Sunny day scenario for use cases that use an HTTP simulator.
+ *
+ * @param policyFile file containing the ToscaPolicy to be loaded
+ * @param onsetFile file containing the ONSET to be injected
+ * @param operation expected APPC operation request
+ */
+ protected void httpSunnyDay(String policyFile, String onsetFile) {
+ policyClMgt = topics.createListener(POLICY_CL_MGT_TOPIC, VirtualControlLoopNotification.class, controller);
+
+ assertEquals(0, controller.getDrools().factCount(rules.getControllerName()));
+ policy = rules.setupPolicyFromFile(policyFile);
+ assertEquals(2, controller.getDrools().factCount(rules.getControllerName()));
+
+ /* Inject an ONSET event over the DCAE topic */
+ topics.inject(DCAE_TOPIC, onsetFile);
+
+ /* Wait to acquire a LOCK and a PDP-X PERMIT */
+ waitForLockAndPermit(policy, policyClMgt);
+
+ /* --- Operation Completed --- */
+
+ waitForOperationSuccess();
+
+ /* --- Transaction Completed --- */
+ waitForFinalSuccess(policy, policyClMgt);
+ }
+
+ /**
+ * Waits for a OPERATION SUCCESS transaction notification.
+ */
+ protected void waitForOperationSuccess() {
+ policyClMgt.await(notif -> notif.getNotification() == ControlLoopNotificationType.OPERATION_SUCCESS);
+ }
+
+ /**
+ * Waits for a FINAL SUCCESS transaction notification.
+ *
+ * @return the FINAL SUCCESS notification
+ */
+ protected VirtualControlLoopNotification waitForFinalSuccess(ToscaPolicy policy,
+ Listener<VirtualControlLoopNotification> policyClMgt) {
+
+ return this.waitForFinal(policy, policyClMgt, ControlLoopNotificationType.FINAL_SUCCESS);
+ }
+
+ /**
+ * Waits for notifications for LOCK acquisition and GUARD Permit so that event
+ * processing may proceed.
+ */
+ protected abstract void waitForLockAndPermit(ToscaPolicy policy,
+ Listener<VirtualControlLoopNotification> policyClMgt);
+
+ /**
+ * Waits for a FINAL transaction notification.
+ *
+ * @param finalType FINAL_xxx type for which to wait
+ *
+ * @return the FINAL notification
+ */
+ protected abstract VirtualControlLoopNotification waitForFinal(ToscaPolicy policy,
+ Listener<VirtualControlLoopNotification> policyClMgt, ControlLoopNotificationType finalType);
+}
diff --git a/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/HttpClients.java b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/HttpClients.java
new file mode 100644
index 000000000..eb729ad6d
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/HttpClients.java
@@ -0,0 +1,67 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.common.rules.test;
+
+import org.onap.policy.common.endpoints.http.client.HttpClientConfigException;
+import org.onap.policy.common.endpoints.http.client.HttpClientFactory;
+import org.onap.policy.common.endpoints.http.client.HttpClientFactoryInstance;
+import org.onap.policy.drools.persistence.SystemPersistenceConstants;
+
+/**
+ * Mechanism by which junit tests can manage HTTP Clients.
+ */
+public class HttpClients {
+
+ /**
+ * Constructs the object.
+ */
+ public HttpClients() {
+ super();
+ }
+
+
+ /**
+ * Adds Http Clients specified in the property file.
+ *
+ * @param propFilePrefix prefix prepended to "-http-client.properties" to yield the
+ * full name of the property file containing http client properties
+ */
+ public void addClients(String propFilePrefix) {
+ try {
+ getClientFactory().build(SystemPersistenceConstants.getManager().getHttpClientProperties(propFilePrefix));
+ } catch (HttpClientConfigException e) {
+ throw new IllegalArgumentException("cannot initialize HTTP clients", e);
+ }
+ }
+
+ /**
+ * Destroys all Http Clients.
+ */
+ public void destroy() {
+ getClientFactory().destroy();
+ }
+
+ // these methods may be overridden by junit tests
+
+ protected HttpClientFactory getClientFactory() {
+ return HttpClientFactoryInstance.getClientFactory();
+ }
+}
diff --git a/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Listener.java b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Listener.java
new file mode 100644
index 000000000..5042f54c6
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Listener.java
@@ -0,0 +1,180 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.common.rules.test;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Function;
+import java.util.function.Predicate;
+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.TopicEndpointManager;
+import org.onap.policy.common.endpoints.event.comm.TopicListener;
+import org.onap.policy.common.endpoints.event.comm.TopicSink;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Listener for messages published on a topic SINK.
+ *
+ * @param <T> message type
+ */
+public class Listener<T> implements TopicListener {
+ private static final Logger logger = LoggerFactory.getLogger(Listener.class);
+ public static long DEFAULT_WAIT_SEC = 5L;
+
+ private final TopicSink sink;
+ private final Function<String, T> decoder;
+ private final BlockingQueue<T> messages = new LinkedBlockingQueue<>();
+
+ /**
+ * Constructs the object.
+ *
+ * @param topicName name of the NOOP topic SINK on which to listen
+ * @param decoder function that takes a topic name and a message and decodes it into
+ * the desired type
+ */
+ public Listener(String topicName, Function<String, T> decoder) {
+ this.sink = getTopicManager().getNoopTopicSink(topicName);
+ this.decoder = decoder;
+ this.sink.register(this);
+ }
+
+ /**
+ * Determines if there are any messages waiting.
+ *
+ * @return {@code true} if there are no messages waiting, {@code false} otherwise
+ */
+ public boolean isEmpty() {
+ return messages.isEmpty();
+ }
+
+ /**
+ * Waits, for the default amount of time, for a message to be published to the topic.
+ *
+ * @return the message that was published
+ * @throws TopicException if interrupted or no message is received within the
+ * specified time
+ */
+ public T await() {
+ return await(DEFAULT_WAIT_SEC, TimeUnit.SECONDS);
+ }
+
+ /**
+ * Waits, for the specified period of time, for a message to be published to the
+ * topic.
+ *
+ * @param twait maximum time to wait
+ * @param unit time unit
+ * @return the message that was published
+ * @throws TopicException if interrupted or no message is received within the
+ * specified time
+ */
+ public T await(long twait, TimeUnit unit) {
+ return await(twait, unit, msg -> true);
+ }
+
+ /**
+ * Waits, for the default amount of time, for a message to be published to the topic.
+ *
+ * @param filter filter used to select the message of interest; preceding messages
+ * that do not pass the filter are discarded
+ * @return the message that was published
+ * @throws TopicException if interrupted or no message is received within the
+ * specified time
+ */
+ public T await(Predicate<T> filter) {
+ return await(DEFAULT_WAIT_SEC, TimeUnit.SECONDS, filter);
+ }
+
+ /**
+ * Waits, for the specified period of time, for a message to be published to the
+ * topic.
+ *
+ * @param twait maximum time to wait
+ * @param unit time unit
+ * @param filter filter used to select the message of interest; preceding messages
+ * that do not pass the filter are discarded
+ * @return the message that was published
+ * @throws TopicException if interrupted or no message is received within the
+ * specified time
+ */
+ public T await(long twait, TimeUnit unit, Predicate<T> filter) {
+ long endMs = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(twait, unit);
+
+ for (;;) {
+ try {
+ long remainingMs = endMs - System.currentTimeMillis();
+ if (remainingMs < 0) {
+ throw new TimeoutException();
+ }
+
+ T msg = pollMessage(remainingMs);
+ if (msg == null) {
+ throw new TimeoutException();
+ }
+
+ if (filter.test(msg)) {
+ return msg;
+ }
+
+ logger.info("message discarded by the filter on topic {}", sink.getTopic());
+
+ } catch (InterruptedException e) {
+ logger.warn("'await' interrupted on topic {}", sink.getTopic());
+ Thread.currentThread().interrupt();
+ throw new TopicException(e);
+
+ } catch (TimeoutException e) {
+ logger.warn("'await' timed out on topic {}", sink.getTopic());
+ throw new TopicException(e);
+ }
+ }
+ }
+
+ /**
+ * Unregisters the listener.
+ */
+ public void unregister() {
+ sink.unregister(this);
+ }
+
+ @Override
+ public void onTopicEvent(CommInfrastructure commType, String topic, String event) {
+ try {
+ messages.add(decoder.apply(event));
+ } catch (RuntimeException e) {
+ logger.warn("cannot decode message on topic {} for event {}", topic, event, e);
+ }
+ }
+
+ // these methods may be overridden by junit tests
+
+ protected TopicEndpoint getTopicManager() {
+ return TopicEndpointManager.getManager();
+ }
+
+ protected T pollMessage(long remainingMs) throws InterruptedException {
+ return messages.poll(remainingMs, TimeUnit.MILLISECONDS);
+ }
+}
diff --git a/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/NamedRunner.java b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/NamedRunner.java
new file mode 100644
index 000000000..1cbe5a56a
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/NamedRunner.java
@@ -0,0 +1,85 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.common.rules.test;
+
+import org.junit.Ignore;
+import org.junit.runner.Description;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.InitializationError;
+
+/**
+ * Runs tests listed via the {@link TestNames} annotation.
+ */
+public class NamedRunner extends BlockJUnit4ClassRunner {
+
+ /**
+ * Constructs the object.
+ */
+ public NamedRunner(Class<?> testClass) throws InitializationError {
+ super(testClass);
+ }
+
+ @Override
+ protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
+ Description description = describeChild(method);
+
+ if (method.getAnnotation(Ignore.class) != null) {
+ notifier.fireTestIgnored(description);
+
+ } else if (!isNamed(description.getTestClass(), method.getName())) {
+ notifier.fireTestIgnored(description);
+
+ } else {
+ runLeaf(methodBlock(method), description, notifier);
+ }
+ }
+
+ /**
+ * Determines if the test is in the list of tests to be included.
+ *
+ * @param testClass class under test
+ * @param testName name of the test of interest
+ * @return {@code true} if the test is in the list, {@code false} otherwise
+ */
+ private boolean isNamed(Class<?> testClass, String testName) {
+ TestNames annot = testClass.getAnnotation(TestNames.class);
+ if (annot == null) {
+ // no annotation - everything passes
+ return true;
+ }
+
+ for (String name : annot.names()) {
+ if (testName.equals(name)) {
+ return true;
+ }
+ }
+
+ for (String prefix : annot.prefixes()) {
+ if (testName.startsWith(prefix)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Rules.java b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Rules.java
new file mode 100644
index 000000000..7549c6be6
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Rules.java
@@ -0,0 +1,413 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.common.rules.test;
+
+import static org.awaitility.Awaitility.await;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import lombok.Getter;
+import org.apache.commons.lang3.StringUtils;
+import org.kie.api.event.rule.AfterMatchFiredEvent;
+import org.kie.api.event.rule.BeforeMatchFiredEvent;
+import org.kie.api.event.rule.DefaultAgendaEventListener;
+import org.kie.api.event.rule.DefaultRuleRuntimeEventListener;
+import org.kie.api.event.rule.MatchCancelledEvent;
+import org.kie.api.event.rule.MatchCreatedEvent;
+import org.kie.api.event.rule.ObjectDeletedEvent;
+import org.kie.api.event.rule.ObjectInsertedEvent;
+import org.kie.api.event.rule.ObjectUpdatedEvent;
+import org.kie.api.event.rule.RuleRuntimeEventListener;
+import org.kie.api.runtime.KieSession;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.common.utils.resources.ResourceUtils;
+import org.onap.policy.controlloop.ControlLoopEvent;
+import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
+import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager;
+import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager2;
+import org.onap.policy.drools.controller.DroolsController;
+import org.onap.policy.drools.persistence.SystemPersistence;
+import org.onap.policy.drools.persistence.SystemPersistenceConstants;
+import org.onap.policy.drools.system.PolicyController;
+import org.onap.policy.drools.system.PolicyControllerConstants;
+import org.onap.policy.drools.system.PolicyControllerFactory;
+import org.onap.policy.drools.system.PolicyEngine;
+import org.onap.policy.drools.system.PolicyEngineConstants;
+import org.onap.policy.drools.util.KieUtils;
+import org.onap.policy.drools.utils.logging.LoggerUtil;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Mechanism by which junit tests can manage the rule engine.
+ */
+public class Rules {
+ private static final Logger logger = LoggerFactory.getLogger(Rules.class);
+ private static final StandardCoder coder = new StandardCoder();
+
+ /**
+ * PDP-D Engine.
+ */
+ @Getter
+ private final PolicyEngine pdpd = makeEngine();
+
+ /**
+ * PDP-D Configuration Repository.
+ */
+ @Getter
+ private final SystemPersistence pdpdRepo = makePdpdRepo();
+
+
+ @Getter
+ private final String controllerName;
+
+ @Getter
+ private PolicyController controller;
+
+
+ /**
+ * Constructs the object.
+ *
+ * @param controllerName name of the controller
+ */
+ public Rules(String controllerName) {
+ this.controllerName = controllerName;
+ }
+
+ /**
+ * Configures various items, including the PDP-D Engine.
+ *
+ * @param resourceDir path to resource directory
+ */
+ public void configure(String resourceDir) {
+ pdpdRepo.setConfigurationDir("src/test/resources/config");
+
+ try {
+ File kmoduleFile = new File(resourceDir + "/META-INF/kmodule.xml");
+ File pomFile = new File("src/test/resources/" + controllerName + ".pom");
+ String resourceDir2 = resourceDir + "/org/onap/policy/controlloop/";
+ File ruleFile = new File(resourceDir + "/" + controllerName + ".drl");
+ List<File> ruleFiles = Collections.singletonList(ruleFile);
+
+ installArtifact(kmoduleFile, pomFile, resourceDir2, ruleFiles);
+ } catch (IOException e) {
+ throw new IllegalArgumentException("cannot configure KIE session for " + controllerName, e);
+ }
+
+ setupLogging();
+
+ pdpd.configure(new Properties());
+ }
+
+ /**
+ * Starts various items, including the PDP-D Engine.
+ */
+ public void start() {
+ controller = pdpd.createPolicyController(controllerName, pdpdRepo.getControllerProperties(controllerName));
+ pdpd.start();
+
+ setupDroolsLogging();
+ }
+
+ /**
+ * Stop PDP-D.
+ */
+ public void destroy() {
+ getControllerFactory().shutdown(controllerName);
+ pdpd.stop();
+ }
+
+ /**
+ * Removes various facts from working memory, including the Policy and Params, as well
+ * as any event managers and events.
+ */
+ public void resetFacts() {
+ List<Class<?>> classes = List.of(ToscaPolicy.class, ControlLoopParams.class, ControlLoopEventManager2.class,
+ ControlLoopEvent.class);
+
+ // delete all objects of the listed classes
+ DroolsController drools = controller.getDrools();
+ classes.forEach(drools::delete);
+
+ // wait for them to be deleted
+ for (Class<?> clazz : classes) {
+ await(clazz.getSimpleName()).atMost(5, TimeUnit.SECONDS)
+ .until(() -> drools.facts(controllerName, clazz).isEmpty());
+ }
+
+ /*
+ * We can't delete this class directly; we have to wait for the rules to clean it
+ * up, because the rule also cleans up a number of other associated objects.
+ */
+ Class<?> clazz = ControlLoopEventManager.class;
+ await(clazz.getSimpleName()).atMost(5, TimeUnit.SECONDS)
+ .until(() -> drools.facts(controllerName, clazz).isEmpty());
+ }
+
+ /**
+ * Installs a policy from policy/models (examples) repo.
+ */
+ public ToscaPolicy setupPolicyFromTemplate(String templatePath, String policyName) {
+ try {
+ return setupPolicy(getPolicyFromTemplate(templatePath, policyName));
+
+ } catch (InterruptedException | CoderException e) {
+ throw new IllegalArgumentException("policy " + policyName, e);
+ }
+ }
+
+ private ToscaPolicy getPolicyFromTemplate(String resourcePath, String policyName) throws CoderException {
+ String policyJson = ResourceUtils.getResourceAsString(resourcePath);
+ if (policyJson == null) {
+ throw new CoderException(new FileNotFoundException(resourcePath));
+ }
+
+ ToscaServiceTemplate serviceTemplate = coder.decode(policyJson, ToscaServiceTemplate.class);
+ ToscaPolicy policy = serviceTemplate.getToscaTopologyTemplate().getPolicies().get(0).get(policyName);
+ assertNotNull(policy);
+
+ /*
+ * name and version are used within a drl. api component and drools core will
+ * ensure that these are populated.
+ */
+ if (StringUtils.isBlank(policy.getName())) {
+ policy.setName(policyName);
+ }
+
+ if (StringUtils.isBlank(policy.getVersion())) {
+ policy.setVersion(policy.getTypeVersion());
+ }
+
+ return serviceTemplate.getToscaTopologyTemplate().getPolicies().get(0).get(policyName);
+ }
+
+ /**
+ * Installs a given policy.
+ */
+ public ToscaPolicy setupPolicyFromFile(String policyPath) {
+ try {
+ return setupPolicy(getPolicyFromFile(policyPath));
+
+ } catch (InterruptedException | IOException | CoderException e) {
+ throw new IllegalArgumentException("policy " + policyPath, e);
+ }
+ }
+
+ private ToscaPolicy getPolicyFromFile(String policyPath) throws IOException, CoderException {
+ String policyJson = ResourceUtils.getResourceAsString(policyPath);
+ if (policyJson == null) {
+ throw new CoderException(new FileNotFoundException(policyPath));
+ }
+
+ return coder.decode(policyJson, ToscaPolicy.class);
+ }
+
+ private ToscaPolicy setupPolicy(ToscaPolicy policy) throws InterruptedException {
+ final KieObjectExpectedCallback<?> policyTracker = new KieObjectInsertedExpectedCallback<>(policy);
+ final KieObjectExpectedCallback<?> paramsTracker =
+ new KieClassInsertedExpectedCallback<>(ControlLoopParams.class);
+
+ controller.getDrools().offer(policy);
+
+ assertTrue(policyTracker.isNotified());
+ assertTrue(paramsTracker.isNotified());
+
+ assertEquals(1, controller.getDrools().facts(controllerName, ToscaPolicy.class).stream()
+ .filter((anotherPolicy) -> anotherPolicy == policy).count());
+
+ assertEquals(1, controller.getDrools().facts(controllerName, ControlLoopParams.class).stream()
+ .filter((params) -> params.getToscaPolicy() == policy).count());
+ return policy;
+ }
+
+ /**
+ * Sets up overall logging.
+ */
+ private void setupLogging() {
+ LoggerUtil.setLevel(LoggerUtil.ROOT_LOGGER, "WARN");
+ LoggerUtil.setLevel("org.eclipse.jetty", "WARN");
+ LoggerUtil.setLevel("org.onap.policy.controlloop", "INFO");
+ LoggerUtil.setLevel("network", "INFO");
+ }
+
+ /**
+ * Sets up Drools Logging for events of interest.
+ */
+ private void setupDroolsLogging() {
+ KieSession session = getKieSession();
+
+ session.addEventListener(new RuleListenerLogger());
+ session.addEventListener(new AgendaListenerLogger());
+ }
+
+ /**
+ * Logs Modifications to Working Memory.
+ */
+ private static class RuleListenerLogger implements RuleRuntimeEventListener {
+ @Override
+ public void objectInserted(ObjectInsertedEvent event) {
+ String ruleName = (event.getRule() != null) ? event.getRule().getName() : "null";
+ logger.info("RULE {}: inserted {}", ruleName, event.getObject());
+ }
+
+ @Override
+ public void objectUpdated(ObjectUpdatedEvent event) {
+ String ruleName = (event.getRule() != null) ? event.getRule().getName() : "null";
+ logger.info("RULE {}: updated {}", ruleName, event.getObject());
+
+ }
+
+ @Override
+ public void objectDeleted(ObjectDeletedEvent event) {
+ String ruleName = (event.getRule() != null) ? event.getRule().getName() : "null";
+ logger.info("RULE {}: deleted {}", ruleName, event.getOldObject());
+ }
+ }
+
+ /**
+ * Logs Rule Matches.
+ */
+ private static class AgendaListenerLogger extends DefaultAgendaEventListener {
+ @Override
+ public void matchCreated(MatchCreatedEvent event) {
+ logger.info("RULE {}: match created", event.getMatch().getRule().getName());
+ }
+
+ @Override
+ public void matchCancelled(MatchCancelledEvent event) {
+ logger.info("RULE {}: match cancelled", event.getMatch().getRule().getName());
+ }
+
+ @Override
+ public void beforeMatchFired(BeforeMatchFiredEvent event) {
+ logger.info("RULE {}: before match fired", event.getMatch().getRule().getName());
+ }
+
+ @Override
+ public void afterMatchFired(AfterMatchFiredEvent event) {
+ logger.info("RULE {}: after match fired", event.getMatch().getRule().getName());
+ }
+ }
+
+ /**
+ * Base Class to track Working Memory updates for objects of type T.
+ */
+ private abstract class KieObjectExpectedCallback<T> extends DefaultRuleRuntimeEventListener {
+ protected T subject;
+
+ protected CountDownLatch countDownLatch = new CountDownLatch(1);
+
+ public KieObjectExpectedCallback(T affected) {
+ subject = affected;
+ register();
+ }
+
+ public boolean isNotified() throws InterruptedException {
+ return countDownLatch.await(9L, TimeUnit.SECONDS);
+ }
+
+ protected void callbacked() {
+ unregister();
+ countDownLatch.countDown();
+ }
+
+ public KieObjectExpectedCallback<T> register() {
+ getKieSession().addEventListener(this);
+ return this;
+ }
+
+ public KieObjectExpectedCallback<T> unregister() {
+ getKieSession().removeEventListener(this);
+ return this;
+ }
+ }
+
+ /**
+ * Tracks inserts in Working Memory for an object of type T.
+ */
+ private class KieObjectInsertedExpectedCallback<T> extends KieObjectExpectedCallback<T> {
+ public KieObjectInsertedExpectedCallback(T affected) {
+ super(affected);
+ }
+
+ @Override
+ public void objectInserted(ObjectInsertedEvent event) {
+ if (subject == event.getObject()) {
+ callbacked();
+ }
+ }
+ }
+
+ /**
+ * Tracks inserts in Working Memory for any object of class T.
+ */
+ private class KieClassInsertedExpectedCallback<T> extends KieObjectInsertedExpectedCallback<T> {
+
+ public KieClassInsertedExpectedCallback(T affected) {
+ super(affected);
+ }
+
+ public void objectInserted(ObjectInsertedEvent event) {
+ if (subject == event.getObject().getClass()) {
+ callbacked();
+ }
+ }
+ }
+
+ // these may be overridden by junit tests
+
+
+ protected PolicyEngine makeEngine() {
+ return PolicyEngineConstants.getManager();
+ }
+
+
+ protected SystemPersistence makePdpdRepo() {
+ return SystemPersistenceConstants.getManager();
+ }
+
+ protected KieSession getKieSession() {
+ return getControllerFactory().get(controllerName).getDrools().getContainer().getPolicySession(controllerName)
+ .getKieSession();
+ }
+
+ protected PolicyControllerFactory getControllerFactory() {
+ return PolicyControllerConstants.getFactory();
+ }
+
+ protected void installArtifact(File kmoduleFile, File pomFile, String resourceDir, List<File> ruleFiles)
+ throws IOException {
+
+ KieUtils.installArtifact(kmoduleFile, pomFile, resourceDir, ruleFiles);
+ }
+}
diff --git a/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/SimulatorException.java b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/SimulatorException.java
new file mode 100644
index 000000000..f9880d07a
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/SimulatorException.java
@@ -0,0 +1,44 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.common.rules.test;
+
+/**
+ * Exception thrown by <i>Simulators</i>.
+ */
+public class SimulatorException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public SimulatorException() {
+ super();
+ }
+
+ public SimulatorException(String message) {
+ super(message);
+ }
+
+ public SimulatorException(Throwable cause) {
+ super(cause);
+ }
+
+ public SimulatorException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Simulators.java b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Simulators.java
new file mode 100644
index 000000000..4a325a447
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Simulators.java
@@ -0,0 +1,85 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.common.rules.test;
+
+import java.util.LinkedList;
+import java.util.List;
+import lombok.AccessLevel;
+import lombok.Getter;
+import org.onap.policy.common.endpoints.http.server.HttpServletServer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Simulator container.
+ */
+public class Simulators {
+ private static final Logger logger = LoggerFactory.getLogger(Simulators.class);
+
+ @Getter(AccessLevel.PROTECTED)
+ private final List<HttpServletServer> servers = new LinkedList<>();
+
+ /**
+ * Constructs the object.
+ */
+ public Simulators() {
+ super();
+ }
+
+ /**
+ * Invokes the given functions to start the simulators. Destroys <i>all</i> of the
+ * simulators if any fail to start.
+ *
+ * @param builders functions to invoke to build the simulators
+ */
+ public void start(SimulatorBuilder... builders) {
+ try {
+ for (SimulatorBuilder builder : builders) {
+ servers.add(builder.build());
+ }
+ } catch (InterruptedException e) {
+ logger.warn("interrupted building the simulators");
+ destroy();
+ Thread.currentThread().interrupt();
+ throw new SimulatorException(e);
+ }
+ }
+
+ /**
+ * Stops all of the simulators.
+ */
+ public void destroy() {
+ for (HttpServletServer server : servers) {
+ try {
+ server.shutdown();
+ } catch (RuntimeException e) {
+ logger.warn("error stopping simulator {}", server.getName(), e);
+ }
+ }
+
+ servers.clear();
+ }
+
+ @FunctionalInterface
+ public static interface SimulatorBuilder {
+ public HttpServletServer build() throws InterruptedException;
+ }
+}
diff --git a/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/TestNames.java b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/TestNames.java
new file mode 100644
index 000000000..5f3d856ef
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/TestNames.java
@@ -0,0 +1,44 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.common.rules.test;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation for a test CLASS. Used to identify the names of the test methods to be
+ * included.
+ */
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface TestNames {
+ /**
+ * Test method names to be included.
+ */
+ String[] names() default {};
+
+ /**
+ * Prefixes of test method names to be included.
+ */
+ String[] prefixes() default {};
+}
diff --git a/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/TopicException.java b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/TopicException.java
new file mode 100644
index 000000000..01a866966
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/TopicException.java
@@ -0,0 +1,44 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.common.rules.test;
+
+/**
+ * Exception thrown by <i>Topics</i>.
+ */
+public class TopicException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public TopicException() {
+ super();
+ }
+
+ public TopicException(String message) {
+ super(message);
+ }
+
+ public TopicException(Throwable cause) {
+ super(cause);
+ }
+
+ public TopicException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Topics.java b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Topics.java
new file mode 100644
index 000000000..f71559acb
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Topics.java
@@ -0,0 +1,176 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.common.rules.test;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.function.Function;
+import org.onap.policy.common.endpoints.event.comm.TopicEndpoint;
+import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager;
+import org.onap.policy.common.utils.coder.Coder;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.resources.ResourceUtils;
+import org.onap.policy.drools.protocol.coders.EventProtocolCoder;
+import org.onap.policy.drools.protocol.coders.EventProtocolCoderConstants;
+import org.onap.policy.drools.system.PolicyController;
+
+/**
+ * Mechanism by which junit tests can manage topic messages.
+ */
+public class Topics {
+ /**
+ * Wherever this string appears within an input file, it is replaced by a value passed
+ * as a parameter to the {@link #inject(String, String, String)} method.
+ */
+ private static final String REPLACE_ME = "${replaceMe}";
+
+ /**
+ * Listeners that have been created and registered by "this" object.
+ */
+ private final List<Listener<?>> listeners = new LinkedList<>();
+
+
+ /**
+ * Constructs the object.
+ */
+ public Topics() {
+ super();
+ }
+
+ /**
+ * Unregisters all of the listeners.
+ */
+ public void destroy() {
+ listeners.forEach(Listener::unregister);
+ }
+
+ /**
+ * Injects the content of the given file onto a NOOP topic SOURCE.
+ *
+ * @param topicName topic on which to inject
+ * @param file file whose content is to be injected
+ */
+ public void inject(String topicName, String file) {
+ inject(topicName, file, REPLACE_ME);
+ }
+
+ /**
+ * Injects the content of the given file onto a NOOP topic SOURCE, with the given
+ * substitution.
+ *
+ * @param topicName topic on which to inject
+ * @param file file whose content is to be injected
+ * @param newText text to be substituted for occurrences of "${replaceMe}" in the
+ * source file
+ */
+ public void inject(String topicName, String file, String newText) {
+ try {
+ String text = ResourceUtils.getResourceAsString(file);
+ if (text == null) {
+ throw new FileNotFoundException(file);
+ }
+ text = text.replace(REPLACE_ME, newText);
+ getTopicManager().getNoopTopicSource(topicName).offer(text);
+ } catch (IOException e) {
+ throw new TopicException(e);
+ }
+ }
+
+ /**
+ * Creates a listener for messages published on a NOOP topic SINK. Messages are
+ * decoded using the coder associated with the controller.
+ *
+ * @param <T> message type
+ * @param topicName name of the topic on which to listen
+ * @param expectedClass type of message expected
+ * @param controller controller whose decoders are to be used
+ * @return a new listener
+ */
+ public <T> Listener<T> createListener(String topicName, Class<T> expectedClass, PolicyController controller) {
+ EventProtocolCoder mgr = getProtocolCoder();
+ String groupId = controller.getDrools().getGroupId();
+ String artifactId = controller.getDrools().getArtifactId();
+
+ // @formatter:off
+ return createListener(topicName,
+ event -> expectedClass.cast(mgr.decode(groupId, artifactId, topicName, event)));
+ // @formatter:on
+ }
+
+ /**
+ * Creates a listener for messages published on a NOOP topic SINK. Messages are
+ * decoded using the specified coder.
+ *
+ * @param <T> message type
+ * @param topicName name of the topic on which to listen
+ * @param expectedClass type of message expected
+ * @param coder coder to decode the messages
+ * @return a new listener
+ */
+ public <T> Listener<T> createListener(String topicName, Class<T> expectedClass, Coder coder) {
+ Function<String, T> decoder = event -> {
+ try {
+ return coder.decode(event, expectedClass);
+ } catch (CoderException e) {
+ throw new IllegalArgumentException("cannot decode message", e);
+ }
+ };
+
+ return createListener(topicName, decoder);
+ }
+
+ /**
+ * Creates a listener for messages published on a NOOP topic SINK. Messages are
+ * decoded using the specified decoder.
+ *
+ * @param <T> message type
+ * @param topicName name of the topic on which to listen
+ * @param decoder function that takes a message and decodes it into the desired type
+ * @return a new listener
+ */
+ public <T> Listener<T> createListener(String topicName, Function<String, T> decoder) {
+ Listener<T> listener = makeListener(topicName, decoder);
+ listeners.add(listener);
+
+ return listener;
+ }
+
+ // these methods may be overridden by junit tests
+
+ protected TopicEndpoint getTopicManager() {
+ return TopicEndpointManager.getManager();
+ }
+
+ protected EventProtocolCoder getProtocolCoder() {
+ return EventProtocolCoderConstants.getManager();
+ }
+
+ protected <T> Listener<T> makeListener(String topicName, Function<String, T> decoder) {
+ return new Listener<>(topicName, decoder) {
+ @Override
+ protected TopicEndpoint getTopicManager() {
+ return Topics.this.getTopicManager();
+ }
+ };
+ }
+}
diff --git a/controlloop/common/rules-test/src/main/resources/duplicates/duplicates.appc.success.json b/controlloop/common/rules-test/src/main/resources/duplicates/duplicates.appc.success.json
new file mode 100644
index 000000000..331021798
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/resources/duplicates/duplicates.appc.success.json
@@ -0,0 +1,22 @@
+{
+ "body": {
+ "output": {
+ "common-header": {
+ "timestamp": "2017-08-25T21:06:23.037Z",
+ "api-ver": "5.00",
+ "originator-id": "664be3d2-6c12-4f4b-a3e7-c349acced200",
+ "request-id": "664be3d2-6c12-4f4b-a3e7-c349acced200",
+ "sub-request-id": "${replaceMe}",
+ "flags": {}
+ },
+ "status": {
+ "code": 400,
+ "message": "Restart Successful"
+ }
+ }
+ },
+ "version": "2.0",
+ "rpc-name": "restart",
+ "correlation-id": "664be3d2-6c12-4f4b-a3e7-c349acced200-1",
+ "type": "response"
+}
diff --git a/controlloop/common/rules-test/src/main/resources/duplicates/duplicates.onset.1.json b/controlloop/common/rules-test/src/main/resources/duplicates/duplicates.onset.1.json
new file mode 100644
index 000000000..70f48442f
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/resources/duplicates/duplicates.onset.1.json
@@ -0,0 +1,16 @@
+{
+ "closedLoopControlName": "ControlLoop-Duplicates-48f0c2c3-a172-4192-9ae3-052274181b6e",
+ "closedLoopAlarmStart": 1463679805324,
+ "closedLoopEventClient": "DCAE_INSTANCE_ID.dcae-tca",
+ "closedLoopEventStatus": "ONSET",
+ "requestID": "${replaceMe}",
+ "target_type": "VNF",
+ "target": "generic-vnf.vnf-id",
+ "AAI": {
+ "vserver.is-closed-loop-disabled": "false",
+ "vserver.prov-status": "ACTIVE",
+ "generic-vnf.vnf-id": "duplicate-VNF"
+ },
+ "from": "DCAE",
+ "version": "1.0.2"
+}
diff --git a/controlloop/common/rules-test/src/main/resources/duplicates/duplicates.onset.2.json b/controlloop/common/rules-test/src/main/resources/duplicates/duplicates.onset.2.json
new file mode 100644
index 000000000..046243609
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/resources/duplicates/duplicates.onset.2.json
@@ -0,0 +1,16 @@
+{
+ "closedLoopControlName": "ControlLoop-Duplicates-48f0c2c3-a172-4192-9ae3-052274181b6e",
+ "closedLoopAlarmStart": 1463679805324,
+ "closedLoopEventClient": "DCAE_INSTANCE_ID.dcae-tca",
+ "closedLoopEventStatus": "ONSET",
+ "requestID": "664be3d2-6c12-4f4b-a3e7-c349acced200",
+ "target_type": "VNF",
+ "target": "generic-vnf.vnf-id",
+ "AAI": {
+ "vserver.is-closed-loop-disabled": "false",
+ "vserver.prov-status": "ACTIVE",
+ "generic-vnf.vnf-id": "vCPE_Infrastructure_vGMUX_demo_app"
+ },
+ "from": "DCAE",
+ "version": "1.0.2"
+}
diff --git a/controlloop/common/rules-test/src/main/resources/duplicates/tosca-compliant-duplicates.json b/controlloop/common/rules-test/src/main/resources/duplicates/tosca-compliant-duplicates.json
new file mode 100644
index 000000000..d67472743
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/resources/duplicates/tosca-compliant-duplicates.json
@@ -0,0 +1,37 @@
+{
+ "type": "onap.policies.controlloop.operational.common.Drools",
+ "type_version": "1.0.0",
+ "version": "1.0.0",
+ "name": "operational.restart",
+ "metadata": {
+ "policy-id": "operational.restart"
+ },
+ "properties": {
+ "controllerName": "usecases",
+ "id": "ControlLoop-Duplicates-48f0c2c3-a172-4192-9ae3-052274181b6e",
+ "timeout": 3600,
+ "abatement": false,
+ "trigger": "unique-policy-id-1-restart",
+ "operations": [
+ {
+ "id": "unique-policy-id-1-restart",
+ "description": "Restart the VM",
+ "operation": {
+ "actor": "APPC",
+ "operation": "Restart",
+ "target": {
+ "targetType": "VM"
+ }
+ },
+ "timeout": 1200,
+ "retries": 3,
+ "success": "final_success",
+ "failure": "final_failure",
+ "failure_timeout": "final_failure_timeout",
+ "failure_retries": "final_failure_retries",
+ "failure_exception": "final_failure_exception",
+ "failure_guard": "final_failure_guard"
+ }
+ ]
+ }
+}
diff --git a/controlloop/common/rules-test/src/main/resources/service123/service123.appc.migrate.success.json b/controlloop/common/rules-test/src/main/resources/service123/service123.appc.migrate.success.json
new file mode 100644
index 000000000..563e2c762
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/resources/service123/service123.appc.migrate.success.json
@@ -0,0 +1,22 @@
+{
+ "body": {
+ "output": {
+ "common-header": {
+ "timestamp": "2017-08-25T21:06:23.037Z",
+ "api-ver": "5.00",
+ "originator-id": "664be3d2-6c12-4f4b-a3e7-c349acced200",
+ "request-id": "664be3d2-6c12-4f4b-a3e7-c349acced200",
+ "sub-request-id": "${replaceMe}",
+ "flags": {}
+ },
+ "status": {
+ "code": 400,
+ "message": "Migrate Successful"
+ }
+ }
+ },
+ "version": "2.0",
+ "rpc-name": "migrate",
+ "correlation-id": "664be3d2-6c12-4f4b-a3e7-c349acced200-1",
+ "type": "response"
+}
diff --git a/controlloop/common/rules-test/src/main/resources/service123/service123.appc.rebuild.failure.json b/controlloop/common/rules-test/src/main/resources/service123/service123.appc.rebuild.failure.json
new file mode 100644
index 000000000..88e70ef7a
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/resources/service123/service123.appc.rebuild.failure.json
@@ -0,0 +1,22 @@
+{
+ "body": {
+ "output": {
+ "common-header": {
+ "timestamp": "2017-08-25T21:06:23.037Z",
+ "api-ver": "5.00",
+ "originator-id": "664be3d2-6c12-4f4b-a3e7-c349acced200",
+ "request-id": "664be3d2-6c12-4f4b-a3e7-c349acced200",
+ "sub-request-id": "${replaceMe}",
+ "flags": {}
+ },
+ "status": {
+ "code": 401,
+ "message": "Rebuild Failed"
+ }
+ }
+ },
+ "version": "2.0",
+ "rpc-name": "rebuild",
+ "correlation-id": "664be3d2-6c12-4f4b-a3e7-c349acced200-1",
+ "type": "response"
+}
diff --git a/controlloop/common/rules-test/src/main/resources/service123/service123.appc.restart.failure.json b/controlloop/common/rules-test/src/main/resources/service123/service123.appc.restart.failure.json
new file mode 100644
index 000000000..7376c8ad3
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/resources/service123/service123.appc.restart.failure.json
@@ -0,0 +1,22 @@
+{
+ "body": {
+ "output": {
+ "common-header": {
+ "timestamp": "2017-08-25T21:06:23.037Z",
+ "api-ver": "5.00",
+ "originator-id": "664be3d2-6c12-4f4b-a3e7-c349acced200",
+ "request-id": "664be3d2-6c12-4f4b-a3e7-c349acced200",
+ "sub-request-id": "${replaceMe}",
+ "flags": {}
+ },
+ "status": {
+ "code": 401,
+ "message": "Restart Failed"
+ }
+ }
+ },
+ "version": "2.0",
+ "rpc-name": "restart",
+ "correlation-id": "664be3d2-6c12-4f4b-a3e7-c349acced200-1",
+ "type": "response"
+}
diff --git a/controlloop/common/rules-test/src/main/resources/service123/service123.onset.json b/controlloop/common/rules-test/src/main/resources/service123/service123.onset.json
new file mode 100644
index 000000000..a78cb5337
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/resources/service123/service123.onset.json
@@ -0,0 +1,17 @@
+{
+ "closedLoopControlName": "ControlLoop-Service123-cbed919f-2212-4ef7-8051-fe6308da1bda",
+ "closedLoopAlarmStart": 1463679805324,
+ "closedLoopEventClient": "DCAE_INSTANCE_ID.dcae-tca",
+ "closedLoopEventStatus": "ONSET",
+ "requestID": "664be3d2-6c12-4f4b-a3e7-c349acced200",
+ "target_type": "VNF",
+ "target": "generic-vnf.vnf-name",
+ "AAI": {
+ "vserver.is-closed-loop-disabled": "false",
+ "vserver.prov-status": "ACTIVE",
+ "generic-vnf.vnf-name": "fw0002vm002fw002",
+ "vserver.vserver-name": "OzVServer"
+ },
+ "from": "DCAE",
+ "version": "1.0.2"
+}
diff --git a/controlloop/common/rules-test/src/main/resources/service123/tosca-compliant-service123.json b/controlloop/common/rules-test/src/main/resources/service123/tosca-compliant-service123.json
new file mode 100644
index 000000000..4eaa9e70a
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/resources/service123/tosca-compliant-service123.json
@@ -0,0 +1,75 @@
+{
+ "type": "onap.policies.controlloop.operational.common.Drools",
+ "type_version": "1.0.0",
+ "version": "1.0.0",
+ "name": "operational.service123",
+ "metadata": {
+ "policy-id": "operational.service123"
+ },
+ "properties": {
+ "controllerName": "usecases",
+ "id": "ControlLoop-Service123-cbed919f-2212-4ef7-8051-fe6308da1bda",
+ "timeout": 60,
+ "abatement": true,
+ "trigger": "unique-policy-id-1-restart",
+ "operations": [
+ {
+ "id": "unique-policy-id-1-restart",
+ "description": "Restart the VM",
+ "operation": {
+ "actor": "APPC",
+ "operation": "Restart",
+ "target": {
+ "targetType": "VM"
+ }
+ },
+ "timeout": 20,
+ "retries": 3,
+ "success": "final_success",
+ "failure": "unique-policy-id-2-rebuild",
+ "failure_timeout": "unique-policy-id-2-rebuild",
+ "failure_retries": "unique-policy-id-2-rebuild",
+ "failure_guard": "unique-policy-id-2-rebuild",
+ "failure_exception": "final_failure_exception"
+ },
+ {
+ "id": "unique-policy-id-2-rebuild",
+ "name": "Rebuild Policy",
+ "operation": {
+ "actor": "APPC",
+ "operation": "Rebuild",
+ "target": {
+ "targetType": "VM"
+ }
+ },
+ "timeout": 10,
+ "retries": 0,
+ "success": "final_success",
+ "failure": "unique-policy-id-3-migrate",
+ "failure_timeout": "unique-policy-id-3-migrate",
+ "failure_retries": "unique-policy-id-3-migrate",
+ "failure_guard": "unique-policy-id-3-migrate",
+ "failure_exception": "final_failure_exception"
+ },
+ {
+ "id": "unique-policy-id-3-migrate",
+ "name": "Migrate Policy",
+ "operation": {
+ "actor": "APPC",
+ "operation": "Migrate",
+ "target": {
+ "targetType": "VM"
+ }
+ },
+ "timeout": 30,
+ "retries": 0,
+ "success": "final_success",
+ "failure": "final_failure",
+ "failure_timeout": "final_failure_timeout",
+ "failure_retries": "final_failure_retries",
+ "failure_guard": "final_failure_guard",
+ "failure_exception": "final_failure_exception"
+ }
+ ]
+ }
+}
diff --git a/controlloop/common/rules-test/src/main/resources/vcpe/tosca-compliant-vcpe.json b/controlloop/common/rules-test/src/main/resources/vcpe/tosca-compliant-vcpe.json
new file mode 100644
index 000000000..b70145b68
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/resources/vcpe/tosca-compliant-vcpe.json
@@ -0,0 +1,37 @@
+{
+ "type": "onap.policies.controlloop.operational.common.Drools",
+ "type_version": "1.0.0",
+ "version": "1.0.0",
+ "name": "operational.restart",
+ "metadata": {
+ "policy-id": "operational.restart"
+ },
+ "properties": {
+ "controllerName": "usecases",
+ "id": "ControlLoop-vCPE-48f0c2c3-a172-4192-9ae3-052274181b6e",
+ "timeout": 3600,
+ "abatement": false,
+ "trigger": "unique-policy-id-1-restart",
+ "operations": [
+ {
+ "id": "unique-policy-id-1-restart",
+ "description": "Restart the VM",
+ "operation": {
+ "actor": "APPC",
+ "operation": "Restart",
+ "target": {
+ "targetType": "VM"
+ }
+ },
+ "timeout": 1200,
+ "retries": 3,
+ "success": "final_success",
+ "failure": "final_failure",
+ "failure_timeout": "final_failure_timeout",
+ "failure_retries": "final_failure_retries",
+ "failure_exception": "final_failure_exception",
+ "failure_guard": "final_failure_guard"
+ }
+ ]
+ }
+}
diff --git a/controlloop/common/rules-test/src/main/resources/vcpe/tosca-legacy-vcpe.json b/controlloop/common/rules-test/src/main/resources/vcpe/tosca-legacy-vcpe.json
new file mode 100644
index 000000000..cb0806e16
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/resources/vcpe/tosca-legacy-vcpe.json
@@ -0,0 +1,9 @@
+{
+ "type": "onap.policies.controlloop.Operational",
+ "type_version": "1.0.0",
+ "properties": {
+ "content": "controlLoop%3A%0A%20%20version%3A%202.0.0%0A%20%20controlLoopName%3A%20ControlLoop-vCPE-48f0c2c3-a172-4192-9ae3-052274181b6e%0A%20%20trigger_policy%3A%20unique-policy-id-1-restart%0A%20%20timeout%3A%203600%0A%20%20abatement%3A%20false%0A%20%0Apolicies%3A%0A%20%20-%20id%3A%20unique-policy-id-1-restart%0A%20%20%20%20name%3A%20Restart%20the%20VM%0A%20%20%20%20description%3A%0A%20%20%20%20actor%3A%20APPC%0A%20%20%20%20recipe%3A%20Restart%0A%20%20%20%20target%3A%0A%20%20%20%20%20%20type%3A%20VM%0A%20%20%20%20retry%3A%203%0A%20%20%20%20timeout%3A%201200%0A%20%20%20%20success%3A%20final_success%0A%20%20%20%20failure%3A%20final_failure%0A%20%20%20%20failure_timeout%3A%20final_failure_timeout%0A%20%20%20%20failure_retries%3A%20final_failure_retries%0A%20%20%20%20failure_exception%3A%20final_failure_exception%0A%20%20%20%20failure_guard%3A%20final_failure_guard"
+ },
+ "name": "vcpe",
+ "version": "1.0.0"
+}
diff --git a/controlloop/common/rules-test/src/main/resources/vcpe/vcpe.appc.success.json b/controlloop/common/rules-test/src/main/resources/vcpe/vcpe.appc.success.json
new file mode 100644
index 000000000..331021798
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/resources/vcpe/vcpe.appc.success.json
@@ -0,0 +1,22 @@
+{
+ "body": {
+ "output": {
+ "common-header": {
+ "timestamp": "2017-08-25T21:06:23.037Z",
+ "api-ver": "5.00",
+ "originator-id": "664be3d2-6c12-4f4b-a3e7-c349acced200",
+ "request-id": "664be3d2-6c12-4f4b-a3e7-c349acced200",
+ "sub-request-id": "${replaceMe}",
+ "flags": {}
+ },
+ "status": {
+ "code": 400,
+ "message": "Restart Successful"
+ }
+ }
+ },
+ "version": "2.0",
+ "rpc-name": "restart",
+ "correlation-id": "664be3d2-6c12-4f4b-a3e7-c349acced200-1",
+ "type": "response"
+}
diff --git a/controlloop/common/rules-test/src/main/resources/vcpe/vcpe.onset.1.json b/controlloop/common/rules-test/src/main/resources/vcpe/vcpe.onset.1.json
new file mode 100644
index 000000000..d08ee47cd
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/resources/vcpe/vcpe.onset.1.json
@@ -0,0 +1,16 @@
+{
+ "closedLoopControlName": "ControlLoop-vCPE-48f0c2c3-a172-4192-9ae3-052274181b6e",
+ "closedLoopAlarmStart": 1463679805324,
+ "closedLoopEventClient": "DCAE_INSTANCE_ID.dcae-tca",
+ "closedLoopEventStatus": "ONSET",
+ "requestID": "664be3d2-6c12-4f4b-a3e7-c349acced200",
+ "target_type": "VNF",
+ "target": "generic-vnf.vnf-id",
+ "AAI": {
+ "vserver.is-closed-loop-disabled": "false",
+ "vserver.prov-status": "ACTIVE",
+ "generic-vnf.vnf-id": "vCPE_Infrastructure_vGMUX_demo_app"
+ },
+ "from": "DCAE",
+ "version": "1.0.2"
+} \ No newline at end of file
diff --git a/controlloop/common/rules-test/src/main/resources/vcpe/vcpe.onset.2.json b/controlloop/common/rules-test/src/main/resources/vcpe/vcpe.onset.2.json
new file mode 100644
index 000000000..b8c76514e
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/resources/vcpe/vcpe.onset.2.json
@@ -0,0 +1,16 @@
+{
+ "closedLoopControlName": "ControlLoop-vCPE-48f0c2c3-a172-4192-9ae3-052274181b6e",
+ "closedLoopAlarmStart": 1463679805324,
+ "closedLoopEventClient": "DCAE_INSTANCE_ID.dcae-tca",
+ "closedLoopEventStatus": "ONSET",
+ "requestID": "8cf3cd05-1218-4224-931b-601494ffe55b",
+ "target_type": "VNF",
+ "target": "generic-vnf.vnf-id",
+ "AAI": {
+ "vserver.is-closed-loop-disabled": "false",
+ "vserver.prov-status": "ACTIVE",
+ "generic-vnf.vnf-id": "vCPE_Infrastructure_vGMUX_demo_app"
+ },
+ "from": "DCAE",
+ "version": "1.0.2"
+} \ No newline at end of file
diff --git a/controlloop/common/rules-test/src/main/resources/vcpe/vcpe.onset.3.json b/controlloop/common/rules-test/src/main/resources/vcpe/vcpe.onset.3.json
new file mode 100644
index 000000000..40f29b3cf
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/resources/vcpe/vcpe.onset.3.json
@@ -0,0 +1,17 @@
+{
+ "closedLoopControlName": "ControlLoop-vCPE-48f0c2c3-a172-4192-9ae3-052274181b6e",
+ "closedLoopAlarmStart": 1570722876324905,
+ "closedLoopAlarmEnd": 1570722876324999,
+ "closedLoopEventClient": "DCAE_INSTANCE_ID.dcae-tca",
+ "closedLoopEventStatus": "ONSET",
+ "requestID": "8cf3cd05-1218-4224-931b-601494ffe55b",
+ "target_type": "VNF",
+ "target": "generic-vnf.vnf-id",
+ "AAI": {
+ "vserver.is-closed-loop-disabled": "false",
+ "vserver.prov-status": "ACTIVE",
+ "generic-vnf.vnf-id": "vCPE_Infrastructure_vGMUX_demo_app"
+ },
+ "from": "DCAE",
+ "version": "1.0.2"
+} \ No newline at end of file
diff --git a/controlloop/common/rules-test/src/main/resources/vdns/tosca-compliant-vdns.json b/controlloop/common/rules-test/src/main/resources/vdns/tosca-compliant-vdns.json
new file mode 100644
index 000000000..918e92770
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/resources/vdns/tosca-compliant-vdns.json
@@ -0,0 +1,48 @@
+{
+ "type": "onap.policies.controlloop.operational.common.Drools",
+ "type_version": "1.0.0",
+ "version": "1.0.0",
+ "name": "operational.scale.up",
+ "metadata": {
+ "policy-id": "operational.scale.up"
+ },
+ "properties": {
+ "controllerName": "usecases",
+ "id": "ControlLoop-vDNS-6f37f56d-a87d-4b85-b6a9-cc953cf779b3",
+ "timeout": 60,
+ "abatement": false,
+ "trigger": "unique-policy-id-1-scale-up",
+ "operations": [
+ {
+ "id": "unique-policy-id-1-scale-up",
+ "description": "Scale up",
+ "operation": {
+ "actor": "SO",
+ "operation": "VF Module Create",
+ "target": {
+ "targetType": "VFMODULE",
+ "entityIds": {
+ "modelInvariantId": "e6130d03-56f1-4b0a-9a1d-e1b2ebc30e0e",
+ "modelVersionId": "94b18b1d-cc91-4f43-911a-e6348665f292",
+ "modelName": "VfwclVfwsnkBbefb8ce2bde..base_vfw..module-0",
+ "modelVersion": 1,
+ "modelCustomizationId": "47958575-138f-452a-8c8d-d89b595f8164"
+ }
+ },
+ "payload": {
+ "requestParameters": "{\"usePreload\":true,\"userParams\":[]}",
+ "configurationParameters": "[{\"ip-addr\":\"$.vf-module-topology.vf-module-parameters.param[9]\",\"oam-ip-addr\":\"$.vf-module-topology.vf-module-parameters.param[16]\",\"enabled\":\"$.vf-module-topology.vf-module-parameters.param[23]\"}]"
+ }
+ },
+ "timeout": 30,
+ "retries": 0,
+ "success": "final_success",
+ "failure": "final_failure",
+ "failure_timeout": "final_failure_timeout",
+ "failure_retries": "final_failure_retries",
+ "failure_exception": "final_failure_exception",
+ "failure_guard": "final_failure_guard"
+ }
+ ]
+ }
+}
diff --git a/controlloop/common/rules-test/src/main/resources/vdns/vdns.onset.json b/controlloop/common/rules-test/src/main/resources/vdns/vdns.onset.json
new file mode 100644
index 000000000..13f690952
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/resources/vdns/vdns.onset.json
@@ -0,0 +1,16 @@
+{
+ "closedLoopControlName": "ControlLoop-vDNS-6f37f56d-a87d-4b85-b6a9-cc953cf779b3",
+ "closedLoopAlarmStart": 1463679805324,
+ "closedLoopEventClient": "microservice.stringmatcher",
+ "closedLoopEventStatus": "ONSET",
+ "requestID": "c7c6a4aa-bb61-4a15-b831-ba1472dd4a65",
+ "target_type": "VNF",
+ "target": "vserver.vserver-name",
+ "AAI": {
+ "vserver.is-closed-loop-disabled": "false",
+ "vserver.prov-status": "ACTIVE",
+ "vserver.vserver-name": "OzVServer"
+ },
+ "from": "DCAE",
+ "version": "1.0.2"
+}
diff --git a/controlloop/common/rules-test/src/main/resources/vfw/tosca-compliant-vfw.json b/controlloop/common/rules-test/src/main/resources/vfw/tosca-compliant-vfw.json
new file mode 100644
index 000000000..f55145658
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/resources/vfw/tosca-compliant-vfw.json
@@ -0,0 +1,40 @@
+{
+ "type": "onap.policies.controlloop.operational.common.Drools",
+ "type_version": "1.0.0",
+ "name": "operational.modifyconfig",
+ "version": "1.0.0",
+ "metadata": {
+ "policy-id": "operational.modifyconfig"
+ },
+ "properties": {
+ "controllerName": "usecases",
+ "id": "ControlLoop-vFirewall-d0a1dfc6-94f5-4fd4-a5b5-4630b438850a",
+ "timeout": 60,
+ "abatement": false,
+ "trigger": "unique-policy-id-1-modifyConfig",
+ "operations": [
+ {
+ "id": "unique-policy-id-1-modifyConfig",
+ "description": "Modify the packet generator",
+ "operation": {
+ "actor": "APPC",
+ "operation": "ModifyConfig",
+ "target": {
+ "targetType": "VNF",
+ "entityIds": {
+ "resourceID": "bbb3cefd-01c8-413c-9bdd-2b92f9ca3d38"
+ }
+ }
+ },
+ "timeout": 300,
+ "retries": 0,
+ "success": "final_success",
+ "failure": "final_failure",
+ "failure_timeout": "final_failure_timeout",
+ "failure_retries": "final_failure_retries",
+ "failure_exception": "final_failure_exception",
+ "failure_guard": "final_failure_guard"
+ }
+ ]
+ }
+}
diff --git a/controlloop/common/rules-test/src/main/resources/vfw/tosca-vfw.json b/controlloop/common/rules-test/src/main/resources/vfw/tosca-vfw.json
new file mode 100644
index 000000000..35a839698
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/resources/vfw/tosca-vfw.json
@@ -0,0 +1,9 @@
+{
+ "type": "onap.policies.controlloop.Operational",
+ "type_version": "1.0.0",
+ "properties": {
+ "content": "controlLoop%3A%0A%20%20version%3A%202.0.0%0A%20%20controlLoopName%3A%20ControlLoop-vFirewall-d0a1dfc6-94f5-4fd4-a5b5-4630b438850a%0A%20%20services%3A%0A%20%20%20%20-%20serviceInvariantUUID%3A%20f6937c86-584c-47ae-ad29-8d41d6f0cc7c%0A%20%20%20%20%20%20serviceUUID%3A%207be584e2-0bb2-4126-adaf-ced2c77ca0b3%0A%20%20%20%20%20%20serviceName%3A%20Service_Ete_Name7ba1fbde-6187-464a-a62d-d9dd25bdf4e8%0A%20%20trigger_policy%3A%20unique-policy-id-1-modifyConfig%0A%20%20timeout%3A%2060%0A%20%20abatement%3A%20false%0A%20%0Apolicies%3A%0A%20%20-%20id%3A%20unique-policy-id-1-modifyConfig%0A%20%20%20%20name%3A%20modify%20packet%20gen%20config%0A%20%20%20%20description%3A%0A%20%20%20%20actor%3A%20APPC%0A%20%20%20%20recipe%3A%20ModifyConfig%0A%20%20%20%20target%3A%0A%20%20%20%20%20%20resourceID%3A%20bbb3cefd-01c8-413c-9bdd-2b92f9ca3d38%0A%20%20%20%20%20%20type%3A%20VNF%0A%20%20%20%20retry%3A%200%0A%20%20%20%20timeout%3A%2030%0A%20%20%20%20success%3A%20final_success%0A%20%20%20%20failure%3A%20final_failure%0A%20%20%20%20failure_timeout%3A%20final_failure_timeout%0A%20%20%20%20failure_retries%3A%20final_failure_retries%0A%20%20%20%20failure_exception%3A%20final_failure_exception%0A%20%20%20%20failure_guard%3A%20final_failure_guard"
+ },
+ "name": "vfw",
+ "version": "1.0.0"
+}
diff --git a/controlloop/common/rules-test/src/main/resources/vfw/vfw.appc.success.json b/controlloop/common/rules-test/src/main/resources/vfw/vfw.appc.success.json
new file mode 100644
index 000000000..eb19c6832
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/resources/vfw/vfw.appc.success.json
@@ -0,0 +1,17 @@
+{
+ "CommonHeader": {
+ "TimeStamp": 1506051879001,
+ "APIver": "1.01",
+ "RequestID": "c7c6a4aa-bb61-4a15-b831-ba1472dd4a65",
+ "SubRequestID": "${replaceMe}",
+ "RequestTrack": [],
+ "Flags": []
+ },
+ "Status": {
+ "Code": 400,
+ "Value": "SUCCESS"
+ },
+ "Payload": {
+ "generic-vnf.vnf-id": "jimmy-test-vnf2"
+ }
+}
diff --git a/controlloop/common/rules-test/src/main/resources/vfw/vfw.onset.json b/controlloop/common/rules-test/src/main/resources/vfw/vfw.onset.json
new file mode 100644
index 000000000..7782867a1
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/resources/vfw/vfw.onset.json
@@ -0,0 +1,17 @@
+{
+ "closedLoopControlName": "ControlLoop-vFirewall-d0a1dfc6-94f5-4fd4-a5b5-4630b438850a",
+ "closedLoopAlarmStart": 1463679805324,
+ "closedLoopEventClient": "microservice.stringmatcher",
+ "closedLoopEventStatus": "ONSET",
+ "requestID": "c7c6a4aa-bb61-4a15-b831-ba1472dd4a65",
+ "target_type": "VNF",
+ "target": "generic-vnf.vnf-name",
+ "AAI": {
+ "vserver.is-closed-loop-disabled": "false",
+ "vserver.prov-status": "ACTIVE",
+ "generic-vnf.vnf-name": "fw0002vm002fw002",
+ "vserver.vserver-name": "OzVServer"
+ },
+ "from": "DCAE",
+ "version": "1.0.2"
+}
diff --git a/controlloop/common/rules-test/src/main/resources/vlb/tosca-compliant-vlb.json b/controlloop/common/rules-test/src/main/resources/vlb/tosca-compliant-vlb.json
new file mode 100644
index 000000000..8a3b64a0a
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/resources/vlb/tosca-compliant-vlb.json
@@ -0,0 +1,48 @@
+{
+ "type": "onap.policies.controlloop.operational.common.Drools",
+ "type_version": "1.0.0",
+ "name": "operational.scaleout",
+ "version": "1.0.0",
+ "metadata": {
+ "policy-id": "operational.scaleout"
+ },
+ "properties": {
+ "controllerName": "usecases",
+ "id": "ControlLoop-vDNS-6f37f56d-a87d-4b85-b6a9-cc953cf779b3",
+ "timeout": 1200,
+ "abatement": false,
+ "trigger": "unique-policy-id-1-scale-up",
+ "operations": [
+ {
+ "id": "unique-policy-id-1-scale-up",
+ "description": "Create a new VF Module",
+ "operation": {
+ "actor": "SO",
+ "operation": "VF Module Create",
+ "target": {
+ "targetType": "VFMODULE",
+ "entityIds": {
+ "modelInvariantId": "e6130d03-56f1-4b0a-9a1d-e1b2ebc30e0e",
+ "modelVersionId": "94b18b1d-cc91-4f43-911a-e6348665f292",
+ "modelName": "VfwclVfwsnkBbefb8ce2bde..base_vfw..module-0",
+ "modelVersion": 1,
+ "modelCustomizationId": "47958575-138f-452a-8c8d-d89b595f8164"
+ }
+ },
+ "payload": {
+ "requestParameters": "{\"usePreload\":true,\"userParams\":[]}",
+ "configurationParameters": "[{\"ip-addr\":\"$.vf-module-topology.vf-module-parameters.param[9]\",\"oam-ip-addr\":\"$.vf-module-topology.vf-module-parameters.param[16]\",\"enabled\":\"$.vf-module-topology.vf-module-parameters.param[23]\"}]"
+ }
+ },
+ "timeout": 1200,
+ "retries": 0,
+ "success": "final_success",
+ "failure": "final_failure",
+ "failure_timeout": "final_failure_timeout",
+ "failure_retries": "final_failure_retries",
+ "failure_exception": "final_failure_exception",
+ "failure_guard": "final_failure_guard"
+ }
+ ]
+ }
+}
diff --git a/controlloop/common/rules-test/src/main/resources/vlb/tosca-vlb.json b/controlloop/common/rules-test/src/main/resources/vlb/tosca-vlb.json
new file mode 100644
index 000000000..c4f2b8179
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/resources/vlb/tosca-vlb.json
@@ -0,0 +1,9 @@
+{
+ "type": "onap.policies.controlloop.Operational",
+ "type_version": "1.0.0",
+ "properties": {
+ "content": "controlLoop%3A%0A%20%20version%3A%202.0.0%0A%20%20controlLoopName%3A%20ControlLoop-vDNS-6f37f56d-a87d-4b85-b6a9-cc953cf779b3%0A%20%20services%3A%0A%20%20%20%20-%20serviceName%3A%20d4738992-6497-4dca-9db9%0A%20%20%20%20%20%20serviceInvariantUUID%3A%20dc112d6e-7e73-4777-9c6f-1a7fb5fd1b6f%0A%20%20%20%20%20%20serviceUUID%3A%202eea06c6-e1d3-4c3a-b9c4-478c506eeedf%0A%20%20trigger_policy%3A%20unique-policy-id-1-scale-up%0A%20%20timeout%3A%2060%0A%0Apolicies%3A%0A%20%20-%20id%3A%20unique-policy-id-1-scale-up%0A%20%20%20%20name%3A%20Create%20a%20new%20VF%20Module%0A%20%20%20%20description%3A%0A%20%20%20%20actor%3A%20SO%0A%20%20%20%20recipe%3A%20VF%20Module%20Create%0A%20%20%20%20target%3A%0A%20%20%20%20%20%20type%3A%20VFMODULE%0A%20%20%20%20%20%20modelInvariantId%3A%20e6130d03-56f1-4b0a-9a1d-e1b2ebc30e0e%0A%20%20%20%20%20%20modelVersionId%3A%2094b18b1d-cc91-4f43-911a-e6348665f292%0A%20%20%20%20%20%20modelName%3A%20VfwclVfwsnkBbefb8ce2bde..base_vfw..module-0%0A%20%20%20%20%20%20modelVersion%3A%201%0A%20%20%20%20%20%20modelCustomizationId%3A%2047958575-138f-452a-8c8d-d89b595f8164%0A%20%20%20%20payload%3A%0A%20%20%20%20%20%20requestParameters%3A%20%27%7B%22usePreload%22%3Atrue%2C%22userParams%22%3A%5B%5D%7D%27%0A%20%20%20%20%20%20configurationParameters%3A%20%27%5B%7B%22ip-addr%22%3A%22%24.vf-module-topology.vf-module-parameters.param%5B9%5D%22%2C%22oam-ip-addr%22%3A%22%24.vf-module-topology.vf-module-parameters.param%5B16%5D%22%2C%22enabled%22%3A%22%24.vf-module-topology.vf-module-parameters.param%5B23%5D%22%7D%5D%27%0A%20%20%20%20retry%3A%200%0A%20%20%20%20timeout%3A%2030%0A%20%20%20%20success%3A%20final_success%0A%20%20%20%20failure%3A%20final_failure%0A%20%20%20%20failure_timeout%3A%20final_failure_timeout%0A%20%20%20%20failure_retries%3A%20final_failure_retries%0A%20%20%20%20failure_exception%3A%20final_failure_exception%0A%20%20%20%20failure_guard%3A%20final_failure_guard%0A"
+ },
+ "name": "vlb",
+ "version": "1.0.0"
+}
diff --git a/controlloop/common/rules-test/src/main/resources/vlb/vlb.onset.json b/controlloop/common/rules-test/src/main/resources/vlb/vlb.onset.json
new file mode 100644
index 000000000..23ad03cbb
--- /dev/null
+++ b/controlloop/common/rules-test/src/main/resources/vlb/vlb.onset.json
@@ -0,0 +1,16 @@
+{
+ "closedLoopControlName": "ControlLoop-vDNS-6f37f56d-a87d-4b85-b6a9-cc953cf779b3",
+ "closedLoopAlarmStart": 1484677482204798,
+ "closedLoopEventClient": "DCAE_INSTANCE_ID.dcae-tca",
+ "closedLoopEventStatus": "ONSET",
+ "requestID": "e4f95e0c-a013-4530-8e59-c5c5f9e539b6",
+ "target_type": "VNF",
+ "target": "vserver.vserver-name",
+ "AAI": {
+ "vserver.is-closed-loop-disabled": "false",
+ "vserver.prov-status": "ACTIVE",
+ "vserver.vserver-name": "OzVServer"
+ },
+ "from": "DCAE",
+ "version": "1.0.2"
+}
diff --git a/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/BaseRuleTestTest.java b/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/BaseRuleTestTest.java
new file mode 100644
index 000000000..745013b3b
--- /dev/null
+++ b/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/BaseRuleTestTest.java
@@ -0,0 +1,459 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.common.rules.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Queue;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.policy.appc.CommonHeader;
+import org.onap.policy.appc.Request;
+import org.onap.policy.appclcm.AppcLcmBody;
+import org.onap.policy.appclcm.AppcLcmCommonHeader;
+import org.onap.policy.appclcm.AppcLcmDmaapWrapper;
+import org.onap.policy.appclcm.AppcLcmInput;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.common.utils.coder.StandardCoderInstantAsMillis;
+import org.onap.policy.controlloop.ControlLoopNotificationType;
+import org.onap.policy.controlloop.VirtualControlLoopNotification;
+import org.onap.policy.drools.controller.DroolsController;
+import org.onap.policy.drools.system.PolicyController;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifier;
+import org.powermock.reflect.Whitebox;
+
+public class BaseRuleTestTest {
+ private static final String CONTROLLER_NAME = "my-controller-name";
+ private static final String POLICY_NAME = "my-policy-name";
+
+ // saved values
+ private static Function<String, Rules> ruleMaker;
+ private static Supplier<HttpClients> httpClientMaker;
+ private static Supplier<Simulators> simMaker;
+ private static Supplier<Topics> topicMaker;
+
+ private BaseRuleTest base;
+ private LinkedList<VirtualControlLoopNotification> clMgtQueue;
+ private Queue<AppcLcmDmaapWrapper> appcLcmQueue;
+ private Queue<Request> appcLegacyQueue;
+ private int permitCount;
+ private int finalCount;
+
+ @Mock
+ private PolicyController controller;
+ @Mock
+ private Rules rules;
+ @Mock
+ private HttpClients httpClients;
+ @Mock
+ private Simulators simulators;
+ @Mock
+ private Topics topics;
+ @Mock
+ private Listener<VirtualControlLoopNotification> policyClMgt;
+ @Mock
+ private Listener<Request> appcClSink;
+ @Mock
+ private Listener<AppcLcmDmaapWrapper> appcLcmRead;
+ @Mock
+ private DroolsController drools;
+ @Mock
+ private ToscaPolicy policy;
+ @Mock
+ private ToscaPolicyIdentifier policyIdent;
+
+
+ /**
+ * Saves static values from the class.
+ */
+ @BeforeClass
+ public static void setUpBeforeClass() {
+ ruleMaker = Whitebox.getInternalState(BaseRuleTest.class, "ruleMaker");
+ httpClientMaker = Whitebox.getInternalState(BaseRuleTest.class, "httpClientMaker");
+ simMaker = Whitebox.getInternalState(BaseRuleTest.class, "simMaker");
+ topicMaker = Whitebox.getInternalState(BaseRuleTest.class, "topicMaker");
+ }
+
+ /**
+ * Restores static values.
+ */
+ @AfterClass
+ public static void tearDownAfterClass() {
+ Whitebox.setInternalState(BaseRuleTest.class, "ruleMaker", ruleMaker);
+ Whitebox.setInternalState(BaseRuleTest.class, "httpClientMaker", httpClientMaker);
+ Whitebox.setInternalState(BaseRuleTest.class, "simMaker", simMaker);
+ Whitebox.setInternalState(BaseRuleTest.class, "topicMaker", topicMaker);
+ }
+
+ /**
+ * Sets up.
+ */
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(policy.getIdentifier()).thenReturn(policyIdent);
+ when(policyIdent.getName()).thenReturn(POLICY_NAME);
+
+ when(drools.factCount(CONTROLLER_NAME)).thenReturn(0L);
+ when(controller.getDrools()).thenReturn(drools);
+
+ when(rules.getControllerName()).thenReturn(CONTROLLER_NAME);
+ when(rules.getController()).thenReturn(controller);
+ when(rules.setupPolicyFromFile(any())).thenAnswer(args -> {
+ when(drools.factCount(CONTROLLER_NAME)).thenReturn(2L);
+ return policy;
+ });
+
+ when(topics.createListener(BaseRuleTest.POLICY_CL_MGT_TOPIC, VirtualControlLoopNotification.class, controller))
+ .thenReturn(policyClMgt);
+ when(topics.createListener(eq(BaseRuleTest.APPC_LCM_READ_TOPIC), eq(AppcLcmDmaapWrapper.class),
+ any(StandardCoder.class))).thenReturn(appcLcmRead);
+ when(topics.createListener(eq(BaseRuleTest.APPC_CL_TOPIC), eq(Request.class),
+ any(StandardCoderInstantAsMillis.class))).thenReturn(appcClSink);
+
+ Function<String, Rules> ruleMaker = this::makeRules;
+ Supplier<HttpClients> httpClientMaker = this::makeHttpClients;
+ Supplier<Simulators> simMaker = this::makeSim;
+ Supplier<Topics> topicMaker = this::makeTopics;
+
+ Whitebox.setInternalState(BaseRuleTest.class, "ruleMaker", ruleMaker);
+ Whitebox.setInternalState(BaseRuleTest.class, "httpClientMaker", httpClientMaker);
+ Whitebox.setInternalState(BaseRuleTest.class, "simMaker", simMaker);
+ Whitebox.setInternalState(BaseRuleTest.class, "topicMaker", topicMaker);
+
+ clMgtQueue = new LinkedList<>();
+ appcLcmQueue = new LinkedList<>();
+ appcLegacyQueue = new LinkedList<>();
+
+ when(policyClMgt.await(any())).thenAnswer(args -> {
+ VirtualControlLoopNotification notif = clMgtQueue.remove();
+ Predicate<VirtualControlLoopNotification> pred = args.getArgument(0);
+ assertTrue(pred.test(notif));
+ return notif;
+ });
+
+ when(appcLcmRead.await(any())).thenAnswer(args -> {
+ AppcLcmDmaapWrapper req = appcLcmQueue.remove();
+ Predicate<AppcLcmDmaapWrapper> pred = args.getArgument(0);
+ assertTrue(pred.test(req));
+ return req;
+ });
+
+ when(appcClSink.await(any())).thenAnswer(args -> {
+ Request req = appcLegacyQueue.remove();
+ Predicate<Request> pred = args.getArgument(0);
+ assertTrue(pred.test(req));
+ return req;
+ });
+
+ permitCount = 0;
+ finalCount = 0;
+
+ base = new MyTest();
+
+ BaseRuleTest.initStatics(CONTROLLER_NAME);
+ base.init();
+ }
+
+ @Test
+ public void testInitStatics() {
+ assertSame(rules, BaseRuleTest.rules);
+ assertSame(httpClients, BaseRuleTest.httpClients);
+ assertSame(simulators, BaseRuleTest.simulators);
+ }
+
+ @Test
+ public void testFinishStatics() {
+ BaseRuleTest.finishStatics();
+
+ verify(rules).destroy();
+ verify(httpClients).destroy();
+ verify(simulators).destroy();
+ }
+
+ @Test
+ public void testInit() {
+ assertSame(topics, base.getTopics());
+ assertSame(controller, base.controller);
+ }
+
+ @Test
+ public void testFinish() {
+ base.finish();
+
+ verify(topics).destroy();
+ verify(rules).resetFacts();
+ }
+
+ @Test
+ public void testTestService123Compliant() {
+ enqueueAppcLcm("restart", "restart", "restart", "restart", "rebuild", "migrate");
+ enqueueClMgt(ControlLoopNotificationType.OPERATION_SUCCESS);
+ enqueueClMgt(ControlLoopNotificationType.FINAL_SUCCESS);
+
+ base.testService123Compliant();
+
+ assertEquals(1, permitCount);
+ assertEquals(1, finalCount);
+
+ assertTrue(appcLcmQueue.isEmpty());
+ assertTrue(clMgtQueue.isEmpty());
+
+ // initial event
+ verify(topics).inject(eq(BaseRuleTest.DCAE_TOPIC), any());
+
+ // replies to each APPC request
+ verify(topics, times(6)).inject(eq(BaseRuleTest.APPC_LCM_WRITE_TOPIC), any(), any());
+ }
+
+ @Test
+ public void testTestDuplicatesEvents() {
+ enqueueAppcLcm("restart", "restart");
+ enqueueClMgt(ControlLoopNotificationType.FINAL_FAILURE);
+ enqueueClMgt(ControlLoopNotificationType.FINAL_SUCCESS);
+ enqueueClMgt(ControlLoopNotificationType.FINAL_SUCCESS);
+
+ clMgtQueue.get(1).setAai(Map.of("generic-vnf.vnf-id", "duplicate-VNF"));
+ clMgtQueue.get(2).setAai(Map.of("generic-vnf.vnf-id", "vCPE_Infrastructure_vGMUX_demo_app"));
+
+ base.testDuplicatesEvents();
+
+ assertEquals(0, permitCount);
+ assertEquals(3, finalCount);
+
+ assertTrue(appcLcmQueue.isEmpty());
+ assertTrue(clMgtQueue.isEmpty());
+
+ // initial events
+ verify(topics).inject(eq(BaseRuleTest.DCAE_TOPIC), any());
+ verify(topics, times(2)).inject(eq(BaseRuleTest.DCAE_TOPIC), any(), any());
+
+ // two restarts
+ verify(topics, times(2)).inject(eq(BaseRuleTest.APPC_LCM_WRITE_TOPIC), any(), any());
+ }
+
+ @Test
+ public void testTestVcpeSunnyDayLegacy() {
+ checkAppcLcmPolicy("restart", base::testVcpeSunnyDayLegacy);
+ }
+
+ @Test
+ public void testTestVcpeSunnyDayCompliant() {
+ checkAppcLcmPolicy("restart", base::testVcpeSunnyDayCompliant);
+ }
+
+ @Test
+ public void testTestVcpeOnsetFloodPrevention() {
+ enqueueAppcLcm("restart");
+ enqueueClMgt(ControlLoopNotificationType.OPERATION_SUCCESS);
+ enqueueClMgt(ControlLoopNotificationType.FINAL_SUCCESS);
+
+ base.testVcpeOnsetFloodPrevention();
+
+ assertEquals(1, permitCount);
+ assertEquals(1, finalCount);
+
+ assertTrue(appcLcmQueue.isEmpty());
+ assertTrue(clMgtQueue.isEmpty());
+
+ // initial events
+ verify(topics, times(3)).inject(eq(BaseRuleTest.DCAE_TOPIC), any());
+
+ // one restart
+ verify(topics).inject(eq(BaseRuleTest.APPC_LCM_WRITE_TOPIC), any(), any());
+ }
+
+ @Test
+ public void testTestVdnsSunnyDayCompliant() {
+ checkHttpPolicy(base::testVdnsSunnyDayCompliant);
+ }
+
+ @Test
+ public void testTestVfwSunnyDayLegacy() {
+ checkAppcLegacyPolicy("ModifyConfig", base::testVfwSunnyDayLegacy);
+ }
+
+ @Test
+ public void testTestVfwSunnyDayCompliant() {
+ checkAppcLegacyPolicy("ModifyConfig", base::testVfwSunnyDayCompliant);
+ }
+
+ @Test
+ public void testTestVlbSunnyDayLegacy() {
+ checkHttpPolicy(base::testVlbSunnyDayLegacy);
+ }
+
+ @Test
+ public void testTestVlbSunnyDayCompliant() {
+ checkHttpPolicy(base::testVlbSunnyDayCompliant);
+ }
+
+ protected void checkAppcLcmPolicy(String operation, Runnable test) {
+ enqueueAppcLcm(operation);
+ enqueueClMgt(ControlLoopNotificationType.OPERATION_SUCCESS);
+ enqueueClMgt(ControlLoopNotificationType.FINAL_SUCCESS);
+
+ test.run();
+
+ assertEquals(1, permitCount);
+ assertEquals(1, finalCount);
+
+ assertTrue(appcLcmQueue.isEmpty());
+ assertTrue(clMgtQueue.isEmpty());
+
+ // initial event
+ verify(topics).inject(eq(BaseRuleTest.DCAE_TOPIC), any());
+
+ // reply to each APPC request
+ verify(topics).inject(eq(BaseRuleTest.APPC_LCM_WRITE_TOPIC), any(), any());
+ }
+
+ protected void checkAppcLegacyPolicy(String operation, Runnable test) {
+ enqueueAppcLegacy(operation);
+ enqueueClMgt(ControlLoopNotificationType.OPERATION_SUCCESS);
+ enqueueClMgt(ControlLoopNotificationType.FINAL_SUCCESS);
+
+ test.run();
+
+ assertEquals(1, permitCount);
+ assertEquals(1, finalCount);
+
+ assertTrue(appcLcmQueue.isEmpty());
+ assertTrue(clMgtQueue.isEmpty());
+
+ // initial event
+ verify(topics).inject(eq(BaseRuleTest.DCAE_TOPIC), any());
+
+ // reply to each APPC request
+ verify(topics).inject(eq(BaseRuleTest.APPC_CL_TOPIC), any(), any());
+ }
+
+ protected void checkHttpPolicy(Runnable test) {
+ enqueueClMgt(ControlLoopNotificationType.OPERATION_SUCCESS);
+ enqueueClMgt(ControlLoopNotificationType.FINAL_SUCCESS);
+
+ test.run();
+
+ assertEquals(1, permitCount);
+ assertEquals(1, finalCount);
+
+ assertTrue(clMgtQueue.isEmpty());
+
+ // initial event
+ verify(topics).inject(eq(BaseRuleTest.DCAE_TOPIC), any());
+ }
+
+ private void enqueueClMgt(ControlLoopNotificationType type) {
+ VirtualControlLoopNotification notif = new VirtualControlLoopNotification();
+ notif.setNotification(type);
+ notif.setPolicyName(POLICY_NAME + ".EVENT.MANAGER.FINAL");
+
+ clMgtQueue.add(notif);
+ }
+
+ private void enqueueAppcLcm(String... operationNames) {
+ for (String oper : operationNames) {
+ AppcLcmDmaapWrapper req = new AppcLcmDmaapWrapper();
+ req.setRpcName(oper);
+
+ AppcLcmBody body = new AppcLcmBody();
+ req.setBody(body);
+
+ AppcLcmInput input = new AppcLcmInput();
+ body.setInput(input);
+
+ AppcLcmCommonHeader header = new AppcLcmCommonHeader();
+ input.setCommonHeader(header);
+
+ header.setSubRequestId("my-subrequest-id");
+
+ appcLcmQueue.add(req);
+ }
+ }
+
+ private void enqueueAppcLegacy(String... operationNames) {
+ for (String oper : operationNames) {
+ Request req = new Request();
+ req.setAction(oper);
+
+ CommonHeader header = new CommonHeader();
+ req.setCommonHeader(header);
+
+ header.setSubRequestId("my-subrequest-id");
+
+ appcLegacyQueue.add(req);
+ }
+ }
+
+ private Rules makeRules(String controllerName) {
+ return rules;
+ }
+
+ private HttpClients makeHttpClients() {
+ return httpClients;
+ }
+
+ private Simulators makeSim() {
+ return simulators;
+ }
+
+ private Topics makeTopics() {
+ return topics;
+ }
+
+ /*
+ * We don't want junit trying to run this, so it's marked "Ignore".
+ */
+ @Ignore
+ private class MyTest extends BaseRuleTest {
+
+ @Override
+ protected void waitForLockAndPermit(ToscaPolicy policy, Listener<VirtualControlLoopNotification> policyClMgt) {
+ permitCount++;
+ }
+
+ @Override
+ protected VirtualControlLoopNotification waitForFinal(ToscaPolicy policy,
+ Listener<VirtualControlLoopNotification> policyClMgt, ControlLoopNotificationType finalType) {
+ finalCount++;
+ return policyClMgt.await(notif -> notif.getNotification() == finalType);
+ }
+ }
+}
diff --git a/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/ExceptionsTest.java b/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/ExceptionsTest.java
new file mode 100644
index 000000000..a42ff6f5e
--- /dev/null
+++ b/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/ExceptionsTest.java
@@ -0,0 +1,38 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.common.rules.test;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.onap.policy.common.utils.test.ExceptionsTester;
+import org.onap.policy.controlloop.common.rules.test.SimulatorException;
+import org.onap.policy.controlloop.common.rules.test.TopicException;
+
+public class ExceptionsTest {
+
+ @Test
+ public void test() {
+ assertEquals(4, new ExceptionsTester().testAllException(TopicException.class));
+
+ assertEquals(4, new ExceptionsTester().testAllException(SimulatorException.class));
+ }
+}
diff --git a/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/HttpClientsTest.java b/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/HttpClientsTest.java
new file mode 100644
index 000000000..5db835343
--- /dev/null
+++ b/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/HttpClientsTest.java
@@ -0,0 +1,94 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.common.rules.test;
+
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+import java.util.Properties;
+import org.junit.After;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.policy.common.endpoints.http.client.HttpClient;
+import org.onap.policy.common.endpoints.http.client.HttpClientConfigException;
+import org.onap.policy.common.endpoints.http.client.HttpClientFactory;
+import org.onap.policy.common.endpoints.http.client.HttpClientFactoryInstance;
+import org.onap.policy.controlloop.common.rules.test.HttpClients;
+import org.onap.policy.drools.persistence.SystemPersistenceConstants;
+
+public class HttpClientsTest {
+ private static final String CLIENT_NAME = "MY-CLIENT";
+
+ @Mock
+ private HttpClientFactory factory;
+
+
+ @BeforeClass
+ public static void setUpBeforeClass() {
+ SystemPersistenceConstants.getManager().setConfigurationDir("src/test/resources");
+ }
+
+ @After
+ public void tearDown() {
+ HttpClientFactoryInstance.getClientFactory().destroy();
+ }
+
+ @Test
+ public void test() throws HttpClientConfigException {
+ MockitoAnnotations.initMocks(this);
+
+ HttpClientFactoryInstance.getClientFactory().destroy();
+
+ HttpClients clients = new HttpClients();
+
+ clients.addClients("my");
+
+ // should find the client now
+ HttpClient client = HttpClientFactoryInstance.getClientFactory().get(CLIENT_NAME);
+ assertNotNull(client);
+
+ clients.destroy();
+
+ // destroyed - should NOT find the client
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> HttpClientFactoryInstance.getClientFactory().get(CLIENT_NAME));
+
+ // unknown property file
+ assertThatIllegalArgumentException().isThrownBy(() -> clients.addClients("unknown"));
+
+ // force exception from builder
+ HttpClients clients2 = new HttpClients() {
+ @Override
+ protected HttpClientFactory getClientFactory() {
+ return factory;
+ }
+ };
+
+ when(factory.build(any(Properties.class))).thenThrow(new HttpClientConfigException("expected exception"));
+
+ assertThatIllegalArgumentException().isThrownBy(() -> clients2.addClients("my"))
+ .withMessage("cannot initialize HTTP clients");
+ }
+}
diff --git a/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/ListenerTest.java b/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/ListenerTest.java
new file mode 100644
index 000000000..07ccc405b
--- /dev/null
+++ b/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/ListenerTest.java
@@ -0,0 +1,202 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.common.rules.test;
+
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+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.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+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.TopicEndpointManager;
+import org.onap.policy.common.endpoints.event.comm.bus.NoopTopicSink;
+import org.onap.policy.common.endpoints.parameters.TopicParameters;
+import org.onap.policy.controlloop.common.rules.test.Listener;
+import org.onap.policy.controlloop.common.rules.test.TopicException;
+
+public class ListenerTest {
+ private static final String EXPECTED_EXCEPTION = "expected exception";
+ private static final String MY_TOPIC = "my-topic";
+ private static final String MESSAGE = "the-message";
+ private static final String MESSAGE2 = "other-message";
+ private static final String MSG_SUFFIX = "s";
+ private static final String DECODED_MESSAGE = MESSAGE + MSG_SUFFIX;
+
+ @Mock
+ private NoopTopicSink sink;
+ @Mock
+ private TopicEndpoint mgr;
+
+ private Listener<String> listener;
+
+ /**
+ * Creates topics.
+ */
+ @BeforeClass
+ public static void setUpBeforeClass() {
+ TopicEndpointManager.getManager().shutdown();
+
+ TopicParameters params = new TopicParameters();
+ params.setTopic(MY_TOPIC);
+ params.setManaged(true);
+ params.setTopicCommInfrastructure("NOOP");
+
+ TopicEndpointManager.getManager().addTopicSinks(List.of(params));
+ }
+
+ @AfterClass
+ public static void tearDownAfterClass() {
+ TopicEndpointManager.getManager().shutdown();
+ }
+
+ /**
+ * Sets up.
+ */
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mgr.getNoopTopicSink(MY_TOPIC)).thenReturn(sink);
+
+ listener = new Listener<>(MY_TOPIC, msg -> msg + MSG_SUFFIX) {
+ @Override
+ protected TopicEndpoint getTopicManager() {
+ return mgr;
+ }
+ };
+ }
+
+ @Test
+ public void testListener() {
+ verify(sink).register(listener);
+ }
+
+ @Test
+ public void testAwait_testAwaitLongTimeUnit_testIsEmpty() {
+ assertTrue(listener.isEmpty());
+
+ listener.onTopicEvent(CommInfrastructure.NOOP, MY_TOPIC, MESSAGE);
+ assertFalse(listener.isEmpty());
+
+ assertEquals(DECODED_MESSAGE, listener.await());
+
+ assertTrue(listener.isEmpty());
+ }
+
+ @Test
+ public void testAwaitPredicateOfT() {
+ listener.onTopicEvent(CommInfrastructure.NOOP, MY_TOPIC, MESSAGE);
+ listener.onTopicEvent(CommInfrastructure.NOOP, MY_TOPIC, MESSAGE2);
+ assertEquals(MESSAGE2 + MSG_SUFFIX, listener.await(msg -> msg.startsWith("other-")));
+ }
+
+ /**
+ * Tests await() when the remaining time is negative.
+ */
+ @Test
+ public void testAwaitLongTimeUnitPredicateNoTime() {
+ assertThatThrownBy(() -> listener.await(-1, TimeUnit.SECONDS)).isInstanceOf(TopicException.class);
+ }
+
+ /**
+ * Tests await() when the poll() returns {@code null}.
+ */
+ @Test
+ public void testAwaitLongTimeUnitPredicateNoMessage() {
+ assertThatThrownBy(() -> listener.await(1, TimeUnit.MILLISECONDS)).isInstanceOf(TopicException.class);
+ }
+
+ /**
+ * Tests await() when the poll() is interrupted.
+ */
+ @Test
+ public void testAwaitLongTimeUnitPredicateInterrupted() throws InterruptedException {
+ listener = new Listener<String>(MY_TOPIC, msg -> msg) {
+ @Override
+ protected String pollMessage(long remainingMs) throws InterruptedException {
+ throw new InterruptedException(EXPECTED_EXCEPTION);
+ }
+ };
+
+ AtomicReference<TopicException> exref = new AtomicReference<>();
+ CountDownLatch interrupted = new CountDownLatch(1);
+
+ Thread thread = new Thread() {
+ @Override
+ public void run() {
+ try {
+ listener.await();
+ } catch (TopicException e) {
+ exref.set(e);
+ }
+
+ if (Thread.currentThread().isInterrupted()) {
+ interrupted.countDown();
+ }
+ }
+ };
+
+ thread.start();
+ assertTrue(interrupted.await(5, TimeUnit.SECONDS));
+ assertNotNull(exref.get());
+ }
+
+ @Test
+ public void testUnregister() {
+ listener.unregister();
+ verify(sink).unregister(listener);
+ }
+
+ @Test
+ public void testOnTopicEvent() {
+ listener = new Listener<>(MY_TOPIC, msg -> {
+ throw new IllegalArgumentException(EXPECTED_EXCEPTION);
+ });
+
+ // onTopicEvent() should not throw an exception
+ assertThatCode(() -> listener.onTopicEvent(CommInfrastructure.NOOP, MY_TOPIC, MESSAGE))
+ .doesNotThrowAnyException();
+
+ // should not have queued a message
+ assertTrue(listener.isEmpty());
+ }
+
+ @Test
+ public void testGetTopicManager() {
+ // use a listener with a real manager
+ assertNotNull(new Listener<>(MY_TOPIC, msg -> msg).getTopicManager());
+ }
+}
diff --git a/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/NamedRunnerTest.java b/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/NamedRunnerTest.java
new file mode 100644
index 000000000..fe9ff8083
--- /dev/null
+++ b/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/NamedRunnerTest.java
@@ -0,0 +1,87 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.common.rules.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.AfterClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+/**
+ * Tests NamedRunner. The tests don't do much, because all we really want to check is
+ * which tests are executed based on the {@link TestNames} annotation.
+ */
+@RunWith(NamedRunner.class)
+@TestNames(names = {"testAbc", "testDef", "testIgnore"}, prefixes = {"testGhi", "testJkl"})
+public class NamedRunnerTest {
+
+ private static int testCount = 0;
+
+ @AfterClass
+ public static void tearDownAfterClass() {
+ assertEquals(5, testCount);
+ }
+
+ @Test
+ public void testAbc() {
+ checkTest();
+ }
+
+ @Test
+ public void testAbc2() {
+ fail("should not run");
+ }
+
+ @Test
+ public void testDef() {
+ checkTest();
+ }
+
+ @Test
+ @Ignore
+ public void testIgnore() {
+ fail("should not run");
+ }
+
+ @Test
+ public void testGhi1() {
+ checkTest();
+ }
+
+ @Test
+ public void testGhi2() {
+ checkTest();
+ }
+
+ @Test
+ public void testJkl() {
+ checkTest();
+ }
+
+
+ private static void checkTest() {
+ ++testCount;
+ }
+}
diff --git a/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/NamedRunnerTest2.java b/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/NamedRunnerTest2.java
new file mode 100644
index 000000000..1ed5b20bc
--- /dev/null
+++ b/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/NamedRunnerTest2.java
@@ -0,0 +1,58 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.common.rules.test;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.AfterClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+/**
+ * Tests NamedRunner when the TestNames annotation is missing - all tests should be
+ * executed.
+ */
+@RunWith(NamedRunner.class)
+public class NamedRunnerTest2 {
+
+ private static int testCount = 0;
+
+ @AfterClass
+ public static void tearDownAfterClass() {
+ assertEquals(2, testCount);
+ }
+
+ @Test
+ public void testAbc() {
+ checkTest();
+ }
+
+ @Test
+ public void testDef() {
+ checkTest();
+ }
+
+
+ private static void checkTest() {
+ ++testCount;
+ }
+}
diff --git a/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/RulesTest.java b/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/RulesTest.java
new file mode 100644
index 000000000..28cb977fc
--- /dev/null
+++ b/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/RulesTest.java
@@ -0,0 +1,341 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.common.rules.test;
+
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Properties;
+import java.util.stream.Collectors;
+import org.junit.Before;
+import org.junit.Test;
+import org.kie.api.definition.rule.Rule;
+import org.kie.api.event.rule.AfterMatchFiredEvent;
+import org.kie.api.event.rule.AgendaEventListener;
+import org.kie.api.event.rule.BeforeMatchFiredEvent;
+import org.kie.api.event.rule.MatchCancelledEvent;
+import org.kie.api.event.rule.MatchCreatedEvent;
+import org.kie.api.event.rule.ObjectDeletedEvent;
+import org.kie.api.event.rule.ObjectInsertedEvent;
+import org.kie.api.event.rule.ObjectUpdatedEvent;
+import org.kie.api.event.rule.RuleRuntimeEventListener;
+import org.kie.api.runtime.KieSession;
+import org.kie.api.runtime.rule.Match;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.policy.controlloop.ControlLoopEvent;
+import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
+import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager2;
+import org.onap.policy.drools.controller.DroolsController;
+import org.onap.policy.drools.persistence.SystemPersistence;
+import org.onap.policy.drools.system.PolicyController;
+import org.onap.policy.drools.system.PolicyControllerFactory;
+import org.onap.policy.drools.system.PolicyEngine;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
+
+public class RulesTest {
+ private static final String CONTROLLER_NAME = "rulesTest";
+ private static final String POLICY_FILE = "src/test/resources/tosca-policy.json";
+ private static final String MY_POLICY = "operational.restart";
+ private static final String RESOURCE_DIR = "src/test/resources";
+ private static final String MY_RULE_NAME = "my-rule-name";
+ private static final String MY_TEXT = "my text";
+
+ @Mock
+ private PolicyEngine engine;
+ @Mock
+ private SystemPersistence repo;
+ @Mock
+ private PolicyController controller;
+ @Mock
+ private KieSession kieSession;
+ @Mock
+ private PolicyControllerFactory controllerFactory;
+ @Mock
+ private DroolsController drools;
+
+ private List<Object> facts;
+ private List<RuleRuntimeEventListener> ruleListeners;
+ private List<AgendaEventListener> agendaListeners;
+ private Properties properties;
+ private boolean installed;
+
+ private Rules rules;
+
+ /**
+ * Sets up.
+ */
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ facts = new LinkedList<>();
+ ruleListeners = new LinkedList<>();
+ agendaListeners = new LinkedList<>();
+ installed = false;
+ properties = new Properties();
+
+ when(engine.createPolicyController(any(), any())).thenReturn(controller);
+ when(repo.getControllerProperties(CONTROLLER_NAME)).thenReturn(properties);
+ when(controller.getDrools()).thenReturn(drools);
+
+ when(drools.facts(eq(CONTROLLER_NAME), any())).thenAnswer(args -> {
+ Class<?> clazz = args.getArgument(1);
+ return facts.stream().filter(obj -> obj.getClass() == clazz).collect(Collectors.toList());
+ });
+
+ // notify listeners when objects are added to drools
+ when(drools.offer(any())).thenAnswer(args -> {
+ Object object = args.getArgument(0);
+ notifyInserted(object);
+
+ if (!(object instanceof ToscaPolicy)) {
+ return true;
+ }
+
+ // "insert" Params objects associated with the policy (i.e., mimic the rules)
+ ToscaPolicy policy = (ToscaPolicy) object;
+ ControlLoopParams params = new ControlLoopParams();
+ params.setToscaPolicy(policy);
+ notifyInserted(params);
+
+ return true;
+ });
+
+ when(drools.delete(any())).thenAnswer(args -> {
+ Class<?> clazz = args.getArgument(0);
+ facts.removeIf(obj -> obj.getClass() == clazz);
+ return null;
+ });
+
+ // handle rule listener registration and deregistration with the kieSession
+ doAnswer(args -> {
+ ruleListeners.add(args.getArgument(0));
+ return null;
+ }).when(kieSession).addEventListener(any(RuleRuntimeEventListener.class));
+
+ doAnswer(args -> {
+ ruleListeners.remove(args.getArgument(0));
+ return null;
+ }).when(kieSession).removeEventListener(any(RuleRuntimeEventListener.class));
+
+ // handle agenda listener registration and deregistration with the kieSession
+ doAnswer(args -> {
+ agendaListeners.add(args.getArgument(0));
+ return null;
+ }).when(kieSession).addEventListener(any(AgendaEventListener.class));
+
+ doAnswer(args -> {
+ agendaListeners.remove(args.getArgument(0));
+ return null;
+ }).when(kieSession).removeEventListener(any(AgendaEventListener.class));
+
+ rules = new MyRules();
+
+ rules.configure(RESOURCE_DIR);
+ rules.start();
+ }
+
+ @Test
+ public void testRules() {
+ assertEquals(CONTROLLER_NAME, rules.getControllerName());
+
+ assertSame(engine, rules.getPdpd());
+ assertSame(repo, rules.getPdpdRepo());
+ assertSame(controller, rules.getController());
+ }
+
+ @Test
+ public void testStart() throws Exception {
+ verify(repo).setConfigurationDir("src/test/resources/config");
+ assertTrue(installed);
+ verify(engine).configure(any(Properties.class));
+ verify(engine).createPolicyController(CONTROLLER_NAME, properties);
+ verify(engine).start();
+
+ verify(kieSession).addEventListener(any(RuleRuntimeEventListener.class));
+ verify(kieSession).addEventListener(any(AgendaEventListener.class));
+ }
+
+ @Test
+ public void testDestroy() {
+ rules.destroy();
+
+ verify(controllerFactory).shutdown(CONTROLLER_NAME);
+ verify(engine).stop();
+ }
+
+ @Test
+ public void testResetFacts() {
+ rules.resetFacts();
+
+ verify(drools).delete(ToscaPolicy.class);
+ verify(drools).delete(ControlLoopParams.class);
+ verify(drools).delete(ControlLoopEventManager2.class);
+ verify(drools).delete(ControlLoopEvent.class);
+ }
+
+ @Test
+ public void testSetupPolicyFromTemplate_testGetPolicyFromTemplate() {
+ rules.setupPolicyFromTemplate("tosca-template.json", MY_POLICY);
+
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> rules.setupPolicyFromTemplate("missing-file.json", "a-policy"));
+ }
+
+ @Test
+ public void testSetupPolicyFromFile_testGetPolicyFromFile_testSetupPolicy() {
+ assertNotNull(rules.setupPolicyFromFile(POLICY_FILE));
+
+ assertThatIllegalArgumentException().isThrownBy(() -> rules.setupPolicyFromFile("missing-file.json"));
+ }
+
+ @Test
+ public void testRuleListenerLogger() {
+ Rule rule = mock(Rule.class);
+ when(rule.getName()).thenReturn(MY_RULE_NAME);
+
+ // insertions - with and without rule name
+ ObjectInsertedEvent insert = mock(ObjectInsertedEvent.class);
+ when(insert.getObject()).thenReturn(MY_TEXT);
+ ruleListeners.forEach(listener -> listener.objectInserted(insert));
+ when(insert.getRule()).thenReturn(rule);
+ ruleListeners.forEach(listener -> listener.objectInserted(insert));
+
+ // updates - with and without rule name
+ ObjectUpdatedEvent update = mock(ObjectUpdatedEvent.class);
+ when(update.getObject()).thenReturn(MY_TEXT);
+ ruleListeners.forEach(listener -> listener.objectUpdated(update));
+ when(update.getRule()).thenReturn(rule);
+ ruleListeners.forEach(listener -> listener.objectUpdated(update));
+
+ // deletions - with and without rule name
+ ObjectDeletedEvent delete = mock(ObjectDeletedEvent.class);
+ when(delete.getOldObject()).thenReturn(MY_TEXT);
+ ruleListeners.forEach(listener -> listener.objectDeleted(delete));
+ when(delete.getRule()).thenReturn(rule);
+ ruleListeners.forEach(listener -> listener.objectDeleted(delete));
+ }
+
+ @Test
+ public void testAgendaListenerLogger() {
+ Rule rule = mock(Rule.class);
+ when(rule.getName()).thenReturn(MY_RULE_NAME);
+
+ Match match = mock(Match.class);
+ when(match.getRule()).thenReturn(rule);
+
+ // create
+ MatchCreatedEvent create = mock(MatchCreatedEvent.class);
+ when(create.getMatch()).thenReturn(match);
+ agendaListeners.forEach(listener -> listener.matchCreated(create));
+
+ // cancel
+ MatchCancelledEvent cancel = mock(MatchCancelledEvent.class);
+ when(cancel.getMatch()).thenReturn(match);
+ agendaListeners.forEach(listener -> listener.matchCancelled(cancel));
+
+ // before-fire
+ BeforeMatchFiredEvent before = mock(BeforeMatchFiredEvent.class);
+ when(before.getMatch()).thenReturn(match);
+ agendaListeners.forEach(listener -> listener.beforeMatchFired(before));
+
+ // after-fire
+ AfterMatchFiredEvent after = mock(AfterMatchFiredEvent.class);
+ when(after.getMatch()).thenReturn(match);
+ agendaListeners.forEach(listener -> listener.afterMatchFired(after));
+ }
+
+ @Test
+ public void testMakePdpd_testMakePdpdRepo() {
+ // need rules that makes real objects
+ rules = new Rules(CONTROLLER_NAME);
+
+ assertNotNull(rules.getPdpd());
+ assertNotNull(rules.getPdpdRepo());
+ }
+
+ protected void notifyInserted(Object object) {
+ // add it to our list
+ facts.add(object);
+
+ // increase code coverage by adding random objects
+ ObjectInsertedEvent event0 = mock(ObjectInsertedEvent.class);
+ when(event0.getObject()).thenReturn(new Object());
+ ruleListeners.forEach(listener -> listener.objectInserted(event0));
+
+ // increase code coverage by adding a random object
+ ObjectInsertedEvent event = mock(ObjectInsertedEvent.class);
+ when(event.getObject()).thenReturn(object);
+
+ if (object instanceof ToscaPolicy) {
+ // increase code coverage by associating it with a random rule
+ Rule rule = mock(Rule.class);
+ when(rule.getName()).thenReturn(MY_RULE_NAME);
+ when(event.getRule()).thenReturn(rule);
+ }
+
+ ruleListeners.forEach(listener -> listener.objectInserted(event));
+ }
+
+ private class MyRules extends Rules {
+ public MyRules() {
+ super(CONTROLLER_NAME);
+ }
+
+ @Override
+ protected PolicyEngine makeEngine() {
+ return engine;
+ }
+
+ @Override
+ protected SystemPersistence makePdpdRepo() {
+ return repo;
+ }
+
+ @Override
+ protected KieSession getKieSession() {
+ return kieSession;
+ }
+
+ @Override
+ protected PolicyControllerFactory getControllerFactory() {
+ return controllerFactory;
+ }
+
+ @Override
+ protected void installArtifact(File kmoduleFile, File pomFile, String resourceDir, List<File> ruleFiles) {
+ installed = true;
+ }
+ }
+}
diff --git a/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/SimulatorsTest.java b/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/SimulatorsTest.java
new file mode 100644
index 000000000..8a87c4f26
--- /dev/null
+++ b/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/SimulatorsTest.java
@@ -0,0 +1,105 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.common.rules.test;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.policy.common.endpoints.http.server.HttpServletServer;
+import org.onap.policy.controlloop.common.rules.test.SimulatorException;
+import org.onap.policy.controlloop.common.rules.test.Simulators;
+import org.onap.policy.controlloop.common.rules.test.Simulators.SimulatorBuilder;
+
+public class SimulatorsTest {
+ private static final String EXPECTED_EXCEPTION = "expected exception";
+
+ @Mock
+ private HttpServletServer server1;
+ @Mock
+ private HttpServletServer server2;
+ @Mock
+ private HttpServletServer server3;
+
+ private Simulators simulators;
+
+ /**
+ * Sets up.
+ */
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ simulators = new Simulators();
+ }
+
+ @Test
+ public void testStart() {
+ simulators.start(() -> server1, () -> server2);
+ assertEquals(List.of(server1, server2), simulators.getServers());
+
+ verify(server1, never()).shutdown();
+ verify(server2, never()).shutdown();
+ }
+
+ /**
+ * Tests start() when one of the builders throws an exception.
+ */
+ @Test
+ public void testStartException() {
+ SimulatorBuilder exbuilder = () -> {
+ throw new InterruptedException(EXPECTED_EXCEPTION);
+ };
+
+ assertThatThrownBy(() -> simulators.start(() -> server1, () -> server2, exbuilder, () -> server3))
+ .isInstanceOf(SimulatorException.class);
+
+ assertTrue(simulators.getServers().isEmpty());
+
+ verify(server1).shutdown();
+ verify(server2).shutdown();
+
+ // shouldn't have reached this builder, so nothing to shut down
+ verify(server3, never()).shutdown();
+ }
+
+ @Test
+ public void testDestroy() {
+ simulators.start(() -> server1, () -> server2, () -> server3);
+
+ doThrow(new IllegalStateException(EXPECTED_EXCEPTION)).when(server2).shutdown();
+
+ simulators.destroy();
+
+ verify(server1).shutdown();
+ verify(server3).shutdown();
+
+ assertTrue(simulators.getServers().isEmpty());
+ }
+}
diff --git a/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/TopicsTest.java b/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/TopicsTest.java
new file mode 100644
index 000000000..6ed8c3828
--- /dev/null
+++ b/controlloop/common/rules-test/src/test/java/org/onap/policy/controlloop/common/rules/test/TopicsTest.java
@@ -0,0 +1,230 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.common.rules.test;
+
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import lombok.ToString;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+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.TopicEndpointManager;
+import org.onap.policy.common.endpoints.event.comm.bus.NoopTopicSink;
+import org.onap.policy.common.endpoints.event.comm.bus.NoopTopicSource;
+import org.onap.policy.common.endpoints.parameters.TopicParameters;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.drools.controller.DroolsController;
+import org.onap.policy.drools.protocol.coders.EventProtocolCoder;
+import org.onap.policy.drools.system.PolicyController;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
+
+public class TopicsTest {
+ private static final String EXPECTED_EXCEPTION = "expected exception";
+ private static final String MY_SOURCE_TOPIC = "my-source-topic";
+ private static final String MY_SINK_TOPIC = "my-sink-topic";
+ private static final String MY_GROUP = "my-group";
+ private static final String MY_ARTIFACT = "my-artifact";
+ private static final String MESSAGE = "{\"text\": \"hello\"}";
+ private static final String TEXT = "hello";
+ private static final String INJECT_FILE = "src/test/resources/topics.json";
+ private static final String POLICY_NAME = "my-policy";
+
+ @Mock
+ private DroolsController drools;
+ @Mock
+ private PolicyController controller;
+ @Mock
+ private EventProtocolCoder protocolCoder;
+ @Mock
+ private NoopTopicSink sink;
+ @Mock
+ private NoopTopicSource source;
+ @Mock
+ private TopicEndpoint mgr;
+
+ private ToscaPolicy policy;
+
+ private Topics topics;
+
+ /**
+ * Creates topics.
+ */
+ @BeforeClass
+ public static void setUpBeforeClass() {
+ TopicEndpointManager.getManager().shutdown();
+
+ TopicParameters params = new TopicParameters();
+ params.setTopic(MY_SOURCE_TOPIC);
+ params.setManaged(true);
+ params.setTopicCommInfrastructure("NOOP");
+ TopicEndpointManager.getManager().addTopicSources(List.of(params));
+ }
+
+ @AfterClass
+ public static void tearDownAfterClass() {
+ TopicEndpointManager.getManager().shutdown();
+ }
+
+ /**
+ * Sets up.
+ */
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ policy = new ToscaPolicy();
+ policy.setName(POLICY_NAME);
+ policy.setVersion("1.0.0");
+
+ when(drools.getGroupId()).thenReturn(MY_GROUP);
+ when(drools.getArtifactId()).thenReturn(MY_ARTIFACT);
+
+ when(controller.getDrools()).thenReturn(drools);
+
+ when(protocolCoder.decode(MY_GROUP, MY_ARTIFACT, MY_SINK_TOPIC, MESSAGE)).thenReturn(TEXT);
+
+ when(mgr.getNoopTopicSink(MY_SINK_TOPIC)).thenReturn(sink);
+ when(mgr.getNoopTopicSource(MY_SOURCE_TOPIC)).thenReturn(source);
+
+ topics = new Topics() {
+ @Override
+ protected TopicEndpoint getTopicManager() {
+ return mgr;
+ }
+
+ @Override
+ protected EventProtocolCoder getProtocolCoder() {
+ return protocolCoder;
+ }
+ };
+ }
+
+ @Test
+ public void testDestroy() {
+ Listener<String> listener1 = topics.createListener(MY_SINK_TOPIC, msg -> msg);
+ Listener<String> listener2 = topics.createListener(MY_SINK_TOPIC, msg -> msg + "a suffix");
+
+ topics.destroy();
+
+ verify(sink).unregister(listener1);
+ verify(sink).unregister(listener2);
+ }
+
+ @Test
+ public void testInjectStringFile() throws IOException {
+ topics.inject(MY_SOURCE_TOPIC, INJECT_FILE);
+
+ // nothing should have been replaced
+ String expected = new String(Files.readAllBytes(Paths.get(INJECT_FILE)));
+ verify(source).offer(expected);
+ }
+
+ @Test
+ public void testInjectStringFileString() throws IOException {
+ topics.inject(MY_SOURCE_TOPIC, INJECT_FILE, "hello");
+
+ // text should have been replaced with "hello"
+ String expected = new String(Files.readAllBytes(Paths.get("src", "test", "resources", "topicsReplaced.json")));
+ verify(source).offer(expected);
+
+ // exception reading file
+ assertThatThrownBy(() -> topics.inject(MY_SOURCE_TOPIC, "missing-file.json", "some text"))
+ .isInstanceOf(TopicException.class);
+ }
+
+ @Test
+ public void testCreateListenerStringClassOfTPolicyController() {
+ Listener<String> listener = topics.createListener(MY_SINK_TOPIC, String.class, controller);
+ listener.onTopicEvent(CommInfrastructure.NOOP, MY_SINK_TOPIC, MESSAGE);
+
+ assertEquals(TEXT, listener.await());
+ }
+
+ @Test
+ public void testCreateListenerStringClassOfTCoder() {
+ Listener<Data> listener = topics.createListener(MY_SINK_TOPIC, Data.class, new StandardCoder());
+ listener.onTopicEvent(CommInfrastructure.NOOP, MY_SINK_TOPIC, MESSAGE);
+
+ Data expected = new Data();
+ expected.text = TEXT;
+ assertEquals(expected.toString(), listener.await().toString());
+ }
+
+ /**
+ * Tests createListener() when the coder throws an exception.
+ */
+ @Test
+ public void testCreateListenerStringClassOfTCoderException() {
+ StandardCoder coder = new StandardCoder() {
+ @Override
+ public <T> T decode(String arg0, Class<T> arg1) throws CoderException {
+ throw new CoderException(EXPECTED_EXCEPTION);
+ }
+ };
+
+ Listener<Data> listener = topics.createListener(MY_SINK_TOPIC, Data.class, coder);
+
+ // onTopicEvent() should not throw an exception
+ assertThatCode(() -> listener.onTopicEvent(CommInfrastructure.NOOP, MY_SINK_TOPIC, MESSAGE))
+ .doesNotThrowAnyException();
+
+ // should not have queued a message
+ assertThatThrownBy(() -> listener.await(0, TimeUnit.MILLISECONDS)).isInstanceOf(TopicException.class);
+ }
+
+ @Test
+ public void testCreateListenerStringFunctionOfStringT() {
+ Listener<String> listener = topics.createListener(MY_SINK_TOPIC, msg -> msg);
+ listener.onTopicEvent(CommInfrastructure.NOOP, MY_SINK_TOPIC, MESSAGE);
+
+ assertEquals(MESSAGE, listener.await());
+ }
+
+ @Test
+ public void testGetTopicManager_testGetProtocolCoder() {
+ // use a topic with a real manager
+ topics = new Topics();
+
+ assertNotNull(topics.getTopicManager());
+ assertNotNull(topics.getProtocolCoder());
+ }
+
+ @ToString
+ private static class Data {
+ private String text;
+ }
+}
diff --git a/controlloop/common/rules-test/src/test/resources/META-INF/kmodule.xml b/controlloop/common/rules-test/src/test/resources/META-INF/kmodule.xml
new file mode 100644
index 000000000..07041c6b1
--- /dev/null
+++ b/controlloop/common/rules-test/src/test/resources/META-INF/kmodule.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ============LICENSE_START=======================================================
+ ONAP
+ ================================================================================
+ Copyright (C) 2020 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=========================================================
+ -->
+<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">
+ <kbase name="onap.policies.controlloop.operational.common.Drools" default="false" equalsBehavior="equality"/>
+ <kbase name="onap.policies.controlloop.Operational" equalsBehavior="equality"
+ packages="org.onap.policy.controlloop" includes="onap.policies.controlloop.operational.common.Drools">
+ <ksession name="rulesTest"/>
+ </kbase>
+</kmodule>
diff --git a/controlloop/common/rules-test/src/test/resources/config/rulesTest-controller.properties b/controlloop/common/rules-test/src/test/resources/config/rulesTest-controller.properties
new file mode 100644
index 000000000..11df5e99e
--- /dev/null
+++ b/controlloop/common/rules-test/src/test/resources/config/rulesTest-controller.properties
@@ -0,0 +1,25 @@
+#
+# ============LICENSE_START=======================================================
+# ONAP
+# ================================================================================
+# Copyright (C) 2020 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=========================================================
+#
+
+controller.name=rulesTest
+
+rules.groupId=org.onap.policy.controlloop
+rules.artifactId=rulesTest
+rules.version=1.0.0
diff --git a/controlloop/common/rules-test/src/test/resources/my-http-client.properties b/controlloop/common/rules-test/src/test/resources/my-http-client.properties
new file mode 100644
index 000000000..7d4ef90b9
--- /dev/null
+++ b/controlloop/common/rules-test/src/test/resources/my-http-client.properties
@@ -0,0 +1,26 @@
+#
+# ============LICENSE_START=======================================================
+# ONAP
+# ================================================================================
+# Copyright (C) 2020 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=========================================================
+#
+
+http.client.services=MY-CLIENT
+
+http.client.services.MY-CLIENT.managed=true
+http.client.services.MY-CLIENT.host=localhost
+http.client.services.MY-CLIENT.port=6669
+http.client.services.MY-CLIENT.contextUriPath=some/path
diff --git a/controlloop/common/rules-test/src/test/resources/rulesTest.drl b/controlloop/common/rules-test/src/test/resources/rulesTest.drl
new file mode 100644
index 000000000..11e99e960
--- /dev/null
+++ b/controlloop/common/rules-test/src/test/resources/rulesTest.drl
@@ -0,0 +1,96 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop;
+
+import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
+import org.onap.policy.controlloop.CanonicalOnset;
+import org.onap.policy.controlloop.VirtualControlLoopEvent;
+import org.onap.policy.controlloop.VirtualControlLoopNotification;
+import org.onap.policy.controlloop.ControlLoopNotificationType;
+import org.onap.policy.controlloop.policy.Policy;
+import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager2;
+import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager2.NewEventStatus;
+import org.onap.policy.controlloop.eventmanager.ControlLoopOperationManager2;
+import org.onap.policy.controlloop.utils.ControlLoopUtils;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
+
+import org.slf4j.LoggerFactory;
+import org.slf4j.Logger;
+
+import org.onap.policy.drools.system.PolicyEngineConstants;
+
+rule "STARTED"
+ when
+ then
+ System.out.println(drools.getRule().getName());
+end
+
+/*
+*
+* Called when the ControlLoopParams object has been inserted into working memory from the BRMSGW.
+*
+*/
+rule "INSERT.PARAMS"
+ when
+ $params : ControlLoopParams()
+ then
+
+ Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+ logger.info("{}: {} : TOSCA-POLICY=[{}]", $params.getClosedLoopControlName(), $params.getPolicyName() + "."
+ + drools.getRule().getName(), $params.getToscaPolicy());
+end
+
+/*
+*
+* Called when a Tosca Policy is present.
+*
+*/
+rule "NEW.TOSCA.POLICY"
+ when
+ $policy : ToscaPolicy()
+ then
+
+ Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+ logger.info("{}: [{}|{}|{}|{}]: CONTENT: {}", drools.getRule().getName(),
+ $policy.getType(), $policy.getTypeVersion(), $policy.getName(),
+ $policy.getVersion(), $policy);
+
+ ControlLoopParams params = ControlLoopUtils.toControlLoopParams($policy);
+ if (params != null) {
+ insert(params);
+ }
+end
+
+/*
+ * Remove Control Loop Parameters.
+ */
+rule "REMOVE.PARAMS"
+ when
+ $params : ControlLoopParams( $policyName : getPolicyName(), $policyVersion : getPolicyVersion() )
+ not ( ToscaPolicy( getName() == $policyName, getVersion() == $policyVersion ) )
+ then
+
+ Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+ logger.info("{}: [{}|{}|{}]", drools.getRule().getName(),
+ $params.getPolicyScope(), $params.getPolicyName(), $params.getPolicyVersion());
+
+ retract($params);
+end
diff --git a/controlloop/common/rules-test/src/test/resources/rulesTest.pom b/controlloop/common/rules-test/src/test/resources/rulesTest.pom
new file mode 100644
index 000000000..625e736a4
--- /dev/null
+++ b/controlloop/common/rules-test/src/test/resources/rulesTest.pom
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ============LICENSE_START=======================================================
+ ONAP
+ ================================================================================
+ Copyright (C) 2020 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=========================================================
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.onap.policy.controlloop</groupId>
+ <artifactId>rulesTest</artifactId>
+ <version>1.0.0</version>
+</project>
diff --git a/controlloop/common/rules-test/src/test/resources/topics.json b/controlloop/common/rules-test/src/test/resources/topics.json
new file mode 100644
index 000000000..4e08bee83
--- /dev/null
+++ b/controlloop/common/rules-test/src/test/resources/topics.json
@@ -0,0 +1,5 @@
+{
+ "closedLoopControlName": "ControlLoop-vCPE-48f0c2c3-a172-4192-9ae3-052274181b6e",
+ "text": "${replaceMe}",
+ "moreText": "${replaceMe}"
+}
diff --git a/controlloop/common/rules-test/src/test/resources/topicsReplaced.json b/controlloop/common/rules-test/src/test/resources/topicsReplaced.json
new file mode 100644
index 000000000..743ef4c71
--- /dev/null
+++ b/controlloop/common/rules-test/src/test/resources/topicsReplaced.json
@@ -0,0 +1,5 @@
+{
+ "closedLoopControlName": "ControlLoop-vCPE-48f0c2c3-a172-4192-9ae3-052274181b6e",
+ "text": "hello",
+ "moreText": "hello"
+}
diff --git a/controlloop/common/rules-test/src/test/resources/tosca-policy.json b/controlloop/common/rules-test/src/test/resources/tosca-policy.json
new file mode 100644
index 000000000..42f54ab12
--- /dev/null
+++ b/controlloop/common/rules-test/src/test/resources/tosca-policy.json
@@ -0,0 +1,37 @@
+{
+ "type": "onap.policies.controlloop.operational.common.Drools",
+ "type_version": "1.0.0",
+ "version": "1.0.0",
+ "name": "operational.restart",
+ "metadata": {
+ "policy-id": "operational.restart"
+ },
+ "properties": {
+ "controllerName": "rulesTest",
+ "id": "ControlLoop-vCPE-48f0c2c3-a172-4192-9ae3-052274181b6e",
+ "timeout": 3600,
+ "abatement": false,
+ "trigger": "unique-policy-id-1-restart",
+ "operations": [
+ {
+ "id": "unique-policy-id-1-restart",
+ "description": "Restart the VM",
+ "operation": {
+ "actor": "APPC",
+ "operation": "Restart",
+ "target": {
+ "targetType": "VM"
+ }
+ },
+ "timeout": 1200,
+ "retries": 3,
+ "success": "final_success",
+ "failure": "final_failure",
+ "failure_timeout": "final_failure_timeout",
+ "failure_retries": "final_failure_retries",
+ "failure_exception": "final_failure_exception",
+ "failure_guard": "final_failure_guard"
+ }
+ ]
+ }
+}
diff --git a/controlloop/common/rules-test/src/test/resources/tosca-template.json b/controlloop/common/rules-test/src/test/resources/tosca-template.json
new file mode 100644
index 000000000..305756064
--- /dev/null
+++ b/controlloop/common/rules-test/src/test/resources/tosca-template.json
@@ -0,0 +1,45 @@
+{
+ "tosca_definitions_version": "tosca_simple_yaml_1_1_0",
+ "topology_template": {
+ "policies": [
+ {
+ "operational.restart": {
+ "type": "onap.policies.controlloop.operational.common.Drools",
+ "type_version": "1.0.0",
+ "version": "1.0.0",
+ "metadata": {
+ "policy-id": "operational.restart"
+ },
+ "properties": {
+ "id": "ControlLoop-vCPE-48f0c2c3-a172-4192-9ae3-052274181b6e",
+ "timeout": 3600,
+ "abatement": true,
+ "trigger": "unique-policy-id-1-restart",
+ "operations": [
+ {
+ "id": "unique-policy-id-1-restart",
+ "description": "Restart the VM",
+ "operation": {
+ "actor": "APPC",
+ "operation": "Restart",
+ "target": {
+ "targetType": "VNF"
+ }
+ },
+ "timeout": 1200,
+ "retries": 3,
+ "success": "final_success",
+ "failure": "final_failure",
+ "failure_timeout": "final_failure_timeout",
+ "failure_retries": "final_failure_retries",
+ "failure_exception": "final_failure_exception",
+ "failure_guard": "final_failure_guard"
+ }
+ ],
+ "controllerName": "usecases"
+ }
+ }
+ }
+ ]
+ }
+}