marty 1.2.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -1
  3. data/Gemfile.lock +2 -2
  4. data/app/components/marty/base_rule_view.rb +279 -0
  5. data/app/components/marty/delorean_rule_view.rb +26 -0
  6. data/app/components/marty/extras/layout.rb +22 -7
  7. data/app/components/marty/log_view.rb +1 -1
  8. data/app/components/marty/mcfly_grid_panel.rb +53 -0
  9. data/app/components/marty/mcfly_grid_panel/client/dup_in_form.js +20 -0
  10. data/app/models/marty/base_rule.rb +126 -0
  11. data/app/models/marty/delorean_rule.rb +121 -0
  12. data/lib/marty/data_importer.rb +0 -1
  13. data/lib/marty/rule_script_set.rb +176 -0
  14. data/lib/marty/version.rb +1 -1
  15. data/lib/tasks/marty_tasks.rake +42 -0
  16. data/spec/dummy/app/components/gemini/cm_auth_app.rb +18 -0
  17. data/spec/dummy/app/components/gemini/my_rule_view.rb +63 -0
  18. data/spec/dummy/app/components/gemini/xyz_rule_view.rb +25 -0
  19. data/spec/dummy/app/models/gemini/guard_one.rb +5 -0
  20. data/spec/dummy/app/models/gemini/guard_two.rb +5 -0
  21. data/spec/dummy/app/models/gemini/my_rule.rb +46 -0
  22. data/spec/dummy/app/models/gemini/my_rule_type.rb +5 -0
  23. data/spec/dummy/app/models/gemini/xyz_enum.rb +5 -0
  24. data/spec/dummy/app/models/gemini/xyz_rule.rb +63 -0
  25. data/spec/dummy/app/models/gemini/xyz_rule_type.rb +5 -0
  26. data/spec/dummy/config/locales/en.yml +10 -0
  27. data/spec/dummy/db/migrate/20171220150101_add_rule_type_enums.rb +14 -0
  28. data/spec/dummy/db/migrate/20171221095312_create_gemini_my_rules.rb +22 -0
  29. data/spec/dummy/db/migrate/20171221095359_create_gemini_xyz_rules.rb +21 -0
  30. data/spec/dummy/db/migrate/20171222150100_add_rule_indices.rb +34 -0
  31. data/spec/dummy/db/seeds.rb +1 -1
  32. data/spec/dummy/delorean/base_code.dl +6 -0
  33. data/spec/dummy/lib/gemini/my_rule_script_set.rb +13 -0
  34. data/spec/dummy/lib/gemini/xyz_rule_script_set.rb +22 -0
  35. data/spec/features/rule_spec.rb +265 -0
  36. data/spec/fixtures/csv/rule/DataGrid.csv +6 -0
  37. data/spec/fixtures/csv/rule/MyRule.csv +14 -0
  38. data/spec/fixtures/csv/rule/XyzRule.csv +6 -0
  39. data/spec/models/rule_spec.rb +322 -0
  40. data/spec/support/integration_helpers.rb +1 -0
  41. metadata +29 -2
