/* * Copyright 2015-2017 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * 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. */ package org.hawkular.alerts.engine; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.UUID; import java.util.concurrent.CopyOnWriteArraySet; import org.hawkular.alerts.api.json.JsonUtil; import org.hawkular.alerts.api.model.condition.AvailabilityCondition; import org.hawkular.alerts.api.model.condition.AvailabilityConditionEval; import org.hawkular.alerts.api.model.condition.CompareCondition; import org.hawkular.alerts.api.model.condition.CompareConditionEval; import org.hawkular.alerts.api.model.condition.ConditionEval; import org.hawkular.alerts.api.model.condition.EventCondition; import org.hawkular.alerts.api.model.condition.ExternalCondition; import org.hawkular.alerts.api.model.condition.ExternalConditionEval; import org.hawkular.alerts.api.model.condition.MissingCondition; import org.hawkular.alerts.api.model.condition.MissingConditionEval; import org.hawkular.alerts.api.model.condition.NelsonCondition; import org.hawkular.alerts.api.model.condition.NelsonCondition.NelsonRule; import org.hawkular.alerts.api.model.condition.NelsonConditionEval; import org.hawkular.alerts.api.model.condition.RateCondition; import org.hawkular.alerts.api.model.condition.RateConditionEval; import org.hawkular.alerts.api.model.condition.StringCondition; import org.hawkular.alerts.api.model.condition.StringConditionEval; import org.hawkular.alerts.api.model.condition.ThresholdCondition; import org.hawkular.alerts.api.model.condition.ThresholdConditionEval; import org.hawkular.alerts.api.model.condition.ThresholdRangeCondition; import org.hawkular.alerts.api.model.condition.ThresholdRangeConditionEval; import org.hawkular.alerts.api.model.dampening.Dampening; import org.hawkular.alerts.api.model.data.AvailabilityType; import org.hawkular.alerts.api.model.data.Data; import org.hawkular.alerts.api.model.event.Alert; import org.hawkular.alerts.api.model.event.Event; import org.hawkular.alerts.api.model.event.EventCategory; import org.hawkular.alerts.api.model.event.EventType; import org.hawkular.alerts.api.model.trigger.Match; import org.hawkular.alerts.api.model.trigger.Mode; import org.hawkular.alerts.api.model.trigger.Trigger; import org.hawkular.alerts.engine.impl.DroolsRulesEngineImpl; import org.hawkular.alerts.engine.service.RulesEngine; import org.hawkular.alerts.engine.util.MissingState; import org.jboss.logging.Logger; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * Basic test of RulesEngine implementation. * * @author Jay Shaughnessy * @author Lucas Ponce */ public class RulesEngineTest { private static final Logger log = Logger.getLogger(RulesEngineTest.class); RulesEngine rulesEngine = new DroolsRulesEngineImpl(); List<Alert> alerts = new ArrayList<>(); Set<Dampening> pendingTimeouts = new HashSet<>(); Map<Trigger, List<Set<ConditionEval>>> autoResolvedTriggers = new HashMap<>(); Set<Trigger> disabledTriggers = new CopyOnWriteArraySet<>(); TreeSet<Data> datums = new TreeSet<Data>(); TreeSet<Event> inputEvents = new TreeSet<>(); List<Event> outputEvents = new ArrayList<>(); Set<MissingState> missingStates = new HashSet<>(); @Before public void before() { rulesEngine.addGlobal("log", log); rulesEngine.addGlobal("alerts", alerts); rulesEngine.addGlobal("events", outputEvents); rulesEngine.addGlobal("pendingTimeouts", pendingTimeouts); rulesEngine.addGlobal("autoResolvedTriggers", autoResolvedTriggers); rulesEngine.addGlobal("disabledTriggers", disabledTriggers); } @After public void after() { rulesEngine.reset(); alerts.clear(); pendingTimeouts.clear(); datums.clear(); inputEvents.clear(); outputEvents.clear(); missingStates.clear(); } @Test public void thresholdTest() { // 1 alert Trigger t1 = new Trigger("tenant", "trigger-1", "Threshold-LT"); ThresholdCondition t1c1 = new ThresholdCondition("tenant", "trigger-1", 1, 1, "NumericData-01", ThresholdCondition.Operator.LT, 10.0); // 2 alert3 Trigger t2 = new Trigger("tenant", "trigger-2", "Threshold-LTE"); ThresholdCondition t2c1 = new ThresholdCondition("tenant", "trigger-2", 1, 1, "NumericData-01", ThresholdCondition.Operator.LTE, 10.0); // 1 alert Trigger t3 = new Trigger("tenant", "trigger-3", "Threshold-GT"); ThresholdCondition t3c1 = new ThresholdCondition("tenant", "trigger-3", 1, 1, "NumericData-01", ThresholdCondition.Operator.GT, 10.0); // 2 alerts Trigger t4 = new Trigger("tenant", "trigger-4", "Threshold-GTE"); ThresholdCondition t4c1 = new ThresholdCondition("tenant", "trigger-4", 1, 1, "NumericData-01", ThresholdCondition.Operator.GTE, 10.0); datums.add(Data.forNumeric("tenant", "NumericData-01", 1000, 10.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 2000, 5.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 3000, 15.0)); // default dampening t1.setEnabled(true); t2.setEnabled(true); t3.setEnabled(true); t4.setEnabled(true); rulesEngine.addFact(t1); rulesEngine.addFact(t1c1); rulesEngine.addFact(t2); rulesEngine.addFact(t2c1); rulesEngine.addFact(t3); rulesEngine.addFact(t3c1); rulesEngine.addFact(t4); rulesEngine.addFact(t4c1); rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 6, alerts.size()); Collections.sort(alerts, (Alert a1, Alert a2) -> a1.getTriggerId().compareTo(a2.getTriggerId())); Alert a = alerts.get(0); assertEquals(a.getTriggerId(), "trigger-1", a.getTriggerId()); assertEquals(a.getEvalSets().toString(), 1, a.getEvalSets().size()); Set<ConditionEval> eval = a.getEvalSets().get(0); assertEquals(eval.toString(), 1, eval.size()); ThresholdConditionEval e = (ThresholdConditionEval) eval.iterator().next(); assertEquals(e.toString(), 1, e.getConditionSetIndex()); assertEquals(e.toString(), 1, e.getConditionSetSize()); assertEquals("trigger-1", e.getTriggerId()); assertTrue(e.isMatch()); Double v = e.getValue(); assertTrue(e.toString(), v.equals(5.0D)); assertEquals(e.getCondition().toString(), "NumericData-01", e.getCondition().getDataId()); a = alerts.get(1); assertEquals(a.getTriggerId(), "trigger-2", a.getTriggerId()); assertEquals(a.getEvalSets().toString(), 1, a.getEvalSets().size()); eval = a.getEvalSets().get(0); assertEquals(eval.toString(), 1, eval.size()); e = (ThresholdConditionEval) eval.iterator().next(); assertEquals(e.toString(), 1, e.getConditionSetIndex()); assertEquals(e.toString(), 1, e.getConditionSetSize()); assertEquals("trigger-2", e.getTriggerId()); assertTrue(e.isMatch()); v = e.getValue(); assertTrue(e.toString(), v.equals(5.0D) || v.equals(10.0D)); assertEquals(e.getCondition().toString(), "NumericData-01", e.getCondition().getDataId()); a = alerts.get(2); assertEquals(a.getTriggerId(), "trigger-2", a.getTriggerId()); assertEquals(a.getEvalSets().toString(), 1, a.getEvalSets().size()); eval = a.getEvalSets().get(0); assertEquals(eval.toString(), 1, eval.size()); e = (ThresholdConditionEval) eval.iterator().next(); assertEquals(e.toString(), 1, e.getConditionSetIndex()); assertEquals(e.toString(), 1, e.getConditionSetSize()); assertEquals("trigger-2", e.getTriggerId()); assertTrue(e.isMatch()); assertTrue(!v.equals(e.getValue())); v = e.getValue(); assertTrue(e.toString(), v.equals(5.0D) || v.equals(10.0D)); assertEquals(e.getCondition().toString(), "NumericData-01", e.getCondition().getDataId()); assertTrue(e.getCondition().getDataId().equals("NumericData-01")); a = alerts.get(3); assertEquals(a.getTriggerId(), "trigger-3", a.getTriggerId()); assertEquals(a.getEvalSets().toString(), 1, a.getEvalSets().size()); eval = a.getEvalSets().get(0); assertEquals(eval.toString(), 1, eval.size()); e = (ThresholdConditionEval) eval.iterator().next(); assertEquals(e.toString(), 1, e.getConditionSetIndex()); assertEquals(e.toString(), 1, e.getConditionSetSize()); assertEquals("trigger-3", e.getTriggerId()); assertTrue(e.isMatch()); v = e.getValue(); assertTrue(e.toString(), v.equals(15.0D)); assertEquals(e.getCondition().toString(), "NumericData-01", e.getCondition().getDataId()); a = alerts.get(4); assertEquals(a.getTriggerId(), "trigger-4", a.getTriggerId()); assertEquals(a.getEvalSets().toString(), 1, a.getEvalSets().size()); eval = a.getEvalSets().get(0); assertEquals(eval.toString(), 1, eval.size()); e = (ThresholdConditionEval) eval.iterator().next(); assertEquals(e.toString(), 1, e.getConditionSetIndex()); assertEquals(e.toString(), 1, e.getConditionSetSize()); assertEquals("trigger-4", e.getTriggerId()); assertTrue(e.isMatch()); v = e.getValue(); assertTrue(e.toString(), v.equals(15.0D) || v.equals(10.0D)); assertEquals(e.getCondition().toString(), "NumericData-01", e.getCondition().getDataId()); a = alerts.get(5); assertEquals(a.getTriggerId(), "trigger-4", a.getTriggerId()); assertEquals(a.getEvalSets().toString(), 1, a.getEvalSets().size()); eval = a.getEvalSets().get(0); assertEquals(eval.toString(), 1, eval.size()); e = (ThresholdConditionEval) eval.iterator().next(); assertEquals(e.toString(), 1, e.getConditionSetIndex()); assertEquals(e.toString(), 1, e.getConditionSetSize()); assertEquals("trigger-4", e.getTriggerId()); assertTrue(e.isMatch()); assertTrue(!v.equals(e.getValue())); v = e.getValue(); assertTrue(e.toString(), v.equals(15.0D) || v.equals(10.0D)); assertEquals(e.getCondition().toString(), "NumericData-01", e.getCondition().getDataId()); } @Test public void thresholdRangeTest() { Trigger t1 = new Trigger("tenant", "trigger-1", "NumericData-01-"); // should fire 2 alerts ThresholdRangeCondition t1c1 = new ThresholdRangeCondition("tenant", "trigger-1", 1, 1, "NumericData-01", ThresholdRangeCondition.Operator.INCLUSIVE, ThresholdRangeCondition.Operator.INCLUSIVE, 10.0, 15.0, true); // should fine 0 alerts Trigger t2 = new Trigger("tenant", "trigger-2", "NumericData-01"); ThresholdRangeCondition t2c1 = new ThresholdRangeCondition("tenant", "trigger-2", 1, 1, "NumericData-01", ThresholdRangeCondition.Operator.EXCLUSIVE, ThresholdRangeCondition.Operator.EXCLUSIVE, 10.0, 15.0, true); // should fire 1 alert Trigger t3 = new Trigger("tenant", "trigger-3", "NumericData-01"); ThresholdRangeCondition t3c1 = new ThresholdRangeCondition("tenant", "trigger-3", 1, 1, "NumericData-01", ThresholdRangeCondition.Operator.INCLUSIVE, ThresholdRangeCondition.Operator.INCLUSIVE, 10.0, 15.0, false); datums.add(Data.forNumeric("tenant", "NumericData-01", 1000, 10.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 2000, 5.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 3000, 15.0)); // default dampening t1.setEnabled(true); t2.setEnabled(true); t3.setEnabled(true); rulesEngine.addFact(t1); rulesEngine.addFact(t1c1); rulesEngine.addFact(t2); rulesEngine.addFact(t2c1); rulesEngine.addFact(t3); rulesEngine.addFact(t3c1); rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 3, alerts.size()); Collections.sort(alerts, (Alert a1, Alert a2) -> a1.getTriggerId().compareTo(a2.getTriggerId())); Alert a = alerts.get(0); assertTrue(a.getTriggerId(), a.getTriggerId().equals("trigger-1")); assertEquals(1, a.getEvalSets().size()); Set<ConditionEval> evals = a.getEvalSets().get(0); assertEquals(evals.toString(), 1, evals.size()); ThresholdRangeConditionEval e = (ThresholdRangeConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertTrue(e.toString(), e.getTriggerId().equals("trigger-1")); assertTrue(e.isMatch()); Double v = e.getValue(); assertTrue(e.toString(), v.equals(10.0D) || v.equals(15.0D)); assertTrue(e.getCondition().toString(), e.getCondition().getDataId().equals("NumericData-01")); a = alerts.get(1); assertTrue(a.getTriggerId(), a.getTriggerId().equals("trigger-1")); assertEquals(1, a.getEvalSets().size()); evals = a.getEvalSets().get(0); assertEquals(evals.toString(), 1, evals.size()); e = (ThresholdRangeConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertTrue(e.toString(), e.getTriggerId().equals("trigger-1")); assertTrue(e.isMatch()); assertTrue(!v.equals(e.getValue())); v = e.getValue(); assertTrue(e.toString(), v.equals(10.0D) || v.equals(15.0D)); assertTrue(e.getCondition().toString(), e.getCondition().getDataId().equals("NumericData-01")); a = alerts.get(2); assertTrue(a.getTriggerId(), a.getTriggerId().equals("trigger-3")); assertEquals(1, a.getEvalSets().size()); evals = a.getEvalSets().get(0); assertEquals(evals.toString(), 1, evals.size()); e = (ThresholdRangeConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertTrue(e.toString(), e.getTriggerId().equals("trigger-3")); assertTrue(e.isMatch()); v = e.getValue(); assertTrue(e.toString(), v.equals(5.0D)); assertTrue(e.getCondition().toString(), e.getCondition().getDataId().equals("NumericData-01")); } @Test public void compareTest() { Trigger t1 = new Trigger("tenant", "trigger-1", "Compare-D1-LT-Half-D2"); CompareCondition t1c1 = new CompareCondition("tenant", "trigger-1", 1, 1, "NumericData-01", CompareCondition.Operator.LT, 0.5, "NumericData-02"); Trigger t2 = new Trigger("tenant", "trigger-2", "Compare-D1-LTE-Half-D2"); CompareCondition t2c1 = new CompareCondition("tenant", "trigger-2", 1, 1, "NumericData-01", CompareCondition.Operator.LTE, 0.5, "NumericData-02"); Trigger t3 = new Trigger("tenant", "trigger-3", "Compare-D1-GT-Half-D2"); CompareCondition t3c1 = new CompareCondition("tenant", "trigger-3", 1, 1, "NumericData-01", CompareCondition.Operator.GT, 0.5, "NumericData-02"); Trigger t4 = new Trigger("tenant", "trigger-4", "Compare-D1-GTE-Half-D2"); CompareCondition t4c1 = new CompareCondition("tenant", "trigger-4", 1, 1, "NumericData-01", CompareCondition.Operator.GTE, 0.5, "NumericData-02"); // default dampening t1.setEnabled(true); t2.setEnabled(true); t3.setEnabled(true); t4.setEnabled(true); rulesEngine.addFact(t1); rulesEngine.addFact(t1c1); rulesEngine.addFact(t2); rulesEngine.addFact(t2c1); rulesEngine.addFact(t3); rulesEngine.addFact(t3c1); rulesEngine.addFact(t4); rulesEngine.addFact(t4c1); // note that for compare conditions both datums need to be in WM for the same rules execution. This // has some subtleties. Because one rule execution will only see the the oldest datum for a specific // dataId, we need several rule executions to test all of the above triggers. // Test LT (also LTE) datums.add(Data.forNumeric("tenant", "NumericData-01", 1000, 10.0)); datums.add(Data.forNumeric("tenant", "NumericData-02", 2000, 30.0)); rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 2, alerts.size()); Collections.sort(alerts, (Alert a1, Alert a2) -> a1.getTriggerId().compareTo(a2.getTriggerId())); Alert a = alerts.get(0); assertTrue(a.getTriggerId(), a.getTriggerId().equals("trigger-1")); assertEquals(1, a.getEvalSets().size()); Set<ConditionEval> evals = a.getEvalSets().get(0); assertEquals(evals.toString(), 1, evals.size()); CompareConditionEval e = (CompareConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertTrue(e.toString(), e.getTriggerId().equals("trigger-1")); assertTrue(e.isMatch()); Double v1 = e.getValue1(); Double v2 = e.getValue2(); assertTrue(e.toString(), v1.equals(10.0D)); assertTrue(e.toString(), v2.equals(30.0D)); assertTrue(e.getCondition().toString(), e.getCondition().getDataId().equals("NumericData-01")); assertTrue(e.getCondition().toString(), e.getCondition().getData2Id().equals("NumericData-02")); a = alerts.get(1); assertTrue(a.getTriggerId(), a.getTriggerId().equals("trigger-2")); assertEquals(1, a.getEvalSets().size()); evals = a.getEvalSets().get(0); assertEquals(evals.toString(), 1, evals.size()); e = (CompareConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertTrue(e.toString(), e.getTriggerId().equals("trigger-2")); assertTrue(e.isMatch()); v1 = e.getValue1(); v2 = e.getValue2(); assertTrue(e.toString(), v1.equals(10.0D)); assertTrue(e.toString(), v2.equals(30.0D)); assertTrue(e.getCondition().toString(), e.getCondition().getDataId().equals("NumericData-01")); assertTrue(e.getCondition().toString(), e.getCondition().getData2Id().equals("NumericData-02")); // Test LTE + GTE datums.clear(); alerts.clear(); datums.add(Data.forNumeric("tenant", "NumericData-01", 3000, 10.0)); datums.add(Data.forNumeric("tenant", "NumericData-02", 4000, 20.0)); rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 2, alerts.size()); Collections.sort(alerts, (Alert a1, Alert a2) -> a1.getTriggerId().compareTo(a2.getTriggerId())); a = alerts.get(0); assertTrue(a.getTriggerId(), a.getTriggerId().equals("trigger-2")); assertEquals(1, a.getEvalSets().size()); evals = a.getEvalSets().get(0); assertEquals(evals.toString(), 1, evals.size()); e = (CompareConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertTrue(e.toString(), e.getTriggerId().equals("trigger-2")); assertTrue(e.isMatch()); v1 = e.getValue1(); v2 = e.getValue2(); assertTrue(e.toString(), v1.equals(10.0D)); assertTrue(e.toString(), v2.equals(20.0D)); assertTrue(e.getCondition().toString(), e.getCondition().getDataId().equals("NumericData-01")); assertTrue(e.getCondition().toString(), e.getCondition().getData2Id().equals("NumericData-02")); a = alerts.get(1); assertTrue(a.getTriggerId(), a.getTriggerId().equals("trigger-4")); assertEquals(1, a.getEvalSets().size()); evals = a.getEvalSets().get(0); assertEquals(evals.toString(), 1, evals.size()); e = (CompareConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertTrue(e.toString(), e.getTriggerId().equals("trigger-4")); assertTrue(e.isMatch()); v1 = e.getValue1(); v2 = e.getValue2(); assertTrue(e.toString(), v1.equals(10.0D)); assertTrue(e.toString(), v2.equals(20.0D)); assertTrue(e.getCondition().toString(), e.getCondition().getDataId().equals("NumericData-01")); assertTrue(e.getCondition().toString(), e.getCondition().getData2Id().equals("NumericData-02")); // Test GT (also GTE) datums.clear(); alerts.clear(); datums.add(Data.forNumeric("tenant", "NumericData-01", 5000, 15.0)); datums.add(Data.forNumeric("tenant", "NumericData-02", 6000, 20.0)); rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 2, alerts.size()); Collections.sort(alerts, (Alert a1, Alert a2) -> a1.getTriggerId().compareTo(a2.getTriggerId())); a = alerts.get(0); assertTrue(a.getTriggerId(), a.getTriggerId().equals("trigger-3")); assertEquals(1, a.getEvalSets().size()); evals = a.getEvalSets().get(0); assertEquals(evals.toString(), 1, evals.size()); e = (CompareConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertTrue(e.toString(), e.getTriggerId().equals("trigger-3")); assertTrue(e.isMatch()); v1 = e.getValue1(); v2 = e.getValue2(); assertTrue(e.toString(), v1.equals(15.0D)); assertTrue(e.toString(), v2.equals(20.0D)); assertTrue(e.getCondition().toString(), e.getCondition().getDataId().equals("NumericData-01")); assertTrue(e.getCondition().toString(), e.getCondition().getData2Id().equals("NumericData-02")); a = alerts.get(1); assertTrue(a.getTriggerId(), a.getTriggerId().equals("trigger-4")); assertEquals(1, a.getEvalSets().size()); evals = a.getEvalSets().get(0); assertEquals(evals.toString(), 1, evals.size()); e = (CompareConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertTrue(e.toString(), e.getTriggerId().equals("trigger-4")); assertTrue(e.isMatch()); v1 = e.getValue1(); v2 = e.getValue2(); assertTrue(e.toString(), v1.equals(15.0D)); assertTrue(e.toString(), v2.equals(20.0D)); assertTrue(e.getCondition().toString(), e.getCondition().getDataId().equals("NumericData-01")); assertTrue(e.getCondition().toString(), e.getCondition().getData2Id().equals("NumericData-02")); } @Test public void compareTest2() { Trigger t1 = new Trigger("tenant", "trigger-1", "Compare-D1-LT-Half-D2"); CompareCondition t1c1 = new CompareCondition("tenant", "trigger-1", 1, 1, "NumericData-01", CompareCondition.Operator.LT, 0.5, "NumericData-02"); Dampening t1d = Dampening.forRelaxedCount("tenant", "trigger-1", Mode.FIRING, 2, 3); t1.setEnabled(true); rulesEngine.addFact(t1); rulesEngine.addFact(t1c1); rulesEngine.addFact(t1d); // Test Adding 1 datum at a time, with some repetition // T1: D1=10, D2 unset : no condition eval datums.add(Data.forNumeric("tenant", "NumericData-01", 1000, 10.0)); rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 0, alerts.size()); // T2: D1=15, D2 unset : no condition eval datums.clear(); datums.add(Data.forNumeric("tenant", "NumericData-01", 2000, 15.0)); rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 0, alerts.size()); // T3: D1=15, D2=25 : Eval False, dampening 0 for 1 datums.clear(); datums.add(Data.forNumeric("tenant", "NumericData-02", 3000, 25.0)); rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 0, alerts.size()); // T4: D1=15, D2=50 : Eval True, dampening 1 for 2 datums.clear(); datums.add(Data.forNumeric("tenant", "NumericData-02", 4000, 50.0)); rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 0, alerts.size()); // T5: D1=20, D2=50 : Eval True, dampening 2 for 3 -> fire datums.clear(); datums.add(Data.forNumeric("tenant", "NumericData-01", 5000, 20.0)); rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 1, alerts.size()); Alert a = alerts.get(0); assertTrue(a.getTriggerId(), a.getTriggerId().equals("trigger-1")); assertEquals(2, a.getEvalSets().size()); Set<ConditionEval> evals = a.getEvalSets().get(0); assertEquals(evals.toString(), 1, evals.size()); CompareConditionEval e = (CompareConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertTrue(e.toString(), e.getTriggerId().equals("trigger-1")); assertTrue(e.isMatch()); Double v1 = e.getValue1(); Double v2 = e.getValue2(); assertTrue(e.toString(), v1.equals(15.0D)); assertTrue(e.toString(), v2.equals(50.0D)); assertTrue(e.getCondition().toString(), e.getCondition().getDataId().equals("NumericData-01")); assertTrue(e.getCondition().toString(), e.getCondition().getData2Id().equals("NumericData-02")); evals = a.getEvalSets().get(1); assertEquals(evals.toString(), 1, evals.size()); e = (CompareConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertTrue(e.toString(), e.getTriggerId().equals("trigger-1")); assertTrue(e.isMatch()); v1 = e.getValue1(); v2 = e.getValue2(); assertTrue(e.toString(), v1.equals(20.0D)); assertTrue(e.toString(), v2.equals(50.0D)); assertTrue(e.getCondition().toString(), e.getCondition().getDataId().equals("NumericData-01")); assertTrue(e.getCondition().toString(), e.getCondition().getData2Id().equals("NumericData-02")); } @Test public void multipleConditionsOnSameDataIdWithAnyMatching() { Trigger t1 = new Trigger("tenant", "trigger-1", "Multiple Conditions in ANY"); t1.setFiringMatch(Match.ANY); t1.setEventType(EventType.EVENT); t1.setEnabled(true); ThresholdCondition t1c1 = new ThresholdCondition("tenant", "trigger-1", 2, 1, "HeapUsed", ThresholdCondition.Operator.LT, 10.0); ThresholdCondition t1c2 = new ThresholdCondition("tenant", "trigger-1", 2, 2, "HeapUsed", ThresholdCondition.Operator.GT, 20.0); // Default dampening rulesEngine.addFacts(Arrays.asList(t1, t1c1, t1c2)); datums.add(Data.forNumeric("tenant", "HeapUsed", 1000, 9.0)); datums.add(Data.forNumeric("tenant", "HeapUsed", 2000, 11.0)); datums.add(Data.forNumeric("tenant", "HeapUsed", 3000, 21.0)); datums.add(Data.forNumeric("tenant", "HeapUsed", 4000, 19.0)); rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(2, outputEvents.size()); } @Test public void StringTest() { // StringData-01 Triggers // 2 alerts Trigger t1 = new Trigger("tenant", "trigger-1", "String-StartsWith"); StringCondition t1c1 = new StringCondition("tenant", "trigger-1", 1, 1, "StringData-01", StringCondition.Operator.STARTS_WITH, "Fred", false); // 1 alert Trigger t2 = new Trigger("tenant", "trigger-2", "String-Equal"); StringCondition t3c1 = new StringCondition("tenant", "trigger-2", 1, 1, "StringData-01", StringCondition.Operator.EQUAL, "Fred", false); // 1 alert Trigger t3 = new Trigger("tenant", "trigger-3", "String-Contains"); StringCondition t5c1 = new StringCondition("tenant", "trigger-3", 1, 1, "StringData-01", StringCondition.Operator.CONTAINS, "And", false); // 1 alert Trigger t4 = new Trigger("tenant", "trigger-4", "String-Match"); StringCondition t6c1 = new StringCondition("tenant", "trigger-4", 1, 1, "StringData-01", StringCondition.Operator.MATCH, "Fred.*Barney", false); // StringData-02 Triggers // 1 alert Trigger t5 = new Trigger("tenant", "trigger-5", "String-EndsWith"); StringCondition t2c1 = new StringCondition("tenant", "trigger-5", 1, 1, "StringData-02", StringCondition.Operator.ENDS_WITH, "Fred", false); // 1 alert Trigger t6 = new Trigger("tenant", "trigger-6", "String-StartsWith"); StringCondition t4c1 = new StringCondition("tenant", "trigger-6", 1, 1, "StringData-02", // note StringCondition.Operator.NOT_EQUAL, "Fred", false); datums.add(Data.forString("tenant", "StringData-01", 1000, "Fred")); datums.add(Data.forString("tenant", "StringData-01", 2000, "Fred And Barney")); datums.add(Data.forString("tenant", "StringData-02", 1000, "Barney And Fred")); // default dampening t1.setEnabled(true); t2.setEnabled(true); t3.setEnabled(true); t4.setEnabled(true); t5.setEnabled(true); t6.setEnabled(true); rulesEngine.addFact(t1); rulesEngine.addFact(t1c1); rulesEngine.addFact(t2); rulesEngine.addFact(t2c1); rulesEngine.addFact(t3); rulesEngine.addFact(t3c1); rulesEngine.addFact(t4); rulesEngine.addFact(t4c1); rulesEngine.addFact(t5); rulesEngine.addFact(t5c1); rulesEngine.addFact(t6); rulesEngine.addFact(t6c1); rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 7, alerts.size()); Collections.sort(alerts, (Alert a1, Alert a2) -> a1.getTriggerId().compareTo(a2.getTriggerId())); Alert a = alerts.get(0); assertTrue(a.getTriggerId(), a.getTriggerId().equals("trigger-1")); assertEquals(1, a.getEvalSets().size()); Set<ConditionEval> evals = a.getEvalSets().get(0); assertEquals(1, evals.size()); StringConditionEval e = (StringConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertEquals("trigger-1", e.getTriggerId()); assertTrue(e.isMatch()); String v = e.getValue(); assertEquals("Fred", v); assertEquals("StringData-01", e.getCondition().getDataId()); a = alerts.get(1); assertTrue(a.getTriggerId(), a.getTriggerId().equals("trigger-1")); assertEquals(1, a.getEvalSets().size()); evals = a.getEvalSets().get(0); assertEquals(1, evals.size()); e = (StringConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertEquals("trigger-1", e.getTriggerId()); assertTrue(e.isMatch()); v = e.getValue(); assertEquals("Fred And Barney", v); assertEquals("StringData-01", e.getCondition().getDataId()); a = alerts.get(2); assertTrue(a.getTriggerId(), a.getTriggerId().equals("trigger-2")); assertEquals(1, a.getEvalSets().size()); evals = a.getEvalSets().get(0); assertEquals(1, evals.size()); e = (StringConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertEquals("trigger-2", e.getTriggerId()); assertTrue(e.isMatch()); v = e.getValue(); assertEquals("Fred", v); assertEquals("StringData-01", e.getCondition().getDataId()); a = alerts.get(3); assertTrue(a.getTriggerId(), a.getTriggerId().equals("trigger-3")); assertEquals(1, a.getEvalSets().size()); evals = a.getEvalSets().get(0); assertEquals(1, evals.size()); e = (StringConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertEquals("trigger-3", e.getTriggerId()); assertTrue(e.isMatch()); v = e.getValue(); assertEquals("Fred And Barney", v); assertEquals("StringData-01", e.getCondition().getDataId()); a = alerts.get(4); assertTrue(a.getTriggerId(), a.getTriggerId().equals("trigger-4")); assertEquals(1, a.getEvalSets().size()); evals = a.getEvalSets().get(0); assertEquals(1, evals.size()); e = (StringConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertEquals("trigger-4", e.getTriggerId()); assertTrue(e.isMatch()); v = e.getValue(); assertEquals("Fred And Barney", v); assertEquals("StringData-01", e.getCondition().getDataId()); a = alerts.get(5); assertTrue(a.getTriggerId(), a.getTriggerId().equals("trigger-5")); assertEquals(1, a.getEvalSets().size()); evals = a.getEvalSets().get(0); assertEquals(1, evals.size()); e = (StringConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertEquals("trigger-5", e.getTriggerId()); assertTrue(e.isMatch()); v = e.getValue(); assertEquals("Barney And Fred", v); assertEquals("StringData-02", e.getCondition().getDataId()); a = alerts.get(6); assertTrue(a.getTriggerId(), a.getTriggerId().equals("trigger-6")); assertEquals(1, a.getEvalSets().size()); evals = a.getEvalSets().get(0); assertEquals(1, evals.size()); e = (StringConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertEquals("trigger-6", e.getTriggerId()); assertTrue(e.isMatch()); v = e.getValue(); assertEquals("Barney And Fred", v); assertEquals("StringData-02", e.getCondition().getDataId()); } @Test public void StringTestIgnoreCase() { // StringData-01 Triggers // 2 alerts Trigger t1 = new Trigger("tenant", "trigger-1", "String-StartsWith"); StringCondition t1c1 = new StringCondition("tenant", "trigger-1", 1, 1, "StringData-01", StringCondition.Operator.STARTS_WITH, "FRED", true); // 1 alert Trigger t2 = new Trigger("tenant", "trigger-2", "String-Equal"); StringCondition t3c1 = new StringCondition("tenant", "trigger-2", 1, 1, "StringData-01", StringCondition.Operator.EQUAL, "FRED", true); // 1 alert Trigger t3 = new Trigger("tenant", "trigger-3", "String-Contains"); StringCondition t5c1 = new StringCondition("tenant", "trigger-3", 1, 1, "StringData-01", StringCondition.Operator.CONTAINS, "AND", true); // 1 alert Trigger t4 = new Trigger("tenant", "trigger-4", "String-Match"); StringCondition t6c1 = new StringCondition("tenant", "trigger-4", 1, 1, "StringData-01", StringCondition.Operator.MATCH, "FRED.*barney", true); // StringData-02 Triggers // 1 alert Trigger t5 = new Trigger("tenant", "trigger-5", "String-EndsWith"); StringCondition t2c1 = new StringCondition("tenant", "trigger-5", 1, 1, "StringData-02", StringCondition.Operator.ENDS_WITH, "FRED", true); // 1 alert Trigger t6 = new Trigger("tenant", "trigger-6", "String-StartsWith"); StringCondition t4c1 = new StringCondition("tenant", "trigger-6", 1, 1, "StringData-02", // note StringCondition.Operator.NOT_EQUAL, "FRED", true); datums.add(Data.forString("tenant", "StringData-01", 1000, "Fred")); datums.add(Data.forString("tenant", "StringData-01", 2000, "Fred And Barney")); datums.add(Data.forString("tenant", "StringData-02", 1000, "Barney And Fred")); // default dampening t1.setEnabled(true); t2.setEnabled(true); t3.setEnabled(true); t4.setEnabled(true); t5.setEnabled(true); t6.setEnabled(true); rulesEngine.addFact(t1); rulesEngine.addFact(t1c1); rulesEngine.addFact(t2); rulesEngine.addFact(t2c1); rulesEngine.addFact(t3); rulesEngine.addFact(t3c1); rulesEngine.addFact(t4); rulesEngine.addFact(t4c1); rulesEngine.addFact(t5); rulesEngine.addFact(t5c1); rulesEngine.addFact(t6); rulesEngine.addFact(t6c1); rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 7, alerts.size()); Collections.sort(alerts, (Alert a1, Alert a2) -> a1.getTriggerId().compareTo(a2.getTriggerId())); Alert a = alerts.get(0); assertEquals("trigger-1", a.getTriggerId()); assertEquals(1, a.getEvalSets().size()); Set<ConditionEval> evals = a.getEvalSets().get(0); assertEquals(1, evals.size()); StringConditionEval e = (StringConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertEquals("trigger-1", e.getTriggerId()); assertTrue(e.isMatch()); String v = e.getValue(); assertEquals("Fred", v); assertEquals("StringData-01", e.getCondition().getDataId()); a = alerts.get(1); assertEquals("trigger-1", a.getTriggerId()); assertEquals(1, a.getEvalSets().size()); evals = a.getEvalSets().get(0); assertEquals(1, evals.size()); e = (StringConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertEquals("trigger-1", e.getTriggerId()); assertTrue(e.isMatch()); v = e.getValue(); assertEquals("Fred And Barney", v); assertEquals("StringData-01", e.getCondition().getDataId()); a = alerts.get(2); assertEquals("trigger-2", a.getTriggerId()); assertEquals(1, a.getEvalSets().size()); evals = a.getEvalSets().get(0); assertEquals(1, evals.size()); e = (StringConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertEquals("trigger-2", e.getTriggerId()); assertTrue(e.isMatch()); v = e.getValue(); assertEquals("Fred", v); assertEquals("StringData-01", e.getCondition().getDataId()); a = alerts.get(3); assertEquals("trigger-3", a.getTriggerId()); assertEquals(1, a.getEvalSets().size()); evals = a.getEvalSets().get(0); assertEquals(1, evals.size()); e = (StringConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertEquals("trigger-3", e.getTriggerId()); assertTrue(e.isMatch()); v = e.getValue(); assertEquals("Fred And Barney", v); assertEquals("StringData-01", e.getCondition().getDataId()); a = alerts.get(4); assertEquals("trigger-4", a.getTriggerId()); assertEquals(1, a.getEvalSets().size()); evals = a.getEvalSets().get(0); assertEquals(1, evals.size()); e = (StringConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertEquals("trigger-4", e.getTriggerId()); assertTrue(e.isMatch()); v = e.getValue(); assertEquals("Fred And Barney", v); assertEquals("StringData-01", e.getCondition().getDataId()); a = alerts.get(5); assertEquals("trigger-5", a.getTriggerId()); assertEquals(1, a.getEvalSets().size()); evals = a.getEvalSets().get(0); assertEquals(1, evals.size()); e = (StringConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertEquals("trigger-5", e.getTriggerId()); assertTrue(e.isMatch()); v = e.getValue(); assertEquals("Barney And Fred", v); assertEquals("StringData-02", e.getCondition().getDataId()); a = alerts.get(6); assertEquals("trigger-6", a.getTriggerId()); assertEquals(1, a.getEvalSets().size()); evals = a.getEvalSets().get(0); assertEquals(1, evals.size()); e = (StringConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertEquals("trigger-6", e.getTriggerId()); assertTrue(e.isMatch()); v = e.getValue(); assertEquals("Barney And Fred", v); assertEquals("StringData-02", e.getCondition().getDataId()); } @Test public void availabilityTest() { Trigger t1 = new Trigger("tenant", "trigger-1", "Avail-DOWN"); AvailabilityCondition t1c1 = new AvailabilityCondition("tenant", "trigger-1", 1, 1, "AvailData-01", AvailabilityCondition.Operator.NOT_UP); datums.add(Data.forAvailability("tenant", "AvailData-01", 1000, AvailabilityType.DOWN)); datums.add(Data.forAvailability("tenant", "AvailData-01", 2000, AvailabilityType.UNAVAILABLE)); datums.add(Data.forAvailability("tenant", "AvailData-01", 3000, AvailabilityType.UP)); // default dampening t1.setEnabled(true); rulesEngine.addFact(t1); rulesEngine.addFact(t1c1); rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 2, alerts.size()); Alert a = alerts.get(0); assertEquals("trigger-1", a.getTriggerId()); assertEquals(1, a.getEvalSets().size()); Set<ConditionEval> evals = a.getEvalSets().get(0); assertEquals(1, evals.size()); AvailabilityConditionEval e = (AvailabilityConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertEquals("trigger-1", e.getTriggerId()); assertTrue(e.isMatch()); AvailabilityType v = e.getValue(); assertEquals(AvailabilityType.DOWN, v); assertEquals("AvailData-01", e.getCondition().getDataId()); a = alerts.get(1); assertEquals("trigger-1", a.getTriggerId()); assertEquals(1, a.getEvalSets().size()); evals = a.getEvalSets().get(0); assertEquals(1, evals.size()); e = (AvailabilityConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertEquals("trigger-1", e.getTriggerId()); assertTrue(e.isMatch()); v = e.getValue(); assertEquals(AvailabilityType.UNAVAILABLE, v); assertEquals("AvailData-01", e.getCondition().getDataId()); } @Test public void externalDataTest() { Trigger t1 = new Trigger("tenant", "trigger-1", "External-Metrics"); ExternalCondition t1c1 = new ExternalCondition("tenant", "trigger-1", Mode.FIRING, 1, 1, "ExternalData-01", "HawkularMetrics", "metric:5:avg(foo > 100.5)"); datums.add(Data.forString("tenant", "ExternalData-01", 1000, "Ignored")); // default dampening t1.setEnabled(true); rulesEngine.addFact(t1); rulesEngine.addFact(t1c1); rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 1, alerts.size()); Alert a = alerts.get(0); assertEquals("trigger-1", a.getTriggerId()); assertEquals(1, a.getEvalSets().size()); Set<ConditionEval> evals = a.getEvalSets().get(0); assertEquals(1, evals.size()); ExternalConditionEval e = (ExternalConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertEquals("trigger-1", e.getTriggerId()); assertTrue(e.isMatch()); String v = e.getValue(); assertEquals("Ignored", v); assertEquals("ExternalData-01", e.getCondition().getDataId()); } @Test public void externalEventTest() { Trigger t1 = new Trigger("tenant", "trigger-1", "External-Metrics"); ExternalCondition t1c1 = new ExternalCondition("tenant", "trigger-1", Mode.FIRING, 1, 1, "ExternalData-01", "HawkularMetrics", "event:groupBy(tags.accountId):having(count > 1)"); Event appDownEvent = new Event("tenant", UUID.randomUUID().toString(), "ExternalData-01", EventCategory.DEPLOYMENT.name(), "DOWN"); inputEvents.add(appDownEvent); // default dampening t1.setEnabled(true); rulesEngine.addFact(t1); rulesEngine.addFact(t1c1); rulesEngine.addEvents(inputEvents); rulesEngine.fire(); assertEquals(alerts.toString(), 1, alerts.size()); Alert a = alerts.get(0); assertEquals("trigger-1", a.getTriggerId()); assertEquals(1, a.getEvalSets().size()); Set<ConditionEval> evals = a.getEvalSets().get(0); assertEquals(1, evals.size()); ExternalConditionEval e = (ExternalConditionEval) evals.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertEquals("trigger-1", e.getTriggerId()); assertTrue(e.isMatch()); Event event = e.getEvent(); assertEquals("DOWN", event.getText()); assertEquals("ExternalData-01", e.getCondition().getDataId()); } @Test public void eventTest() { Trigger t1 = new Trigger("tenant", "trigger-1", "Events Test"); t1.setEventType(EventType.EVENT); EventCondition t1c1 = new EventCondition("tenant", "trigger-1", "myapp.war", "text == 'DOWN'"); Event appDownEvent = new Event("tenant", UUID.randomUUID().toString(), "myapp.war", EventCategory.DEPLOYMENT.name(), "DOWN"); inputEvents.add(appDownEvent); t1.setEnabled(true); rulesEngine.addFact(t1); rulesEngine.addFact(t1c1); rulesEngine.addEvents(inputEvents); rulesEngine.fire(); assertEquals(outputEvents.toString(), 1, outputEvents.size()); } @Test public void rateTest() { // 1 alert Trigger t1 = new Trigger("tenant", "trigger-1", "Rate-Increasing"); RateCondition t1c1 = new RateCondition("tenant", "trigger-1", "CounterUp", RateCondition.Direction.INCREASING, RateCondition.Period.MINUTE, RateCondition.Operator.GT, 20.0); // 1 alert Trigger t2 = new Trigger("tenant", "trigger-2", "Rate-Decreasing"); RateCondition t2c1 = new RateCondition("tenant", "trigger-2", "CounterDown", RateCondition.Direction.DECREASING, RateCondition.Period.HOUR, RateCondition.Operator.GT, 2000.0); long t1minute = 60000L * 1; long t3minute = t1minute * 3; long t5minute = t1minute * 5; datums.add(Data.forNumeric("tenant", "CounterUp", t1minute, 10.0)); // minute 1 datums.add(Data.forNumeric("tenant", "CounterUp", t3minute, 20.0)); // minute 3 (rate = 5 per minute) datums.add(Data.forNumeric("tenant", "CounterUp", t5minute, 100.0)); // minute 5 (rate = 40 per minute) datums.add(Data.forNumeric("tenant", "CounterDown", t1minute, 100.0)); // minute 1 datums.add(Data.forNumeric("tenant", "CounterDown", t3minute, 90.0)); // minute 3 (rate = 300 per hour) datums.add(Data.forNumeric("tenant", "CounterDown", t5minute, 10.0)); // minute 5 (rate = 2400 per hour) // default dampening t1.setEnabled(true); t2.setEnabled(true); rulesEngine.addFact(t1); rulesEngine.addFact(t1c1); rulesEngine.addFact(t2); rulesEngine.addFact(t2c1); rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 2, alerts.size()); Collections.sort(alerts, (Alert a1, Alert a2) -> a1.getTriggerId().compareTo(a2.getTriggerId())); Alert a = alerts.get(0); assertEquals(a.getTriggerId(), "trigger-1", a.getTriggerId()); assertEquals(a.getEvalSets().toString(), 1, a.getEvalSets().size()); Set<ConditionEval> eval = a.getEvalSets().get(0); assertEquals(eval.toString(), 1, eval.size()); RateConditionEval e = (RateConditionEval) eval.iterator().next(); assertEquals(e.toString(), 1, e.getConditionSetIndex()); assertEquals(e.toString(), 1, e.getConditionSetSize()); assertEquals("trigger-1", e.getTriggerId()); assertTrue(e.isMatch()); assertTrue(e.toString(), e.getTime() == t5minute); assertTrue(e.toString(), e.getPreviousTime() == t3minute); assertTrue(e.toString(), e.getValue().equals(100.0)); assertTrue(e.toString(), e.getPreviousValue().equals(20.0)); assertTrue(e.toString(), e.getRate().equals(40.0)); assertEquals(e.getCondition().toString(), "CounterUp", e.getCondition().getDataId()); a = alerts.get(1); assertEquals(a.getTriggerId(), "trigger-2", a.getTriggerId()); assertEquals(a.getEvalSets().toString(), 1, a.getEvalSets().size()); eval = a.getEvalSets().get(0); assertEquals(eval.toString(), 1, eval.size()); e = (RateConditionEval) eval.iterator().next(); assertEquals(e.toString(), 1, e.getConditionSetIndex()); assertEquals(e.toString(), 1, e.getConditionSetSize()); assertEquals("trigger-2", e.getTriggerId()); assertTrue(e.isMatch()); assertTrue(e.toString(), e.getTime() == t5minute); assertTrue(e.toString(), e.getPreviousTime() == t3minute); assertTrue(e.toString(), e.getValue().equals(10.0)); assertTrue(e.toString(), e.getPreviousValue().equals(90.0)); assertTrue(e.toString(), e.getRate().equals(2400.0)); assertEquals(e.getCondition().toString(), "CounterDown", e.getCondition().getDataId()); } @Test public void multipleEventConditions() { Trigger t1 = new Trigger("tenant", "trigger-1", "Events Test"); t1.setEventType(EventType.EVENT); EventCondition t1c1 = new EventCondition("tenant", "trigger-1", Mode.FIRING, 3, 1, "myapp.war", "text == 'DOWN'"); EventCondition t1c2 = new EventCondition("tenant", "trigger-1", Mode.FIRING, 3, 2, "datacenter1", "text starts 'ERROR'"); EventCondition t1c3 = new EventCondition("tenant", "trigger-1", Mode.FIRING, 3, 3, "datacenter2", "text starts 'WARN'"); /* On multiple conditions timestamps on input events are important. */ Event appDownEvent1 = new Event("tenant", UUID.randomUUID().toString(), 1000, "myapp.war", EventCategory.DEPLOYMENT.name(), "DOWN"); Event logErrorEvent1 = new Event("tenant", UUID.randomUUID().toString(), 2000, "datacenter1", EventCategory.LOG.name(), "ERROR [Time] This is a sample as app logging"); Event logWarnEvent1 = new Event("tenant", UUID.randomUUID().toString(), 3000, "datacenter2", EventCategory.LOG.name(), "WARN [Time] This is a sample as app logging"); Event appDownEvent2 = new Event("tenant", UUID.randomUUID().toString(), 4000, "myapp.war", EventCategory.DEPLOYMENT.name(), "UP"); Event logErrorEvent2 = new Event("tenant", UUID.randomUUID().toString(), 5000, "datacenter1", EventCategory.LOG.name(), "ERROR [Time] This is a sample as app logging 2"); Event logWarnEvent2 = new Event("tenant", UUID.randomUUID().toString(), 6000, "datacenter2", EventCategory.LOG.name(), "WARN [Time] This is a sample as app logging 2"); Event appDownEvent3 = new Event("tenant", UUID.randomUUID().toString(), 7000, "myapp.war", EventCategory.DEPLOYMENT.name(), "UP"); Event logErrorEvent3 = new Event("tenant", UUID.randomUUID().toString(), 8000, "datacenter1", EventCategory.LOG.name(), "ERROR [Time] This is a sample as app logging 3"); Event logWarnEvent3 = new Event("tenant", UUID.randomUUID().toString(), 9000, "datacenter2", EventCategory.LOG.name(), "WARN [Time] This is a sample as app logging 3"); inputEvents.add(appDownEvent1); inputEvents.add(logErrorEvent1); inputEvents.add(logWarnEvent1); inputEvents.add(appDownEvent2); inputEvents.add(logErrorEvent2); inputEvents.add(logWarnEvent2); inputEvents.add(appDownEvent3); inputEvents.add(logErrorEvent3); inputEvents.add(logWarnEvent3); t1.setEnabled(true); rulesEngine.addFact(t1); rulesEngine.addFact(t1c1); rulesEngine.addFact(t1c2); rulesEngine.addFact(t1c3); rulesEngine.addEvents(inputEvents); rulesEngine.fire(); /* Trigger1 Conditions "myapp.war" text == 'DOWN' AND "datacenter1" text starts 'ERROR' AND "datacenter2" text starts 'WARN' Incoming data: t1. "myapp.war" "DOWN" t2. "datacenter1" "ERROR [Time] This is a sample as app logging" t3. "datacenter2" "WARN [Time] This is a sample as app logging" --> EVENT t4. "myapp.war" "UP" t5. "datacenter1" "ERROR [Time] This is a sample as app logging" t6. "datacenter2" "WARN [Time] This is a sample as app logging" --> NOT EVENT t7. "myapp.war" "UP" t8. "datacenter1" "ERROR [Time] This is a sample as app logging" t9. "datacenter2" "WARN [Time] This is a sample as app logging" --> NOT EVENT */ assertEquals(outputEvents.toString(), 1, outputEvents.size()); } @Test public void chainedEventsRules() { Trigger t1 = new Trigger("tenant", "trigger-1", "A.war"); t1.setEventType(EventType.EVENT); EventCondition t1c1 = new EventCondition("tenant", "trigger-1", Mode.FIRING, "A.war", "text == 'DOWN'"); Trigger t2 = new Trigger("tenant", "trigger-2", "B.war"); t2.setEventType(EventType.EVENT); EventCondition t2c1 = new EventCondition("tenant", "trigger-2", Mode.FIRING, "B.war", "text == 'DOWN'"); Trigger t3 = new Trigger("tenant", "trigger-3", "A.war and B.war DOWN"); EventCondition t3c1 = new EventCondition("tenant", "trigger-3", Mode.FIRING, 2, 1, "trigger-1"); EventCondition t3c2 = new EventCondition("tenant", "trigger-3", Mode.FIRING, 2, 2, "trigger-2"); Event appADownEvent1 = new Event("tenant", UUID.randomUUID().toString(), 1000, "A.war", EventCategory.DEPLOYMENT.name(), "DOWN"); Event appBDownEvent1 = new Event("tenant", UUID.randomUUID().toString(), 1000, "B.war", EventCategory.DEPLOYMENT.name(), "DOWN"); Event appADownEvent2 = new Event("tenant", UUID.randomUUID().toString(), 2000, "A.war", EventCategory.DEPLOYMENT.name(), "DOWN"); Event appBDownEvent2 = new Event("tenant", UUID.randomUUID().toString(), 2000, "B.war", EventCategory.DEPLOYMENT.name(), "DOWN"); Event appADownEvent3 = new Event("tenant", UUID.randomUUID().toString(), 3000, "A.war", EventCategory.DEPLOYMENT.name(), "DOWN"); Event appBDownEvent3 = new Event("tenant", UUID.randomUUID().toString(), 3000, "B.war", EventCategory.DEPLOYMENT.name(), "DOWN"); inputEvents.add(appADownEvent1); inputEvents.add(appBDownEvent1); inputEvents.add(appADownEvent2); inputEvents.add(appBDownEvent2); inputEvents.add(appADownEvent3); inputEvents.add(appBDownEvent3); t1.setEnabled(true); t2.setEnabled(true); t3.setEnabled(true); rulesEngine.addFact(t1); rulesEngine.addFact(t1c1); rulesEngine.addFact(t2); rulesEngine.addFact(t2c1); rulesEngine.addFact(t3); rulesEngine.addFact(t3c1); rulesEngine.addFact(t3c2); rulesEngine.addEvents(inputEvents); rulesEngine.fire(); assertEquals(outputEvents.toString(), 6, outputEvents.size()); /* Expected inference: Alert 1: id=trigger-1-Event-timestamp-1 id=trigger-2-Event-timestamp-1 Alert 2: id=trigger-1-Event-timestamp-2 ==> NEW Event from trigger-1 id=trigger-2-Event-timestamp-1 Alert 3: id=trigger-1-Event-timestamp-2 id=trigger-2-Event-timestamp-2 ==> NEW Event from trigger-2 Alert 4: id=trigger-1-Event-timestamp-3 ==> NEW Event from trigger-1 id=trigger-2-Event-timestamp-2 Alert 5: id=trigger-1-Event-timestamp-3 id=trigger-2-Event-timestamp-3 ==> NEW Event from trigger-2 */ assertEquals(alerts.toString(), 5, alerts.size()); } @Test public void dampeningStrictTest() { Trigger t1 = new Trigger("tenant", "trigger-1", "Avail-DOWN"); AvailabilityCondition t1c1 = new AvailabilityCondition("tenant", "trigger-1", 1, 1, "AvailData-01", AvailabilityCondition.Operator.DOWN); Dampening t1d = Dampening.forStrict("tenant", "trigger-1", Mode.FIRING, 3); datums.add(Data.forAvailability("tenant", "AvailData-01", 1000, AvailabilityType.DOWN)); datums.add(Data.forAvailability("tenant", "AvailData-01", 2000, AvailabilityType.UNAVAILABLE)); datums.add(Data.forAvailability("tenant", "AvailData-01", 3000, AvailabilityType.UP)); datums.add(Data.forAvailability("tenant", "AvailData-01", 4000, AvailabilityType.DOWN)); datums.add(Data.forAvailability("tenant", "AvailData-01", 5000, AvailabilityType.DOWN)); datums.add(Data.forAvailability("tenant", "AvailData-01", 6000, AvailabilityType.DOWN)); datums.add(Data.forAvailability("tenant", "AvailData-01", 7000, AvailabilityType.UP)); t1.setEnabled(true); rulesEngine.addFact(t1); rulesEngine.addFact(t1c1); rulesEngine.addFact(t1d); rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 1, alerts.size()); Alert a = alerts.get(0); assertEquals("trigger-1", a.getTriggerId()); assertEquals(3, a.getEvalSets().size()); long expectedTimestamp = 4000; for (Set<ConditionEval> evalSet : a.getEvalSets()) { assertEquals(1, evalSet.size()); AvailabilityConditionEval e = (AvailabilityConditionEval) evalSet.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertEquals("trigger-1", e.getTriggerId()); assertTrue(e.isMatch()); assertEquals(expectedTimestamp, e.getDataTimestamp()); expectedTimestamp += 1000; AvailabilityType v = e.getValue(); assertEquals(AvailabilityType.DOWN, v); assertEquals("AvailData-01", e.getCondition().getDataId()); } } @Test public void dampeningRelaxedCountTest() { Trigger t1 = new Trigger("tenant", "trigger-1", "Avail-DOWN"); AvailabilityCondition t1c1 = new AvailabilityCondition("tenant", "trigger-1", 1, 1, "AvailData-01", AvailabilityCondition.Operator.DOWN); Dampening t1d = Dampening.forRelaxedCount("tenant", "trigger-1", Mode.FIRING, 3, 5); datums.add(Data.forAvailability("tenant", "AvailData-01", 1000, AvailabilityType.DOWN)); datums.add(Data.forAvailability("tenant", "AvailData-01", 2000, AvailabilityType.UNAVAILABLE)); datums.add(Data.forAvailability("tenant", "AvailData-01", 3000, AvailabilityType.UP)); datums.add(Data.forAvailability("tenant", "AvailData-01", 4000, AvailabilityType.DOWN)); datums.add(Data.forAvailability("tenant", "AvailData-01", 5000, AvailabilityType.DOWN)); datums.add(Data.forAvailability("tenant", "AvailData-01", 6000, AvailabilityType.DOWN)); datums.add(Data.forAvailability("tenant", "AvailData-01", 7000, AvailabilityType.UP)); t1.setEnabled(true); rulesEngine.addFact(t1); rulesEngine.addFact(t1c1); rulesEngine.addFact(t1d); rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 1, alerts.size()); Alert a = alerts.get(0); assertEquals("trigger-1", a.getTriggerId()); assertEquals(3, a.getEvalSets().size()); int i = 0; long[] expectedTimestamps = new long[] { 1000, 4000, 5000 }; for (Set<ConditionEval> evalSet : a.getEvalSets()) { assertEquals(1, evalSet.size()); AvailabilityConditionEval e = (AvailabilityConditionEval) evalSet.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertEquals("trigger-1", e.getTriggerId()); assertTrue(e.isMatch()); assertEquals(expectedTimestamps[i++], e.getDataTimestamp()); AvailabilityType v = e.getValue(); assertEquals(AvailabilityType.DOWN, v); assertEquals("AvailData-01", e.getCondition().getDataId()); } } @Test public void dampeningRelaxedTimeTest() { Trigger t1 = new Trigger("tenant", "trigger-1", "Avail-DOWN"); AvailabilityCondition t1c1 = new AvailabilityCondition("tenant", "trigger-1", 1, 1, "AvailData-01", AvailabilityCondition.Operator.DOWN); Dampening t1d = Dampening.forRelaxedTime("tenant", "trigger-1", Mode.FIRING, 2, 500L); t1.setEnabled(true); rulesEngine.addFact(t1); rulesEngine.addFact(t1c1); rulesEngine.addFact(t1d); datums.add(Data.forAvailability("tenant", "AvailData-01", 1000, AvailabilityType.DOWN)); rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 0, alerts.size()); try { Thread.sleep(750L); } catch (InterruptedException e1) { } datums.clear(); datums.add(Data.forAvailability("tenant", "AvailData-01", 2000, AvailabilityType.DOWN)); datums.add(Data.forAvailability("tenant", "AvailData-01", 3000, AvailabilityType.UP)); datums.add(Data.forAvailability("tenant", "AvailData-01", 4000, AvailabilityType.DOWN)); datums.add(Data.forAvailability("tenant", "AvailData-01", 5000, AvailabilityType.UP)); rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 1, alerts.size()); Alert a = alerts.get(0); assertEquals("trigger-1", a.getTriggerId()); assertEquals(2, a.getEvalSets().size()); int i = 0; long[] expectedTimestamps = new long[] { 2000, 4000 }; for (Set<ConditionEval> evalSet : a.getEvalSets()) { assertEquals(1, evalSet.size()); AvailabilityConditionEval e = (AvailabilityConditionEval) evalSet.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertEquals("trigger-1", e.getTriggerId()); assertTrue(e.isMatch()); assertEquals(expectedTimestamps[i++], e.getDataTimestamp()); AvailabilityType v = e.getValue(); assertEquals(AvailabilityType.DOWN, v); assertEquals("AvailData-01", e.getCondition().getDataId()); } } @Test public void dampeningStrictTimeTest() { Trigger t1 = new Trigger("tenant", "trigger-1", "Avail-DOWN"); AvailabilityCondition t1c1 = new AvailabilityCondition("tenant", "trigger-1", 1, 1, "AvailData-01", AvailabilityCondition.Operator.DOWN); Dampening t1d = Dampening.forStrictTime("tenant", "trigger-1", Mode.FIRING, 250L); t1.setEnabled(true); rulesEngine.addFact(t1); rulesEngine.addFact(t1c1); rulesEngine.addFact(t1d); long start = System.currentTimeMillis(); int i = 0; while ((alerts.size() == 0) && ((System.currentTimeMillis() - start) < 500)) { rulesEngineAddData(Data.forAvailability("tenant", "AvailData-01", i += 1000, AvailabilityType.DOWN)); rulesEngine.fire(); } assertEquals(alerts.toString(), 1, alerts.size()); Alert a = alerts.get(0); assertTrue(String.valueOf((alerts.get(0).getCtime() - start)), (alerts.get(0).getCtime() - start) >= 250); assertEquals("trigger-1", a.getTriggerId()); assertTrue(String.valueOf(a.getEvalSets().size()), a.getEvalSets().size() >= 2); for (Set<ConditionEval> evalSet : a.getEvalSets()) { assertEquals(1, evalSet.size()); AvailabilityConditionEval e = (AvailabilityConditionEval) evalSet.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertEquals("trigger-1", e.getTriggerId()); assertTrue(e.isMatch()); AvailabilityType v = e.getValue(); assertEquals(AvailabilityType.DOWN, v); assertEquals("AvailData-01", e.getCondition().getDataId()); } } private void rulesEngineAddData(Data data) { TreeSet<Data> ts = new TreeSet<>(); ts.add(data); rulesEngine.addData(ts); } @Test public void dampeningStrictTimeoutTest() { Trigger t1 = new Trigger("tenant", "trigger-1", "Avail-DOWN"); AvailabilityCondition t1c1 = new AvailabilityCondition("tenant", "trigger-1", 1, 1, "AvailData-01", AvailabilityCondition.Operator.DOWN); Dampening t1d = Dampening.forStrictTimeout("tenant", "trigger-1", Mode.FIRING, 200L); t1.setEnabled(true); rulesEngine.addFact(t1); rulesEngine.addFact(t1c1); rulesEngine.addFact(t1d); assertTrue(alerts.isEmpty()); assertTrue(pendingTimeouts.isEmpty()); rulesEngineAddData(Data.forAvailability("tenant", "AvailData-01", 1000, AvailabilityType.DOWN)); rulesEngine.fire(); assertTrue(alerts.isEmpty()); assertEquals(String.valueOf(pendingTimeouts), 1, pendingTimeouts.size()); Dampening pendingTimeout = pendingTimeouts.iterator().next(); pendingTimeout.setSatisfied(true); rulesEngine.updateFact(pendingTimeout); rulesEngine.fireNoData(); assertEquals(alerts.toString(), 1, alerts.size()); Alert a = alerts.get(0); assertEquals("trigger-1", a.getTriggerId()); assertEquals(1, a.getEvalSets().size()); Set<ConditionEval> evalSet = a.getEvalSets().get(0); assertEquals(1, evalSet.size()); AvailabilityConditionEval e = (AvailabilityConditionEval) evalSet.iterator().next(); assertEquals(1, e.getConditionSetIndex()); assertEquals(1, e.getConditionSetSize()); assertEquals("trigger-1", e.getTriggerId()); assertTrue(e.isMatch()); AvailabilityType v = e.getValue(); assertEquals(AvailabilityType.DOWN, v); assertEquals("AvailData-01", e.getCondition().getDataId()); } @Test public void multiConditionTest() { Trigger t1 = new Trigger("tenant", "trigger-1", "Two-Conditions"); ThresholdCondition t1c1 = new ThresholdCondition("tenant", "trigger-1", 2, 1, "NumericData-01", ThresholdCondition.Operator.LT, 10.0); ThresholdRangeCondition t1c2 = new ThresholdRangeCondition("tenant", "trigger-1", 2, 2, "NumericData-02", ThresholdRangeCondition.Operator.INCLUSIVE, ThresholdRangeCondition.Operator.EXCLUSIVE, 100.0, 200.0, true); // default dampening t1.setEnabled(true); rulesEngine.addFact(t1); rulesEngine.addFact(t1c1); rulesEngine.addFact(t1c2); // break up the arrivals of the relevant datums so that we get a more complicated series of evaluations. // remember that for any batch of datums: // 1) one datum for a specific dataId will will be processed at a time // 2) only the most recent conditionEvals will be used in a condition set tuple for a multi-condition trigger datums.add(Data.forNumeric("tenant", "NumericData-01", 1000, 10.0)); // eval(d1,t1)=no match, datums.add(Data.forNumeric("tenant", "NumericData-01", 2000, 5.0)); // eval(d1,t2)= match, replaces eval(d1,t1) datums.add(Data.forNumeric("tenant", "NumericData-01", 3000, 15.0)); // eval(d1,t3)=no match, replaces eval(d1,t2) rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 0, alerts.size()); datums.clear(); // eval(d2,t4) = no match, tuple(eval(d1,t3), eval(d2,t4)) = false datums.add(Data.forNumeric("tenant", "NumericData-02", 4000, 10.0)); // eval(d2,t5) = match, tuple(eval(d1,t3), eval(d2,t5)) = false datums.add(Data.forNumeric("tenant", "NumericData-02", 5000, 150.0)); rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 0, alerts.size()); datums.clear(); // eval(d1,t6) = match, tuple(eval(d1,t6), eval(d2,t5)) = true datums.add(Data.forNumeric("tenant", "NumericData-01", 6000, 8.0)); rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 1, alerts.size()); Alert a = alerts.get(0); assertEquals("trigger-1", a.getTriggerId()); assertEquals(1, a.getEvalSets().size()); Set<ConditionEval> evals = a.getEvalSets().get(0); assertEquals(2, evals.size()); List<ConditionEval> evalsList = new ArrayList<>(evals); Collections.sort( evalsList, (ConditionEval c1, ConditionEval c2) -> Integer.compare(c1.getConditionSetIndex(), c2.getConditionSetIndex())); Iterator<ConditionEval> i = evalsList.iterator(); ThresholdConditionEval e = (ThresholdConditionEval) i.next(); assertEquals(2, e.getConditionSetSize()); assertEquals(1, e.getConditionSetIndex()); assertEquals("trigger-1", e.getTriggerId()); assertTrue(e.isMatch()); Double v = e.getValue(); assertTrue(v.equals(8.0)); assertEquals("NumericData-01", e.getCondition().getDataId()); ThresholdRangeConditionEval e2 = (ThresholdRangeConditionEval) i.next(); assertEquals(2, e2.getConditionSetSize()); assertEquals(2, e2.getConditionSetIndex()); assertEquals("trigger-1", e2.getTriggerId()); assertTrue(e2.isMatch()); v = e2.getValue(); assertTrue(v.equals(150.0)); assertEquals("NumericData-02", e2.getCondition().getDataId()); } @Test public void matchAnyTest() { Trigger t1 = new Trigger("tenant", "trigger-1", "Any-Two-Conditions"); t1.setFiringMatch(Match.ANY); ThresholdCondition t1c1 = new ThresholdCondition("tenant", "trigger-1", 2, 1, "X", ThresholdCondition.Operator.GT, 100.0); ThresholdCondition t1c2 = new ThresholdCondition("tenant", "trigger-1", 2, 2, "Y", ThresholdCondition.Operator.GT, 200.0); Dampening t1d = Dampening.forStrict("tenant", "trigger-1", Mode.FIRING, 2); t1.setEnabled(true); rulesEngine.addFact(t1); rulesEngine.addFact(t1c1); rulesEngine.addFact(t1c2); rulesEngine.addFact(t1d); // for clarity deliver datums independently datums.add(Data.forNumeric("tenant", "X", 1000, 125.0)); // match, dampening eval true (X), no alert rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 0, alerts.size()); datums.clear(); datums.add(Data.forNumeric("tenant", "X", 2000, 50.0)); // no match, dampening reset rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 0, alerts.size()); datums.clear(); datums.add(Data.forNumeric("tenant", "Y", 3000, 300.0)); // match, dampening eval true (Y), no alert rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 0, alerts.size()); datums.clear(); datums.add(Data.forNumeric("tenant", "X", 4000, 110.0)); // match, dampening eval true (X,Y), alert! damp reset rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 1, alerts.size()); Alert a = alerts.get(0); assertEquals("trigger-1", a.getTriggerId()); assertEquals(2, a.getEvalSets().size()); Set<ConditionEval> evals = a.getEvalSets().get(0); assertEquals(2, evals.size()); List<ConditionEval> evalsList = new ArrayList<>(evals); Collections.sort( evalsList, (ConditionEval c1, ConditionEval c2) -> Integer.compare(c1.getConditionSetIndex(), c2.getConditionSetIndex())); Iterator<ConditionEval> i = evalsList.iterator(); ThresholdConditionEval e = (ThresholdConditionEval) i.next(); assertEquals(2, e.getConditionSetSize()); assertEquals(1, e.getConditionSetIndex()); assertEquals("trigger-1", e.getTriggerId()); assertTrue(!e.isMatch()); Double v = e.getValue(); assertTrue(v.equals(50.0)); assertEquals("X", e.getCondition().getDataId()); ThresholdConditionEval e2 = (ThresholdConditionEval) i.next(); assertEquals(2, e2.getConditionSetSize()); assertEquals(2, e2.getConditionSetIndex()); assertEquals("trigger-1", e2.getTriggerId()); assertTrue(e2.isMatch()); v = e2.getValue(); assertTrue(v.equals(300.0)); assertEquals("Y", e2.getCondition().getDataId()); evals = a.getEvalSets().get(1); assertEquals(2, evals.size()); evalsList = new ArrayList<>(evals); Collections.sort( evalsList, (ConditionEval c1, ConditionEval c2) -> Integer.compare(c1.getConditionSetIndex(), c2.getConditionSetIndex())); i = evalsList.iterator(); e = (ThresholdConditionEval) i.next(); assertEquals(2, e.getConditionSetSize()); assertEquals(1, e.getConditionSetIndex()); assertEquals("trigger-1", e.getTriggerId()); assertTrue(e.isMatch()); v = e.getValue(); assertTrue(v.equals(110.0)); assertEquals("X", e.getCondition().getDataId()); e2 = (ThresholdConditionEval) i.next(); assertEquals(2, e2.getConditionSetSize()); assertEquals(2, e2.getConditionSetIndex()); assertEquals("trigger-1", e2.getTriggerId()); assertTrue(e2.isMatch()); v = e2.getValue(); assertTrue(v.equals(300.0)); assertEquals("Y", e2.getCondition().getDataId()); alerts.clear(); datums.clear(); datums.add(Data.forNumeric("tenant", "Y", 5000, 150.0)); // match, dampening eval true (X), no alert rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 0, alerts.size()); } @Test public void autoResolveTest() { // The single trigger has definitions for both FIRING and AUTORESOLVE modes Trigger t1 = new Trigger("tenant", "trigger-1", "Avail-DOWN"); // Firing Mode AvailabilityCondition fmt1c1 = new AvailabilityCondition("tenant", "trigger-1", Mode.FIRING, 1, 1, "AvailData-01", AvailabilityCondition.Operator.DOWN); Dampening fmt1d = Dampening.forStrict("tenant", "trigger-1", Mode.FIRING, 2); // AutoResolve Mode AvailabilityCondition smt1c1 = new AvailabilityCondition("tenant", "trigger-1", Mode.AUTORESOLVE, 1, 1, "AvailData-01", AvailabilityCondition.Operator.UP); Dampening smt1d = Dampening.forStrict("tenant", "trigger-1", Mode.AUTORESOLVE, 2); datums.add(Data.forAvailability("tenant", "AvailData-01", 1000, AvailabilityType.DOWN)); datums.add(Data.forAvailability("tenant", "AvailData-01", 2000, AvailabilityType.UNAVAILABLE)); datums.add(Data.forAvailability("tenant", "AvailData-01", 3000, AvailabilityType.UP)); datums.add(Data.forAvailability("tenant", "AvailData-01", 4000, AvailabilityType.DOWN)); datums.add(Data.forAvailability("tenant", "AvailData-01", 5000, AvailabilityType.DOWN)); datums.add(Data.forAvailability("tenant", "AvailData-01", 6000, AvailabilityType.DOWN)); datums.add(Data.forAvailability("tenant", "AvailData-01", 7000, AvailabilityType.DOWN)); datums.add(Data.forAvailability("tenant", "AvailData-01", 8000, AvailabilityType.UP)); t1.setEnabled(true); t1.setAutoDisable(false); t1.setAutoEnable(false); t1.setAutoResolve(true); t1.setAutoResolveAlerts(true); rulesEngine.addFact(t1); rulesEngine.addFact(fmt1c1); rulesEngine.addFact(fmt1d); rulesEngine.addFact(smt1c1); rulesEngine.addFact(smt1d); // The Trigger should fire on the consecutive DOWN datums at T4,T5. It should then switch to // AutoResolve mode and not fire again at the next two consecutive down datums at T6,T7. T8 should be the // first match for the AutoResolve dampening but it should not yet be satisfied until T9 (see below). rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 1, alerts.size()); assertTrue(disabledTriggers.toString(), disabledTriggers.isEmpty()); assertTrue(autoResolvedTriggers.toString(), autoResolvedTriggers.isEmpty()); Alert a = alerts.get(0); assertTrue(a.getStatus() == Alert.Status.OPEN); assertTrue(a.getTriggerId(), a.getTriggerId().equals("trigger-1")); assertTrue(a.getEvalSets().toString(), a.getEvalSets().size() == 2); long expectedTimestamp = 4000; for (Set<ConditionEval> evalSet : a.getEvalSets()) { assertEquals(evalSet.toString(), 1, evalSet.size()); AvailabilityConditionEval e = (AvailabilityConditionEval) evalSet.iterator().next(); assertEquals(e.toString(), 1, e.getConditionSetIndex()); assertEquals(e.toString(), 1, e.getConditionSetSize()); assertEquals("trigger-1", e.getTriggerId()); assertTrue(e.isMatch()); assertEquals(expectedTimestamp, e.getDataTimestamp()); expectedTimestamp += 1000; AvailabilityType v = e.getValue(); assertEquals(AvailabilityType.DOWN, v); assertEquals("AvailData-01", e.getCondition().getDataId()); } assertTrue(t1.toString(), t1.getMode() == Mode.AUTORESOLVE); alerts.clear(); datums.clear(); datums.add(Data.forAvailability("tenant", "AvailData-01", 9000, AvailabilityType.UP)); rulesEngine.addData(datums); rulesEngine.fire(); // The second consecutive UP should satisfy the AutoResolve requirements and return the Trigger // in the autoResolvedTriggers global. Note the trigger is set to FIRING mode but in production // the real handling reloads the trigger and as such it is reset to FIRING mode in the engine. assertTrue(alerts.isEmpty()); assertTrue(disabledTriggers.isEmpty()); assertEquals(1, autoResolvedTriggers.size()); assertTrue(t1.toString(), t1.getMode() == Mode.FIRING); } @Test public void checkEqualityInRulesEngine() throws Exception { Trigger t1 = new Trigger("tenant", "trigger-1", "Avail-DOWN"); AvailabilityCondition fmt1c1 = new AvailabilityCondition("tenant", "trigger-1", Mode.FIRING, 1, 1, "AvailData-01", AvailabilityCondition.Operator.DOWN); Data adata = Data.forAvailability("tenant", "AvailData-01", System.currentTimeMillis(), AvailabilityType.UP); AvailabilityConditionEval fmt1c1eval = new AvailabilityConditionEval(fmt1c1, adata); Dampening fmt1d = Dampening.forStrict("tenant", "trigger-1", Mode.FIRING, 2); ThresholdCondition fmt1c2 = new ThresholdCondition("tenant", "trigger-1", Mode.FIRING, 1, 1, "ThreData-01", ThresholdCondition.Operator.GT, 10d); Data ndata = Data.forNumeric("tenant", "ThreData-01", System.currentTimeMillis(), 20d); ThresholdConditionEval fmt1c2eval = new ThresholdConditionEval(fmt1c2, ndata); rulesEngine.addFact(t1); rulesEngine.addFact(fmt1c1); rulesEngine.addFact(fmt1c1eval); rulesEngine.addFact(fmt1d); rulesEngine.addFact(fmt1c2); rulesEngine.addFact(fmt1c2eval); assertTrue(rulesEngine.getFact(t1) != null); assertTrue(rulesEngine.getFact(fmt1c1) != null); assertTrue(rulesEngine.getFact(fmt1c1eval) != null); assertTrue(rulesEngine.getFact(fmt1d) != null); assertTrue(rulesEngine.getFact(fmt1c2) != null); assertTrue(rulesEngine.getFact(fmt1c2eval) != null); String strt1 = JsonUtil.toJson(t1); String strfmt1c1 = JsonUtil.toJson(fmt1c1); String strfmt1c1eval = JsonUtil.toJson(fmt1c1eval); String strfmt1d = JsonUtil.toJson(fmt1d); String strfmt1c2 = JsonUtil.toJson(fmt1c2); String strfmt1c2eval = JsonUtil.toJson(fmt1c2eval); Trigger jsont1 = JsonUtil.fromJson(strt1, Trigger.class); AvailabilityCondition jsonfmt1c1 = JsonUtil.fromJson(strfmt1c1, AvailabilityCondition.class); AvailabilityConditionEval jsonfmt1c1eval = JsonUtil.fromJson(strfmt1c1eval, AvailabilityConditionEval.class); Dampening jsonfmt1d = JsonUtil.fromJson(strfmt1d, Dampening.class); ThresholdCondition jsonfmt1c2 = JsonUtil.fromJson(strfmt1c2, ThresholdCondition.class); ThresholdConditionEval jsonfmt1c2eval = JsonUtil.fromJson(strfmt1c2eval, ThresholdConditionEval.class); assertTrue(t1.equals(jsont1)); assertTrue(fmt1c1.equals(jsonfmt1c1)); assertTrue(fmt1c1eval.equals(jsonfmt1c1eval)); assertTrue(fmt1d.equals(jsonfmt1d)); assertTrue(fmt1c2.equals(jsonfmt1c2)); assertTrue(fmt1c2eval.equals(jsonfmt1c2eval)); assertTrue(rulesEngine.getFact(jsont1) != null); assertTrue(rulesEngine.getFact(jsonfmt1c1) != null); assertTrue(rulesEngine.getFact(jsonfmt1c1eval) != null); assertTrue(rulesEngine.getFact(jsonfmt1d) != null); assertTrue(rulesEngine.getFact(jsonfmt1c2) != null); assertTrue(rulesEngine.getFact(jsonfmt1c2eval) != null); } public void checkMissingStates(long evalTime) { missingStates.stream().forEach(missingState -> { MissingConditionEval eval = new MissingConditionEval(missingState.getCondition(), missingState.getPreviousTime(), evalTime); if (eval.isMatch()) { rulesEngine.removeFact(missingState); missingState.setPreviousTime(evalTime); missingState.setTime(evalTime); rulesEngine.addFact(missingState); rulesEngine.addFact(eval); } }); } @Test public void missingDataTestNoData() throws Exception { //Initial trigger and condition Trigger t1 = new Trigger("tenant", "trigger-m", "Missing test Trigger"); MissingCondition fmt1 = new MissingCondition("tenant", "trigger-m", "data-id", 3000L); t1.setEnabled(true); // Missing state is generated on reloadTrigger() MissingState missingState = new MissingState(t1, fmt1); missingStates.add(missingState); rulesEngine.addFact(t1); rulesEngine.addFact(fmt1); rulesEngine.addFact(missingState); checkMissingStates(System.currentTimeMillis() + 4000); // If missing states but not data rules engine should fire rulesEngine.fireNoData(); assertEquals(1, alerts.size()); checkMissingStates(System.currentTimeMillis() + 8000); rulesEngine.fireNoData(); assertEquals(2, alerts.size()); } @Test public void missingDataTestWithData() throws Exception { Trigger t1 = new Trigger("tenant", "trigger-m", "Missing test Trigger"); MissingCondition fmt1 = new MissingCondition("tenant", "trigger-m", "data-id", 3000L); t1.setEnabled(true); // Missing state is generated on reloadTrigger() MissingState missingState = new MissingState(t1, fmt1); missingStates.add(missingState); rulesEngine.addFact(t1); rulesEngine.addFact(fmt1); rulesEngine.addFact(missingState); // Initial check will send a MissingState into the engine checkMissingStates(System.currentTimeMillis()); rulesEngine.fireNoData(); // We add a data, so it should update the missingState and not fire any alert Data data = Data.forAvailability("tenant", "data-id", System.currentTimeMillis() + 4001, AvailabilityType.DOWN); rulesEngineAddData(data); rulesEngine.fire(); checkMissingStates(4000); rulesEngine.fireNoData(); assertEquals(0, alerts.size()); } @Test public void nelsonTest() { // all rules Trigger t1 = new Trigger("tenant", "trigger-1", "Nelson-all"); NelsonCondition t1c1 = new NelsonCondition("tenant", "trigger-1", "NumericData-01", EnumSet.allOf(NelsonRule.class), 10); // only active rule: rule 2 Trigger t2 = new Trigger("tenant", "trigger-2", "Nelson-rule1"); NelsonCondition t2c1 = new NelsonCondition("tenant", "trigger-2", "NumericData-01", EnumSet.of(NelsonRule.Rule2), 10); // first 10 datums to establish mean (10) and std dev 2.58199 // 14, 13, 12, 11, 10, 10, 9, 8, 7, 6 datums.add(Data.forNumeric("tenant", "NumericData-01", 100000, 6.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 101000, 7.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 102000, 8.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 103000, 9.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 104000, 10.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 105000, 10.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 106000, 11.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 107000, 12.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 108000, 13.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 109000, 14.0)); // default dampening t1.setEnabled(true); t2.setEnabled(true); rulesEngine.addFact(t1); rulesEngine.addFact(t1c1); rulesEngine.addFact(t2); rulesEngine.addFact(t2c1); rulesEngine.addData(datums); rulesEngine.fire(); assertEquals(alerts.toString(), 0, alerts.size()); // violate rule 1 : One point is more than 3 standard deviations from the mean // [ 18 ], 14, 13, 12, 11, 10, 10, 9, 8, 7, 6 datums.clear(); datums.add(Data.forNumeric("tenant", "NumericData-01", 200000, 18.0)); rulesEngine.addData(datums); rulesEngine.fire(); // trigger1 should fire, trigger 2 should not assertEquals(alerts.toString(), 1, alerts.size()); Alert a = alerts.get(0); Set<ConditionEval> eval = a.getEvalSets().get(0); NelsonConditionEval e = (NelsonConditionEval) eval.iterator().next(); assertTrue(e.getViolations().toString(), e.getViolations().contains(NelsonRule.Rule1)); assertEquals(e.getMean().toString(), Double.valueOf(10.0), e.getMean()); NumberFormat nf = NumberFormat.getInstance(Locale.ENGLISH); nf.setMaximumFractionDigits(5); assertEquals(e.getStandardDeviation().toString(), "2.58199", nf.format(e.getStandardDeviation())); assertEquals(e.getViolations().toString(), 1, e.getViolations().size()); // violate rule 2: nine (or more) points in a row are on the same side of the mean // [ 11, 11, 11, 11, 11, 11, 11, 18, 14 ],13, 12, 11, 10, 10, 9 datums.clear(); datums.add(Data.forNumeric("tenant", "NumericData-01", 300000, 11.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 301000, 11.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 302000, 11.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 303000, 11.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 304000, 11.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 305000, 11.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 306000, 11.0)); alerts.clear(); rulesEngine.addData(datums); rulesEngine.fire(); // trigger1 and 2 should fire assertEquals(alerts.toString(), 2, alerts.size()); Collections.sort(alerts, (Alert a1, Alert a2) -> a1.getTriggerId().compareTo(a2.getTriggerId())); a = alerts.get(0); assertEquals(a.getTriggerId(), "trigger-1", a.getTriggerId()); eval = a.getEvalSets().get(0); e = (NelsonConditionEval) eval.iterator().next(); assertTrue(e.getViolations().toString(), e.getViolations().contains(NelsonRule.Rule2)); assertEquals(e.getViolations().toString(), 1, e.getViolations().size()); a = alerts.get(1); assertEquals(a.getTriggerId(), "trigger-2", a.getTriggerId()); eval = alerts.get(1).getEvalSets().get(0); e = (NelsonConditionEval) eval.iterator().next(); assertTrue(e.getViolations().toString(), e.getViolations().contains(NelsonRule.Rule2)); assertEquals(e.getViolations().toString(), 1, e.getViolations().size()); // violate rule 3: Six (or more) points in a row are continually increasing // [ 10.4, 10.3, 10.2, 10.1, 10, 9.9, 7.4 ], 11, 11, 11, 11, 11, 11, 11, 18 datums.clear(); datums.add(Data.forNumeric("tenant", "NumericData-01", 400000, 7.4)); // break rules 2, 7 datums.add(Data.forNumeric("tenant", "NumericData-01", 401000, 9.9)); datums.add(Data.forNumeric("tenant", "NumericData-01", 402000, 10.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 403000, 10.1)); datums.add(Data.forNumeric("tenant", "NumericData-01", 404000, 10.2)); datums.add(Data.forNumeric("tenant", "NumericData-01", 405000, 10.3)); datums.add(Data.forNumeric("tenant", "NumericData-01", 406000, 10.4)); alerts.clear(); rulesEngine.addData(datums); rulesEngine.fire(); // trigger1 should fire, trigger 2 should not assertEquals(alerts.toString(), 1, alerts.size()); a = alerts.get(0); eval = a.getEvalSets().get(0); e = (NelsonConditionEval) eval.iterator().next(); assertTrue(e.getViolations().toString(), e.getViolations().contains(NelsonRule.Rule3)); assertEquals(e.getViolations().toString(), 1, e.getViolations().size()); // violate rule 3: Six (or more) points in a row are continually decreasing // [ 9.8, 9.9, 10, 10.1, 10.2, 10.3 ], 10.4, 10.3, 10.2, 10.1, 10, 9.9, 7.4, 11, 11 datums.clear(); datums.add(Data.forNumeric("tenant", "NumericData-01", 500000, 10.3)); datums.add(Data.forNumeric("tenant", "NumericData-01", 501000, 10.2)); datums.add(Data.forNumeric("tenant", "NumericData-01", 502000, 10.1)); datums.add(Data.forNumeric("tenant", "NumericData-01", 503000, 10.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 504000, 9.9)); datums.add(Data.forNumeric("tenant", "NumericData-01", 505000, 9.8)); alerts.clear(); rulesEngine.addData(datums); rulesEngine.fire(); // trigger1 should fire, trigger 2 should not assertEquals(alerts.toString(), 1, alerts.size()); a = alerts.get(0); eval = a.getEvalSets().get(0); e = (NelsonConditionEval) eval.iterator().next(); assertTrue(e.getViolations().toString(), e.getViolations().contains(NelsonRule.Rule3)); assertEquals(e.getViolations().toString(), 1, e.getViolations().size()); // violate rule 4: Fourteen (or more) points in a row alternate in direction, increasing then decreasing. // [ 9.5, 12.6, 9.5, 10.5, 9.5, 10.5, 9.5, 10.5, 9.5, 10.5, 9.5, 10.5, 9.5, 10.5, 9.8 ] datums.clear(); datums.add(Data.forNumeric("tenant", "NumericData-01", 600000, 10.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 601000, 9.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 602000, 10.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 603000, 9.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 604000, 10.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 605000, 9.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 606000, 10.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 607000, 9.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 608000, 10.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 609000, 9.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 610000, 10.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 611000, 9.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 612000, 12.6)); // break rule 7 datums.add(Data.forNumeric("tenant", "NumericData-01", 613000, 9.5)); alerts.clear(); rulesEngine.addData(datums); rulesEngine.fire(); // trigger1 should fire, trigger 2 should not assertEquals(alerts.toString(), 1, alerts.size()); a = alerts.get(0); eval = a.getEvalSets().get(0); e = (NelsonConditionEval) eval.iterator().next(); assertTrue(e.getViolations().toString(), e.getViolations().contains(NelsonRule.Rule4)); assertEquals(e.getViolations().toString(), 1, e.getViolations().size()); // violate rule 5: At least 2 of 3 points in a row are > 2 deviations from the mean, in the same direction. // 16, 4, 9.5, 12.6, 9.5, 10.5, 9.5, 10.5, 9.5, 10.5, 9.5, 10.5, 9.5, 10.5, 9.5 datums.clear(); datums.add(Data.forNumeric("tenant", "NumericData-01", 700000, 4.0)); // break rule 4 datums.add(Data.forNumeric("tenant", "NumericData-01", 701000, 16.0)); alerts.clear(); rulesEngine.addData(datums); rulesEngine.fire(); // no trigger should fire yet, have 1 of 2, need 2 of 3 assertEquals(alerts.toString(), 0, alerts.size()); // [ 4, 16, 4 ], 9.5, 12.6, 9.5, 10.5, 9.5, 10.5, 9.5, 10.5, 9.5, 10.5, 9.5, 10.5 datums.clear(); datums.add(Data.forNumeric("tenant", "NumericData-01", 702000, 4.0)); alerts.clear(); rulesEngine.addData(datums); rulesEngine.fire(); // trigger1 should fire, trigger 2 should not assertEquals(alerts.toString(), 1, alerts.size()); a = alerts.get(0); eval = a.getEvalSets().get(0); e = (NelsonConditionEval) eval.iterator().next(); assertTrue(e.getViolations().toString(), e.getViolations().contains(NelsonRule.Rule5)); assertEquals(e.getViolations().toString(), 1, e.getViolations().size()); // violate rule 6: At least 4 of 5 points in a row are > 1 deviation from the mean in the same direction. // [ 7, 7, 4, 16, 4 ], 9.5, 12.6, 9.5, 10.5, 9.5, 10.5, 9.5, 10.5, 9.5, 10.5 datums.clear(); datums.add(Data.forNumeric("tenant", "NumericData-01", 800000, 7.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 801000, 7.0)); alerts.clear(); rulesEngine.addData(datums); rulesEngine.fire(); // trigger1 should fire, trigger 2 should not assertEquals(alerts.toString(), 1, alerts.size()); a = alerts.get(0); eval = a.getEvalSets().get(0); e = (NelsonConditionEval) eval.iterator().next(); assertTrue(e.getViolations().toString(), e.getViolations().contains(NelsonRule.Rule6)); assertEquals(e.getViolations().toString(), 1, e.getViolations().size()); // violate rule 7: Fifteen points in a row are all within 1 deviation of the mean on either side of the mean. // 9.5, 11.5, 11.5, 9.5, 9.5, 11.5, 11.5, 9.5, 9.5, 11.5, 11.5, 9.5, 9.5, 11.5, 11.5 datums.clear(); datums.add(Data.forNumeric("tenant", "NumericData-01", 900000, 11.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 901000, 11.5)); // break rule 4 datums.add(Data.forNumeric("tenant", "NumericData-01", 902000, 9.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 903000, 9.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 904000, 11.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 905000, 11.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 906000, 9.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 907000, 9.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 908000, 11.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 909000, 11.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 910000, 9.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 911000, 9.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 912000, 11.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 913000, 11.5)); datums.add(Data.forNumeric("tenant", "NumericData-01", 914000, 9.5)); alerts.clear(); rulesEngine.addData(datums); rulesEngine.fire(); // trigger1 should fire, trigger 2 should not assertEquals(alerts.toString(), 1, alerts.size()); a = alerts.get(0); eval = a.getEvalSets().get(0); e = (NelsonConditionEval) eval.iterator().next(); assertTrue(e.getViolations().toString(), e.getViolations().contains(NelsonRule.Rule7)); assertEquals(e.getViolations().toString(), 1, e.getViolations().size()); // violate rule 8: Eight points in a row exist, but none within 1 standard deviation of the mean, // and the points are in both directions from the mean. // 7, 13, 7, 13, 7, 13, 7, 13, 9.5, 11.5, 11.5, 9.5, 9.5, 11.5, 11.5 datums.clear(); datums.add(Data.forNumeric("tenant", "NumericData-01", 1000000, 13.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 1001000, 7.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 1002000, 13.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 1003000, 7.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 1004000, 13.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 1005000, 7.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 1006000, 13.0)); datums.add(Data.forNumeric("tenant", "NumericData-01", 1007000, 7.0)); alerts.clear(); rulesEngine.addData(datums); rulesEngine.fire(); // trigger1 should fire, trigger 2 should not assertEquals(alerts.toString(), 1, alerts.size()); a = alerts.get(0); eval = a.getEvalSets().get(0); e = (NelsonConditionEval) eval.iterator().next(); assertTrue(e.getViolations().toString(), e.getViolations().contains(NelsonRule.Rule8)); assertEquals(e.getViolations().toString(), 1, e.getViolations().size()); } }