Skip to content

Instantly share code, notes, and snippets.

@dacr
Last active October 6, 2024 10:54
Show Gist options
  • Save dacr/ffe6ec1f1a3ce54ea3ac82db2ce69537 to your computer and use it in GitHub Desktop.
Save dacr/ffe6ec1f1a3ce54ea3ac82db2ce69537 to your computer and use it in GitHub Desktop.
Drools understanding rules knowledge base / published by https://github.com/dacr/code-examples-manager #79058fd2-f069-494d-851d-9dea57939d22/c019079a3cb3ba86b296f36ea2225a388c267d47
// summary : Drools understanding rules knowledge base
// keywords : scala, drools, mvel, scalatest, ai, knowledgebase, @testable
// publish : gist
// authors : David Crosson
// license : Apache NON-AI License Version 2.0 (https://raw.githubusercontent.com/non-ai-licenses/non-ai-licenses/main/NON-AI-APACHE2)
// id : 79058fd2-f069-494d-851d-9dea57939d22
// created-on : 2019-10-04T23:01:50+02:00
// managed-by : https://github.com/dacr/code-examples-manager
// run-with : scala-cli $file
// ---------------------
//> using scala "3.5.1"
//> using dep "fr.janalyse::drools-scripting:1.2.0"
//> using dep "org.scalatest::scalatest:3.2.19"
// ---------------------
import fr.janalyse.droolscripting.*, org.scalatest.*, flatspec.*, matchers.*, OptionValues.*
import scala.jdk.CollectionConverters._
object UnderstandingRules extends AnyFlatSpec with should.Matchers {
override def suiteName: String = "UnderstandingRules"
def checkOK(drl:String):Unit = {
val engine = DroolsEngine(drl, DroolsEngineConfig.configWithIdentity)
engine.fireAllRules()
engine.strings shouldBe List("OK")
}
def checkAllOf(that:String*)(drl:String):Unit = {
val engine = DroolsEngine(drl, DroolsEngineConfig.configWithIdentity)
engine.fireAllRules()
engine.strings.sorted shouldBe that.toList.sorted
}
def checkAllOfWithEquality(that:String*)(drl:String):Unit = {
val engine = DroolsEngine(drl, DroolsEngineConfig.configWithEquality)
engine.fireAllRules()
engine.strings.sorted shouldBe that.toList.sorted
}
// ======================================================================
"DROOLS" should "support init rules (=rules without conditions) - #EX1" in checkOK {
"""package test //#EX1
|rule "init" when then insert("OK"); end
|""".stripMargin
}
// ======================================================================
it should "support for one fact found - #EX2A" in checkOK {
"""package test //#EX2A
|declare A end
|rule "init" when then insert(new A()); end
|//-----------------
|rule "for each A" when A() then insert("OK"); end
|""".stripMargin
}
// ======================================================================
it should "support for each fact found - #EX2B" in checkAllOf("OK", "OK") {
"""package test //#EX2B
|declare A end
|rule "init" when then insert(new A()); insert(new A()); end
|//-----------------
|rule "for each A" when A() then insert(new String("OK")); end
|""".stripMargin
}
// ======================================================================
it should "support when none exists - #EX3" in checkOK {
"""package test //#EX3
|declare A end
|//-----------------
|rule "A not exists" when not(A()) then insert("OK"); end
|""".stripMargin
}
// ======================================================================
it should "support AND existence - #EX4" in checkOK {
info("and is implicit but we can write : A() and B()")
"""package test //#EX4
|declare A end
|declare B end
|rule "init" when then
| insert(new A());
| insert(new B());
|end
|//-----------------
|rule "A and B" when A() and B() then insert("OK"); end
|""".stripMargin
}
// ======================================================================
it should "support OR existence - #EX5" in checkOK {
"""package test //#EX5
|declare A end
|declare B end
|rule "init" when then
| insert(new B());
|end
|//-----------------
|rule "A or B" when A() or B() then insert("OK"); end
|""".stripMargin
}
// ======================================================================
it should "support at least one - #EX6" in checkOK {
"""package test //#EX6
|declare A
| x:int
|end
|rule "init" when then
| insert(new A(1));
| insert(new A(2));
|end
|//-----------------
|rule "at least one A" when exists( A() ) then insert("OK"); end
|""".stripMargin
}
// ======================================================================
it should "support collect all in one operation - #EX7" in checkOK {
"""package test //#EX7
|declare A
| x:int
|end
|rule "init" when then
| insert(new A(0));
| insert(new A(1));
|end
|//-----------------
|rule "for all A"
|when
| $found:java.util.LinkedList(size>0) from collect( A() )
|then insert("OK"); end
|""".stripMargin
}
// ======================================================================
it should "support AND conditions - #EX8" in checkAllOf("OK1", "OK2") {
"""package test //#EX8
|declare A
| x:int
| y:int
|end
|rule "init" when then
| insert(new A(24, 42));
|end
|//-----------------
|rule "AND conditions 1" when A(x > 10, y<100) then insert("OK1"); end
|rule "AND conditions 2" when A(x > 10 && y<100) then insert("OK2"); end
|""".stripMargin
}
// ======================================================================
it should "support OR conditions - #EX9" in checkAllOf("OK") {
"""package test //#EX9
|declare A
| x:int
| y:int
|end
|rule "init" when then
| insert(new A(24, 42));
|end
|//-----------------
|rule "OR conditions" when A(x > 100 || y < 100) then insert("OK"); end
|""".stripMargin
}
// ======================================================================
it should "support string conditions - #EX10" in checkAllOf("OK", "truc") {
"""package test //#EX10
|rule "init" when then
| insert("truc");
|end
|//-----------------
|rule "string content" when String(this == "truc") then insert("OK"); end
|""".stripMargin
}
// ======================================================================
it should "support string regular expression - #EX11" in checkAllOf("OK1", "OK2", "truc bidule") {
"""package test //#EX11
|rule "init" when then
| insert("truc bidule");
|end
|//-----------------
|rule "string regexp 1" when String(this.matches("tr.*le")) then insert("OK1"); end
|rule "string regexp 2" when String(this matches "tr.*le" ) then insert("OK2"); end
|""".stripMargin
}
// ======================================================================
it should "support forall check - #EX12" in checkAllOf("OK") {
"""package test //#EX12
|declare A
| weight:int
| color:String
|end
|rule "init" when then
| insert(new A(10, "red"));
| insert(new A(15, "red"));
|end
|//-----------------
|rule "All are red" when forall( A(color=="red")) then insert("OK"); end
|rule "not all has 10" when forall( A(weight==10)) then insert("KO"); end
|""".stripMargin
}
// ======================================================================
it should "support accumulate - #EX13" in checkAllOf("OK 20 15.0") {
"""package test //#EX13
|declare A
| value:int
|end
|rule "init" when then
| insert(new A(10));
| insert(new A(20));
|end
|//-----------------
|rule "Compute average" when
| accumulate(
| A($value:value);
| $max:max($value), $avg:average($value)
| )
|then insert("OK "+$max+" "+$avg); end
|""".stripMargin
}
// ======================================================================
it should "accumulate behavior when no matching facts - #EX13B" in checkAllOf("KO null null") {
"""package test //#EX13B
|declare A
| value:int
|end
|//-----------------
|rule "Compute average" when
| accumulate(
| A($value:value);
| $max:max($value), $avg:average($value)
| )
|then insert("KO "+$max+" "+$avg); end
|
|""".stripMargin
}
// ======================================================================
it should "accumulate behavior when no matching facts, protect against nulls - #EX13C" in checkAllOf() {
"""package test //#EX13C
|declare A
| value:int
|end
|//-----------------
|rule "Compute average" when
| Number(this!=null, $avg:doubleValue) from accumulate(
| A($value:value); average($value)
| )
|then insert("KO "+$avg); end
|
|""".stripMargin
}
// ======================================================================
it should "accumulate behavior when no matching facts, protect against nulls revisited - #EX13D" in checkAllOf() {
"""package test //#EX13D
|declare A
| value:int
|end
|//-----------------
|rule "Compute average" when
| accumulate(
| A($value:value);
| $max:max($value), $avg:average($value); $max != null, $avg !=null
| )
|then insert("KO "+$max+" "+$avg); end
|
|""".stripMargin
}
// ======================================================================
it should "have a default rule activation order - #EX14" in checkAllOf("123") {
"""package test //#EX14
|declare A end
|global String result;
|rule "init" when then
| drools.getKnowledgeRuntime().setGlobal("result", "");
| insert(new A());
|end
|//-----------------
|rule "1" when A() then drools.getKnowledgeRuntime().setGlobal("result", result+"1"); end
|rule "2" when A() then drools.getKnowledgeRuntime().setGlobal("result", result+"2"); end
|rule "3" when A() then drools.getKnowledgeRuntime().setGlobal("result", result+"3"); end
|rule "4" when A() then insert(result); end
|""".stripMargin
}
// ======================================================================
it should "support rule activation custom order - #EX15" in checkAllOf("312") {
"""package test //#EX15
|declare A end
|global String result;
|rule "init" when then
| drools.getKnowledgeRuntime().setGlobal("result", "");
| insert(new A());
|end
|//-----------------
|rule "1" when A() then drools.getKnowledgeRuntime().setGlobal("result", result+"1"); end
|rule "2" when A() then drools.getKnowledgeRuntime().setGlobal("result", result+"2"); end
|rule "3" salience 30 when A() then drools.getKnowledgeRuntime().setGlobal("result", result+"3"); end
|rule "4" when A() then insert(result); end
|""".stripMargin
}
// ======================================================================
it should "support fact removal - #EX16" in checkOK {
"""package test //#EX16
|declare A end
|rule "init" when then insert(new A()); end
|//-----------------
|rule "remove fact" when $a:A() then delete($a); end
|rule "not A?" when not(A()) then insert("OK"); end
|""".stripMargin
}
// ======================================================================
it should "support logical fact insertion - #EX17" in checkOK {
"""package test //#EX17
|declare A end
|declare B end
|rule "init" when then insert(new A()); end
|//-----------------
|rule "A -> B" when A() then insertLogical(new B()); end
|rule "remove A" when $a:A() then delete($a); end
|rule "not B?" when not(B()) then insert("OK"); end // B has been automatically removed ?
|rule "B?" when B() then insert("KO"); end
|""".stripMargin
}
// ======================================================================
it should "support simple no-loop - #EX18" in checkOK {
"""package test //#EX18
|declare A end
|//-----------------
|rule "!A" when not A() then insert(new A()); end
|rule "A" no-loop when A() then insert(new A()); end
|rule "end" when A() then insert("OK"); end
|""".stripMargin
}
// ======================================================================
it should "support customized object identities - #EX19" in checkAllOfWithEquality("OK-joe", "OK-john") {
info("TAKE CARE - Require to use equality - equalsBehavior=equality rather than identity")
info("TAKE CARE - For a given key, the first inserted is always kept !, In this example sarah won't overwrite joe !")
"""package test //#EX19
|declare A
| id:int @key
| name:String
|end
|rule "OK" when then
| insert(new A(1,"joe"));
| insert(new A(1,"sarah"));
| insert(new A(3,"john"));
|end
|//-----------------
|rule "A" when A($name:name) then insert("OK-"+$name); end
|""".stripMargin
}
// ======================================================================
it should "support fact update - #EX20" in checkAllOf("OK") {
"""package test //#EX20
|declare That
| value:int
|end
|rule "OK" when then
| insert(new That(1));
|end
|//-----------------
|rule "A1" when $a:That(value == 1) then modify($a) {setValue(2);} end
|rule "A2" when $a:That(value == 2) then insert("OK"); end
|""".stripMargin
}
// ======================================================================
it should "support rule disabling - #EX21" in checkOK {
"""package test //#EX21
|rule "init1" enabled false when then insert("KO1"); end
|rule "init2" enabled(false) when then insert("KO2"); end
|rule "init3" enabled(Boolean.FALSE) when then insert("KO3"); end
|rule "check" when not(String(this matches "^KO.*")) then insert("OK"); end
|""".stripMargin
}
// ======================================================================
it should "support query for facts - #EX22" in {
val drl =
"""package test //#EX22
|declare Someone name:String age:int end
|rule "init" when then
| insert(new Someone("joe", 42));
| insert(new Someone("john", 24));
| insert(new Someone("sarah", 32));
| insert(new Someone("zoe", 17));
| insert(new Someone("marc", 13));
|end
|//-----------------
|query adultNames()
| Someone($name:name, age>=18)
|end
|""".stripMargin
val engine = DroolsEngine(drl, DroolsEngineConfig.configWithIdentity)
engine.fireAllRules()
val results = engine.session.getQueryResults("adultNames")
results.size() shouldBe 3
val names = results.iterator.asScala.map(_.get("$name")).collect{case n:String =>n}
names.toList should contain only("joe", "john", "sarah")
}
// ======================================================================
it should "support parametrized query for facts - #EX23" in {
val drl =
"""package test //#EX23
|declare Someone name:String age:int end
|rule "init" when then
| insert(new Someone("joe", 42));
| insert(new Someone("john", 24));
| insert(new Someone("sarah", 32));
| insert(new Someone("zoe", 17));
| insert(new Someone("marc", 13));
|end
|//-----------------
|query adultNames(int $limit)
| Someone($name:name, age>=$limit)
|end
|""".stripMargin
val engine = DroolsEngine(drl, DroolsEngineConfig.configWithIdentity)
engine.fireAllRules()
val results = engine.session.getQueryResults("adultNames", 18)
results.size() shouldBe 3
val names = results.iterator.asScala.map(_.get("$name")).collect{case n:String =>n}
names.toList should contain only("joe", "john", "sarah")
}
// ======================================================================
it should "support function definitions - #EX24" in checkAllOf("OK42") {
info("Take care with functions in conditions - it will disable some drools optimization as it will loop over rules")
"""package test //#EX24
|function int sum(int x, int y) { return x+y; }
|declare It value:int end
|rule "init" when then insert(new It(42)); end
|rule "me" when It(value == sum(40, 2)) then insert("OK"+sum(2,40)); end
|""".stripMargin
}
// ======================================================================
it should "not support globals access from function - #EX25" in {
val drl =
"""package test //#EX25
|global org.slf4j.Logger logger
|function int sum(int x, int y) {
| logger.info("ok ?");
| return x+y;
|}
|""".stripMargin
intercept[AssertionError] {
val engine = DroolsEngine(drl)
}
info("You couldn't access globals from function, give the reference as parameter if you need to access it")
}
// ======================================================================
it should "support field null safe dereference - #EX26" in checkOK {
info("Use the !. operator for null safe field dereference")
"""package test //#EX26
|declare ConfigValue value:String end
|declare Config
| scope: ConfigValue
| target: ConfigValue
|end
|rule "init" when then
| insert(new Config(new ConfigValue("truc"), null));
|end
|//-----------------
|rule "check1" when Config(target!.value == "muche") // without ! => NullPointerException
|then insert("KO"); end
|rule "check2" when Config(scope!.value == "truc")
|then insert("OK"); end
|""".stripMargin
}
// ======================================================================
it should "support maps dereference - #EX27" in {
val drl =
"""package test //#EX27
|import java.util.Map
|declare Config
| props:Map
|end
|//-----------------
|rule "check" when
| Config(props["scope"] == "prod")
| Config(props.get("scope") == "prod")
| Config(props["nothing"] == null)
| Config(props.get("nothing") == null)
|then
| insert("OK");
|end
|""".stripMargin
val engine = DroolsEngine(drl, DroolsEngineConfig.configWithIdentity)
engine.insertJson("""{"props":{"scope":"prod"}}""", "test.Config")
engine.fireAllRules()
engine.strings shouldBe List("OK")
}
// ======================================================================
it should "support deep map dereference with type checks - #EX28" in checkOK {
"""package test //#EX28
|import java.util.Map
|import java.util.HashMap
|declare A name:String desc:String end
|declare B truc:String blah:String end
|declare Config props:Map end
|
|rule "init" when then
| Map props = new HashMap();
| props.put("a", new A("joe", "someone"));
| props.put("b", new A("bouh", "ben")); // KeepIt as A !
| insert(new Config(props));
|end
|
|//-----------------
|rule "check1" when
| Config($a:props["a"], $a#A!.name == "joe")
|then
| insert("OK");
|end
|rule "check2" when
| Config($b:props["b"], $b#B!.truc == "bouh")
|then
| insert("KO");
|end
|rule "check3" when
| Config($c:props["c"], $c#B!.truc == "bouh") // No issue with null :)
|then
| insert("KO");
|end
|""".stripMargin
}
// ======================================================================
it should "matching within subfields object - #EX29" in {
val drl =
"""package test //#EX29
|dialect "java"
|global org.slf4j.Logger logger
|import java.util.LinkedList
|
|declare Someone
| age:int
| name:String
| gender:String
|end
|
|declare People
| list:LinkedList
|end
|
|rule "check"
|when
| People($list:list)
|then
| logger.info($list.toString());
|end
|
|rule "search adult"
|when
| People($peopleList:list)
| $adults: LinkedList() from collect(Someone(age>=18) from $peopleList)
|then
| insert(new String("OK"));
|end
|
|""".stripMargin
val engine = DroolsEngine(drl, DroolsEngineConfig.configWithIdentity)
engine.insertJson(
"""{
| "list":[
| {"age":5, "name":"john", "gender":"M"},
| {"age":32, "name":"joe", "gender":"M"},
| {"age":17, "name":"sarah", "gender":"F"}
| ]
|}
|""".stripMargin, "test.People"
)
engine.fireAllRules()
//engine.getObjects.foreach{println}
for{ ob <- engine.getObjects} {println(ob)}
engine.strings shouldBe List("OK")
}
// ======================================================================
it should "matching within subfields object - #EX30" in {
val drl =
"""package test //#EX30
|import java.util.LinkedList
|
|declare Someone
| age:int
| name:String
| gender:String
|end
|
|declare People
| list:LinkedList
|end
|
|rule "init" when then
| LinkedList ll = new LinkedList();
| ll.add(new Someone(5, "john", "M"));
| ll.add(new Someone(32, "joe", "M"));
| ll.add(new Someone(17, "sarah", "F"));
| insert(new People(ll));
|end
|
|
|rule "search adult"
|when
| People($peopleList:list)
| $someone: Someone(age >= 18) from $peopleList
|then
| insert(new String("OK"));
|end
|
|""".stripMargin
val engine = DroolsEngine(drl, DroolsEngineConfig.configWithIdentity)
engine.fireAllRules()
//engine.getObjects.foreach{println}
for{ ob <- engine.getObjects} {println(ob)}
engine.strings shouldBe List("OK")
}
it should " be possible to use accumulate and insertLogical together - #EX31" in {
val drl =
"""package test //#EX31
|global org.slf4j.Logger logger
|
|declare Someone name:String age:int gender:String end
|
|declare AverageAge avg:double end
|
|rule "init" duration 5000 when
|then
| insert(new Someone("john1", 30, "male"));
| insert(new Someone("john2", 38, "male"));
| insert(new Someone("sarah1", 20, "female"));
| insert(new Someone("sarah2", 40, "female"));
| insert(new Someone("sarah3", 50, "female"));
|end
|
|rule "Default when nobody is here" when
| not Someone()
|then insertLogical(new AverageAge(0)); end
|
|rule "Compute average" when
| Number(this!=null, $avg:doubleValue) from accumulate(
| Someone($value:age);
| average($value)
| )
|then
| logger.info("Hello "+$avg);
| insertLogical(new AverageAge($avg));
| //insert(new AverageAge($avg));
|end
|
|rule "react when average is over a limit" when
| AverageAge(avg >= 32)
|then insertLogical("HIGH AVERAGE AGE");
|end
|
|rule "react when average is under a limit" when
| AverageAge(avg>0, avg < 32)
|then insertLogical("LOW AVERAGE AGE");
|end
|
|""".stripMargin
val config = DroolsEngineConfig(withDroolsLogging = false, equalsWithIdentity = false)
val engine = DroolsEngine(drl, config)
engine.fireAllRules()
engine.strings shouldBe List()
engine.getModelInstances("test.AverageAge").size shouldBe 1
engine.advanceTimeSeconds(6)
engine.fireAllRules()
engine.getModelInstances("test.AverageAge").size shouldBe 1
engine.strings shouldBe List("HIGH AVERAGE AGE") // AVG IS ~35.6
engine.insertJson("""{"name":"sarah4", "age":4, "gender":"female"}""", "test.Someone")
engine.fireAllRules()
engine.getModelInstances("test.AverageAge").size shouldBe 1
engine.strings shouldBe List("LOW AVERAGE AGE") // AVG IS ~30.3
}
// ======================================================================
it should "be possible to use inheritance through rules and so simplify conditions - #EX32" in {
val drl =
"""package test //#EX32
|import java.util.LinkedList
|
|declare Someone
| age:int
| name:String
| gender:String
|end
|
|rule "init" when then
| insert(new Someone(5, "john", "M"));
| insert(new Someone(24, "joe", "M"));
| insert(new Someone(42, "sarah", "F"));
|end
|
|rule "Adults"
|when
| $found:Someone(age >= 18)
|then
|end
|
|rule "Femal adults" extends "Adults"
|when
| Someone(gender=="F", this == $found)
|then
| insert(new String("OK"));
|end
|
|
|""".stripMargin
val engine = DroolsEngine(drl, DroolsEngineConfig.configWithIdentity)
engine.fireAllRules()
engine.strings shouldBe List("OK")
}
// ======================================================================
it should "be possible to check instance classes - #EX33" in {
val drl =
"""package test //#EX33
|import java.util.LinkedList
|
|declare Someone
| age:int
| name:String @key
| gender:String
|end
|
|rule "init" when then
| insert(new Someone(5, "john", "M"));
| insert(new Someone(75, "joe", "M"));
| insert(new Someone(42, "sarah", "F"));
|end
|
|declare Category end
|declare Young extends Category name:String end
|declare Adult extends Category name:String end
|declare Old extends Category name:String end
|
|rule "young" when Someone(age < 18, $name:name) then insert(new Young($name)); end
|rule "adult" when Someone(age >= 18, age < 60, $name:name) then insert(new Adult($name)); end
|rule "old" when Someone(age >= 60, $name:name) then insert(new Old($name)); end
|
|rule "adult check 1"
|when
| LinkedList(size == 2) from collect(Category( class == Adult.class || == Old.class ) )
|then
| insert("OK1");
|end
|
|rule "adult check 2"
|when
| LinkedList(size == 2) from collect(Category( this instanceof Adult || this instanceof Old ) )
|then
| insert("OK2");
|end
|
|""".stripMargin
val engine = DroolsEngine(drl, DroolsEngineConfig.configWithEquality)
engine.fireAllRules()
engine.strings should contain allOf("OK1", "OK2")
}
// ======================================================================
it should "be possible to collect all facts - #EX34" in {
val drl =
"""package test //#EX34
|
|import java.util.List;
|
|declare Someone
| age:int
| name:String @key
|end
|
|rule "init#1" when then
| insert(new Someone(5, "john"));
| insert(new Someone(75, "joe"));
| insert(new Someone(42, "sarah"));
|end
|
|rule "init#2" duration 5000 when then
| insert(new Someone(25, "Donald"));
| insert(new Someone(37, "Mary"));
|end
|
|rule "all facts" when
| $newpeople:List(size>0) from collect(Someone())
|then
| insert("OK"+$newpeople.size());
|end
|
|""".stripMargin
val engine = DroolsEngine(drl, DroolsEngineConfig.configWithEquality)
engine.fireAllRules()
engine.strings should contain ("OK3")
engine.advanceTimeSeconds(6)
engine.fireAllRules()
info("each time you add fact, rules with collect give you back all facts event old ones")
engine.strings should contain allOf("OK3", "OK5")
}
// ======================================================================
it should "be possible to get only new facts when a collect rule is fired up again - #EX35" in {
val drl =
"""package test //#EX35
|import java.util.List;
|
|dialect "mvel"
|
|declare Someone
| age:int
| name:String @key
|end
|
|declare Processed
| someone:Someone
|end
|
|rule "init#1" when then
| insert(new Someone(5, "john"));
| insert(new Someone(75, "joe"));
| insert(new Someone(42, "sarah"));
|end
|
|rule "init#2" duration 5000 when then
| insert(new Someone(25, "Donald"));
| insert(new Someone(37, "Mary"));
|end
|
|rule "only unprocessed new facts" when
| $processed:List() from accumulate(Processed($s:someone); collectList($s))
| $newpeople:List(size>0) from collect(Someone($s:this, $s not memberOf $processed))
|then
| for(Someone someone:$newpeople) {
| insert(new Processed(someone));
| }
| insert("OK"+$newpeople.size());
|end
|
|rule "cleanup" when
| Processed($s:someone)
| not Someone(this == $s)
|then
| delete($s);
|end
|
|""".stripMargin
val engine = DroolsEngine(drl, DroolsEngineConfig.configWithEquality)
engine.fireAllRules()
engine.strings should contain ("OK3")
engine.advanceTimeSeconds(6)
engine.fireAllRules()
info("So when new facts are injected (because of the 5s delay in this example), this time only new facts are collected")
engine.strings should contain allOf("OK3", "OK2")
}
}
UnderstandingRules.execute()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment