marty 2.5.2 → 2.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (181) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +4 -0
  3. data/.rubocop.yml +7 -0
  4. data/.rubocop_todo.yml +11 -589
  5. data/Gemfile +9 -9
  6. data/Gemfile.lock +1 -1
  7. data/Rakefile +1 -3
  8. data/app/components/marty/api_auth_view.rb +3 -3
  9. data/app/components/marty/api_config_view.rb +8 -8
  10. data/app/components/marty/api_log_view.rb +16 -20
  11. data/app/components/marty/auth_app.rb +6 -6
  12. data/app/components/marty/base_rule_view.rb +27 -19
  13. data/app/components/marty/config_view.rb +12 -9
  14. data/app/components/marty/data_grid_view.rb +26 -26
  15. data/app/components/marty/delorean_rule_view.rb +0 -1
  16. data/app/components/marty/event_view.rb +27 -27
  17. data/app/components/marty/extras/layout.rb +26 -26
  18. data/app/components/marty/extras/misc.rb +2 -2
  19. data/app/components/marty/grid.rb +13 -13
  20. data/app/components/marty/grid_append_only.rb +0 -1
  21. data/app/components/marty/import_type_view.rb +13 -13
  22. data/app/components/marty/import_view.rb +17 -16
  23. data/app/components/marty/log_view.rb +16 -14
  24. data/app/components/marty/main_auth_app.rb +59 -59
  25. data/app/components/marty/main_auth_app/client/main_auth_app.js +3 -3
  26. data/app/components/marty/mcfly_grid_panel.rb +10 -10
  27. data/app/components/marty/new_posting_form.rb +11 -11
  28. data/app/components/marty/new_posting_window.rb +0 -1
  29. data/app/components/marty/posting_grid.rb +12 -13
  30. data/app/components/marty/promise_view.rb +6 -6
  31. data/app/components/marty/report_form.rb +50 -53
  32. data/app/components/marty/report_select.rb +27 -27
  33. data/app/components/marty/reporting.rb +4 -4
  34. data/app/components/marty/script_form.rb +40 -42
  35. data/app/components/marty/script_grid.rb +24 -24
  36. data/app/components/marty/script_tester.rb +40 -42
  37. data/app/components/marty/scripting.rb +25 -27
  38. data/app/components/marty/simple_app.rb +24 -9
  39. data/app/components/marty/tag_grid.rb +12 -13
  40. data/app/components/marty/user_view.rb +35 -35
  41. data/app/controllers/marty/application_controller.rb +3 -4
  42. data/app/controllers/marty/components_controller.rb +1 -1
  43. data/app/controllers/marty/delayed_job_controller.rb +1 -0
  44. data/app/controllers/marty/diagnostic/controller.rb +4 -6
  45. data/app/controllers/marty/job_controller.rb +6 -6
  46. data/app/controllers/marty/report_controller.rb +11 -11
  47. data/app/controllers/marty/rpc_controller.rb +15 -16
  48. data/app/helpers/marty/script_set.rb +4 -4
  49. data/app/models/marty/api_auth.rb +4 -5
  50. data/app/models/marty/api_config.rb +1 -1
  51. data/app/models/marty/base.rb +9 -8
  52. data/app/models/marty/base_rule.rb +18 -13
  53. data/app/models/marty/config.rb +4 -5
  54. data/app/models/marty/data_grid.rb +157 -181
  55. data/app/models/marty/delorean_rule.rb +63 -62
  56. data/app/models/marty/enum.rb +1 -1
  57. data/app/models/marty/event.rb +56 -59
  58. data/app/models/marty/helper.rb +38 -6
  59. data/app/models/marty/import_type.rb +6 -6
  60. data/app/models/marty/log.rb +3 -2
  61. data/app/models/marty/name_validator.rb +3 -2
  62. data/app/models/marty/pg_enum.rb +3 -4
  63. data/app/models/marty/posting.rb +20 -24
  64. data/app/models/marty/promise.rb +28 -30
  65. data/app/models/marty/script.rb +30 -28
  66. data/app/models/marty/tag.rb +8 -8
  67. data/app/models/marty/token.rb +2 -2
  68. data/app/models/marty/user.rb +24 -23
  69. data/app/models/marty/vw_promise.rb +10 -11
  70. data/config/routes.rb +2 -2
  71. data/delorean/blame_report.dl +268 -0
  72. data/{spec/dummy/delorean/fields.dl → delorean/marty_fields.dl} +8 -0
  73. data/delorean/table_report.dl +34 -0
  74. data/docker-compose.dummy.yml +2 -3
  75. data/lib/marty/aws/base.rb +8 -8
  76. data/lib/marty/aws/request.rb +4 -4
  77. data/lib/marty/cache_adapters/mcfly_ruby_cache.rb +1 -0
  78. data/lib/marty/content_handler.rb +25 -25
  79. data/lib/marty/data_change.rb +49 -71
  80. data/lib/marty/data_conversion.rb +20 -28
  81. data/lib/marty/data_exporter.rb +25 -28
  82. data/lib/marty/data_importer.rb +25 -27
  83. data/lib/marty/engine.rb +1 -2
  84. data/lib/marty/json_schema.rb +22 -24
  85. data/lib/marty/logger.rb +6 -9
  86. data/lib/marty/mcfly_model.rb +20 -24
  87. data/lib/marty/migrations.rb +37 -35
  88. data/lib/marty/monkey.rb +33 -33
  89. data/lib/marty/permissions.rb +18 -18
  90. data/lib/marty/promise_job.rb +17 -17
  91. data/lib/marty/promise_proxy.rb +6 -6
  92. data/lib/marty/relation.rb +6 -7
  93. data/lib/marty/rpc_call.rb +13 -12
  94. data/lib/marty/rule_script_set.rb +32 -28
  95. data/lib/marty/schema_helper.rb +37 -51
  96. data/lib/marty/util.rb +25 -24
  97. data/lib/marty/version.rb +1 -1
  98. data/lib/marty/xl.rb +121 -115
  99. data/make-dummy.mk +3 -0
  100. data/marty.gemspec +21 -21
  101. data/other/marty/api/base.rb +34 -35
  102. data/other/marty/diagnostic/aws/ec2_instance.rb +8 -8
  103. data/other/marty/diagnostic/base.rb +13 -14
  104. data/other/marty/diagnostic/collection.rb +2 -1
  105. data/other/marty/diagnostic/connections.rb +8 -6
  106. data/other/marty/diagnostic/database.rb +1 -0
  107. data/other/marty/diagnostic/delayed_job_version.rb +7 -9
  108. data/other/marty/diagnostic/delayed_job_worker_total_count.rb +1 -1
  109. data/other/marty/diagnostic/delayed_job_workers.rb +1 -1
  110. data/other/marty/diagnostic/environment_variables.rb +17 -15
  111. data/other/marty/diagnostic/fatal.rb +1 -1
  112. data/other/marty/diagnostic/node.rb +5 -9
  113. data/other/marty/diagnostic/nodes.rb +7 -5
  114. data/other/marty/diagnostic/packer.rb +7 -7
  115. data/other/marty/diagnostic/reporter.rb +24 -27
  116. data/other/marty/diagnostic/version.rb +3 -5
  117. data/script/rails +2 -1
  118. data/spec/controllers/application_controller_spec.rb +6 -6
  119. data/spec/controllers/delayed_job_controller_spec.rb +4 -4
  120. data/spec/controllers/diagnostic/controller_spec.rb +59 -60
  121. data/spec/controllers/job_controller_spec.rb +68 -69
  122. data/spec/controllers/rpc_controller_spec.rb +353 -359
  123. data/spec/controllers/rpc_import_spec.rb +15 -16
  124. data/spec/dummy/delorean/blame_report.dl +110 -15
  125. data/spec/dummy/delorean/data_report.dl +4 -4
  126. data/spec/dummy/delorean/marty_fields.dl +63 -0
  127. data/spec/dummy/delorean/table_report.dl +34 -0
  128. data/spec/features/auth_app_spec.rb +1 -2
  129. data/spec/features/data_import_spec.rb +2 -3
  130. data/spec/features/enum_spec.rb +42 -46
  131. data/spec/features/jobs_dashboard_spec.rb +14 -8
  132. data/spec/features/log_view_spec.rb +40 -43
  133. data/spec/features/reporting_spec.rb +15 -15
  134. data/spec/features/rule_spec.rb +195 -190
  135. data/spec/features/scripting_spec.rb +17 -20
  136. data/spec/features/scripting_test_spec.rb +32 -33
  137. data/spec/features/user_view_spec.rb +15 -17
  138. data/spec/job_helper.rb +11 -11
  139. data/spec/lib/data_blame_spec.rb +82 -0
  140. data/spec/lib/data_exporter_spec.rb +31 -32
  141. data/spec/lib/data_importer_spec.rb +382 -395
  142. data/spec/lib/delorean_query_spec.rb +117 -119
  143. data/spec/lib/json_schema_spec.rb +382 -392
  144. data/spec/lib/logger_spec.rb +23 -24
  145. data/spec/lib/mcfly_model_spec.rb +112 -109
  146. data/spec/lib/migrations_spec.rb +10 -10
  147. data/spec/lib/struct_compare_spec.rb +6 -6
  148. data/spec/lib/table_report_spec.rb +90 -0
  149. data/spec/lib/xl_spec.rb +63 -65
  150. data/spec/lib/xl_styles_spec.rb +16 -19
  151. data/spec/models/api_auth_spec.rb +30 -30
  152. data/spec/models/config_spec.rb +32 -32
  153. data/spec/models/data_grid_spec.rb +642 -655
  154. data/spec/models/event_spec.rb +96 -88
  155. data/spec/models/import_type_spec.rb +20 -20
  156. data/spec/models/posting_spec.rb +35 -35
  157. data/spec/models/promise_spec.rb +5 -5
  158. data/spec/models/rule_spec.rb +280 -269
  159. data/spec/models/script_spec.rb +27 -18
  160. data/spec/models/user_spec.rb +9 -9
  161. data/spec/other/diagnostic/base_spec.rb +20 -19
  162. data/spec/other/diagnostic/collection_spec.rb +6 -5
  163. data/spec/other/diagnostic/delayed_job_version_spec.rb +1 -1
  164. data/spec/other/diagnostic/delayed_job_workers_spec.rb +8 -8
  165. data/spec/other/diagnostic/reporter_spec.rb +31 -33
  166. data/spec/spec_helper.rb +5 -5
  167. data/spec/support/chromedriver.rb +3 -5
  168. data/spec/support/components/netzke_combobox.rb +1 -1
  169. data/spec/support/components/netzke_grid.rb +17 -17
  170. data/spec/support/custom_matchers.rb +2 -2
  171. data/spec/support/download_helper.rb +1 -1
  172. data/spec/support/helper.rb +1 -2
  173. data/spec/support/netzke.rb +31 -31
  174. data/spec/support/performance_helper.rb +8 -8
  175. data/spec/support/post_run_logger.rb +1 -2
  176. data/spec/support/setup.rb +1 -4
  177. data/spec/support/shared_connection.rb +2 -2
  178. data/spec/support/structure_compare.rb +21 -22
  179. data/spec/support/suite.rb +1 -2
  180. data/spec/support/users.rb +5 -6
  181. metadata +32 -26
