1
- from unittest import TestCase
2
1
from unittest .mock import patch , MagicMock
3
2
4
3
import pytest
9
8
from business_rules .actions import BaseActions
10
9
11
10
12
- class EngineTests (TestCase ):
13
- ###
14
- ### Run
15
- ###
16
-
17
- @patch .object (engine , "run" )
18
- def test_run_all_some_rule_triggered (self , * args ):
19
- """By default, does not stop on first triggered rule. Returns True if
20
- any rule was triggered, otherwise False
21
- """
22
- rule1 = {"conditions" : "condition1" , "actions" : "action name 1" }
23
- rule2 = {"conditions" : "condition2" , "actions" : "action name 2" }
24
- variables = BaseVariables ()
25
- actions = BaseActions ()
26
-
27
- def return_action1 (rule , * args , ** kwargs ):
28
- return rule ["actions" ] == "action name 1"
29
-
30
- engine .run .side_effect = return_action1
31
-
32
- result = engine .run_all ([rule1 , rule2 ], variables , actions )
33
- self .assertTrue (result )
34
- self .assertEqual (engine .run .call_count , 2 )
35
-
36
- # switch order and try again
37
- engine .run .reset_mock ()
38
-
39
- result = engine .run_all ([rule2 , rule1 ], variables , actions )
40
- self .assertTrue (result )
41
- self .assertEqual (engine .run .call_count , 2 )
42
-
43
- @patch .object (engine , "run" , return_value = True )
44
- def test_run_all_stop_on_first (self , * args ):
45
- rule1 = {"conditions" : "condition1" , "actions" : "action name 1" }
46
- rule2 = {"conditions" : "condition2" , "actions" : "action name 2" }
47
- variables = BaseVariables ()
48
- actions = BaseActions ()
49
-
50
- result = engine .run_all (
51
- [rule1 , rule2 ], variables , actions , stop_on_first_trigger = True
52
- )
53
- self .assertEqual (result , True )
54
- self .assertEqual (engine .run .call_count , 1 )
55
- engine .run .assert_called_once_with (rule1 , variables , actions )
56
-
57
- @patch .object (engine , "check_conditions_recursively" , return_value = True )
58
- @patch .object (engine , "do_actions" )
59
- def test_run_that_triggers_rule (self , * args ):
60
- rule = {"conditions" : "blah" , "actions" : "blah2" }
61
- variables = BaseVariables ()
62
- actions = BaseActions ()
63
-
64
- result = engine .run (rule , variables , actions )
65
- self .assertEqual (result , True )
66
- engine .check_conditions_recursively .assert_called_once_with (
67
- rule ["conditions" ], variables
68
- )
69
- engine .do_actions .assert_called_once_with (rule ["actions" ], actions )
70
-
71
- @patch .object (engine , "check_conditions_recursively" , return_value = False )
72
- @patch .object (engine , "do_actions" )
73
- def test_run_that_doesnt_trigger_rule (self , * args ):
74
- rule = {"conditions" : "blah" , "actions" : "blah2" }
75
- variables = BaseVariables ()
76
- actions = BaseActions ()
77
-
78
- result = engine .run (rule , variables , actions )
79
- self .assertEqual (result , False )
80
- engine .check_conditions_recursively .assert_called_once_with (
81
- rule ["conditions" ], variables
82
- )
83
- self .assertEqual (engine .do_actions .call_count , 0 )
84
-
85
- ###
86
- ### Operator comparisons
87
- ###
88
- def test_check_operator_comparison (self ):
89
- string_type = StringType ("yo yo" )
90
- with patch .object (string_type , "contains" , return_value = True ):
91
- result = engine ._do_operator_comparison (
92
- string_type , "contains" , "its mocked"
93
- )
94
- self .assertTrue (result )
95
- string_type .contains .assert_called_once_with ("its mocked" )
96
-
97
- ###
98
- ### Actions
99
- ###
100
- def test_do_actions (self ):
101
- actions = [
102
- {"name" : "action1" },
103
- {"name" : "action2" , "params" : {"param1" : "foo" , "param2" : 10 }},
104
- ]
105
- defined_actions = BaseActions ()
106
- defined_actions .action1 = MagicMock ()
107
- defined_actions .action2 = MagicMock ()
108
-
109
- engine .do_actions (actions , defined_actions )
110
-
111
- defined_actions .action1 .assert_called_once_with ()
112
- defined_actions .action2 .assert_called_once_with (param1 = "foo" , param2 = 10 )
113
-
114
- def test_do_with_invalid_action (self ):
115
- actions = [{"name" : "fakeone" }]
116
- err_string = "Action fakeone is not defined in class BaseActions"
117
- with self .assertRaisesRegex (AssertionError , err_string ):
118
- engine .do_actions (actions , BaseActions ())
11
+ @patch .object (engine , "run" )
12
+ def test_run_all_some_rule_triggered (_mock_engine_run : MagicMock ) -> None :
13
+ """By default, does not stop on first triggered rule. Returns True if
14
+ any rule was triggered, otherwise False
15
+ """
16
+ rule1 = {"conditions" : "condition1" , "actions" : "action name 1" }
17
+ rule2 = {"conditions" : "condition2" , "actions" : "action name 2" }
18
+ variables = BaseVariables ()
19
+ actions = BaseActions ()
20
+
21
+ def return_action1 (rule , * args , ** kwargs ):
22
+ return rule ["actions" ] == "action name 1"
23
+
24
+ engine .run .side_effect = return_action1
25
+
26
+ result = engine .run_all ([rule1 , rule2 ], variables , actions )
27
+ assert result is True
28
+ assert engine .run .call_count == 2
29
+
30
+ # switch order and try again
31
+ engine .run .reset_mock ()
32
+
33
+ result = engine .run_all ([rule2 , rule1 ], variables , actions )
34
+ assert result is True
35
+ assert engine .run .call_count == 2
36
+
37
+
38
+ @patch .object (engine , "run" , return_value = True )
39
+ def test_run_all_stop_on_first (_mock_engine_run : MagicMock ) -> None :
40
+ rule1 = {"conditions" : "condition1" , "actions" : "action name 1" }
41
+ rule2 = {"conditions" : "condition2" , "actions" : "action name 2" }
42
+ variables = BaseVariables ()
43
+ actions = BaseActions ()
44
+
45
+ result = engine .run_all (
46
+ [rule1 , rule2 ], variables , actions , stop_on_first_trigger = True
47
+ )
48
+ assert result is True
49
+ assert engine .run .call_count == 1
50
+ engine .run .assert_called_once_with (rule1 , variables , actions )
51
+
52
+
53
+ @patch .object (engine , "check_conditions_recursively" , return_value = True )
54
+ @patch .object (engine , "do_actions" )
55
+ def test_run_that_triggers_rule (
56
+ _mock_do_actions : MagicMock , _mock_check_conditions_recursively : MagicMock
57
+ ) -> None :
58
+ rule = {"conditions" : "blah" , "actions" : "blah2" }
59
+ variables = BaseVariables ()
60
+ actions = BaseActions ()
61
+
62
+ result = engine .run (rule , variables , actions )
63
+ assert result is True
64
+ engine .check_conditions_recursively .assert_called_once_with (
65
+ rule ["conditions" ], variables
66
+ )
67
+ engine .do_actions .assert_called_once_with (rule ["actions" ], actions )
68
+
69
+
70
+ @patch .object (engine , "check_conditions_recursively" , return_value = False )
71
+ @patch .object (engine , "do_actions" )
72
+ def test_run_that_doesnt_trigger_rule (
73
+ _mock_do_actions : MagicMock , _mock_check_conditions_recursively : MagicMock
74
+ ) -> None :
75
+ """
76
+ DOCUMENT ME.
77
+ """
78
+ rule = {"conditions" : "blah" , "actions" : "blah2" }
79
+ variables = BaseVariables ()
80
+ actions = BaseActions ()
81
+ # set up
82
+ result = engine .run (rule , variables , actions )
83
+ assert result is False
84
+ engine .check_conditions_recursively .assert_called_once_with (
85
+ rule ["conditions" ], variables
86
+ )
87
+ assert engine .do_actions .call_count == 0
88
+
89
+
90
+ def test_do_actions () -> None :
91
+ actions = [
92
+ {"name" : "action1" },
93
+ {"name" : "action2" , "params" : {"param1" : "foo" , "param2" : 10 }},
94
+ ]
95
+ defined_actions = BaseActions ()
96
+ defined_actions .action1 = MagicMock ()
97
+ defined_actions .action2 = MagicMock ()
98
+
99
+ engine .do_actions (actions , defined_actions )
100
+
101
+ defined_actions .action1 .assert_called_once_with ()
102
+ defined_actions .action2 .assert_called_once_with (param1 = "foo" , param2 = 10 )
103
+
104
+
105
+ def test_check_operator_comparison () -> None :
106
+ string_type = StringType ("yo yo" )
107
+ with patch .object (string_type , "contains" , return_value = True ):
108
+ result = engine ._do_operator_comparison (string_type , "contains" , "its mocked" )
109
+ assert result is True
110
+ string_type .contains .assert_called_once_with ("its mocked" )
119
111
120
112
121
113
@patch .object (engine , "check_condition" , return_value = True )
@@ -138,7 +130,6 @@ def test_check_all_conditions_with_all_false(_mock_check_condition: MagicMock) -
138
130
variables = BaseVariables ()
139
131
# test
140
132
result = engine .check_conditions_recursively (conditions , variables )
141
- # self.assertEqual(result, False)
142
133
assert result is False
143
134
engine .check_condition .assert_called_once_with ({"thing1" : "" }, variables )
144
135
@@ -154,7 +145,9 @@ def test_check_all_and_any_together() -> None:
154
145
155
146
156
147
def test_check_all_conditions_with_no_items_fails () -> None :
157
- with pytest .raises (AssertionError ):
148
+ with pytest .raises (
149
+ ValueError , match = "'all' conditions must have at least one child condition"
150
+ ):
158
151
engine .check_conditions_recursively ({"all" : []}, BaseVariables ())
159
152
160
153
@@ -181,22 +174,24 @@ def test_check_any_conditions_with_all_false(_mock_check_condition: MagicMock) -
181
174
182
175
183
176
def test_check_any_condition_with_no_items_fails () -> None :
184
- with pytest .raises (AssertionError ):
177
+ with pytest .raises (
178
+ ValueError , match = "'any' conditions must have at least one child condition"
179
+ ):
185
180
engine .check_conditions_recursively ({"any" : []}, BaseVariables ())
186
181
187
182
188
183
@patch .object (engine , "check_condition" )
189
184
def test_nested_all_and_any (_mock_check_condition : MagicMock ) -> None :
185
+ # test
190
186
conditions = {"all" : [{"any" : [{"name" : 1 }, {"name" : 2 }]}, {"name" : 3 }]}
191
187
bv = BaseVariables ()
192
188
193
189
def side_effect (condition , _ ):
194
190
return condition ["name" ] in [2 , 3 ]
195
191
196
192
engine .check_condition .side_effect = side_effect
197
-
193
+ # test
198
194
engine .check_conditions_recursively (conditions , bv )
199
- # self.assertEqual(engine.check_condition.call_count, 3)
200
195
assert engine .check_condition .call_count == 3
201
196
engine .check_condition .assert_any_call ({"name" : 1 }, bv )
202
197
engine .check_condition .assert_any_call ({"name" : 2 }, bv )
@@ -235,3 +230,10 @@ def test_check_not_negates_result(
235
230
result = engine .check_conditions_recursively (conditions , variables )
236
231
assert result is expected_result
237
232
mock_check_condition .assert_called_once_with ({"thing1" : "" }, variables )
233
+
234
+
235
+ def test_do_with_invalid_action () -> None :
236
+ actions = [{"name" : "fakeone" }]
237
+ err_string = "Action fakeone is not defined in class BaseActions"
238
+ with pytest .raises (AssertionError , match = err_string ):
239
+ engine .do_actions (actions , BaseActions ())
0 commit comments