import operator import pytest from core.builtin_concepts_ids import BuiltinConcepts from core.concept import Concept, DEFINITION_TYPE_DEF from core.rule import Rule, ACTION_TYPE_EXEC from core.sheerka.Sheerka import RECOGNIZED_BY_ID, RECOGNIZED_BY_NAME from core.sheerka.services.SheerkaEvaluateRules import SheerkaEvaluateRules, LOW_PRIORITY_RULES, DISABLED_RULES from core.sheerka.services.SheerkaExecute import ParserInput from core.sheerka.services.SheerkaRuleManager import RuleCompiledPredicate, SheerkaRuleManager from evaluators.PythonEvaluator import PythonEvaluator, Expando from parsers.PythonParser import PythonParser from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka class TestSheerkaEvaluateRules(TestUsingMemoryBasedSheerka): def test_i_can_evaluate_python_rules(self): sheerka, context, r1, r2, r3, r4, r5, r6, r7, r8, r9 = self.init_test().with_format_rules( Rule(predicate="a == 1", action="", priority=1), # r1 Rule(predicate="a == 2", action="", priority=1), # r2 Rule(predicate="a == 3", action="", priority=0), # r3 Rule(predicate="a == 4", action="", priority=0), # r4 Rule(predicate="a == 5", action="", priority=0), # r5 Rule(predicate="a == 1", action="", priority=1), # r6 Rule(predicate="a == 7", action="", priority=1, is_enabled=False), # r7 Rule(predicate="a == 8", action="", priority=1), # r8 Rule(predicate="a == 9", action="", priority=2), # r9 ).unpack() service = sheerka.services[SheerkaEvaluateRules.NAME] rules = sorted([r1, r2, r3, r4, r5, r6, r7, r8, r9], key=operator.attrgetter('priority'), reverse=True) res = service.evaluate_rules(context, rules, {"a": 1}, set()) assert res == { True: [r1, r6], False: [r9, r2, r8], LOW_PRIORITY_RULES: [r3, r4, r5], DISABLED_RULES: [r7] } def test_i_can_evaluate_question_concept_rules(self): sheerka, context, concept, r1, r2, r3, r4, r5, r6, r7, r8, r9 = self.init_test().with_concepts( Concept("x equals y", body="x == y", pre="is_question()").def_var("x").def_var("y"), create_new=True).with_format_rules( Rule(predicate="a equals 1", action="", priority=1), # r1 Rule(predicate="a equals 2", action="", priority=1), # r2 Rule(predicate="a equals 3", action="", priority=0), # r3 Rule(predicate="a equals 4", action="", priority=0), # r4 Rule(predicate="a equals 5", action="", priority=0), # r5 Rule(predicate="a equals 1", action="", priority=1), # r6 Rule(predicate="a equals 7", action="", priority=1, is_enabled=False), # r7 Rule(predicate="a equals 8", action="", priority=1), # r8 Rule(predicate="a equals 9", action="", priority=2), # r9 ).unpack() service = sheerka.services[SheerkaEvaluateRules.NAME] rules = sorted([r1, r2, r3, r4, r5, r6, r7, r8, r9], key=operator.attrgetter('priority'), reverse=True) res = service.evaluate_rules(context, rules, {"a": 1}, set()) assert res == { True: [r1, r6], False: [r9, r2, r8], LOW_PRIORITY_RULES: [r3, r4, r5], DISABLED_RULES: [r7] } @pytest.mark.parametrize("predicates", [ ("True", "False"), ("False", "True"), ]) def test_i_can_eval_when_multiple_python_predicates(self, predicates): """ In this test, the rule has multiple predicates to evaluate The test make sure that is one predicate is successful, the rule must be evaluated """ sheerka, context, my_rule = self.init_test().with_format_rules(Rule("my rule"), compile_rule=False, create_new=False).unpack() service = sheerka.services[SheerkaEvaluateRules.NAME] # create fake compiled predicates parser = PythonParser() my_rule.compiled_predicates = [ RuleCompiledPredicate("my rule", None, PythonEvaluator.NAME, parser.parse(context, ParserInput(exp)), None) for exp in predicates] my_rule.metadata.is_compiled = True my_rule.metadata.is_enabled = True res = service.evaluate_rules(context, [my_rule], {}, set()) assert res == { True: [my_rule], } def test_i_can_evaluate_rules_when_concepts_are_questions(self): sheerka, context, isa, cat, crocodile, pet, r1, r2, r3 = self.init_test().with_concepts( Concept("x is a y", body="isa(x,y)", pre="is_question()").def_var("x").def_var("y"), "cat", "crocodile", "pet", create_new=True).with_format_rules( Rule(predicate="cat is a pet", action=""), Rule(predicate="crocodile is a pet", action=""), Rule(predicate="not crocodile is a pet", action=""), ).unpack() sheerka.set_isa(context, cat, pet) service = sheerka.services[SheerkaEvaluateRules.NAME] res = service.evaluate_rules(context, [r1, r2, r3], {}, set()) assert res == {True: [r1, r3], False: [r2]} @pytest.mark.parametrize("predicate", [ "greetings", "c:|1001:", "hello 'kodjo'" ]) def test_i_can_evaluate_rules_when_concepts_are_not_questions(self, predicate): """ In this test, we evaluate rules that involves concepts that are not questions """ sheerka, context, greetings, rule = self.init_test().with_concepts( Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"), create_new=True).with_format_rules( Rule(predicate=predicate, action="")).unpack() service = sheerka.services[SheerkaEvaluateRules.NAME] ret = sheerka.ret("evaluator", True, sheerka.new(greetings, a="kodjo")) res = service.evaluate_rules(context, [rule], {"__ret": ret}, set()) assert res == {True: [rule]} @pytest.mark.parametrize("recognized_by", [ RECOGNIZED_BY_ID, RECOGNIZED_BY_NAME, None ]) def test_i_can_evaluate_concept_rules_when_variable_is_a_one_word_concept(self, recognized_by): """ In this test, we evaluate rules that involves concepts that are not questions """ sheerka, context, greetings, there, rule = self.init_test().with_concepts( Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"), Concept("there"), create_new=True).with_format_rules( Rule(predicate="hello there", action="")).unpack() service = sheerka.services[SheerkaEvaluateRules.NAME] there_instance = sheerka.new_from_template(there, there.key) if recognized_by: there_instance.set_hint(BuiltinConcepts.RECOGNIZED_BY, recognized_by) ret = sheerka.ret("evaluator", True, sheerka.new(greetings, a=there_instance)) res = service.evaluate_rules(context, [rule], {"__ret": ret}, set()) assert res == {True: [rule]} @pytest.mark.parametrize("recognized_by", [ RECOGNIZED_BY_ID, RECOGNIZED_BY_NAME, None ]) def test_i_can_evaluate_concept_rules_when_variable_is_a_two_words_concept(self, recognized_by): """ In this test, we evaluate rules that involves concepts that are not questions """ sheerka, context, greetings, my_friend, rule = self.init_test().with_concepts( Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"), Concept("my friend"), create_new=True).with_format_rules( Rule(predicate="hello my friend", action="")).unpack() service = sheerka.services[SheerkaEvaluateRules.NAME] my_friend_instance = sheerka.new_from_template(my_friend, my_friend.key) if recognized_by: my_friend_instance.set_hint(BuiltinConcepts.RECOGNIZED_BY, recognized_by) ret = sheerka.ret("evaluator", True, sheerka.new(greetings, a=my_friend_instance)) res = service.evaluate_rules(context, [rule], {"__ret": ret}, set()) assert res == {True: [rule]} def test_i_can_evaluate_concept_rules_when_variable_is_an_expando(self): sheerka, context, greetings, rule = self.init_test().with_concepts( Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"), create_new=True).with_format_rules( Rule(predicate="hello sheerka", action="")).unpack() service = sheerka.services[SheerkaEvaluateRules.NAME] ret = sheerka.ret("evaluator", True, sheerka.new(greetings, a=Expando("sheerka", {}))) res = service.evaluate_rules(context, [rule], {"__ret": ret}, set()) assert res == {True: [rule]} def test_i_can_evaluate_concept_rules_when_same_name(self): sheerka, context, g1, g2, rule = self.init_test().with_concepts( Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"), Concept("greetings", definition="hi a", definition_type=DEFINITION_TYPE_DEF).def_var("a"), create_new=True).with_format_rules( Rule(predicate="greetings", action="")).unpack() service = sheerka.services[SheerkaEvaluateRules.NAME] ret1 = sheerka.ret("evaluator", True, sheerka.new(g1, a="kodjo")) res = service.evaluate_rules(context, [rule], {"__ret": ret1}, set()) assert res == {True: [rule]} ret2 = sheerka.ret("evaluator", True, sheerka.new(g2, a="kodjo")) res = service.evaluate_rules(context, [rule], {"__ret": ret2}, set()) assert res == {True: [rule]} def test_i_can_evaluate_concept_rule_with_the_same_name_when_the_second_concept_is_declared_after(self): sheerka, context, g1, rule, g2 = self.init_test().with_concepts( Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"), create_new=True).with_format_rules(Rule(predicate="greetings", action="")).with_concepts( Concept("greetings", definition="hi a", definition_type=DEFINITION_TYPE_DEF).def_var("a")).unpack() service = sheerka.services[SheerkaEvaluateRules.NAME] ret1 = sheerka.ret("evaluator", True, sheerka.new(g1, a="kodjo")) res = service.evaluate_rules(context, [rule], {"__ret": ret1}, set()) assert res == {True: [rule]} ret2 = sheerka.ret("evaluator", True, sheerka.new(g2, a="kodjo")) res = service.evaluate_rules(context, [rule], {"__ret": ret2}, set()) assert res == {True: [rule]} def test_i_can_disable_rules_at_runtime(self): sheerka, context, r1, r2, = self.init_format_rules( Rule(predicate="a == 1", action="", priority=2), # r1 Rule(predicate="a == 1", action="", priority=1), # r2 ) service = sheerka.services[SheerkaEvaluateRules.NAME] rules = sorted([r1, r2], key=operator.attrgetter('priority'), reverse=True) res = service.evaluate_rules(context, rules, {"a": 1}, {r1.id}) assert res == { True: [r2], DISABLED_RULES: [r1] } def test_rete_network_is_updated_on_new_rule_creation(self): sheerka, context = self.init_test().unpack() evaluate_rule_service = sheerka.services[SheerkaEvaluateRules.NAME] rule_manager_service = sheerka.services[SheerkaRuleManager.NAME] rule = Rule(ACTION_TYPE_EXEC, "test", "ret.status == True", "test()") rule_manager_service.init_rule(context, rule) sheerka.create_new_rule(context, rule) assert rule in evaluate_rule_service.network.rules assert rule.rete_net == evaluate_rule_service.network