@@ -2,68 +2,68 @@ require 'spec_helper'
2
2
 
3
3
  module Marty
4
4
  describe Marty::Config do
5
- describe "validations" do
6
- it "should return valid config value based on key" do
7
- Marty::Config["TEST 1"] = 2
8
- expect(Marty::Config.lookup("TEST 1")).to eq(2)
9
- expect(Marty::Config["TEST 1"]).to eq(2)
5
+ describe 'validations' do
6
+ it 'should return valid config value based on key' do
7
+ Marty::Config['TEST 1'] = 2
8
+ expect(Marty::Config.lookup('TEST 1')).to eq(2)
9
+ expect(Marty::Config['TEST 1']).to eq(2)
10
10
  end
11
11
 
12
12
  def testval(val)
13
- Marty::Config["testval"] = val
14
- expect(Marty::Config.lookup("testval")).to eq(val)
15
- expect(Marty::Config["testval"]).to eq(val)
13
+ Marty::Config['testval'] = val
14
+ expect(Marty::Config.lookup('testval')).to eq(val)
15
+ expect(Marty::Config['testval']).to eq(val)
16
16
  end
17
17
 
18
18
  def testval_fail(val)
19
- expect{testval(val)}.to raise_error(ActiveRecord::RecordInvalid,
20
- 'Validation failed: bad JSON value')
19
+ expect { testval(val) }.to raise_error(ActiveRecord::RecordInvalid,
20
+ 'Validation failed: bad JSON value')
21
21
  end
22
22
 
23
- it "should handle various structures correctly" do
24
- testval("[1,2,3]")
25
- testval("[1,\"2,3\"]")
26
- testval([1,2,3])
27
- testval([1,"2,3"])
23
+ it 'should handle various structures correctly' do
24
+ testval('[1,2,3]')
25
+ testval('[1,"2,3"]')
26
+ testval([1, 2, 3])
27
+ testval([1, '2,3'])
28
28
 
29
- testval({ "key1" => [1,2,3], "keystr" => { "val" => "val"}})
29
+ testval('key1' => [1, 2, 3], 'keystr' => { 'val' => 'val' })
30
30
  testval(%Q({ "key1" : [1,2,3], "keystr" : { "val" : "val"}}))
31
31
 
32
- testval("123456.1234")
33
- testval("a string")
34
- testval("\"a string\"")
32
+ testval('123456.1234')
33
+ testval('a string')
34
+ testval('"a string"')
35
35
  end
36
36
 
37
- it "should return nil config value for non-existing key" do
38
- expect(Marty::Config.lookup("TEST 2")).to eq(nil)
39
- expect(Marty::Config["TEST 2"]).to eq(nil)
37
+ it 'should return nil config value for non-existing key' do
38
+ expect(Marty::Config.lookup('TEST 2')).to eq(nil)
39
+ expect(Marty::Config['TEST 2']).to eq(nil)
40
40
  end
41
41
 
42
- it "should handle del" do
43
- (0..10).each { |i|
44
- v = {"i" => i}
42
+ it 'should handle del' do
43
+ (0..10).each do |i|
44
+ v = { 'i' => i }
45
45
  Marty::Config["k#{i}"] = v
46
46
  expect(Marty::Config["k#{i}"]).to eq(v)
47
- }
47
+ end
48
48
 
49
- (0..10).each { |i|
49
+ (0..10).each do |i|
50
50
  Marty::Config.del("k#{i}")
51
51
  expect(Marty::Config["k#{i}"]).to eq(nil)
52
- }
52
+ end
53
53
  end
54
54
 
55
- it "should allow the assignment of individual boolean values" do
55
+ it 'should allow the assignment of individual boolean values' do
56
56
  testval(true)
57
57
  testval(false)
58
58
  end
59
59
 
60
- it "should not allow the assignment of individual nil (null) values" do
60
+ it 'should not allow the assignment of individual nil (null) values' do
61
61
  testval_fail(nil)
62
62
  end
63
63
 
64
- it "should allow nil (null) to exist in other structures" do
64
+ it 'should allow nil (null) to exist in other structures' do
65
65
  testval([nil, 1, 2, nil])
66
- testval({"key1" => nil, "key2" => false, "key3" => 'val'})
66
+ testval('key1' => nil, 'key2' => false, 'key3' => 'val')
67
67
  end
68
68
  end
69
69
  end
@@ -2,8 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  module Marty::DataGridSpec
4
4
  describe DataGrid do
5
-
6
- G1 =<<EOS
5
+ G1 = <<EOS
7
6
  state\tstring\tv\t\t
8
7
  ltv\tnumrange\tv\t\t
9
8
  fico\tnumrange\th\t\t
@@ -16,7 +15,7 @@ MA\t>80<=105\t4.5\t5.6\t
16
15
  \t<=80\t11\t22\t33
17
16
  EOS
18
17
 
19
- G2 =<<EOS
18
+ G2 = <<EOS
20
19
  units\tinteger\tv\t\t
21
20
  ltv\tnumrange\tv\t\t
22
21
  cltv\tnumrange\th\t\t
@@ -30,9 +29,9 @@ fico\tnumrange\th\t\t
30
29
  3|4\t>80<=105\t4.5\t5.6\t6.7
31
30
  EOS
32
31
 
33
- G3 = File.open(File.expand_path("../srp_data.csv", __FILE__)).read
32
+ G3 = File.open(File.expand_path('../srp_data.csv', __FILE__)).read
34
33
 
35
- G4 =<<EOS
34
+ G4 = <<EOS
36
35
  lenient
37
36
  hb_indicator\tboolean\tv
38
37
  cltv\tnumrange\th
@@ -41,21 +40,21 @@ cltv\tnumrange\th
41
40
  true\t-0.750\t-0.750\t-0.750\t-1.500\t-1.500\t-1.500\t\t
42
41
  EOS
43
42
 
44
- G5 =<<EOS
43
+ G5 = <<EOS
45
44
  ltv\tnumrange\tv\t\t
46
45
 
47
46
  <=115\t-0.375
48
47
  >115<=135\t-0.750
49
48
  EOS
50
49
 
51
- G6 =<<EOS
50
+ G6 = <<EOS
52
51
  ltv\tnumrange\th
53
52
 
54
53
  <=115\t>115<=135
55
54
  -0.375\t-0.750
56
55
  EOS
57
56
 
58
- G7 =<<EOS
57
+ G7 = <<EOS
59
58
  string
60
59
  hb_indicator\tboolean\tv
61
60
  cltv\tnumrange\th
@@ -64,7 +63,7 @@ cltv\tnumrange\th
64
63
  true\tThis\tis\ta\ttest\tof\tstring type\t\t
65
64
  EOS
66
65
 
67
- G8 =<<EOS
66
+ G8 = <<EOS
68
67
  Marty::DataGrid
69
68
  ltv\tnumrange\tv\t\t
70
69
 
@@ -73,7 +72,7 @@ ltv\tnumrange\tv\t\t
73
72
  >135<=140\tG3
74
73
  EOS
75
74
 
76
- G9 =<<EOS
75
+ G9 = <<EOS
77
76
  state\tstring\tv
78
77
  ltv\tnumrange\tv
79
78
 
@@ -81,14 +80,14 @@ CA|TX\t>80\t123
81
80
  \t>80\t456
82
81
  EOS
83
82
 
84
- Ga =<<EOS
83
+ Ga = <<EOS
85
84
  dg\tMarty::DataGrid\tv\t\t
86
85
 
87
86
  G1|G2\t7
88
87
  G3\t8
89
88
  EOS
90
89
 
91
- Gb =<<EOS
90
+ Gb = <<EOS
92
91
  property_state\tGemini::State\tv\t\t
93
92
 
94
93
  CA|TX\t70
@@ -96,21 +95,21 @@ GA\t80
96
95
  MN\t90
97
96
  EOS
98
97
 
99
- Gc =<<EOS
98
+ Gc = <<EOS
100
99
  Marty::DataGrid
101
100
  property_state\tGemini::State\tv\t\t
102
101
 
103
102
  CA|TX\tGb
104
103
  EOS
105
104
 
106
- Gd =<<EOS
105
+ Gd = <<EOS
107
106
  hb_indicator\tboolean\tv
108
107
 
109
108
  true\t456
110
109
  false\t123
111
110
  EOS
112
111
 
113
- Ge =<<EOS
112
+ Ge = <<EOS
114
113
  ltv\tnumrange\th
115
114
 
116
115
  >110\t>120
@@ -148,7 +147,7 @@ NY\t\t10
148
147
  \tR\t8
149
148
  EOS
150
149
 
151
- Gi =<<EOS
150
+ Gi = <<EOS
152
151
  units\tinteger\tv\t\t
153
152
  ltv\tfloat\tv\t\t
154
153
  cltv\tfloat\th\t\t
@@ -162,7 +161,7 @@ fico\tnumrange\th\t\t
162
161
  3|4\t105.5\t4.5\t5.6\t6.7
163
162
  EOS
164
163
 
165
- Gj =<<EOS
164
+ Gj = <<EOS
166
165
  lenient
167
166
  client_id\tinteger\tv
168
167
  property_state\tstring\tv
@@ -171,7 +170,7 @@ property_state\tstring\tv
171
170
  700127\tCA\t0.35
172
171
  EOS
173
172
 
174
- Gk =<<EOS
173
+ Gk = <<EOS
175
174
  fha_203k_option\tstring\tv\tfha_203k_option
176
175
 
177
176
  Investor Services\t-0.625
@@ -180,657 +179,645 @@ Admin Services Plus\t-1.625
180
179
  Investor Services Acadamy\t-0.5
181
180
  EOS
182
181
 
183
- before(:each) do
184
- #Mcfly.whodunnit = Marty::User.find_by_login('marty')
185
- marty_whodunnit
182
+ before(:each) do
183
+ # Mcfly.whodunnit = Marty::User.find_by_login('marty')
184
+ marty_whodunnit
185
+ end
186
+
187
+ def lookup_grid_helper(pt, gridname, params, follow = false, distinct = true)
188
+ dgh = Marty::DataGrid.lookup_h(pt, gridname)
189
+ res = Marty::DataGrid.lookup_grid_distinct_entry_h(pt, params, dgh, nil, follow,
190
+ false, distinct)
191
+ [res['result'], res['name']]
192
+ end
193
+
194
+ describe 'imports' do
195
+ it 'should not allow imports with trailing blank columns' do
196
+ expect do
197
+ dg_from_import('G1', G1.gsub("\n", "\t\n"))
198
+ end.to raise_error(RuntimeError)
199
+ end
200
+
201
+ it 'should not allow imports with last blank row' do
202
+ expect do
203
+ dg_from_import('Gh', Gh + "\t\t\n")
204
+ end.to raise_error(RuntimeError)
205
+ end
206
+ end
207
+
208
+ describe 'validations' do
209
+ it 'should not allow bad axis types' do
210
+ expect do
211
+ dg_from_import('Gi', Gi)
212
+ end.to raise_error(/unknown metadata type float/)
213
+ expect do
214
+ dg_from_import('Gi', Gi.sub(/float/, 'abcdef'))
215
+ end.to raise_error(/unknown metadata type abcdef/)
216
+ end
217
+
218
+ it 'should not allow dup attr names' do
219
+ g_bad = G1.sub(/fico/, 'ltv')
220
+
221
+ expect do
222
+ dg_from_import('G2', g_bad)
223
+ end.to raise_error(ActiveRecord::RecordInvalid)
224
+ end
225
+
226
+ it 'should not allow dup grid names' do
227
+ dg_from_import('G1', G1)
228
+
229
+ expect do
230
+ dg_from_import('G1', G2)
231
+ end.to raise_error(ActiveRecord::RecordInvalid)
232
+ end
233
+
234
+ it 'should not allow extra attr rows' do
235
+ g_bad = "x\tnumrange\th\t\t\n" + G1
236
+
237
+ expect do
238
+ dg_from_import('G2', g_bad)
239
+ end.to raise_error(RuntimeError)
240
+ end
241
+
242
+ it 'should not allow dup row/col key combos' do
243
+ g_bad = G1 + G1.split("\n").last + "\n"
244
+ expect do
245
+ dg_from_import('G2', g_bad)
246
+ end.to raise_error(ActiveRecord::RecordInvalid)
247
+
248
+ g_bad = G2 + G2.split("\n").last + "\n"
249
+ expect do
250
+ dg_from_import('G2', g_bad)
251
+ end.to raise_error(ActiveRecord::RecordInvalid)
252
+ end
253
+
254
+ it 'Unknown keys for typed grids should raise error' do
255
+ g_bad = G8.sub(/G3/, 'XXXXX')
256
+
257
+ expect do
258
+ dg_from_import('G8', g_bad)
259
+ end.to raise_error(RuntimeError)
260
+
261
+ g_bad = G8.sub(/DataGrid/, 'Division')
262
+
263
+ expect do
264
+ dg_from_import('G8', g_bad)
265
+ end.to raise_error(RuntimeError)
266
+ end
267
+
268
+ it 'Unknown keys for grid headers should raise error' do
269
+ g_bad = Ga.sub(/G3/, 'XXXXX')
270
+
271
+ expect do
272
+ dg_from_import('Ga', g_bad)
273
+ end.to raise_error(RuntimeError)
274
+
275
+ g_bad = Ga.sub(/DataGrid/, 'Division')
276
+
277
+ expect do
278
+ dg_from_import('Ga', g_bad)
279
+ end.to raise_error(RuntimeError)
280
+ end
281
+
282
+ it 'validates grid modifier' do
283
+ bad = ': abc def'
284
+ g_bad = Gk.sub(/fha_203k_option$/, bad)
285
+ expect do
286
+ dg_from_import('Gk', g_bad)
287
+ end.to raise_error(/invalid grid modifier expression: #{bad}/)
288
+ expect do
289
+ dg_from_import('Gk', Gk)
290
+ end.not_to raise_error
291
+ end
292
+ end
293
+
294
+ describe 'lookups for infinity' do
295
+ let(:pt) { 'infinity' }
296
+
297
+ before(:each) do
298
+ ['G1', 'G2', 'G3', 'G4', 'G5', 'G6', 'G7', 'G8', 'Ga', 'Gb',
299
+ 'Gc', 'Gd', 'Ge', 'Gf', 'Gg', 'Gh', 'Gj'].each do |g|
300
+ dg_from_import(g, "Marty::DataGridSpec::#{g}".constantize)
186
301
  end
302
+ end
303
+
304
+ context 'should handle NULL key values' do
305
+ let(:dgh) { 'Gf' }
187
306
 
188
- def lookup_grid_helper(pt, gridname, params, follow=false, distinct=true)
189
- dgh=Marty::DataGrid.lookup_h(pt, gridname)
190
- res=Marty::DataGrid.lookup_grid_distinct_entry_h(pt, params, dgh, nil, follow,
191
- false, distinct)
192
- [res["result"], res["name"]]
307
+ it 'true returns Y' do
308
+ res = Marty::DataGrid.lookup_grid_h(pt, dgh, { 'b' => true }, false)
309
+ expect(res).to eq('Y')
193
310
  end
194
311
 
195
- describe "imports" do
196
- it "should not allow imports with trailing blank columns" do
197
- expect {
198
- dg_from_import("G1", G1.gsub("\n", "\t\n"))
199
- }.to raise_error(RuntimeError)
200
- end
201
-
202
- it "should not allow imports with last blank row" do
203
- expect {
204
- dg_from_import("Gh", Gh+"\t\t\n")
205
- }.to raise_error(RuntimeError)
206
- end
312
+ it '13 returns N' do
313
+ res = Marty::DataGrid.lookup_grid_h(pt, dgh, { 'i' => 13 }, true)
314
+ expect(res).to eq('N')
207
315
  end
208
316
 
209
- describe "validations" do
210
- it "should not allow bad axis types" do
211
- expect {
212
- dg_from_import("Gi", Gi)
213
- }.to raise_error(/unknown metadata type float/)
214
- expect {
215
- dg_from_import("Gi", Gi.sub(/float/, 'abcdef'))
216
- }.to raise_error(/unknown metadata type abcdef/)
217
- end
218
-
219
- it "should not allow dup attr names" do
220
- g_bad = G1.sub(/fico/, "ltv")
221
-
222
- expect {
223
- dg_from_import("G2", g_bad)
224
- }.to raise_error(ActiveRecord::RecordInvalid)
225
- end
226
-
227
- it "should not allow dup grid names" do
228
- dg_from_import("G1", G1)
229
-
230
- expect {
231
- dg_from_import("G1", G2)
232
- }.to raise_error(ActiveRecord::RecordInvalid)
233
- end
234
-
235
- it "should not allow extra attr rows" do
236
- g_bad = "x\tnumrange\th\t\t\n" + G1
237
-
238
- expect {
239
- dg_from_import("G2", g_bad)
240
- }.to raise_error(RuntimeError)
241
- end
242
-
243
- it "should not allow dup row/col key combos" do
244
- g_bad = G1 + G1.split("\n").last + "\n"
245
- expect {
246
- dg_from_import("G2", g_bad)
247
- }.to raise_error(ActiveRecord::RecordInvalid)
248
-
249
- g_bad = G2 + G2.split("\n").last + "\n"
250
- expect {
251
- dg_from_import("G2", g_bad)
252
- }.to raise_error(ActiveRecord::RecordInvalid)
253
- end
254
-
255
- it "Unknown keys for typed grids should raise error" do
256
- g_bad = G8.sub(/G3/, "XXXXX")
257
-
258
- expect {
259
- dg_from_import("G8", g_bad)
260
- }.to raise_error(RuntimeError)
261
-
262
- g_bad = G8.sub(/DataGrid/, "Division")
263
-
264
- expect {
265
- dg_from_import("G8", g_bad)
266
- }.to raise_error(RuntimeError)
267
- end
268
-
269
- it "Unknown keys for grid headers should raise error" do
270
- g_bad = Ga.sub(/G3/, "XXXXX")
271
-
272
- expect {
273
- dg_from_import("Ga", g_bad)
274
- }.to raise_error(RuntimeError)
275
-
276
- g_bad = Ga.sub(/DataGrid/, "Division")
277
-
278
- expect {
279
- dg_from_import("Ga", g_bad)
280
- }.to raise_error(RuntimeError)
281
- end
282
-
283
- it "validates grid modifier" do
284
- bad = ': abc def'
285
- g_bad = Gk.sub(/fha_203k_option$/, bad)
286
- expect { dg_from_import( "Gk", g_bad)
287
- }.to raise_error(/invalid grid modifier expression: #{bad}/)
288
- expect { dg_from_import( "Gk", Gk)
289
- }.not_to raise_error
290
- end
317
+ it '13 & numrange 0 returns nil' do
318
+ res = Marty::DataGrid.lookup_grid_h(pt, dgh, { 'i' => 13, 'n' => 0 }, true)
319
+ expect(res).to eq('N')
291
320
  end
292
321
 
293
- describe "lookups for infinity" do
294
- let(:pt) { 'infinity'}
295
-
296
- before(:each) do
297
- ["G1", "G2", "G3", "G4", "G5", "G6", "G7", "G8", "Ga", "Gb",
298
- "Gc", "Gd", "Ge", "Gf", "Gg", "Gh", "Gj"].each { |g|
299
- dg_from_import(g, "Marty::DataGridSpec::#{g}".constantize)
300
- }
301
- end
302
-
303
- context "should handle NULL key values" do
304
- let(:dgh) { "Gf" }
305
-
306
- it 'true returns Y' do
307
- res = Marty::DataGrid.lookup_grid_h(pt, dgh, {"b"=>true}, false)
308
- expect(res).to eq('Y')
309
- end
310
-
311
- it '13 returns N' do
312
- res = Marty::DataGrid.lookup_grid_h(pt, dgh, {"i"=>13}, true)
313
- expect(res).to eq('N')
314
- end
315
-
316
- it '13 & numrange 0 returns nil' do
317
- res = Marty::DataGrid.lookup_grid_h(pt, dgh, {"i"=>13, "n"=>0}, true)
318
- expect(res).to eq('N')
319
- end
320
-
321
- it '13 & int4range 15 returns N' do
322
- res = Marty::DataGrid.lookup_grid_h(pt, dgh, {"i"=>13, "i4"=>15}, true)
323
- expect(res).to eq('N')
324
- end
325
-
326
- it '13 & int4range 1 returns nil' do
327
- res = Marty::DataGrid.lookup_grid_h(pt, dgh, {"i"=>13, "i4"=>1}, true)
328
- expect(res).to be_nil
329
- end
330
-
331
- it 'false, 3, numrange 15 returns N' do
332
- res = Marty::DataGrid.
333
- lookup_grid_h(pt, dgh, {"b"=>false, "i"=>3, "n"=>15}, true)
334
- expect(res).to eq('N')
335
- end
336
-
337
- it '13, numrange 15 returns N' do
338
- res = Marty::DataGrid.lookup_grid_h(pt, dgh, {"i"=>13, "n"=>15}, true)
339
- expect(res).to eq('N')
340
- end
341
- end
342
-
343
- it "should handle ambiguous lookups" do
344
- h1 = {
345
- "property_state" => "NY",
346
- "county_name" => "R",
347
- }
348
-
349
- res = Marty::DataGrid.lookup_grid_h(pt, "Gh", h1, false)
350
- expect(res).to eq(10)
351
- end
352
-
353
- it "should handle ambiguous lookups (2)" do
354
- res = Marty::DataGrid.
355
- lookup_grid_h(pt, "Gg", {"i1"=>2, "i2"=>1}, false)
356
- expect(res).to eq(1)
357
-
358
- res = Marty::DataGrid.
359
- lookup_grid_h(pt, "Gg", {"i1"=>3, "i2"=>1}, false)
360
- expect(res).to eq(1)
361
-
362
- res = Marty::DataGrid.
363
- lookup_grid_h(pt, "Gg", {"i1"=>2, "i2"=>3}, false)
364
- expect(res).to eq(20)
365
- end
366
-
367
- it "should handle non-distinct lookups" do
368
- res = Marty::DataGrid.lookup_grid_h(pt, "Ge", {"ltv"=>500}, false)
369
-
370
- expect(res).to eq(1.1)
371
-
372
- expect {
373
- Marty::DataGrid.lookup_grid_h(pt, "Ge", {"ltv"=>500}, true)
374
- }.to raise_error(RuntimeError)
375
- end
376
-
377
- it "should handle non-distinct lookups (2)" do
378
- params = {
379
- "client_id" => 700127,
380
- "property_state" => "CA",
381
- }
382
- res = Marty::DataGrid.lookup_grid_h(pt, "Gj", params, false)
383
-
384
- # should return the upper left corner match
385
- expect(res).to eq(0.25)
386
-
387
- expect {
388
- Marty::DataGrid.lookup_grid_h(pt, "Gj", params, true)
389
- }.to raise_error(RuntimeError)
390
- end
391
-
392
- it "should handle boolean lookups" do
393
- res = [true, false].map { |hb_indicator|
394
- lookup_grid_helper('infinity',
395
- "Gd",
396
- {"hb_indicator" => hb_indicator,
397
- },
398
- )
399
- }
400
- expect(res).to eq [[456.0, "Gd"], [123.0, "Gd"]]
401
- end
402
-
403
- it "should handle basic lookups" do
404
- res = lookup_grid_helper('infinity',
405
- "G3",
406
- {"amount" => 160300,
407
- "state" => "HI",
408
- },
409
- )
410
- expect(res).to eq [1.655,"G3"]
411
-
412
- [3,4].each {
413
- |units|
414
- res = lookup_grid_helper('infinity',
415
- "G2",
416
- {"fico" => 720,
417
- "units" => units,
418
- "ltv" => 100,
419
- "cltv" => 110.1,
420
- },
421
- )
422
- expect(res).to eq [5.6,"G2"]
423
- }
424
-
425
- dg = Marty::DataGrid.find_by(obsoleted_dt: 'infinity', name: "G1")
426
-
427
- h = {
428
- "fico" => 600,
429
- "state" => "RI",
430
- "ltv" => 10,
431
- }
432
-
433
- res = lookup_grid_helper('infinity', "G1", h)
434
- expect(res).to eq [11,"G1"]
435
-
436
- dg.update_from_import("G1", G1.sub(/11/, "111"))
437
-
438
- res = lookup_grid_helper('infinity', "G1", h)
439
- expect(res).to eq [111,"G1"]
440
- end
441
-
442
- it "should result in error when there are multiple cell hits" do
443
- expect {
444
- lookup_grid_helper('infinity',
445
- "G2",
446
- {"fico" => 720,
447
- "ltv" => 100,
448
- "cltv" => 110.1,
449
- },
322
+ it '13 & int4range 15 returns N' do
323
+ res = Marty::DataGrid.lookup_grid_h(pt, dgh, { 'i' => 13, 'i4' => 15 }, true)
324
+ expect(res).to eq('N')
325
+ end
326
+
327
+ it '13 & int4range 1 returns nil' do
328
+ res = Marty::DataGrid.lookup_grid_h(pt, dgh, { 'i' => 13, 'i4' => 1 }, true)
329
+ expect(res).to be_nil
330
+ end
331
+
332
+ it 'false, 3, numrange 15 returns N' do
333
+ res = Marty::DataGrid.
334
+ lookup_grid_h(pt, dgh, { 'b' => false, 'i' => 3, 'n' => 15 }, true)
335
+ expect(res).to eq('N')
336
+ end
337
+
338
+ it '13, numrange 15 returns N' do
339
+ res = Marty::DataGrid.lookup_grid_h(pt, dgh, { 'i' => 13, 'n' => 15 }, true)
340
+ expect(res).to eq('N')
341
+ end
342
+ end
343
+
344
+ it 'should handle ambiguous lookups' do
345
+ h1 = {
346
+ 'property_state' => 'NY',
347
+ 'county_name' => 'R',
348
+ }
349
+
350
+ res = Marty::DataGrid.lookup_grid_h(pt, 'Gh', h1, false)
351
+ expect(res).to eq(10)
352
+ end
353
+
354
+ it 'should handle ambiguous lookups (2)' do
355
+ res = Marty::DataGrid.
356
+ lookup_grid_h(pt, 'Gg', { 'i1' => 2, 'i2' => 1 }, false)
357
+ expect(res).to eq(1)
358
+
359
+ res = Marty::DataGrid.
360
+ lookup_grid_h(pt, 'Gg', { 'i1' => 3, 'i2' => 1 }, false)
361
+ expect(res).to eq(1)
362
+
363
+ res = Marty::DataGrid.
364
+ lookup_grid_h(pt, 'Gg', { 'i1' => 2, 'i2' => 3 }, false)
365
+ expect(res).to eq(20)
366
+ end
367
+
368
+ it 'should handle non-distinct lookups' do
369
+ res = Marty::DataGrid.lookup_grid_h(pt, 'Ge', { 'ltv' => 500 }, false)
370
+
371
+ expect(res).to eq(1.1)
372
+
373
+ expect do
374
+ Marty::DataGrid.lookup_grid_h(pt, 'Ge', { 'ltv' => 500 }, true)
375
+ end.to raise_error(RuntimeError)
376
+ end
377
+
378
+ it 'should handle non-distinct lookups (2)' do
379
+ params = {
380
+ 'client_id' => 700127,
381
+ 'property_state' => 'CA',
382
+ }
383
+ res = Marty::DataGrid.lookup_grid_h(pt, 'Gj', params, false)
384
+
385
+ # should return the upper left corner match
386
+ expect(res).to eq(0.25)
387
+
388
+ expect do
389
+ Marty::DataGrid.lookup_grid_h(pt, 'Gj', params, true)
390
+ end.to raise_error(RuntimeError)
391
+ end
392
+
393
+ it 'should handle boolean lookups' do
394
+ res = [true, false].map do |hb_indicator|
395
+ lookup_grid_helper('infinity',
396
+ 'Gd',
397
+ 'hb_indicator' => hb_indicator,
398
+ )
399
+ end
400
+ expect(res).to eq [[456.0, 'Gd'], [123.0, 'Gd']]
401
+ end
402
+
403
+ it 'should handle basic lookups' do
404
+ res = lookup_grid_helper('infinity',
405
+ 'G3',
406
+ 'amount' => 160300,
407
+ 'state' => 'HI',
450
408
  )
451
- }.to raise_error(RuntimeError)
452
- end
453
-
454
- it "should return nil when matching data grid cell is nil" do
455
- res = lookup_grid_helper('infinity',
456
- "G1",
457
- {"fico" => 800,
458
- "state" => "MA",
459
- "ltv" => 81,
460
- },
461
- )
462
- expect(res).to eq [nil,"G1"]
463
- end
464
-
465
- it "should handle string wildcards" do
466
- res = lookup_grid_helper('infinity',
467
- "G1",
468
- {"fico" => 720,
469
- "state" => "GU",
470
- "ltv" => 80,
471
- },
472
- )
473
- expect(res).to eq [22,"G1"]
474
- end
475
-
476
- it "should handle matches which also have a wildcard match" do
477
- dg_from_import("G9", G9)
478
-
479
- expect {
480
- res = lookup_grid_helper('infinity',
481
- "G9",
482
- {"state" => "CA", "ltv" => 81},
483
- )
484
- }.to raise_error(RuntimeError)
485
-
486
- res = lookup_grid_helper('infinity',
487
- "G9",
488
- {"state" => "GU", "ltv" => 81},
489
- )
490
- expect(res).to eq [456,"G9"]
491
- end
492
-
493
- it "should raise on nil attr values" do
494
- dg_from_import("G9", G9)
495
-
496
- expect{lookup_grid_helper('infinity',
497
- "G9",
498
- {"ltv" => 81},
499
- )}.to raise_error(/matches > 1/)
500
-
501
- err = /Data Grid lookup failed/
502
- expect{lookup_grid_helper('infinity',
503
- "G9",
504
- {"state" => "CA", "ltv" => nil},
505
- false, false)}.to raise_error(err)
506
-
507
- res = lookup_grid_helper('infinity',
508
- "G9",
509
- {"state" => nil, "ltv" => 81},
510
- false, false)
511
-
512
- expect(res).to eq [456,"G9"]
513
- end
514
-
515
- it "should handle boolean keys" do
516
- res = lookup_grid_helper('infinity',
517
- "G4",
518
- {"hb_indicator" => true,
519
- "cltv" => 80,
520
- },
521
- )
522
- expect(res).to eq [-1.5,"G4"]
523
-
524
- res = lookup_grid_helper('infinity',
525
- "G4",
526
- {"hb_indicator" => false,
527
- "cltv" => 80,
528
- },
529
- )
530
- expect(res).to eq [nil,"G4"]
531
- end
532
-
533
- it "should handle vertical-only grids" do
534
- res = lookup_grid_helper('infinity',
535
- "G5",
536
- {"ltv" => 80},
537
- )
538
- expect(res).to eq [-0.375,"G5"]
539
- end
540
-
541
- it "should handle horiz-only grids" do
542
- res = lookup_grid_helper('infinity',
543
- "G6",
544
- {"ltv" => 80, "conforming" => true},
545
- )
546
- expect(res).to eq [-0.375,"G6"]
547
- end
548
-
549
- it "should handle string typed data grids" do
550
- expect(Marty::DataGrid.lookup('infinity', "G7").data_type).to eq "string"
551
-
552
- res = lookup_grid_helper('infinity',
553
- "G7",
554
- {"hb_indicator" => true,
555
- "cltv" => 80,
556
- },
557
- )
558
- expect(res).to eq ["test","G7"]
559
- end
560
-
561
- it "should handle DataGrid typed data grids" do
562
- expect(Marty::DataGrid.lookup('infinity', "G8").data_type).
563
- to eq "Marty::DataGrid"
564
- g1 = Marty::DataGrid.lookup('infinity', "G1")
565
-
566
- res = lookup_grid_helper('infinity',
567
- "G8",
568
- {"ltv" => 80,
569
- },
570
- )
571
- expect(res).to eq [g1,"G8"]
572
- end
573
-
574
- it "should handle multi DataGrid lookups" do
575
- expect(Marty::DataGrid.lookup('infinity', "G8").data_type).
576
- to eq "Marty::DataGrid"
577
-
578
- h = {
579
- "fico" => 600,
580
- "state" => "RI",
581
- "ltv" => 10,
582
- }
583
-
584
- g1_res = lookup_grid_helper('infinity', "G1", h)
585
- expect(g1_res).to eq [11,"G1"]
586
-
587
- res = lookup_grid_helper('infinity',
588
- "G8",
589
- h,true
590
- )
591
- expect(g1_res).to eq res
592
- end
593
-
594
- it "should handle DataGrid typed data grids" do
595
- g1 = Marty::DataGrid.find_by(obsoleted_dt: 'infinity', name: "G1")
596
-
597
- res = lookup_grid_helper('infinity',
598
- "Ga",
599
- {"dg" => g1,
600
- },
601
- )
602
- expect(res).to eq [7,"Ga"]
603
-
604
- # should be able to lookup bu name as well
605
- res = lookup_grid_helper('infinity',
606
- "Ga",
607
- {"dg" => "G2",
608
- },
609
- )
610
- expect(res).to eq [7,"Ga"]
611
- end
612
-
613
- it "should handle DataGrid typed data grids -- non mcfly" do
614
- ca = Gemini::State.find_by_name("CA")
615
-
616
- res = lookup_grid_helper('infinity',
617
- "Gb",
618
- {"property_state" => ca,
619
- },
620
- )
621
- expect(res).to eq [70,"Gb"]
622
-
623
- # should be able to lookup bu name as well
624
- res = lookup_grid_helper('infinity',
625
- "Gb",
626
- {"property_state" => "CA",
627
- },
628
- )
629
- expect(res).to eq [70,"Gb"]
630
- end
631
-
632
- it "should handle typed (enum) data lookup_grid" do
633
- pt = 'infinity'
634
- ca = Gemini::State.find_by_name("CA")
635
-
636
- res = Marty::DataGrid.
637
- lookup_grid_h(pt, "Gb", {"property_state" => ca}, false)
638
-
639
- expect(res).to eq 70
640
- end
641
-
642
- it "should return grid data and metadata simple" do
643
- expected_data = [[1.1, 2.2, 3.3], [4.4, 5.5, 6.6], [1.2, 2.3, 3.4],
644
- [4.5, 5.6, 6.7]]
645
- expected_metadata = [{"dir"=>"v",
646
- "attr"=>"units",
647
- "keys"=>[[1, 2], [1, 2], [3, 4], [3, 4]],
648
- "type"=>"integer"},
649
- {"dir"=>"v",
650
- "attr"=>"ltv",
651
- "keys"=>["[,80]", "(80,105]", "[,80]", "(80,105]"],
652
- "type"=>"numrange"},
653
- {"dir"=>"h",
654
- "attr"=>"cltv",
655
- "keys"=>["[100,110)", "[110,120)", "[120,]"],
656
- "type"=>"numrange"},
657
- {"dir"=>"h",
658
- "attr"=>"fico",
659
- "keys"=>["[600,700)", "[700,750)", "[750,]"],
660
- "type"=>"numrange"}]
661
-
662
- dgh = Marty::DataGrid.lookup_h(pt, 'G2')
663
- res = Marty::DataGrid.lookup_grid_distinct_entry_h(pt, {}, dgh,
664
- nil, true, true)
665
- expect(res["data"]).to eq (expected_data)
666
- expect(res["metadata"]).to eq (expected_metadata)
667
- end
668
-
669
- it "should return grid data and metadata multi (following)" do
670
- expected_data = [[1.1, 2.2, 3.3],[4.4, 5.5, 6.6],[1.2, 2.3, 3.4],
671
- [4.5, 5.6, nil],[11.0, 22.0, 33.0]]
672
- expected_metadata = [{"dir"=>"v",
673
- "attr"=>"state",
674
- "keys"=>[["CA"], ["HI", "TX"], ["NM"], ["MA"], nil],
675
- "type"=>"string"},
676
- {"dir"=>"v",
677
- "attr"=>"ltv",
678
- "keys"=>["[,80]", "(80,105]", "[,80]", "(80,105]",
679
- "[,80]"],
680
- "type"=>"numrange"},
681
- {"dir"=>"h",
682
- "attr"=>"fico",
683
- "keys"=>["[600,700)", "[700,750)", "[750,]"],
684
- "type"=>"numrange"}]
685
- dgh = Marty::DataGrid.lookup_h(pt, 'G8')
686
- res = Marty::DataGrid.lookup_grid_distinct_entry_h(pt,
687
- { "ltv" => 10,
688
- "state" => "RI" },
689
- dgh, nil, true,
690
- true)
691
- expect(res["data"]).to eq (expected_data)
692
- expect(res["metadata"]).to eq (expected_metadata)
693
- end
694
-
695
- it "should return grid data and metadata multi (not following)" do
696
- expected_data = [["G1"], ["G2"], ["G3"]]
697
- expected_metadata = [{"dir"=>"v",
698
- "attr"=>"ltv",
699
- "keys"=>["[,115]", "(115,135]", "(135,140]"],
700
- "type"=>"numrange"}]
701
- dgh = Marty::DataGrid.lookup_h(pt, 'G8')
702
- res = Marty::DataGrid.lookup_grid_distinct_entry_h(pt,
703
- { "ltv" => 10,
704
- "state" => "RI" },
705
- dgh, nil, false,
706
- true)
707
- expect(res["data"]).to eq (expected_data)
708
- expect(res["metadata"]).to eq (expected_metadata)
709
- end
710
-
711
- it "should handle all characters in grid inputs" do
712
- dgh = Marty::DataGrid.lookup_h(pt, 'G1')
713
- 5000.times do
714
- st = 30.times.map{rand(224)+32}.pack('U*')
715
- res = Marty::DataGrid.lookup_grid_distinct_entry_h(pt,
716
- { "ltv" => 10,
717
- "fico" => 690,
718
- "state" => st },
719
- dgh, nil, false, true)
720
- end
721
- end
722
- it "should handle all quote chars in grid inputs" do
723
- dgh = Marty::DataGrid.lookup_h(pt, 'G1')
724
- # single, double, backslash, grave, acute, unicode quotes: left single,
725
- # right single, left double, right double
726
- quotes = ["'", '"', '\\', '`', "\u00b4", "\u2018", "\u2019",
727
- "\u201C", "\u201D"]
728
- 100.times do
729
- st = 30.times.map{quotes[rand(9)]}.join
730
- res = Marty::DataGrid.lookup_grid_distinct_entry_h(
731
- pt, {"ltv" => 10, "fico" => 690, "state" => st}, dgh, nil, false, true)
732
- end
733
- end
734
- it "should handle quote chars in object name" do
735
- dgh = Marty::DataGrid.lookup_h(pt, 'G1')
736
- st = Gemini::State.new(name: "'\\")
737
- res = Marty::DataGrid.lookup_grid_distinct_entry_h(
738
- pt, {"ltv" => 10, "fico" => 690, "state" => st}, dgh, nil, false, true)
739
- end
409
+ expect(res).to eq [1.655, 'G3']
410
+
411
+ [3, 4].each do |units|
412
+ res = lookup_grid_helper('infinity',
413
+ 'G2',
414
+ 'fico' => 720,
415
+ 'units' => units,
416
+ 'ltv' => 100,
417
+ 'cltv' => 110.1,
418
+ )
419
+ expect(res).to eq [5.6, 'G2']
740
420
  end
741
421
 
742
- describe "exports" do
743
- it 'should export lenient grids correctly' do
744
- dg = dg_from_import("Gf", Gf)
745
- dg2 = dg_from_import("Gf2", dg.export)
422
+ dg = Marty::DataGrid.find_by(obsoleted_dt: 'infinity', name: 'G1')
423
+
424
+ h = {
425
+ 'fico' => 600,
426
+ 'state' => 'RI',
427
+ 'ltv' => 10,
428
+ }
746
429
 
747
- expect(dg.export).to eq(dg2.export)
748
- end
430
+ res = lookup_grid_helper('infinity', 'G1', h)
431
+ expect(res).to eq [11, 'G1']
432
+
433
+ dg.update_from_import('G1', G1.sub(/11/, '111'))
434
+
435
+ res = lookup_grid_helper('infinity', 'G1', h)
436
+ expect(res).to eq [111, 'G1']
437
+ end
438
+
439
+ it 'should result in error when there are multiple cell hits' do
440
+ expect do
441
+ lookup_grid_helper('infinity',
442
+ 'G2',
443
+ 'fico' => 720,
444
+ 'ltv' => 100,
445
+ 'cltv' => 110.1,
446
+ )
447
+ end.to raise_error(RuntimeError)
448
+ end
449
+
450
+ it 'should return nil when matching data grid cell is nil' do
451
+ res = lookup_grid_helper('infinity',
452
+ 'G1',
453
+ 'fico' => 800,
454
+ 'state' => 'MA',
455
+ 'ltv' => 81,
456
+ )
457
+ expect(res).to eq [nil, 'G1']
458
+ end
459
+
460
+ it 'should handle string wildcards' do
461
+ res = lookup_grid_helper('infinity',
462
+ 'G1',
463
+ 'fico' => 720,
464
+ 'state' => 'GU',
465
+ 'ltv' => 80,
466
+ )
467
+ expect(res).to eq [22, 'G1']
468
+ end
469
+
470
+ it 'should handle matches which also have a wildcard match' do
471
+ dg_from_import('G9', G9)
472
+
473
+ expect do
474
+ res = lookup_grid_helper('infinity',
475
+ 'G9',
476
+ 'state' => 'CA', 'ltv' => 81,
477
+ )
478
+ end.to raise_error(RuntimeError)
479
+
480
+ res = lookup_grid_helper('infinity',
481
+ 'G9',
482
+ 'state' => 'GU', 'ltv' => 81,
483
+ )
484
+ expect(res).to eq [456, 'G9']
485
+ end
486
+
487
+ it 'should raise on nil attr values' do
488
+ dg_from_import('G9', G9)
489
+
490
+ expect do
491
+ lookup_grid_helper('infinity',
492
+ 'G9',
493
+ 'ltv' => 81,
494
+ )
495
+ end .to raise_error(/matches > 1/)
496
+
497
+ err = /Data Grid lookup failed/
498
+ expect do
499
+ lookup_grid_helper('infinity',
500
+ 'G9',
501
+ { 'state' => 'CA', 'ltv' => nil },
502
+ false, false)
503
+ end .to raise_error(err)
504
+
505
+ res = lookup_grid_helper('infinity',
506
+ 'G9',
507
+ { 'state' => nil, 'ltv' => 81 },
508
+ false, false)
509
+
510
+ expect(res).to eq [456, 'G9']
511
+ end
512
+
513
+ it 'should handle boolean keys' do
514
+ res = lookup_grid_helper('infinity',
515
+ 'G4',
516
+ 'hb_indicator' => true,
517
+ 'cltv' => 80,
518
+ )
519
+ expect(res).to eq [-1.5, 'G4']
520
+
521
+ res = lookup_grid_helper('infinity',
522
+ 'G4',
523
+ 'hb_indicator' => false,
524
+ 'cltv' => 80,
525
+ )
526
+ expect(res).to eq [nil, 'G4']
527
+ end
528
+
529
+ it 'should handle vertical-only grids' do
530
+ res = lookup_grid_helper('infinity',
531
+ 'G5',
532
+ 'ltv' => 80,
533
+ )
534
+ expect(res).to eq [-0.375, 'G5']
535
+ end
536
+
537
+ it 'should handle horiz-only grids' do
538
+ res = lookup_grid_helper('infinity',
539
+ 'G6',
540
+ 'ltv' => 80, 'conforming' => true,
541
+ )
542
+ expect(res).to eq [-0.375, 'G6']
543
+ end
544
+
545
+ it 'should handle string typed data grids' do
546
+ expect(Marty::DataGrid.lookup('infinity', 'G7').data_type).to eq 'string'
547
+
548
+ res = lookup_grid_helper('infinity',
549
+ 'G7',
550
+ 'hb_indicator' => true,
551
+ 'cltv' => 80,
552
+ )
553
+ expect(res).to eq ['test', 'G7']
554
+ end
555
+
556
+ it 'should handle DataGrid typed data grids' do
557
+ expect(Marty::DataGrid.lookup('infinity', 'G8').data_type).
558
+ to eq 'Marty::DataGrid'
559
+ g1 = Marty::DataGrid.lookup('infinity', 'G1')
560
+
561
+ res = lookup_grid_helper('infinity',
562
+ 'G8',
563
+ 'ltv' => 80,
564
+ )
565
+ expect(res).to eq [g1, 'G8']
566
+ end
567
+
568
+ it 'should handle multi DataGrid lookups' do
569
+ expect(Marty::DataGrid.lookup('infinity', 'G8').data_type).
570
+ to eq 'Marty::DataGrid'
571
+
572
+ h = {
573
+ 'fico' => 600,
574
+ 'state' => 'RI',
575
+ 'ltv' => 10,
576
+ }
577
+
578
+ g1_res = lookup_grid_helper('infinity', 'G1', h)
579
+ expect(g1_res).to eq [11, 'G1']
580
+
581
+ res = lookup_grid_helper('infinity',
582
+ 'G8',
583
+ h, true
584
+ )
585
+ expect(g1_res).to eq res
586
+ end
587
+
588
+ it 'should handle DataGrid typed data grids' do
589
+ g1 = Marty::DataGrid.find_by(obsoleted_dt: 'infinity', name: 'G1')
590
+
591
+ res = lookup_grid_helper('infinity',
592
+ 'Ga',
593
+ 'dg' => g1,
594
+ )
595
+ expect(res).to eq [7, 'Ga']
596
+
597
+ # should be able to lookup bu name as well
598
+ res = lookup_grid_helper('infinity',
599
+ 'Ga',
600
+ 'dg' => 'G2',
601
+ )
602
+ expect(res).to eq [7, 'Ga']
603
+ end
604
+
605
+ it 'should handle DataGrid typed data grids -- non mcfly' do
606
+ ca = Gemini::State.find_by_name('CA')
607
+
608
+ res = lookup_grid_helper('infinity',
609
+ 'Gb',
610
+ 'property_state' => ca,
611
+ )
612
+ expect(res).to eq [70, 'Gb']
613
+
614
+ # should be able to lookup bu name as well
615
+ res = lookup_grid_helper('infinity',
616
+ 'Gb',
617
+ 'property_state' => 'CA',
618
+ )
619
+ expect(res).to eq [70, 'Gb']
620
+ end
621
+
622
+ it 'should handle typed (enum) data lookup_grid' do
623
+ pt = 'infinity'
624
+ ca = Gemini::State.find_by_name('CA')
625
+
626
+ res = Marty::DataGrid.
627
+ lookup_grid_h(pt, 'Gb', { 'property_state' => ca }, false)
628
+
629
+ expect(res).to eq 70
630
+ end
631
+
632
+ it 'should return grid data and metadata simple' do
633
+ expected_data = [[1.1, 2.2, 3.3], [4.4, 5.5, 6.6], [1.2, 2.3, 3.4],
634
+ [4.5, 5.6, 6.7]]
635
+ expected_metadata = [{ 'dir' => 'v',
636
+ 'attr' => 'units',
637
+ 'keys' => [[1, 2], [1, 2], [3, 4], [3, 4]],
638
+ 'type' => 'integer' },
639
+ { 'dir' => 'v',
640
+ 'attr' => 'ltv',
641
+ 'keys' => ['[,80]', '(80,105]', '[,80]', '(80,105]'],
642
+ 'type' => 'numrange' },
643
+ { 'dir' => 'h',
644
+ 'attr' => 'cltv',
645
+ 'keys' => ['[100,110)', '[110,120)', '[120,]'],
646
+ 'type' => 'numrange' },
647
+ { 'dir' => 'h',
648
+ 'attr' => 'fico',
649
+ 'keys' => ['[600,700)', '[700,750)', '[750,]'],
650
+ 'type' => 'numrange' }]
651
+
652
+ dgh = Marty::DataGrid.lookup_h(pt, 'G2')
653
+ res = Marty::DataGrid.lookup_grid_distinct_entry_h(pt, {}, dgh,
654
+ nil, true, true)
655
+ expect(res['data']).to eq expected_data
656
+ expect(res['metadata']).to eq expected_metadata
657
+ end
658
+
659
+ it 'should return grid data and metadata multi (following)' do
660
+ expected_data = [[1.1, 2.2, 3.3], [4.4, 5.5, 6.6], [1.2, 2.3, 3.4],
661
+ [4.5, 5.6, nil], [11.0, 22.0, 33.0]]
662
+ expected_metadata = [{ 'dir' => 'v',
663
+ 'attr' => 'state',
664
+ 'keys' => [['CA'], ['HI', 'TX'], ['NM'], ['MA'], nil],
665
+ 'type' => 'string' },
666
+ { 'dir' => 'v',
667
+ 'attr' => 'ltv',
668
+ 'keys' => ['[,80]', '(80,105]', '[,80]', '(80,105]',
669
+ '[,80]'],
670
+ 'type' => 'numrange' },
671
+ { 'dir' => 'h',
672
+ 'attr' => 'fico',
673
+ 'keys' => ['[600,700)', '[700,750)', '[750,]'],
674
+ 'type' => 'numrange' }]
675
+ dgh = Marty::DataGrid.lookup_h(pt, 'G8')
676
+ res = Marty::DataGrid.lookup_grid_distinct_entry_h(pt,
677
+ { 'ltv' => 10,
678
+ 'state' => 'RI' },
679
+ dgh, nil, true,
680
+ true)
681
+ expect(res['data']).to eq expected_data
682
+ expect(res['metadata']).to eq expected_metadata
683
+ end
684
+
685
+ it 'should return grid data and metadata multi (not following)' do
686
+ expected_data = [['G1'], ['G2'], ['G3']]
687
+ expected_metadata = [{ 'dir' => 'v',
688
+ 'attr' => 'ltv',
689
+ 'keys' => ['[,115]', '(115,135]', '(135,140]'],
690
+ 'type' => 'numrange' }]
691
+ dgh = Marty::DataGrid.lookup_h(pt, 'G8')
692
+ res = Marty::DataGrid.lookup_grid_distinct_entry_h(pt,
693
+ { 'ltv' => 10,
694
+ 'state' => 'RI' },
695
+ dgh, nil, false,
696
+ true)
697
+ expect(res['data']).to eq expected_data
698
+ expect(res['metadata']).to eq expected_metadata
699
+ end
700
+
701
+ it 'should handle all characters in grid inputs' do
702
+ dgh = Marty::DataGrid.lookup_h(pt, 'G1')
703
+ 5000.times do
704
+ st = 30.times.map { rand(32..255) }.pack('U*')
705
+ res = Marty::DataGrid.lookup_grid_distinct_entry_h(pt,
706
+ { 'ltv' => 10,
707
+ 'fico' => 690,
708
+ 'state' => st },
709
+ dgh, nil, false, true)
749
710
  end
711
+ end
712
+ it 'should handle all quote chars in grid inputs' do
713
+ dgh = Marty::DataGrid.lookup_h(pt, 'G1')
714
+ # single, double, backslash, grave, acute, unicode quotes: left single,
715
+ # right single, left double, right double
716
+ quotes = ["'", '"', '\\', '`', "\u00b4", "\u2018", "\u2019",
717
+ "\u201C", "\u201D"]
718
+ 100.times do
719
+ st = 30.times.map { quotes[rand(9)] }.join
720
+ res = Marty::DataGrid.lookup_grid_distinct_entry_h(
721
+ pt, { 'ltv' => 10, 'fico' => 690, 'state' => st }, dgh, nil, false, true)
722
+ end
723
+ end
724
+ it 'should handle quote chars in object name' do
725
+ dgh = Marty::DataGrid.lookup_h(pt, 'G1')
726
+ st = Gemini::State.new(name: "'\\")
727
+ res = Marty::DataGrid.lookup_grid_distinct_entry_h(
728
+ pt, { 'ltv' => 10, 'fico' => 690, 'state' => st }, dgh, nil, false, true)
729
+ end
730
+ end
731
+
732
+ describe 'exports' do
733
+ it 'should export lenient grids correctly' do
734
+ dg = dg_from_import('Gf', Gf)
735
+ dg2 = dg_from_import('Gf2', dg.export)
736
+
737
+ expect(dg.export).to eq(dg2.export)
738
+ end
739
+ end
740
+
741
+ describe 'updates' do
742
+ it 'should be possible to modify a grid referenced from a multi-grid' do
743
+ dgb = dg_from_import('Gb', Gb, '1/1/2014')
744
+ dgc = dg_from_import('Gc', Gc, '2/2/2014')
745
+
746
+ dgb.update_from_import('Gb', Gb.sub(/70/, '333'), '1/1/2015')
747
+ dgb.update_from_import('Gb', Gb.sub(/70/, '444'), '1/1/2016')
748
+
749
+ dgch = dgc.attributes.
750
+ slice('id', 'group_id', 'created_dt', 'metadata', 'data_type')
751
+ res = Marty::DataGrid.lookup_grid_distinct_entry_h('2/2/2014',
752
+ { 'property_state' => 'CA' }, dgch)
753
+ expect(res['result']).to eq(70)
754
+
755
+ res = Marty::DataGrid.lookup_grid_distinct_entry_h('2/2/2015',
756
+ { 'property_state' => 'CA' }, dgch)
750
757
 
751
- describe "updates" do
752
- it "should be possible to modify a grid referenced from a multi-grid" do
753
- dgb = dg_from_import("Gb", Gb, '1/1/2014')
754
- dgc = dg_from_import("Gc", Gc, '2/2/2014')
755
-
756
- dgb.update_from_import("Gb", Gb.sub(/70/, "333"), '1/1/2015')
757
- dgb.update_from_import("Gb", Gb.sub(/70/, "444"), '1/1/2016')
758
-
759
- dgch = dgc.attributes.
760
- slice("id","group_id","created_dt", "metadata", "data_type")
761
- res = Marty::DataGrid.lookup_grid_distinct_entry_h('2/2/2014',
762
- {"property_state" => "CA"}, dgch)
763
- expect(res["result"]).to eq(70)
764
-
765
- res = Marty::DataGrid.lookup_grid_distinct_entry_h('2/2/2015',
766
- {"property_state" => "CA"}, dgch)
767
-
768
- expect(res["result"]).to eq(333)
769
-
770
- res = Marty::DataGrid.lookup_grid_distinct_entry_h('2/2/2016',
771
- {"property_state" => "CA"}, dgch)
772
-
773
- expect(res["result"]).to eq(444)
774
- end
775
-
776
- it "should not create a new version if no change has been made" do
777
- dg = dg_from_import("G4", G1)
778
- dg.update_from_import("G4", G1)
779
- expect(Marty::DataGrid.where(group_id: dg.group_id).count).to eq 1
780
- end
781
-
782
- it "should be able to export and import back grids" do
783
- [G1, G2, G3, G4, G5, G6, G7, G8, G9, Ga, Gb].each_with_index do
784
- |grid, i|
785
- dg = dg_from_import("G#{i}", grid)
786
- g1 = dg.export
787
- dg = dg_from_import("Gx#{i}", g1)
788
- g2 = dg.export
789
- expect(g1).to eq g2
790
- end
791
- end
792
-
793
- it "should be able to externally export/import grids" do
794
- load_scripts(nil, Date.today)
795
-
796
- dg = dg_from_import("G1", G1)
797
-
798
- p = posting("BASE", DateTime.tomorrow, '?')
799
-
800
- engine = Marty::ScriptSet.new.get_engine("DataReport")
801
- res = engine.evaluate("TableReport",
802
- "result",
803
- {
804
- "pt_name" => p.name,
805
- "class_name" => "Marty::DataGrid",
806
- },
807
- )
808
-
809
- # FIXME: really hacky removing "" (data_grid) -- This is a bug
810
- # in TableReport/CSV generation.
811
- res.gsub!(/\"\"/, '')
812
- sum = do_import_summary(Marty::DataGrid,
813
- res,
814
- 'infinity',
815
- nil,
816
- nil,
817
- ",",
818
- )
819
-
820
- expect(sum).to eq({same: 1})
821
-
822
- res11 = res.sub(/G1/, "G11")
823
-
824
- sum = do_import_summary(
825
- Marty::DataGrid, res11, 'infinity', nil, nil, ",")
826
-
827
- expect(sum).to eq({create: 1})
828
-
829
- g1 = Marty::DataGrid.find_by(obsoleted_dt: 'infinity', name: "G1")
830
- g11 = Marty::DataGrid.find_by(obsoleted_dt: 'infinity', name: "G11")
831
-
832
- expect(g1.export).to eq g11.export
833
- end
758
+ expect(res['result']).to eq(333)
759
+
760
+ res = Marty::DataGrid.lookup_grid_distinct_entry_h('2/2/2016',
761
+ { 'property_state' => 'CA' }, dgch)
762
+
763
+ expect(res['result']).to eq(444)
764
+ end
765
+
766
+ it 'should not create a new version if no change has been made' do
767
+ dg = dg_from_import('G4', G1)
768
+ dg.update_from_import('G4', G1)
769
+ expect(Marty::DataGrid.where(group_id: dg.group_id).count).to eq 1
770
+ end
771
+
772
+ it 'should be able to export and import back grids' do
773
+ [G1, G2, G3, G4, G5, G6, G7, G8, G9, Ga, Gb].each_with_index do |grid, i|
774
+ dg = dg_from_import("G#{i}", grid)
775
+ g1 = dg.export
776
+ dg = dg_from_import("Gx#{i}", g1)
777
+ g2 = dg.export
778
+ expect(g1).to eq g2
834
779
  end
835
780
  end
781
+
782
+ it 'should be able to externally export/import grids' do
783
+ load_scripts(nil, Date.today)
784
+
785
+ dg = dg_from_import('G1', G1)
786
+
787
+ p = posting('BASE', DateTime.tomorrow, '?')
788
+
789
+ engine = Marty::ScriptSet.new.get_engine('DataReport')
790
+ res = engine.evaluate('TableReport',
791
+ 'result',
792
+ 'pt_name' => p.name,
793
+ 'class_name' => 'Marty::DataGrid',
794
+ )
795
+
796
+ # FIXME: really hacky removing "" (data_grid) -- This is a bug
797
+ # in TableReport/CSV generation.
798
+ res.gsub!(/\"\"/, '')
799
+ sum = do_import_summary(Marty::DataGrid,
800
+ res,
801
+ 'infinity',
802
+ nil,
803
+ nil,
804
+ ',',
805
+ )
806
+
807
+ expect(sum).to eq(same: 1)
808
+
809
+ res11 = res.sub(/G1/, 'G11')
810
+
811
+ sum = do_import_summary(
812
+ Marty::DataGrid, res11, 'infinity', nil, nil, ',')
813
+
814
+ expect(sum).to eq(create: 1)
815
+
816
+ g1 = Marty::DataGrid.find_by(obsoleted_dt: 'infinity', name: 'G1')
817
+ g11 = Marty::DataGrid.find_by(obsoleted_dt: 'infinity', name: 'G11')
818
+
819
+ expect(g1.export).to eq g11.export
820
+ end
821
+ end
822
+ end
836
823
  end