dbee-active_record 2.0.4 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +14 -13
  3. data/.ruby-version +1 -1
  4. data/.travis.yml +4 -10
  5. data/CHANGELOG.md +24 -0
  6. data/Guardfile +2 -1
  7. data/README.md +1 -1
  8. data/dbee-active_record.gemspec +21 -8
  9. data/exe/.gitkeep +0 -0
  10. data/lib/dbee/providers/active_record_provider.rb +3 -3
  11. data/lib/dbee/providers/active_record_provider/expression_builder.rb +96 -55
  12. data/lib/dbee/providers/active_record_provider/maker.rb +37 -0
  13. data/lib/dbee/providers/active_record_provider/{expression_builder/constraint_maker.rb → makers/constraint.rb} +12 -12
  14. data/lib/dbee/providers/active_record_provider/{expression_builder/order_maker.rb → makers/order.rb} +9 -9
  15. data/lib/dbee/providers/active_record_provider/makers/select.rb +81 -0
  16. data/lib/dbee/providers/active_record_provider/makers/where.rb +111 -0
  17. data/lib/dbee/providers/active_record_provider/version.rb +1 -1
  18. data/spec/db_helper.rb +134 -14
  19. data/spec/dbee/providers/active_record_provider/expression_builder_spec.rb +90 -0
  20. data/spec/dbee/providers/active_record_provider/makers/where_spec.rb +260 -0
  21. data/spec/dbee/providers/active_record_provider_spec.rb +112 -14
  22. data/spec/fixtures/active_record_snapshots/five_table_query.yaml +1 -0
  23. data/spec/fixtures/active_record_snapshots/multiple_same_table_query_with_static_constraints.yaml +1 -0
  24. data/spec/fixtures/active_record_snapshots/one_table_empty_query.yaml +11 -0
  25. data/spec/fixtures/active_record_snapshots/one_table_query.yaml +1 -0
  26. data/spec/fixtures/active_record_snapshots/one_table_query_with_ascending_sort.yaml +1 -0
  27. data/spec/fixtures/active_record_snapshots/one_table_query_with_descending_sort.yaml +1 -0
  28. data/spec/fixtures/active_record_snapshots/one_table_query_with_filters.yaml +9 -8
  29. data/spec/fixtures/active_record_snapshots/one_table_query_with_limit.yaml +1 -0
  30. data/spec/fixtures/active_record_snapshots/one_table_query_with_multiple_sorts.yaml +1 -0
  31. data/spec/fixtures/active_record_snapshots/partitioner_example_1_query.yaml +1 -0
  32. data/spec/fixtures/active_record_snapshots/partitioner_example_2_query.yaml +1 -0
  33. data/spec/fixtures/active_record_snapshots/reverse_polymorphic_query.yaml +1 -0
  34. data/spec/fixtures/active_record_snapshots/two_table_query.yaml +1 -0
  35. data/spec/fixtures/active_record_snapshots/two_table_query_with_aggregation.yaml +72 -0
  36. data/spec/fixtures/active_record_snapshots/two_table_query_with_pivoting.yaml +89 -0
  37. data/spec/fixtures/models.yaml +112 -84
  38. data/spec/spec_helper.rb +13 -2
  39. metadata +96 -28
  40. data/lib/dbee/providers/active_record_provider/expression_builder/select_maker.rb +0 -33
  41. data/lib/dbee/providers/active_record_provider/expression_builder/where_maker.rb +0 -68
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2019-present, Blue Marble Payroll, LLC
5
+ #
6
+ # This source code is licensed under the MIT license found in the
7
+ # LICENSE file in the root directory of this source tree.
8
+ #
9
+
10
+ require 'spec_helper'
11
+ require 'db_helper'
12
+
13
+ describe Dbee::Providers::ActiveRecordProvider::ExpressionBuilder do
14
+ let(:schema) { Dbee::Schema.new(models['Patients']) }
15
+ let(:alias_maker) { Dbee::Providers::ActiveRecordProvider::SafeAliasMaker.new }
16
+
17
+ let(:empty_query) { Dbee::Query.make(from: :patients) }
18
+
19
+ let(:id_and_average_query) do
20
+ Dbee::Query.make(
21
+ from: :patients,
22
+ fields: [
23
+ {
24
+ key_path: :id,
25
+ display: 'ID #'
26
+ },
27
+ {
28
+ key_path: 'patient_payments.amount',
29
+ display: 'Ave Payment',
30
+ aggregator: :ave
31
+ }
32
+ ],
33
+ sorters: [
34
+ {
35
+ key_path: :id
36
+ }
37
+ ],
38
+ filters: [
39
+ {
40
+ key_path: :id,
41
+ value: 123
42
+ }
43
+ ]
44
+ )
45
+ end
46
+
47
+ let(:first_and_count_query) do
48
+ Dbee::Query.make(
49
+ from: :patients,
50
+ fields: [
51
+ {
52
+ key_path: :first,
53
+ display: 'First Name'
54
+ },
55
+ {
56
+ key_path: 'patient_payments.amount',
57
+ display: 'Number of Payments',
58
+ aggregator: :count
59
+ }
60
+ ]
61
+ )
62
+ end
63
+
64
+ subject { described_class.new(schema, alias_maker, alias_maker) }
65
+
66
+ before(:all) do
67
+ connect_to_db(:sqlite)
68
+ end
69
+
70
+ describe '#to_sql' do
71
+ specify 'when called with no fields, then called with fields removes star select' do
72
+ expect(subject.to_sql(empty_query)).to include('*')
73
+ expect(subject.to_sql(id_and_average_query)).not_to include('*')
74
+ end
75
+
76
+ context 'with aggregation' do
77
+ it 'generates the same sql when called multiple times' do
78
+ first_sql = subject.to_sql(id_and_average_query)
79
+ second_sql = subject.to_sql(id_and_average_query)
80
+
81
+ expect(first_sql).to eq(second_sql)
82
+
83
+ third_sql = subject.to_sql(first_and_count_query)
84
+ fourth_sql = subject.to_sql(first_and_count_query)
85
+
86
+ expect(third_sql).to eq(fourth_sql)
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,260 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2020-present, Blue Marble Payroll, LLC
5
+ #
6
+ # This source code is licensed under the MIT license found in the
7
+ # LICENSE file in the root directory of this source tree.
8
+ #
9
+
10
+ require 'spec_helper'
11
+ require 'db_helper'
12
+
13
+ # rubocop:disable Layout/LineLength
14
+ # Disable the line length cop beause there are some lengthy 'expected' SQL strings in spec which
15
+ # cannot be shortened.
16
+ describe Dbee::Providers::ActiveRecordProvider::Makers::Where do
17
+ before(:all) { connect_to_db(:sqlite) }
18
+
19
+ let(:subject) { described_class.instance }
20
+ let(:column) { Arel::Table.new(:test)[:foo] }
21
+
22
+ describe 'equals' do
23
+ specify 'a string value' do
24
+ filter = Dbee::Query::Filters::Equals.new(key_path: :foo, value: 'bar')
25
+ expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" = 'bar')
26
+ end
27
+
28
+ specify 'a null value' do
29
+ filter = Dbee::Query::Filters::Equals.new(key_path: :foo, value: nil)
30
+ expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NULL'
31
+ end
32
+
33
+ specify 'a set with a null value' do
34
+ filter = Dbee::Query::Filters::Equals.new(key_path: :foo, value: ['a', nil, 'c'])
35
+ expected = %q[("test"."foo" IS NULL OR "test"."foo" IN ('a', 'c'))]
36
+ expect(subject.make(filter, column).to_sql).to eq expected
37
+ end
38
+
39
+ specify 'a set without a null value' do
40
+ filter = Dbee::Query::Filters::Equals.new(key_path: :foo, value: %w[a b c])
41
+ expect(subject.make(filter, column).to_sql).to eq %q["test"."foo" IN ('a', 'b', 'c')]
42
+ end
43
+ end
44
+
45
+ describe 'not equals' do
46
+ specify 'a string value' do
47
+ filter = Dbee::Query::Filters::NotEquals.new(key_path: :foo, value: 'bar')
48
+ expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" != 'bar')
49
+ end
50
+
51
+ specify 'a null value' do
52
+ filter = Dbee::Query::Filters::NotEquals.new(key_path: :foo, value: nil)
53
+ expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NOT NULL'
54
+ end
55
+
56
+ specify 'a set with a null value' do
57
+ filter = Dbee::Query::Filters::NotEquals.new(key_path: :foo, value: ['a', nil, 'c'])
58
+ expected = %q[("test"."foo" IS NOT NULL OR "test"."foo" NOT IN ('a', 'c'))]
59
+ expect(subject.make(filter, column).to_sql).to eq expected
60
+ end
61
+
62
+ specify 'a set without a null value' do
63
+ filter = Dbee::Query::Filters::NotEquals.new(key_path: :foo, value: %w[a b c])
64
+ expect(subject.make(filter, column).to_sql).to eq %q["test"."foo" NOT IN ('a', 'b', 'c')]
65
+ end
66
+ end
67
+
68
+ describe 'contains' do
69
+ specify 'a string value' do
70
+ filter = Dbee::Query::Filters::Contains.new(key_path: :foo, value: 'bar')
71
+ expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" LIKE '%bar%')
72
+ end
73
+
74
+ specify 'a null value' do
75
+ filter = Dbee::Query::Filters::Contains.new(key_path: :foo, value: nil)
76
+ expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NULL'
77
+ end
78
+
79
+ specify 'a set with a null value' do
80
+ filter = Dbee::Query::Filters::Contains.new(key_path: :foo, value: ['a', nil, 'c'])
81
+ expected = %q[(("test"."foo" IS NULL OR "test"."foo" LIKE '%a%') OR "test"."foo" LIKE '%c%')]
82
+ expect(subject.make(filter, column).to_sql).to eq expected
83
+ end
84
+
85
+ specify 'a set without a null value' do
86
+ filter = Dbee::Query::Filters::Contains.new(key_path: :foo, value: %w[a b c])
87
+ expected = %q[(("test"."foo" LIKE '%a%' OR "test"."foo" LIKE '%b%') OR "test"."foo" LIKE '%c%')]
88
+ expect(subject.make(filter, column).to_sql).to eq expected
89
+ end
90
+ end
91
+
92
+ describe 'not contains' do
93
+ specify 'a string value' do
94
+ filter = Dbee::Query::Filters::NotContain.new(key_path: :foo, value: 'bar')
95
+ expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" NOT LIKE '%bar%')
96
+ end
97
+
98
+ specify 'a null value' do
99
+ filter = Dbee::Query::Filters::NotContain.new(key_path: :foo, value: nil)
100
+ expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NOT NULL'
101
+ end
102
+
103
+ specify 'a set with a null value' do
104
+ filter = Dbee::Query::Filters::NotContain.new(key_path: :foo, value: ['a', nil, 'c'])
105
+ expected = %q[(("test"."foo" IS NOT NULL OR "test"."foo" NOT LIKE '%a%') OR "test"."foo" NOT LIKE '%c%')]
106
+ expect(subject.make(filter, column).to_sql).to eq expected
107
+ end
108
+
109
+ specify 'a set without a null value' do
110
+ filter = Dbee::Query::Filters::NotContain.new(key_path: :foo, value: %w[a b c])
111
+ expected = %q[(("test"."foo" NOT LIKE '%a%' OR "test"."foo" NOT LIKE '%b%') OR "test"."foo" NOT LIKE '%c%')]
112
+ expect(subject.make(filter, column).to_sql).to eq expected
113
+ end
114
+ end
115
+
116
+ describe 'starts with' do
117
+ specify 'a string value' do
118
+ filter = Dbee::Query::Filters::StartsWith.new(key_path: :foo, value: 'bar')
119
+ expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" LIKE 'bar%')
120
+ end
121
+
122
+ specify 'a null value' do
123
+ filter = Dbee::Query::Filters::StartsWith.new(key_path: :foo, value: nil)
124
+ expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NULL'
125
+ end
126
+
127
+ specify 'a set with a null value' do
128
+ filter = Dbee::Query::Filters::StartsWith.new(key_path: :foo, value: ['a', nil, 'c'])
129
+ expected = %q[(("test"."foo" IS NULL OR "test"."foo" LIKE 'a%') OR "test"."foo" LIKE 'c%')]
130
+ expect(subject.make(filter, column).to_sql).to eq expected
131
+ end
132
+
133
+ specify 'a set without a null value' do
134
+ filter = Dbee::Query::Filters::StartsWith.new(key_path: :foo, value: %w[a b c])
135
+ expected = %q[(("test"."foo" LIKE 'a%' OR "test"."foo" LIKE 'b%') OR "test"."foo" LIKE 'c%')]
136
+ expect(subject.make(filter, column).to_sql).to eq expected
137
+ end
138
+ end
139
+
140
+ describe 'not start with' do
141
+ specify 'a string value' do
142
+ filter = Dbee::Query::Filters::NotStartWith.new(key_path: :foo, value: 'bar')
143
+ expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" NOT LIKE 'bar%')
144
+ end
145
+
146
+ specify 'a null value' do
147
+ filter = Dbee::Query::Filters::NotStartWith.new(key_path: :foo, value: nil)
148
+ expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NOT NULL'
149
+ end
150
+
151
+ specify 'a set with a null value' do
152
+ filter = Dbee::Query::Filters::NotStartWith.new(key_path: :foo, value: ['a', nil, 'c'])
153
+ expected = %q[(("test"."foo" IS NOT NULL OR "test"."foo" NOT LIKE 'a%') OR "test"."foo" NOT LIKE 'c%')]
154
+ expect(subject.make(filter, column).to_sql).to eq expected
155
+ end
156
+
157
+ specify 'a set without a null value' do
158
+ filter = Dbee::Query::Filters::NotStartWith.new(key_path: :foo, value: %w[a b c])
159
+ expected = %q[(("test"."foo" NOT LIKE 'a%' OR "test"."foo" NOT LIKE 'b%') OR "test"."foo" NOT LIKE 'c%')]
160
+ expect(subject.make(filter, column).to_sql).to eq expected
161
+ end
162
+ end
163
+
164
+ describe 'greater than' do
165
+ specify 'a string value' do
166
+ filter = Dbee::Query::Filters::GreaterThan.new(key_path: :foo, value: 'bar')
167
+ expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" > 'bar')
168
+ end
169
+
170
+ specify 'a null value' do
171
+ filter = Dbee::Query::Filters::GreaterThan.new(key_path: :foo, value: nil)
172
+ expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NULL'
173
+ end
174
+
175
+ specify 'a set with a null value' do
176
+ filter = Dbee::Query::Filters::GreaterThan.new(key_path: :foo, value: ['a', nil, 'c'])
177
+ expected = %q[(("test"."foo" IS NULL OR "test"."foo" > 'a') OR "test"."foo" > 'c')]
178
+ expect(subject.make(filter, column).to_sql).to eq expected
179
+ end
180
+
181
+ specify 'a set without a null value' do
182
+ filter = Dbee::Query::Filters::GreaterThan.new(key_path: :foo, value: %w[a b c])
183
+ expected = %q[(("test"."foo" > 'a' OR "test"."foo" > 'b') OR "test"."foo" > 'c')]
184
+ expect(subject.make(filter, column).to_sql).to eq expected
185
+ end
186
+ end
187
+
188
+ describe 'greater than or equal to' do
189
+ specify 'a string value' do
190
+ filter = Dbee::Query::Filters::GreaterThanOrEqualTo.new(key_path: :foo, value: 'bar')
191
+ expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" >= 'bar')
192
+ end
193
+
194
+ specify 'a null value' do
195
+ filter = Dbee::Query::Filters::GreaterThanOrEqualTo.new(key_path: :foo, value: nil)
196
+ expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NULL'
197
+ end
198
+
199
+ specify 'a set with a null value' do
200
+ filter = Dbee::Query::Filters::GreaterThanOrEqualTo.new(key_path: :foo, value: ['a', nil, 'c'])
201
+ expected = %q[(("test"."foo" IS NULL OR "test"."foo" >= 'a') OR "test"."foo" >= 'c')]
202
+ expect(subject.make(filter, column).to_sql).to eq expected
203
+ end
204
+
205
+ specify 'a set without a null value' do
206
+ filter = Dbee::Query::Filters::GreaterThanOrEqualTo.new(key_path: :foo, value: %w[a b c])
207
+ expected = %q[(("test"."foo" >= 'a' OR "test"."foo" >= 'b') OR "test"."foo" >= 'c')]
208
+ expect(subject.make(filter, column).to_sql).to eq expected
209
+ end
210
+ end
211
+
212
+ describe 'less than' do
213
+ specify 'a string value' do
214
+ filter = Dbee::Query::Filters::LessThan.new(key_path: :foo, value: 'bar')
215
+ expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" < 'bar')
216
+ end
217
+
218
+ specify 'a null value' do
219
+ filter = Dbee::Query::Filters::LessThan.new(key_path: :foo, value: nil)
220
+ expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NULL'
221
+ end
222
+
223
+ specify 'a set with a null value' do
224
+ filter = Dbee::Query::Filters::LessThan.new(key_path: :foo, value: ['a', nil, 'c'])
225
+ expected = %q[(("test"."foo" IS NULL OR "test"."foo" < 'a') OR "test"."foo" < 'c')]
226
+ expect(subject.make(filter, column).to_sql).to eq expected
227
+ end
228
+
229
+ specify 'a set without a null value' do
230
+ filter = Dbee::Query::Filters::LessThan.new(key_path: :foo, value: %w[a b c])
231
+ expected = %q[(("test"."foo" < 'a' OR "test"."foo" < 'b') OR "test"."foo" < 'c')]
232
+ expect(subject.make(filter, column).to_sql).to eq expected
233
+ end
234
+ end
235
+
236
+ describe 'less than or equal to' do
237
+ specify 'a string value' do
238
+ filter = Dbee::Query::Filters::LessThanOrEqualTo.new(key_path: :foo, value: 'bar')
239
+ expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" <= 'bar')
240
+ end
241
+
242
+ specify 'a null value' do
243
+ filter = Dbee::Query::Filters::LessThanOrEqualTo.new(key_path: :foo, value: nil)
244
+ expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NULL'
245
+ end
246
+
247
+ specify 'a set with a null value' do
248
+ filter = Dbee::Query::Filters::LessThanOrEqualTo.new(key_path: :foo, value: ['a', nil, 'c'])
249
+ expected = %q[(("test"."foo" IS NULL OR "test"."foo" <= 'a') OR "test"."foo" <= 'c')]
250
+ expect(subject.make(filter, column).to_sql).to eq expected
251
+ end
252
+
253
+ specify 'a set without a null value' do
254
+ filter = Dbee::Query::Filters::LessThanOrEqualTo.new(key_path: :foo, value: %w[a b c])
255
+ expected = %q[(("test"."foo" <= 'a' OR "test"."foo" <= 'b') OR "test"."foo" <= 'c')]
256
+ expect(subject.make(filter, column).to_sql).to eq expected
257
+ end
258
+ end
259
+ end
260
+ # rubocop:enable Layout/LineLength
@@ -11,22 +11,19 @@ require 'spec_helper'
11
11
  require 'db_helper'
12
12
 
13
13
  describe Dbee::Providers::ActiveRecordProvider do
14
- let(:models) { yaml_fixture('models.yaml') }
15
-
16
14
  describe '#sql' do
17
15
  before(:all) do
18
16
  connect_to_db(:sqlite)
19
17
  end
20
18
 
21
19
  it 'errors when joining tables with no constraints' do
22
- model_hash = {
23
- name: :users,
24
- models: [
25
- { name: :logins }
26
- ]
20
+ schema_hash = {
21
+ users: { relationships: { logins: nil } },
22
+ logins: nil
27
23
  }
28
24
 
29
25
  query_hash = {
26
+ from: :users,
30
27
  fields: [
31
28
  { key_path: 'id' },
32
29
  { key_path: 'logins.id' }
@@ -34,11 +31,11 @@ describe Dbee::Providers::ActiveRecordProvider do
34
31
  }
35
32
 
36
33
  query = Dbee::Query.make(query_hash)
37
- model = Dbee::Model.make(model_hash)
34
+ schema = Dbee::Schema.new(schema_hash)
38
35
 
39
36
  error_class = Dbee::Providers::ActiveRecordProvider::ExpressionBuilder::MissingConstraintError
40
37
 
41
- expect { described_class.new.sql(model, query) }.to raise_error(error_class)
38
+ expect { described_class.new.sql(schema, query) }.to raise_error(error_class)
42
39
  end
43
40
  end
44
41
 
@@ -57,12 +54,13 @@ describe Dbee::Providers::ActiveRecordProvider do
57
54
  yaml_fixture_files('active_record_snapshots').each_pair do |filename, snapshot|
58
55
  specify File.basename(filename) do
59
56
  model_name = snapshot['model_name']
57
+
60
58
  query = Dbee::Query.make(snapshot['query'])
61
- model = Dbee::Model.make(models[model_name])
59
+ schema = Dbee::Schema.new(models[model_name])
62
60
 
63
61
  expected_5_sql = snapshot[key].to_s.chomp.tr("\n", ' ')
64
62
  expected_6_sql = expected_5_sql.gsub(' ', ' ').gsub("'t'", '1').gsub("'f'", '0')
65
- actual_sql = described_class.new(readable: readable).sql(model, query)
63
+ actual_sql = described_class.new(readable: readable).sql(schema, query)
66
64
 
67
65
  error_msg = <<~ERROR_MSG
68
66
  Expected 5 SQL: #{expected_5_sql}
@@ -79,7 +77,7 @@ describe Dbee::Providers::ActiveRecordProvider do
79
77
  end
80
78
  end
81
79
 
82
- context 'Executing SQL' do
80
+ context 'Shallow SQL Execution' do
83
81
  %w[sqlite].each do |dbms|
84
82
  context dbms do
85
83
  before(:all) do
@@ -95,9 +93,9 @@ describe Dbee::Providers::ActiveRecordProvider do
95
93
  specify File.basename(filename) do
96
94
  model_name = snapshot['model_name']
97
95
  query = Dbee::Query.make(snapshot['query'])
98
- model = Dbee::Model.make(models[model_name])
96
+ schema = Dbee::Schema.new(models[model_name])
99
97
 
100
- sql = described_class.new(readable: readable).sql(model, query)
98
+ sql = described_class.new(readable: readable).sql(schema, query)
101
99
 
102
100
  expect { ActiveRecord::Base.connection.execute(sql) }.to_not raise_error
103
101
  end
@@ -108,4 +106,104 @@ describe Dbee::Providers::ActiveRecordProvider do
108
106
  end
109
107
  end
110
108
  end
109
+
110
+ describe 'Deep SQL execution' do
111
+ before(:all) do
112
+ connect_to_db(:sqlite)
113
+ load_schema
114
+ load_data
115
+ end
116
+
117
+ describe 'pivoting' do
118
+ let(:snapshot_path) do
119
+ %w[
120
+ spec
121
+ fixtures
122
+ active_record_snapshots
123
+ two_table_query_with_pivoting.yaml
124
+ ]
125
+ end
126
+
127
+ let(:snapshot) { yaml_file_read(*snapshot_path) }
128
+ let(:query) { Dbee::Query.make(snapshot['query']) }
129
+ let(:schema) { Dbee::Schema.new(models['Patients']) }
130
+
131
+ it 'pivots table rows into columns' do
132
+ sql = described_class.new.sql(schema, query)
133
+
134
+ results = ActiveRecord::Base.connection.execute(sql)
135
+
136
+ expect(results[0]).to include(
137
+ 'First Name' => 'Bozo',
138
+ 'Date of Birth' => '1904-04-04',
139
+ 'Drivers License #' => '82-54-hut-hut-hike!',
140
+ 'Demographic Notes' => 'The patient is funny!',
141
+ 'Contact Notes' => 'Do not call this patient at night!'
142
+ )
143
+
144
+ expect(results[1]).to include(
145
+ 'First Name' => 'Frank',
146
+ 'Date of Birth' => nil,
147
+ 'Drivers License #' => nil,
148
+ 'Demographic Notes' => nil,
149
+ 'Contact Notes' => nil
150
+ )
151
+
152
+ expect(results[2]).to include(
153
+ 'First Name' => 'Bugs',
154
+ 'Date of Birth' => '2040-01-01',
155
+ 'Drivers License #' => nil,
156
+ 'Demographic Notes' => nil,
157
+ 'Contact Notes' => 'Call anytime!!'
158
+ )
159
+ end
160
+ end
161
+
162
+ describe 'aggregation' do
163
+ let(:snapshot_path) do
164
+ %w[
165
+ spec
166
+ fixtures
167
+ active_record_snapshots
168
+ two_table_query_with_aggregation.yaml
169
+ ]
170
+ end
171
+
172
+ let(:snapshot) { yaml_file_read(*snapshot_path) }
173
+ let(:query) { Dbee::Query.make(snapshot['query']) }
174
+ let(:schema) { Dbee::Schema.new(models['Patients']) }
175
+
176
+ it 'executes correct SQL aggregate functions' do
177
+ sql = described_class.new.sql(schema, query)
178
+ results = ActiveRecord::Base.connection.execute(sql)
179
+
180
+ expect(results[0]).to include(
181
+ 'First Name' => 'Bozo',
182
+ 'Ave Payment' => 10,
183
+ 'Number of Payments' => 3,
184
+ 'Max Payment' => 15,
185
+ 'Min Payment' => 5,
186
+ 'Total Paid' => 30
187
+ )
188
+
189
+ expect(results[1]).to include(
190
+ 'First Name' => 'Frank',
191
+ 'Ave Payment' => 100,
192
+ 'Number of Payments' => 2,
193
+ 'Max Payment' => 150,
194
+ 'Min Payment' => 50,
195
+ 'Total Paid' => 200
196
+ )
197
+
198
+ expect(results[2]).to include(
199
+ 'First Name' => 'Bugs',
200
+ 'Ave Payment' => nil,
201
+ 'Number of Payments' => 0,
202
+ 'Max Payment' => nil,
203
+ 'Min Payment' => nil,
204
+ 'Total Paid' => nil
205
+ )
206
+ end
207
+ end
208
+ end
111
209
  end