@@ -0,0 +1,13 @@
1
+ class Gemini::MyRuleScriptSet < Marty::RuleScriptSet
2
+ def self.node_name
3
+ "Node2"
4
+ end
5
+ def self.body_start
6
+ params = <<~END
7
+ param1 =?
8
+ param2 =?
9
+ paramb =? false
10
+ END
11
+ super + indent(params)
12
+ end
13
+ end
@@ -0,0 +1,22 @@
1
+ class Gemini::XyzRuleScriptSet < Marty::RuleScriptSet
2
+ def self.node_name
3
+ "NodeXyz"
4
+ end
5
+ def self.body_start
6
+ "import BaseCode\n#{node_name}: BaseCode::BaseCode\n"
7
+ end
8
+ def xyz_code(rule)
9
+ write_code(rule.computed_guards.select{|k,_|k.starts_with?("xyz_")})
10
+ end
11
+ def guard_code(rule)
12
+ write_code(rule.computed_guards.reject{|k,_|k.starts_with?("xyz_")})
13
+ end
14
+ def get_code(rule)
15
+ x = xyz_code(rule)
16
+ super + (x.blank? ? '' :
17
+ "XyzNode:\n xyz_param =? nil\n" + self.class.indent(x))
18
+ end
19
+ def code_section_counts(rule)
20
+ super + { xyz: xyz_code(rule).count("\n") }
21
+ end
22
+ end
@@ -0,0 +1,265 @@
1
+ require 'spec_helper'
2
+ require 'marty_rspec'
3
+
4
+ feature 'rule view', js: true do
5
+ before(:all) do
6
+ marty_whodunnit
7
+ @save_file = "/tmp/save_#{Process.pid}.psql"
8
+ save_clean_db(@save_file)
9
+ Marty::Script.load_scripts
10
+ dt = DateTime.parse('2017-1-1')
11
+ p = File.expand_path('../../fixtures/csv/rule', __FILE__)
12
+ [Marty::DataGrid, Gemini::XyzRule, Gemini::MyRule].each do |klass|
13
+ f = "%s/%s.csv" % [p, klass.to_s.sub(/(Gemini|Marty)::/,'')]
14
+ Marty::DataImporter.do_import(klass, File.read(f), dt, nil, nil, ",")
15
+ end
16
+ end
17
+ after(:all) do
18
+ restore_clean_db(@save_file)
19
+ end
20
+ def go_to_my_rules
21
+ press("Pricing Config.")
22
+ press("My Rules")
23
+ expect(page).to have_content 'My Rules'
24
+ end
25
+ def go_to_xyz_rules
26
+ press("Pricing Config.")
27
+ press("Xyz Rules")
28
+ expect(page).to have_content 'Xyz Rules'
29
+ end
30
+ def col_id(v, col)
31
+ run_js <<-JS
32
+ #{ext_var(v.grid, 'grid')}
33
+ return #{ext_find(ext_arg('gridcolumn', text: col), 'grid')}.id
34
+ JS
35
+ end
36
+ # click_checkbox in marty_rspec not working here for some reason
37
+ def click_checkbox(name)
38
+ q = %Q(checkbox[fieldLabel="#{name}"])
39
+ id = run_js "return Ext.ComponentQuery.query('#{q}')[0].inputId"
40
+ find_by_id(id).click
41
+ end
42
+ # click_col in marty_rspec is not reliable
43
+ def click_column(rv,name)
44
+ cid = col_id(rv, name)
45
+ c = find('#'+cid)
46
+ c.select_option # .click does not work reliably
47
+ c.send_keys(' ')
48
+ # wait_for_ajax and wait_for_ready do not work here,
49
+ # or in the next two methods
50
+ sleep 1.0
51
+ end
52
+
53
+ def column_filter(rv,name,value)
54
+ cid = col_id(rv, name)
55
+ c = find('#'+cid)
56
+ c.send_keys([:down, :down, :down, :down, :right, value, :return])
57
+ sleep 1.0
58
+ end
59
+ def column_filter_toggle(rv,name)
60
+ cid = col_id(rv, name)
61
+ c = find('#'+cid)
62
+ c.send_keys([:down, :down, :down, :down, ' ', :escape])
63
+ sleep 1.0
64
+ end
65
+ # idx 0 is the start dt, 1 is the end dt
66
+ def date_fill_in(idx, value)
67
+ dt = all(:xpath, "//input[contains(@name, 'datefield')]")[idx]
68
+ dt.native.clear()
69
+ dt.native.send_keys(value)
70
+ end
71
+ def time_fill_in(idx,value)
72
+ tm = all(:xpath, "//input[contains(@name, 'timefield')]")[idx]
73
+ tm.native.clear()
74
+ tm.native.send_keys(value)
75
+ end
76
+ it "rule workflow" do
77
+ log_in_as('marty')
78
+ page.driver.browser.manage.window.maximize
79
+ go_to_my_rules
80
+ mrv = netzke_find("my_rule_view")
81
+ # test required field
82
+ press('Add')
83
+ wait_for_ajax
84
+ fill_in('name', with: 'abc')
85
+ press('OK')
86
+ expect(page).to have_content("Rule type can't be blank")
87
+ expect(page).to have_content("Start dt can't be blank")
88
+ # create and verify rule
89
+ fill_in('rule_type', with: 'SimpleRule')
90
+ date_fill_in(0, '2013-01-01')
91
+ time_fill_in(0, '11:03:01')
92
+ date_fill_in(1, '2030-01-01')
93
+ time_fill_in(1, '08:03:01')
94
+ press("OK")
95
+ wait_for_ajax
96
+ expect(mrv.row_count()).to eq(8)
97
+ expect(mrv.get_row_vals(1)).to include({"name"=>"abc",
98
+ "rule_type"=>"SimpleRule",
99
+ "start_dt"=>"2013-01-01T19:03:01.000Z",
100
+ "end_dt"=>"2030-01-01T16:03:01.000Z",
101
+ "other_flag"=>false,
102
+ "g_array"=>"",
103
+ "g_single"=>"",
104
+ "g_string"=>"",
105
+ "g_bool"=>nil,
106
+ "g_range"=>nil,
107
+ "g_integer"=>nil,
108
+ "computed_guards"=>"",
109
+ "grids"=>"",
110
+ "results"=>"",
111
+ })
112
+ r = Gemini::MyRule.lookup('infinity','abc')
113
+ expect(r.as_json).to include({"user_id"=>1,
114
+ "o_user_id"=>nil,
115
+ "name"=>"abc",
116
+ "engine"=>"Gemini::MyRuleScriptSet",
117
+ "rule_type"=>"SimpleRule",
118
+ "simple_guards"=>{"g_has_default"=>
119
+ "string default"},
120
+ "computed_guards"=>{},
121
+ "grids"=>{},
122
+ "results"=>{},
123
+ })
124
+ # type validation (string with values list)
125
+ mrv.select_row(1)
126
+ press("Edit")
127
+ fill_in(:g_string, with: "12345")
128
+ press("OK")
129
+ expect(page).to have_content("Bad value '12345' for 'g_string'")
130
+ # type validation (range)
131
+ fill_in(:g_string, with: "Hi Mom")
132
+ click_checkbox("Bool Guard")
133
+ click_checkbox("Other")
134
+ netzke_find('Array Guard', 'combobox').select_values("G1V1,G1V3")
135
+ netzke_find('Single Guard', 'combobox').select_values("G2V2")
136
+ fill_in(:g_integer, with: 123)
137
+ fill_in(:g_range, with: "asfd")
138
+ press("OK")
139
+ expect(page).to have_content("Wrong type for 'g_range'")
140
+ # validate rule
141
+ fill_in(:g_range, with: "<=100")
142
+ netzke_find('Grid1', 'combobox').select_values("DataGrid1")
143
+ netzke_find('Grid2', 'combobox').select_values("DataGrid2")
144
+ press("OK")
145
+ wait_for_ajax
146
+ exp = {"name"=>"abc",
147
+ "rule_type"=>"SimpleRule",
148
+ "start_dt"=>"2013-01-01T19:03:01.000Z",
149
+ "end_dt"=>"2030-01-01T16:03:01.000Z",
150
+ "other_flag"=>true,
151
+ "g_array"=>"G1V1,G1V3",
152
+ "g_single"=>"G2V2",
153
+ "g_string"=>"Hi Mom",
154
+ "g_bool"=>true,
155
+ "g_range"=>"<=100",
156
+ "g_integer"=>123,
157
+ "computed_guards"=>"",
158
+ "grids"=>"{\"grid1\":\"DataGrid1\",\"grid2\":\"DataGrid2\"}",
159
+ "results"=>"",
160
+ }
161
+ expect(mrv.get_row_vals(1)).to include(exp)
162
+ # grid edits
163
+ press("Edit")
164
+ netzke_find('Grid2', 'combobox').select_values("---")
165
+ press("OK")
166
+ wait_for_ajax
167
+ expect(mrv.get_row_vals(1)).to include(exp+{"grids"=>
168
+ "{\"grid1\":\"DataGrid1\"}"})
169
+ # computed fields
170
+ press("Edit")
171
+ fill_in(:computed_guards, with: 'sadf asdf ljsf')
172
+ press("OK")
173
+ exp = "Computed - Error in rule 'abc' field 'computed_guards': Syntax error on line 1"
174
+ expect(page).to have_content(exp)
175
+ fill_in(:computed_guards, with: 'sadf = 123j s /*fdjOIb')
176
+ press("OK")
177
+ exp = "Computed - Error in rule 'abc' field 'computed_guards': syntax error"
178
+ expect(page).to have_content(exp)
179
+ fill_in(:computed_guards, with: '')
180
+ fill_in(:results, with: %Q(abc = "def"\ndef = 5\nxyz=def+10\nsadf asdf lsf))
181
+ press("OK")
182
+ exp = "Computed - Error in rule 'abc' field 'results': Syntax error on line 4"
183
+ expect(page).to have_content(exp)
184
+ fill_in(:results,
185
+ with: %Q(abc = "def"\ndef = "abc"\nklm = "3"\nabc = "xyz"))
186
+ exp = "Computed - Error in rule 'abc' field 'results': Keyword 'abc' specified more"\
187
+ " than once (line 4)"
188
+ press("OK")
189
+ expect(page).to have_content(exp)
190
+ fill_in(:results,
191
+ with: %Q(abc = "def"\ndef = "abc"\nklm = "3"))
192
+ press("OK")
193
+
194
+ press("Dup in form")
195
+ press("OK")
196
+ exp = Regexp.new("Can't have rule with same name and overlapping start"\
197
+ "/end dates - abc")
198
+ expect(page).to have_content(exp)
199
+ netzke_find('Single Guard', 'combobox').select_values("G2V3")
200
+ press("OK")
201
+ exp = Regexp.new("Can't have rule with same name and different "\
202
+ "type/guards - abc")
203
+ expect(page).to have_content(exp)
204
+
205
+ press("Cancel")
206
+ # column sorting, etc
207
+ go_to_xyz_rules
208
+ xrv = netzke_find("xyz_rule_view")
209
+ expect(page).to have_content("Rule type")
210
+ expect(xrv.col_values(:name, 5, 0)).to eq(["ZRule1", "ZRule2",
211
+ "ZRule3", "ZRule4",
212
+ "ZRule5"])
213
+ xrv.select_row(1)
214
+ press("Edit")
215
+ fill_in("Range Guard 1", with: "[100,200)")
216
+ fill_in("Range Guard 2", with: "[30,40)")
217
+ press("OK")
218
+ r = Gemini::XyzRule.get_matches('infinity', {}, {"g_range1"=> 150,
219
+ "g_range2"=> 35})
220
+ expect(r.to_a.count).to eq(1)
221
+ exp = {"user_id"=>1,
222
+ "o_user_id"=>nil,
223
+ "name"=>"ZRule1",
224
+ "engine"=>"Gemini::XyzRuleScriptSet",
225
+ "rule_type"=>"ZRule",
226
+ "start_dt"=>DateTime.parse("2017-1-1 08:01:00"),
227
+ "simple_guards"=>{"g_date"=>"2017-1-1",
228
+ "g_range1"=>"[100,200)",
229
+ "g_range2"=>"[30,40)",
230
+ "g_string"=>"aaa",
231
+ "g_integer"=>"5",
232
+ "g_datetime"=>"2017-1-1 12:00:01"},
233
+ "computed_guards"=>{},
234
+ "grids"=>{"grid1"=>"DataGrid1"},
235
+ "results"=>
236
+ {"bvlen"=>"base_value.length",
237
+ "bv"=>"base_value"}}
238
+
239
+ expect(r.first.as_json).to include(exp)
240
+ expect(xrv.col_values(:g_string, 5, 0)).to eq(["aaa", "bbb", "ccc",
241
+ "ddd", "eee"])
242
+ click_column(xrv, "G string")
243
+ expect(xrv.col_values(:g_string, 5, 0)).to eq(["eee", "ddd", "ccc",
244
+ "bbb", "aaa"])
245
+ column_filter(xrv, "G string", "eee")
246
+ rc = xrv.row_count
247
+ expect(xrv.col_values(:g_string,rc,0)).to eq(["eee"])
248
+ column_filter_toggle(xrv, "G string")
249
+ rc = xrv.row_count
250
+ expect(xrv.col_values(:g_string,rc,0)).to eq(["eee", "ddd", "ccc",
251
+ "bbb", "aaa"])
252
+ column_filter(xrv, "Grids", "Grid1")
253
+ rc = xrv.row_count
254
+ # netzke reports jsonb as string
255
+ expect(xrv.col_values(:grids,rc,0)).to eq([%Q({"grid1":"DataGrid1"}),
256
+ %Q({"grid1":"DataGrid1"})])
257
+ column_filter_toggle(xrv, "Grids")
258
+ rc = xrv.row_count
259
+ expect(xrv.col_values(:grids,rc,0)).to eq([%Q({"grid1":"DataGrid3"}),
260
+ %Q({"grid1":"DataGrid3"}),
261
+ %Q({"grid1":"DataGrid2"}),
262
+ %Q({"grid1":"DataGrid1"}),
263
+ %Q({"grid1":"DataGrid1"})])
264
+ end
265
+ end
@@ -0,0 +1,6 @@
1
+ name,data,metadata,data_type,lenient
2
+ DataGrid1,"[[9,8,7,6,5,4,3,2,1,0]]","[{""dir"": ""h"", ""attr"": ""param1"", ""type"": ""int4range"", ""keys"": [""[,10]"",""(10,20]"",""(20,30]"",""(30,40]"",""(40,50]"",""(50,60]"",""(60,70]"",""(70,80]"",""(80,90]"",""(90,100]""]}]",,t
3
+ DataGrid2,"[[9,8,7,6,5,4,3,2,1,0],[19,18,17,16,15,14,1300,12,11,10]]","[{""dir"": ""h"", ""attr"": ""param1"", ""type"": ""int4range"", ""keys"": [""[,10]"",""(10,20]"",""(20,30]"",""(30,40]"",""(40,50]"",""(50,60]"",""(60,70]"",""(70,80]"",""(80,90]"",""(90,100]""]},{""dir"":""v"",""attr"":""paramb"",""type"":""boolean"",""keys"":[true, false]}]",,t
4
+ DataGrid3,"[[9,8,7,6,5,4,3,2,1,0],[19,18,17,16,15,14,1300,12,11,10]]","[{""dir"": ""h"", ""attr"": ""p2"", ""type"": ""int4range"", ""keys"": [""[,10]"",""(10,20]"",""(20,30]"",""(30,40]"",""(40,50]"",""(50,60]"",""(60,70]"",""(70,80]"",""(80,90]"",""(90,100]""]},{""dir"":""v"",""attr"":""flavor"",""type"":""string"",""keys"":[[""lemon""], [""cherry""]]}]",,f
5
+ DataGrid4,"[[9,8,7,6,5,4,3,2,1,0],[19,18,17,16,15,14,1300,12,11,10]]","[{""dir"": ""h"", ""attr"": ""param1"", ""type"": ""int4range"", ""keys"": [""[,10]"",""(10,20]"",""(20,30]"",""(30,40]"",""(40,50]"",""(50,60]"",""(60,70]"",""(70,80]"",""(80,90]"",""(90,100]""]},{""dir"":""v"",""attr"":""flavor"",""type"":""string"",""keys"":[[""lemon""], [""cherry""]]}]",,f
6
+
@@ -0,0 +1,14 @@
1
+ name,rule_type,start_dt,end_dt,other_flag,simple_guards,computed_guards,grids,results
2
+ Rule1,SimpleRule,2017-1-1 12:00:00,2017-4-1,,"{""g_has_default"":""different"",""g_array"":[""G1V1"",""G1V3""],""g_single"":""G2V2"",""g_string"":""Hi Mom"",""g_bool"":true,""g_range"":""[50,)"",""g_integer"":10}",,,"{""simple_result"":""\""a value\""""}"
3
+ Rule2,SimpleRule,2017-2-1 14:00:00,2017-4-1,true,"{""g_array"":[""G1V2""],""g_single"":""G2V3"",""g_string"":""abc"",""g_bool"":true,""g_range"":""(,50]"",""g_integer"":11}",,,"{
4
+ ""simple_result"":""\""b value\"" # with comment "",
5
+ ""sr2"":""true # with comment"",
6
+ ""sr3"": ""123 # with comment "",
7
+ ""c1"":""123 + 456 # with comment"",
8
+ ""single_quote"":""'string with single quotes'"",
9
+ ""stringwithhash"": ""\"" string that contains a # character\""""}"
10
+ Rule2a,SimpleRule,2017-2-1 14:00:00,2017-4-1,true,"{""g_array"":[""G1V2""],""g_single"":""G2V3"",""g_string"":""abc"",""g_bool"":true,""g_range"":""(,50]"",""g_integer"":99}",,,"{""simple_result"":""\""b value\"""", ""sr2"":""true"", ""sr3"": ""123""}"
11
+ Rule2b,SimpleRule,2017-2-1 14:00:00,2017-4-1,true,"{""g_array"":[""G1V2""],""g_single"":""G2V3"",""g_string"":""abc"",""g_bool"":true,""g_range"":""(,50]"",""g_integer"":999}",,"{""grid1"":""DataGrid1"",""grid2"":""DataGrid2""}",
12
+ Rule3,ComplexRule,2017-3-1 00:00:00,2017-4-1,false,"{""g_array"":[""G1V2"",""G1V3""],""g_string"":""def"",""g_integer"":11}","{""cguard1"":""1==1"",""cguard2"":""param2 == 'abc'""}","{""grid1"":""DataGrid1"",""grid2"":""DataGrid2""}","{""simple_result"":""\""c value\"""",""computed_value"":""if paramb then param1 / (grid1_grid||1) else (grid2_grid||1) / param1""}"
13
+ Rule4,ComplexRule,2017-4-1 15:00:01,2017-5-1,,"{""g_array"":[""G1V2"",""G1V3""],""g_string"":""Hi Mom"",""g_integer"":11}","{""cguard1"":""1==1"",""cguard2"":""param2 == \""abc\""""}","{""grid1"":""DataGrid1"",""grid2"":""DataGrid2""}","{""simple_result"":""\""c value\"""",""grid_sum"":""(grid1_grid||0)+(grid2_grid||0)""}"
14
+ Rule5,ComplexRule,2017-4-2 15:00:01,2017-5-1,,"{""g_string"":""zzz"",""g_integer"":3757,""g_has_default"":""foo""}","{""cguard1"":""1==1""}",,"{""flavor"": ""[\""cherry\"",\""lemon\""][param2]"",""other_grid"": ""DataGrid4"",""final_value"": ""other_grid * 3""}"
@@ -0,0 +1,6 @@
1
+ name,rule_type,start_dt,simple_guards,computed_guards,grids,results
2
+ ZRule1,ZRule,2017-1-1 08:01:00,"{""g_string"":""aaa"",""g_range1"":""[10,20)"",""g_integer"":5,""g_date"":""2017-1-1"",""g_datetime"":""2017-1-1 12:00:01""}","{}","{""grid1"":""DataGrid1""}","{""bvlen"": ""base_value.length"", ""bv"":""base_value""}"
3
+ ZRule2,ZRule,2017-2-1 09:01:00,"{""g_string"":""bbb"",""g_range1"":""[20,30)"",""g_integer"":4,""g_date"":""2017-1-2"",""g_datetime"":""2017-1-1 12:00:02""}","{}","{""grid1"":""DataGrid1""}","{""key1"":""\""value1\"""",""bvlen"": ""base_value.length"", ""bv"":""base_value""}"
4
+ ZRule3,ZRule,2017-3-1 10:00:00,"{""g_string"":""ccc"",""g_range1"":""[30,40)"",""g_integer"":3,""g_date"":""2017-1-3"",""g_datetime"":""2017-1-1 12:00:03""}","{}","{""grid1"":""DataGrid2""}","{""bvlen"": ""base_value.length"", ""bv"":""base_value""}"
5
+ ZRule4,ZRule,2017-3-1 11:00:00,"{""g_string"":""ddd"",""g_range1"":""[40,50)"",""g_integer"":2,""g_date"":""2017-1-4"",""g_datetime"":""2017-1-1 12:00:04""}","{}","{""grid1"":""DataGrid3""}","{""bvlength"": ""base_value.length"", ""bv"":""base_value""}"
6
+ ZRule5,ZRule,2017-3-1 11:00:01,"{""g_string"":""eee"",""g_range1"":""[50,60)"",""g_integer"":1,""g_date"":""2017-1-5"",""g_datetime"":""2017-1-1 12:00:05""}","{}","{""grid1"":""DataGrid3""}","{""bvlen"": ""base_value.length"", ""bv"":""base_value""}"
@@ -0,0 +1,322 @@
1
+ require 'spec_helper'
2
+
3
+ module Marty::RuleSpec
4
+ describe "Rule" do
5
+ before(:all) do
6
+ @save_file = "/tmp/save_#{Process.pid}.psql"
7
+ save_clean_db(@save_file)
8
+ Marty::Script.load_scripts
9
+ Marty::Config['RULEOPTS_MYRULE']={'simple_result'=>{},
10
+ 'computed_value'=>{},
11
+ 'final_value'=>{},
12
+ }
13
+ Marty::Config['RULEOPTS_XYZ']={'bvlength'=>{},
14
+ 'bv'=>{},
15
+ }
16
+ end
17
+ after(:all) do
18
+ restore_clean_db(@save_file)
19
+ end
20
+ before(:each) do
21
+ marty_whodunnit
22
+ dt = DateTime.parse('2017-1-1')
23
+ p = File.expand_path('../../fixtures/csv/rule', __FILE__)
24
+ [Marty::DataGrid, Gemini::XyzRule, Gemini::MyRule].each do |klass|
25
+ f = "%s/%s.csv" % [p, klass.to_s.sub(/(Gemini|Marty)::/,'')]
26
+ Marty::DataImporter.do_import(klass, File.read(f), dt, nil, nil, ",")
27
+ end
28
+ Marty::Tag.do_create('2017-01-01', 'tag')
29
+ end
30
+ context "validation" do
31
+ subject do
32
+ guards = (@g_array ? {"g_array" =>@g_array} : {}) +
33
+ (@g_single ? {"g_single" =>@g_single} : {}) +
34
+ (@g_string ? {"g_string" =>@g_string} : {}) +
35
+ (@g_bool ? {"g_bool" =>@g_bool} : {}) +
36
+ (@g_range ? {"g_range" =>@g_range} : {}) +
37
+ (@g_integer ? {"g_integer" =>@g_integer} : {})
38
+ Gemini::MyRule.create!(name: "testrule",
39
+ rule_type: @rule_type,
40
+ start_dt: @start_dt || '2013-1-1',
41
+ end_dt: @end_dt,
42
+ simple_guards: guards,
43
+ computed_guards: @computed_guards || {},
44
+ grids: @grids || {},
45
+ results: @results || {}
46
+ )
47
+ end
48
+ it "detects type errors" do
49
+ @rule_type = 'SimpleRule'
50
+ @g_integer = "abc"
51
+ expect{subject}.to raise_error(/Guards - Wrong type for 'g_integer'/)
52
+ end
53
+ it "detects value errors 1" do
54
+ @rule_type = "SimpleRule"
55
+ @g_array = ["G1V1", "abcd"]
56
+ expect{subject}.to raise_error(/Guards - Bad value 'abcd' for 'g_array'/)
57
+ end
58
+ it "detects value errors 2" do
59
+ @rule_type = "SimpleRule"
60
+ @g_array = ["G1V1", "xyz", "abc"]
61
+ exp = /Guards - Bad values 'xyz', 'abc' for 'g_array'/
62
+ expect{subject}.to raise_error(exp)
63
+ end
64
+ it "detects arity errors 1" do
65
+ @rule_type = "SimpleRule"
66
+ @g_single = ["G2V1","G2V2"]
67
+ exp = /Guards - Wrong arity for 'g_single' .expected single got multi./
68
+ expect{subject}.to raise_error(exp)
69
+ end
70
+ it "detects arity errors 2" do
71
+ @rule_type = "SimpleRule"
72
+ @g_array = "G1V1"
73
+ exp = /Guards - Wrong arity for 'g_array' .expected multi got single./
74
+ expect{subject}.to raise_error(exp)
75
+ end
76
+ it "detects errors in computed guards" do
77
+ @rule_type = "SimpleRule"
78
+ @computed_guards = {"guard1"=> "zvjsdf12.z8*"}
79
+ exp = /Computed - Error in rule 'testrule' field 'computed_guards': syntax error/
80
+ expect{subject}.to raise_error(exp)
81
+ end
82
+ it "detects errors in computed results" do
83
+ @rule_type = "SimpleRule"
84
+ @results = {"does_not_compute"=> "zvjsdf12.z8*"}
85
+ @grids = {"grid1"=>"DataGrid1","grid2"=>"DataGrid2"}
86
+ exp = /Computed - Error in rule 'testrule' field 'results': syntax error/
87
+ expect{subject}.to raise_error(exp)
88
+ end
89
+ it "detects errors in computed results 2" do
90
+ @rule_type = "SimpleRule"
91
+ @results = {"does_not_compute"=> "zvjsdf12.z8*"}
92
+ @grids = {"grid1"=>"DataGrid1","grid2"=>"DataGrid1","grid3"=>"DataGrid3"}
93
+ exp = /Computed - Error in rule 'testrule' field 'results': syntax error/
94
+ expect{subject}.to raise_error(exp)
95
+ end
96
+ it "detects errors in computed results 3" do
97
+ @rule_type = "SimpleRule"
98
+ @results = {"does_not_compute"=> "zvjsdf12.z8*"}
99
+ @grids = {"grid1"=>"DataGrid1","grid2"=>"DataGrid1","grid3"=>"DataGrid1"}
100
+ exp = /Computed - Error in rule 'testrule' field 'results': syntax error/
101
+ expect{subject}.to raise_error(exp)
102
+ end
103
+ it "reports bad grid names" do
104
+ @rule_type = "SimpleRule"
105
+ @grids = {"grid1"=>"xyz","grid2"=>"DataGrid2","grid3"=>"DataGrid1"}
106
+ exp = /Grids - Bad grid name 'xyz' for 'grid1'/
107
+ expect{subject}.to raise_error(exp)
108
+ end
109
+ it "sets guard defaults correctly" do
110
+ vals = Gemini::MyRule.all.map do
111
+ |r|
112
+ [r.name, r.simple_guards["g_has_default"]]
113
+ end
114
+ expect(vals).to eq([["Rule1", "different"],
115
+ ["Rule2", "string default"],
116
+ ["Rule2a", "string default"],
117
+ ["Rule2b", "string default"],
118
+ ["Rule3", "string default"],
119
+ ["Rule4", "string default"],
120
+ ["Rule5", "foo"]])
121
+ end
122
+ end
123
+ context "validation (xyz type)" do
124
+ subject do
125
+ r=Gemini::XyzRule.create!(name: "testrule",
126
+ rule_type: @rule_type,
127
+ start_dt: @start_dt || '2013-1-1',
128
+ end_dt: @end_dt,
129
+ simple_guards: {},
130
+ computed_guards: @computed_guards || {},
131
+ grids: @grids || {},
132
+ results: @results || {}
133
+ )
134
+ r.reload
135
+ end
136
+ it "detects script errors" do
137
+ @rule_type = 'XRule'
138
+ @results = {"x"=>"zx sdf wer"}
139
+ exp = /Computed - Error in rule 'testrule' field 'results': syntax error/
140
+ expect{subject}.to raise_error(exp)
141
+ end
142
+ it "rule script stuff overrides 1" do
143
+ @rule_type = 'XRule'
144
+ @computed_guards = {"abc"=>"true", "xyz_guard"=> "err err err"}
145
+ exp = /Computed - Error in rule 'testrule' field 'xyz': syntax error/
146
+ expect{subject}.to raise_error(exp)
147
+ end
148
+ it "rule script stuff overrides 2" do
149
+ @rule_type = 'XRule'
150
+ @computed_guards = {"abc"=>"err err err", "xyz_guard"=> "xyz_param"}
151
+ exp = /Computed - Error in rule 'testrule' field 'computed_guards': syntax error/
152
+ expect{subject}.to raise_error(exp)
153
+ end
154
+ it "rule script stuff overrides 3" do
155
+ @rule_type = 'XRule'
156
+ @computed_guards = {"abc"=>"true", "xyz_guard"=> "!xyz_param"}
157
+ rule = subject
158
+ expect(rule.compute_xyz('infinity',true)).to be false
159
+ expect(rule.compute_xyz('infinity',false)).to be true
160
+ end
161
+ it "no error" do
162
+ @rule_type = 'XRule'
163
+ @results = {"x"=>"1"}
164
+ expect{subject}.not_to raise_error
165
+ end
166
+ end
167
+
168
+ context "lookups" do
169
+ it "matches" do
170
+ lookup = Gemini::MyRule.get_matches('infinity',
171
+ {'rule_type'=>'SimpleRule'},
172
+ {'g_array'=>'G1V3'})
173
+ expect(lookup.to_a.count).to eq(1)
174
+ expect(lookup.first.name).to eq("Rule1")
175
+ lookup = Gemini::MyRule.get_matches('infinity',
176
+ {'rule_type'=>'SimpleRule',
177
+ 'other_flag'=>true},
178
+ {})
179
+ expect(lookup.to_a.count).to eq(3)
180
+ expect(lookup.map{|l|l.name}.to_set).to eq(Set["Rule2","Rule2a","Rule2b"])
181
+ lookup = Gemini::MyRule.get_matches('infinity',
182
+ {'rule_type'=>'ComplexRule',
183
+ 'other_flag'=>false},
184
+ {})
185
+ expect(lookup.to_a.count).to eq(1)
186
+ expect(lookup.first.name).to eq("Rule3")
187
+ lookup = Gemini::MyRule.get_matches('infinity',
188
+ {'rule_type'=>'ComplexRule'},
189
+ {'g_string'=>'def'})
190
+ expect(lookup.to_a.count).to eq(1)
191
+ expect(lookup.first.name).to eq("Rule3")
192
+ lookup = Gemini::MyRule.get_matches('infinity',
193
+ {'rule_type'=>'ComplexRule'},
194
+ {'g_string'=>'abc'})
195
+ expect(lookup).to eq([])
196
+ lookup = Gemini::MyRule.get_matches('infinity',
197
+ {'rule_type'=>'SimpleRule'},
198
+ {'g_bool'=>true, "g_range"=>25,
199
+ 'g_integer'=>99})
200
+ expect(lookup.to_a.count).to eq(1)
201
+ expect(lookup.first.name).to eq("Rule2a")
202
+ lookup = Gemini::MyRule.get_matches('infinity',
203
+ {'rule_type'=>'SimpleRule'},
204
+ {'g_bool'=>true, "g_range"=>75})
205
+ expect(lookup.to_a.count).to eq(1)
206
+ expect(lookup.first.name).to eq("Rule1")
207
+ lookup = Gemini::MyRule.get_matches('infinity',
208
+ {'rule_type'=>'SimpleRule'},
209
+ {'g_bool'=>true, "g_range"=>75,
210
+ 'g_integer'=>11})
211
+ expect(lookup).to eq([])
212
+ lookup = Gemini::MyRule.get_matches('infinity',
213
+ {'rule_type'=>'SimpleRule'},
214
+ {'g_bool'=>true, "g_range"=>75,
215
+ 'g_integer'=>10})
216
+ expect(lookup.to_a.count).to eq(1)
217
+ expect(lookup.first.name).to eq("Rule1")
218
+ lookup = Gemini::MyRule.get_matches('infinity',
219
+ {'rule_type'=>'SimpleRule'},
220
+ {'g_bool'=>false, "g_range"=>75,
221
+ 'g_integer'=>10})
222
+ expect(lookup).to eq([])
223
+ lookup = Gemini::MyRule.get_matches('infinity',
224
+ {'rule_type'=>'SimpleRule'}, {})
225
+ expect(lookup.to_a.count).to eq(4)
226
+ lookup = Gemini::MyRule.get_matches('infinity',
227
+ {'rule_dt'=>"2017-3-1 02:00:00"},
228
+ {})
229
+ expect(lookup.to_a.count).to eq(5)
230
+ expect(lookup.pluck(:name).to_set).to eq(Set["Rule1", "Rule2", "Rule2a",
231
+ "Rule2b", "Rule3"])
232
+ lookup = Gemini::MyRule.get_matches('infinity',
233
+ {'rule_dt'=>"2017-4-1 16:00:00"},
234
+ {})
235
+ expect(lookup.to_a.count).to eq(1)
236
+ expect(lookup.pluck(:name).first).to eq("Rule4")
237
+ lookup = Gemini::MyRule.get_matches('infinity',
238
+ {'rule_dt'=>"2016-12-31"}, {})
239
+ expect(lookup.to_a).to eq([])
240
+ lookup = Gemini::MyRule.get_matches('infinity',
241
+ {'rule_dt'=>"2017-5-1 00:00:01"}, {})
242
+ expect(lookup.to_a).to eq([])
243
+ end
244
+ end
245
+ context "rule compute" do
246
+ let(:complex) { Gemini::MyRule.get_matches('infinity',
247
+ {'rule_type'=>'ComplexRule'},
248
+ {'g_string'=>'def'}).first }
249
+ let(:xyz) { Gemini::XyzRule.get_matches('infinity',
250
+ {'rule_type'=>'ZRule'},
251
+ {'g_integer'=> 2}).first }
252
+ let(:simple) {
253
+ Gemini::MyRule.get_matches('infinity',
254
+ {'rule_type'=>'SimpleRule'},
255
+ {'g_bool'=>true, "g_range"=>25}).first }
256
+ let(:simple2a) {
257
+ Gemini::MyRule.get_matches('infinity',
258
+ {'rule_type'=>'SimpleRule'},
259
+ {'g_bool'=>true, "g_integer"=>99}).first }
260
+ let(:simple2b) {
261
+ Gemini::MyRule.get_matches('infinity',
262
+ {'rule_type'=>'SimpleRule'},
263
+ {'g_bool'=>true, "g_integer"=>999}).first }
264
+ let(:altgridmethod) {
265
+ Gemini::MyRule.get_matches('infinity',
266
+ {'rule_type'=>'ComplexRule'},
267
+ {"g_integer"=>3757}).first }
268
+ it "computed guards work" do
269
+ c = complex.compute({"pt"=>Time.zone.now,
270
+ 'param2'=>'def'})
271
+ expect(c).to eq({"cguard2"=>false})
272
+ end
273
+ it "returns simple results via #fixed_results" do
274
+ expect(simple.fixed_results["simple_result"]).to eq("b value")
275
+ expect(simple.fixed_results["sr2"]).to eq(true)
276
+ expect(simple.fixed_results["sr3"]).to eq(123)
277
+ ssq = "string with single quotes"
278
+ expect(simple.fixed_results["single_quote"]).to eq(ssq)
279
+ swh = " string that contains a # character"
280
+ expect(simple.fixed_results["stringwithhash"]).to eq(swh)
281
+ expect(simple.fixed_results.count).to eq(5)
282
+ allow_any_instance_of(Delorean::Engine).
283
+ to receive(:evaluate).and_raise('hi mom')
284
+ expect{simple.compute({"pt"=>Time.now})}.to raise_error(/hi mom/)
285
+ # simple2a should return results without evaluation (they are all fixed)
286
+ expect(simple2a.compute({"pt"=>Time.zone.now})).to eq(
287
+ {"simple_result"=>"b value",
288
+ "sr2"=>true,
289
+ "sr3"=>123})
290
+ # simple2b should return grid results without evaluation
291
+ expect(simple2b.compute({"pt"=>Time.zone.now,
292
+ 'param1'=> 66,
293
+ 'param2'=>'abc',
294
+ 'paramb'=>false})).to eq({"grid1_grid"=>3,
295
+ "grid2_grid"=>1300})
296
+
297
+ end
298
+ it "returns computed results" do
299
+ c = complex.compute({"pt"=>Time.zone.now,
300
+ 'param1'=> 66,
301
+ 'param2'=>'abc',
302
+ 'paramb'=>false})
303
+ expect(c).to eq({"simple_result"=>"c value",
304
+ "computed_value"=>19, "grid1_grid"=>3, "grid2_grid"=>1300})
305
+ end
306
+ it "returns computed results (with delorean import)" do
307
+ c = xyz.compute({"pt"=>Time.zone.now+1,
308
+ "p1"=>12,
309
+ "p2"=>3,
310
+ "flavor"=>"cherry"})
311
+ expect(c).to eq({"bvlength"=>13,"bv"=>"cherry --> 36",
312
+ "grid1_grid"=>19})
313
+ end
314
+ it "grids embedded in result work properly and receive prior attrs" do
315
+ v = altgridmethod.compute({"pt"=>Time.zone.now,
316
+ 'param1'=> 45,
317
+ 'param2' => 1})
318
+ expect(v["final_value"]).to eq(15)
319
+ end
320
+ end
321
+ end
322
+ end