2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.onap.policy.controlloop.common.rules.test;
23 import static org.assertj.core.api.Assertions.assertThat;
24 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
25 import static org.junit.Assert.assertEquals;
26 import static org.junit.Assert.assertNotNull;
27 import static org.junit.Assert.assertSame;
28 import static org.junit.Assert.assertTrue;
29 import static org.mockito.ArgumentMatchers.any;
30 import static org.mockito.ArgumentMatchers.eq;
31 import static org.mockito.Mockito.doAnswer;
32 import static org.mockito.Mockito.mock;
33 import static org.mockito.Mockito.verify;
34 import static org.mockito.Mockito.when;
36 import ch.qos.logback.classic.Logger;
38 import java.util.LinkedList;
39 import java.util.List;
40 import java.util.Properties;
41 import java.util.concurrent.BlockingQueue;
42 import java.util.concurrent.LinkedBlockingQueue;
43 import java.util.concurrent.TimeUnit;
44 import java.util.stream.Collectors;
45 import org.junit.AfterClass;
46 import org.junit.Before;
47 import org.junit.BeforeClass;
48 import org.junit.Test;
49 import org.kie.api.definition.rule.Rule;
50 import org.kie.api.event.rule.AfterMatchFiredEvent;
51 import org.kie.api.event.rule.AgendaEventListener;
52 import org.kie.api.event.rule.BeforeMatchFiredEvent;
53 import org.kie.api.event.rule.MatchCancelledEvent;
54 import org.kie.api.event.rule.MatchCreatedEvent;
55 import org.kie.api.event.rule.ObjectDeletedEvent;
56 import org.kie.api.event.rule.ObjectInsertedEvent;
57 import org.kie.api.event.rule.ObjectUpdatedEvent;
58 import org.kie.api.event.rule.RuleRuntimeEventListener;
59 import org.kie.api.runtime.KieSession;
60 import org.kie.api.runtime.rule.Match;
61 import org.mockito.Mock;
62 import org.mockito.MockitoAnnotations;
63 import org.onap.policy.common.utils.test.log.logback.ExtractAppender;
64 import org.onap.policy.controlloop.ControlLoopEvent;
65 import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
66 import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager2;
67 import org.onap.policy.drools.controller.DroolsController;
68 import org.onap.policy.drools.persistence.SystemPersistence;
69 import org.onap.policy.drools.system.PolicyController;
70 import org.onap.policy.drools.system.PolicyControllerFactory;
71 import org.onap.policy.drools.system.PolicyEngine;
72 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
73 import org.slf4j.LoggerFactory;
75 public class RulesTest {
76 private static final String EXPECTED_EXCEPTION = "expected exception";
77 private static final String CONTROLLER_NAME = "rulesTest";
78 private static final String POLICY_FILE = "src/test/resources/tosca-policy.json";
79 private static final String MY_POLICY = "operational.restart";
80 private static final String RESOURCE_DIR = "src/test/resources";
81 private static final String MY_RULE_NAME = "my-rule-name";
82 private static final String MY_TEXT = "my text";
85 * Used to attach an appender to the class' logger.
87 private static final Logger logger = (Logger) LoggerFactory.getLogger(Rules.class);
88 private static final ExtractAppender appender = new ExtractAppender();
91 private PolicyEngine engine;
93 private SystemPersistence repo;
95 private PolicyController controller;
97 private KieSession kieSession;
99 private PolicyControllerFactory controllerFactory;
101 private DroolsController drools;
103 private List<Object> facts;
104 private List<RuleRuntimeEventListener> ruleListeners;
105 private List<AgendaEventListener> agendaListeners;
106 private Properties properties;
107 private boolean installed;
112 * Attaches the appender to the logger.
115 public static void setUpBeforeClass() throws Exception {
117 * Attach appender to the logger.
119 appender.setContext(logger.getLoggerContext());
122 logger.addAppender(appender);
126 * Stops the appender.
129 public static void tearDownAfterClass() {
137 public void setUp() {
138 MockitoAnnotations.initMocks(this);
140 facts = new LinkedList<>();
141 ruleListeners = new LinkedList<>();
142 agendaListeners = new LinkedList<>();
144 properties = new Properties();
146 when(engine.createPolicyController(any(), any())).thenReturn(controller);
147 when(repo.getControllerProperties(CONTROLLER_NAME)).thenReturn(properties);
148 when(controller.getDrools()).thenReturn(drools);
150 when(drools.facts(eq(CONTROLLER_NAME), any())).thenAnswer(args -> {
151 Class<?> clazz = args.getArgument(1);
152 return facts.stream().filter(obj -> obj.getClass() == clazz).collect(Collectors.toList());
155 // notify listeners when objects are added to drools
156 when(drools.offer(any())).thenAnswer(args -> {
157 Object object = args.getArgument(0);
158 notifyInserted(object);
160 if (!(object instanceof ToscaPolicy)) {
164 // "insert" Params objects associated with the policy (i.e., mimic the rules)
165 ToscaPolicy policy = (ToscaPolicy) object;
166 ControlLoopParams params = new ControlLoopParams();
167 params.setToscaPolicy(policy);
168 notifyInserted(params);
173 when(drools.delete(any())).thenAnswer(args -> {
174 Class<?> clazz = args.getArgument(0);
175 facts.removeIf(obj -> obj.getClass() == clazz);
179 // handle rule listener registration and deregistration with the kieSession
181 ruleListeners.add(args.getArgument(0));
183 }).when(kieSession).addEventListener(any(RuleRuntimeEventListener.class));
186 ruleListeners.remove(args.getArgument(0));
188 }).when(kieSession).removeEventListener(any(RuleRuntimeEventListener.class));
190 // handle agenda listener registration and deregistration with the kieSession
192 agendaListeners.add(args.getArgument(0));
194 }).when(kieSession).addEventListener(any(AgendaEventListener.class));
197 agendaListeners.remove(args.getArgument(0));
199 }).when(kieSession).removeEventListener(any(AgendaEventListener.class));
201 rules = new MyRules();
203 rules.configure(RESOURCE_DIR);
208 public void testRules() {
209 assertEquals(CONTROLLER_NAME, rules.getControllerName());
211 assertSame(engine, rules.getPdpd());
212 assertSame(repo, rules.getPdpdRepo());
213 assertSame(controller, rules.getController());
217 public void testStart() throws Exception {
218 verify(repo).setConfigurationDir("src/test/resources/config");
219 assertTrue(installed);
220 verify(engine).configure(any(Properties.class));
221 verify(engine).createPolicyController(CONTROLLER_NAME, properties);
222 verify(engine).start();
224 verify(kieSession).addEventListener(any(RuleRuntimeEventListener.class));
225 verify(kieSession).addEventListener(any(AgendaEventListener.class));
229 public void testDestroy() {
232 verify(controllerFactory).shutdown(CONTROLLER_NAME);
233 verify(engine).stop();
237 public void testResetFacts() {
240 verify(drools).delete(ToscaPolicy.class);
241 verify(drools).delete(ControlLoopParams.class);
242 verify(drools).delete(ControlLoopEventManager2.class);
243 verify(drools).delete(ControlLoopEvent.class);
247 public void testSetupPolicyFromTemplate_testGetPolicyFromTemplate() throws InterruptedException {
248 rules.setupPolicyFromTemplate("tosca-template.json", MY_POLICY);
250 assertThatIllegalArgumentException()
251 .isThrownBy(() -> rules.setupPolicyFromTemplate("missing-file.json", "a-policy"));
253 // check interrupt case
254 checkInterrupt(() -> rules.setupPolicyFromTemplate("tosca-template.json", MY_POLICY),
255 "policy operational.restart");
259 public void testSetupPolicyFromFile_testGetPolicyFromFile_testSetupPolicy() throws InterruptedException {
260 assertNotNull(rules.setupPolicyFromFile(POLICY_FILE));
262 assertThatIllegalArgumentException().isThrownBy(() -> rules.setupPolicyFromFile("missing-file.json"));
264 // check interrupt case
265 checkInterrupt(() -> rules.setupPolicyFromFile(POLICY_FILE), "policy " + POLICY_FILE);
269 public void testRuleListenerLogger() {
270 Rule rule = mock(Rule.class);
271 when(rule.getName()).thenReturn(MY_RULE_NAME);
273 // insertions - with and without rule name
274 ObjectInsertedEvent insert = mock(ObjectInsertedEvent.class);
275 when(insert.getObject()).thenReturn(MY_TEXT);
276 checkLogging("inserted", () -> ruleListeners.forEach(listener -> listener.objectInserted(insert)));
277 when(insert.getRule()).thenReturn(rule);
278 checkLogging("inserted", () -> ruleListeners.forEach(listener -> listener.objectInserted(insert)));
280 // updates - with and without rule name
281 ObjectUpdatedEvent update = mock(ObjectUpdatedEvent.class);
282 when(update.getObject()).thenReturn(MY_TEXT);
283 checkLogging("updated", () -> ruleListeners.forEach(listener -> listener.objectUpdated(update)));
284 when(update.getRule()).thenReturn(rule);
285 checkLogging("updated", () -> ruleListeners.forEach(listener -> listener.objectUpdated(update)));
287 // deletions - with and without rule name
288 ObjectDeletedEvent delete = mock(ObjectDeletedEvent.class);
289 when(delete.getOldObject()).thenReturn(MY_TEXT);
290 checkLogging("deleted", () -> ruleListeners.forEach(listener -> listener.objectDeleted(delete)));
291 when(delete.getRule()).thenReturn(rule);
292 checkLogging("deleted", () -> ruleListeners.forEach(listener -> listener.objectDeleted(delete)));
296 public void testAgendaListenerLogger() {
297 Rule rule = mock(Rule.class);
298 when(rule.getName()).thenReturn(MY_RULE_NAME);
300 Match match = mock(Match.class);
301 when(match.getRule()).thenReturn(rule);
304 MatchCreatedEvent create = mock(MatchCreatedEvent.class);
305 when(create.getMatch()).thenReturn(match);
306 checkLogging("match created", () -> agendaListeners.forEach(listener -> listener.matchCreated(create)));
309 MatchCancelledEvent cancel = mock(MatchCancelledEvent.class);
310 when(cancel.getMatch()).thenReturn(match);
311 checkLogging("match cancelled", () -> agendaListeners.forEach(listener -> listener.matchCancelled(cancel)));
314 BeforeMatchFiredEvent before = mock(BeforeMatchFiredEvent.class);
315 when(before.getMatch()).thenReturn(match);
317 checkLogging("before match fired",
318 () -> agendaListeners.forEach(listener -> listener.beforeMatchFired(before)));
322 AfterMatchFiredEvent after = mock(AfterMatchFiredEvent.class);
323 when(after.getMatch()).thenReturn(match);
324 checkLogging("after match fired", () -> agendaListeners.forEach(listener -> listener.afterMatchFired(after)));
328 public void testMakePdpd_testMakePdpdRepo() {
329 // need rules that makes real objects
330 rules = new Rules(CONTROLLER_NAME);
332 assertNotNull(rules.getPdpd());
333 assertNotNull(rules.getPdpdRepo());
336 private void checkInterrupt(Runnable command, String expectedMsg) throws InterruptedException {
337 rules = new MyRules() {
339 protected ToscaPolicy setupPolicy(ToscaPolicy policy) throws InterruptedException {
340 throw new InterruptedException(EXPECTED_EXCEPTION);
343 rules.configure(RESOURCE_DIR);
346 BlockingQueue<IllegalArgumentException> exceptions = new LinkedBlockingQueue<>();
348 Thread thread = new Thread(() -> {
351 } catch (IllegalArgumentException e) {
356 thread.setDaemon(true);
359 assertThat(exceptions.poll(10, TimeUnit.SECONDS)).isNotNull().hasMessage(expectedMsg)
360 .hasCauseInstanceOf(InterruptedException.class);
363 private void checkLogging(String expectedMsg, Runnable command) {
364 appender.clearExtractions();
366 List<String> messages = appender.getExtracted();
367 assertEquals(1, messages.size());
368 assertThat(messages.get(0)).contains(expectedMsg);
371 protected void notifyInserted(Object object) {
372 // add it to our list
375 // increase code coverage by adding random objects
376 ObjectInsertedEvent event0 = mock(ObjectInsertedEvent.class);
377 when(event0.getObject()).thenReturn(new Object());
378 ruleListeners.forEach(listener -> listener.objectInserted(event0));
380 // increase code coverage by adding a random object
381 ObjectInsertedEvent event = mock(ObjectInsertedEvent.class);
382 when(event.getObject()).thenReturn(object);
384 if (object instanceof ToscaPolicy) {
385 // increase code coverage by associating it with a random rule
386 Rule rule = mock(Rule.class);
387 when(rule.getName()).thenReturn(MY_RULE_NAME);
388 when(event.getRule()).thenReturn(rule);
391 ruleListeners.forEach(listener -> listener.objectInserted(event));
394 private class MyRules extends Rules {
396 super(CONTROLLER_NAME);
400 protected PolicyEngine makeEngine() {
405 protected SystemPersistence makePdpdRepo() {
410 protected KieSession getKieSession() {
415 protected PolicyControllerFactory getControllerFactory() {
416 return controllerFactory;
420 protected void installArtifact(File kmoduleFile, File pomFile, String resourceDir, List<File> ruleFiles) {