diff options
author | Jim Hahn <jrh3@att.com> | 2020-03-09 15:15:05 -0400 |
---|---|---|
committer | Jim Hahn <jrh3@att.com> | 2020-03-12 12:12:48 -0400 |
commit | aa8225b5211485b3c1150c21e51fd3e93b7f31d3 (patch) | |
tree | 922b78d2c8a4c657d4c32604a0b73df40d2fb330 /controlloop/common/rules-test/src/test | |
parent | 0e658768fc0573bf6acf7f849a49c9da98c8e47f (diff) |
Retool rules tests
Extracted common code from various XxxBaseTest classes into:
- Topics class to manage messages for test topics
- HttpClients class to manage HttpClient objects for tests
- Simulators class to manage simulators for tests
- Rules class to manage start up and shutdown of rules
Merged remaining code from XxxBaseTest classes into a single
class. Modified the Frankfurt and Usescases tests to subclass
from this new class and specify just the relevant tests to be
executed.
Issue-ID: POLICY-2385
Signed-off-by: Jim Hahn <jrh3@att.com>
Change-Id: Iaf83c9d2b205a4c343e0dde23ec86508f5773693
Diffstat (limited to 'controlloop/common/rules-test/src/test')
18 files changed, 1910 insertions, 0 deletions
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" + } + } + } + ] + } +} |