slacker 1.0.14 → 1.0.15

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -4
  3. data/README.markdown +22 -22
  4. data/Rakefile +11 -11
  5. data/bin/slacker +32 -32
  6. data/bin/slacker_new +34 -34
  7. data/lib/slacker.rb +180 -175
  8. data/lib/slacker/application.rb +224 -214
  9. data/lib/slacker/command_line_formatter.rb +61 -61
  10. data/lib/slacker/configuration.rb +59 -59
  11. data/lib/slacker/formatter.rb +14 -14
  12. data/lib/slacker/query_result_matcher.rb +178 -178
  13. data/lib/slacker/rspec_ext.rb +57 -57
  14. data/lib/slacker/rspec_monkey.rb +7 -7
  15. data/lib/slacker/sql.rb +39 -39
  16. data/lib/slacker/sql_preprocessor.rb +23 -23
  17. data/lib/slacker/string_helper.rb +16 -16
  18. data/lib/slacker/version.rb +3 -3
  19. data/lib/slacker_new/project/data/sample_1/my_table_expected_power_results.csv +11 -11
  20. data/lib/slacker_new/project/data/sample_1/my_table_initial_data.csv +11 -11
  21. data/lib/slacker_new/project/data/sample_1/numbers_expected_output.csv +3 -3
  22. data/lib/slacker_new/project/database.yml +9 -9
  23. data/lib/slacker_new/project/lib/helpers/my_helper.rb +1 -1
  24. data/lib/slacker_new/project/spec/sample_1.rb +66 -66
  25. data/lib/slacker_new/project/sql/sample_1/my_table_on_power.sql.erb +1 -1
  26. data/lib/slacker_new/project/sql/sample_1/play_with_numbers.sql.erb +16 -16
  27. data/lib/slacker_new/project/sql/sample_1/sysobjects_with_params.sql.erb +1 -1
  28. data/slacker.gemspec +27 -27
  29. data/spec/application_spec.rb +13 -13
  30. data/spec/query_result_matcher_spec.rb +268 -268
  31. data/spec/rspec_ext_spec.rb +87 -87
  32. data/spec/slacker_spec.rb +59 -59
  33. data/spec/spec_helper.rb +19 -9
  34. data/spec/test_files/matcher/test_1.csv +3 -3
  35. data/spec/test_files/test_slacker_project/data/test_1.csv +3 -3
  36. data/spec/test_files/test_slacker_project/sql/nest/nested_1.sql.erb +1 -1
  37. data/spec/test_files/test_slacker_project/sql/no_params.sql.erb +2 -2
  38. data/spec/test_files/test_slacker_project/sql/params.sql.erb +1 -1
  39. metadata +24 -24
@@ -1,2 +1,2 @@
1
- select x, y, convert(int, Power(x, y)) as [power]
1
+ select x, y, convert(int, Power(x, y)) as [power]
2
2
  from MyTable;
@@ -1,16 +1,16 @@
1
- declare @x int;
2
- declare @y int;
3
-
4
- set @x = <%= options[:x]%>;
5
- set @y = <%= options[:y]%>;
6
-
7
- -- Return just the product of @x and @y
8
- select @x * @y [product] union all
9
- select 12;
10
-
11
- -- Return the numbers and their sum
12
- select @x x, @y y, @x + @y [sum];
13
-
14
- -- Get the two numbers in one column and their sum with 32 in another
15
- select @x p, @x + 32 s union all
16
- select @y p, @y + 32 s;
1
+ declare @x int;
2
+ declare @y int;
3
+
4
+ set @x = <%= options[:x]%>;
5
+ set @y = <%= options[:y]%>;
6
+
7
+ -- Return just the product of @x and @y
8
+ select @x * @y [product] union all
9
+ select 12;
10
+
11
+ -- Return the numbers and their sum
12
+ select @x x, @y y, @x + @y [sum];
13
+
14
+ -- Get the two numbers in one column and their sum with 32 in another
15
+ select @x p, @x + 32 s union all
16
+ select @y p, @y + 32 s;
@@ -1,2 +1,2 @@
1
- <%# Every parameter passed to a SQL template appears in the options hash available to the template %>
1
+ <%# Every parameter passed to a SQL template appears in the options hash available to the template %>
2
2
  select * from sysobjects where xtype = '<%= options[:xtype] %>';
data/slacker.gemspec CHANGED
@@ -1,27 +1,27 @@
1
- # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "slacker/version"
4
-
5
- Gem::Specification.new do |s|
6
- s.name = "slacker"
7
- s.version = Slacker::VERSION
8
- s.authors = ["Vassil Kovatchev"]
9
- s.email = ["vassil.kovatchev@gmail.com"]
10
- s.homepage = "https://github.com/vassilvk/slacker/wiki"
11
- s.summary = %q{Behavior Driven Development for SQL Server}
12
- s.description = %q{RSpec-based framework for developing automated tests for SQL Server}
13
- s.license = 'MIT'
14
-
15
- s.rubyforge_project = "slacker"
16
-
17
- s.files = ['README.markdown', 'Rakefile', 'Gemfile', 'slacker.gemspec'] + Dir.glob("{bin,lib,spec}/**/*")
18
- s.test_files = Dir.glob("spec/**/*")
19
- s.executables = ['slacker', 'slacker_new']
20
- s.require_paths = ["lib"]
21
-
22
- s.required_ruby_version = '>= 1.9.2'
23
-
24
- s.add_dependency 'bundler', '~> 1.0', '>= 1.0.15'
25
- s.add_dependency 'ruby-odbc', '= 0.99997'
26
- s.add_dependency 'rspec', '~> 3.0'
27
- end
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "slacker/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "slacker"
7
+ s.version = Slacker::VERSION
8
+ s.authors = ["Vassil Kovatchev"]
9
+ s.email = ["vassil.kovatchev@gmail.com"]
10
+ s.homepage = "https://github.com/vassilvk/slacker/wiki"
11
+ s.summary = %q{Behavior Driven Development for SQL Server}
12
+ s.description = %q{RSpec-based framework for developing automated tests for SQL Server}
13
+ s.license = 'MIT'
14
+
15
+ s.rubyforge_project = "slacker"
16
+
17
+ s.files = ['README.markdown', 'Rakefile', 'Gemfile', 'slacker.gemspec'] + Dir.glob("{bin,lib,spec}/**/*")
18
+ s.test_files = Dir.glob("spec/**/*")
19
+ s.executables = ['slacker', 'slacker_new']
20
+ s.require_paths = ["lib"]
21
+
22
+ s.required_ruby_version = '>= 1.9.2'
23
+
24
+ s.add_dependency 'bundler', '~> 1.0', '>= 1.0.15'
25
+ s.add_dependency 'ruby-odbc', '= 0.99997'
26
+ s.add_dependency 'rspec', '~> 3.0'
27
+ end
@@ -1,14 +1,14 @@
1
- require 'slacker'
2
-
3
- describe Slacker::Application do
4
- it 'responds to run' do
5
- Slacker.application.should respond_to(:run)
6
- end
7
-
8
- it 'responds to target_folder_structure with the correct list of folders' do
9
- app = Slacker.application
10
- app.should respond_to(:target_folder_structure)
11
- folder_struct = app.target_folder_structure
12
- folder_struct.should == ['data', 'debug', 'debug/passed_examples', 'debug/failed_examples', 'sql', 'spec', 'lib', 'lib/helpers']
13
- end
1
+ require 'slacker'
2
+
3
+ describe Slacker::Application do
4
+ it 'responds to run' do
5
+ Slacker.application.should respond_to(:run)
6
+ end
7
+
8
+ it 'responds to target_folder_structure with the correct list of folders' do
9
+ app = Slacker.application
10
+ app.should respond_to(:target_folder_structure)
11
+ folder_struct = app.target_folder_structure
12
+ folder_struct.should == ['data', 'debug/passed_examples', 'debug/failed_examples', 'sql', 'spec', 'lib', 'lib/helpers']
13
+ end
14
14
  end
@@ -1,268 +1,268 @@
1
- require 'slacker'
2
- require 'spec_helper'
3
- require 'time'
4
- require 'odbc'
5
-
6
- describe Slacker::QueryResultMatcher do
7
- def deep_copy(obj)
8
- Marshal.load(Marshal.dump(obj))
9
- end
10
-
11
- before(:each) do
12
- @subject = [{'Field 1' => 12, 'Field_2' => nil, 'b' => ''},
13
- {'Field 1' => 'test string', 'Field_2' => ODBC::TimeStamp.new('2011-01-30'), 'b' => 8.9}]
14
- end
15
-
16
- shared_examples_for 'table-based matcher' do
17
- it 'correctly rejects a non-matching query result based on wrong columns' do
18
- one_column_too_few = deep_copy(@subject).each{|row| row.delete('b')}
19
- one_column_too_many = deep_copy(@subject).each{|row| row['new column'] = 'val 1'}
20
- wrong_column_name = deep_copy(@subject).each{|row| row.delete('Field_2'); row['Field_x'] = 'val x'}
21
-
22
- @matcher.matches?(one_column_too_few).should be_false
23
- @matcher.failure_message_for_should.should == 'Expected 3 field(s), got 2'
24
-
25
- @matcher.matches?(one_column_too_many).should be_false
26
- @matcher.failure_message_for_should.should == 'Expected 3 field(s), got 4'
27
-
28
- @matcher.matches?(wrong_column_name).should be_false
29
- @matcher.failure_message_for_should.should == 'Expected field "Field_2", got field "b"'
30
- end
31
-
32
- it 'correctly rejects a non-matching query result based on number of records' do
33
- one_row_too_few = deep_copy(@subject)
34
- one_row_too_few.shift
35
- one_row_too_many = deep_copy(@subject) << @subject[0]
36
- empty_query_result = []
37
-
38
- @matcher.matches?(one_row_too_few).should be_false
39
- @matcher.failure_message_for_should.should == 'Expected 2 record(s), got 1'
40
-
41
- @matcher.matches?(one_row_too_many).should be_false
42
- @matcher.failure_message_for_should.should == 'Expected 2 record(s), got 3'
43
-
44
- @matcher.matches?(empty_query_result).should be_false
45
- @matcher.failure_message_for_should.should == 'Expected 2 record(s), got 0'
46
- end
47
-
48
- it 'correctly rejects a non-matching query result based on a value' do
49
- wrong_int = deep_copy(@subject)
50
- wrong_int[0]['Field 1'] = 14
51
-
52
- wrong_int_type = deep_copy(@subject)
53
- wrong_int_type[1]['Field 1'] = 14
54
-
55
- wrong_nil = deep_copy(@subject)
56
- wrong_nil[0]['Field 1'] = nil
57
-
58
- wrong_non_nil = deep_copy(@subject)
59
- wrong_non_nil[0]['Field_2'] = 'whatever'
60
-
61
- misplaced_fields = deep_copy(@subject)
62
- misplaced_fields[0].delete('Field 1')
63
- misplaced_fields[1].delete('Field 1')
64
- misplaced_fields[0]['Field 1'] = 12
65
- misplaced_fields[1]['Field 1'] = 'test string'
66
-
67
- @matcher.matches?(wrong_int).should be_false
68
- @matcher.failure_message_for_should.should == 'Field "Field 1", Record 1: Expected value "12", got "14"'
69
-
70
-
71
- @matcher.matches?(wrong_int_type).should be_false
72
- @matcher.failure_message_for_should.should == 'Field "Field 1", Record 2: Expected value "test string", got "14"'
73
-
74
- @matcher.matches?(wrong_nil).should be_false
75
- @matcher.failure_message_for_should.should == 'Field "Field 1", Record 1: Expected value "12", got <NULL>'
76
-
77
- @matcher.matches?(wrong_non_nil).should be_false
78
- @matcher.failure_message_for_should.should == 'Field "Field_2", Record 1: Expected value <NULL>, got "whatever"'
79
-
80
- @matcher.matches?(misplaced_fields).should be_false
81
- @matcher.failure_message_for_should.should == 'Expected field "Field 1", got field "Field_2"'
82
- end
83
-
84
- it 'correctly compares with a query result' do
85
- @matcher.matches?(@subject).should be_true(@matcher.failure_message_for_should)
86
- end
87
-
88
- it 'only accepts well formed query result subjects' do
89
- expected_message = /Can perform query matches only against a well formed query result subject/
90
- @matcher.matches?(1).should be_false
91
- @matcher.failure_message_for_should.should =~ expected_message
92
-
93
- @matcher.matches?('test string').should be_false
94
- @matcher.failure_message_for_should.should =~ expected_message
95
-
96
- @matcher.matches?(1.1).should be_false
97
- @matcher.failure_message_for_should.should =~ expected_message
98
-
99
- @matcher.matches?(Time.now).should be_false
100
- @matcher.failure_message_for_should.should =~ expected_message
101
-
102
- @matcher.matches?(nil).should be_false
103
- @matcher.failure_message_for_should.should =~ expected_message
104
-
105
- @matcher.matches?([{:f1 => 'x', :f2 => 'y'}, {:f1 => 'x'}]).should be_false
106
- @matcher.failure_message_for_should.should =~ expected_message
107
-
108
- @matcher.matches?([{:f1 => 'x', :f2 => 'y'}, {:f1 => 'x', :f3 => 'y'}]).should be_false
109
- @matcher.failure_message_for_should.should =~ expected_message
110
-
111
- @matcher.matches?([{:f1 => 'x', :f2 => 'y'}, {:f1 => 'x', :f2 => 'y', :f3 => 'z'}]).should be_false
112
- @matcher.failure_message_for_should.should =~ expected_message
113
-
114
- @matcher.matches?([{:f1 => 'x', :f2 => 'y'}, 12]).should be_false
115
- @matcher.failure_message_for_should.should =~ expected_message
116
-
117
- @matcher.matches?([12, {:f1 => 'x', :f2 => 'y'}]).should be_false
118
- @matcher.failure_message_for_should.should =~ expected_message
119
-
120
- # An array with a single empty row is not a well-formed query result
121
- @matcher.matches?([{}]).should be_false
122
- @matcher.failure_message_for_should.should =~ expected_message
123
- end
124
-
125
- end
126
-
127
- describe 'CSV-based golden master' do
128
- before(:each) do
129
- @matcher = Slacker::QueryResultMatcher.new(SpecHelper.load_csv('matcher/test_1.csv'))
130
- end
131
-
132
- it_behaves_like 'table-based matcher'
133
-
134
- it 'correctly compares an empty subject with an empty CSV file' do
135
- matcher = Slacker::QueryResultMatcher.new(SpecHelper.load_csv('matcher/no_rows.csv'))
136
- matcher.matches?([]).should be_true(matcher.failure_message_for_should)
137
-
138
- matcher = Slacker::QueryResultMatcher.new(SpecHelper.load_csv('matcher/completely_blank.csv'))
139
- matcher.matches?([]).should be_true(matcher.failure_message_for_should)
140
- end
141
- end
142
-
143
- describe 'Array-based golden master' do
144
- before(:each) do
145
- @matcher = Slacker::QueryResultMatcher.new(deep_copy(@subject))
146
- end
147
-
148
- it_behaves_like 'table-based matcher'
149
-
150
- it 'only accepts well formed query result golden master' do
151
- expected_message = /Cannot match against a non-well formed golden master array/
152
-
153
- matcher = Slacker::QueryResultMatcher.new([{:f1 => 'x', :f2 => 'y'}, {:f1 => 'x'}])
154
- matcher.matches?([]).should be_false
155
- matcher.failure_message_for_should.should =~ expected_message
156
-
157
- matcher = Slacker::QueryResultMatcher.new([{:f1 => 'x', :f2 => 'y'}, {:f1 => 'x', :f3 => 'y'}])
158
- matcher.matches?([]).should be_false
159
- matcher.failure_message_for_should.should =~ expected_message
160
-
161
- matcher = Slacker::QueryResultMatcher.new([{:f1 => 'x', :f2 => 'y'}, {:f1 => 'x', :f2 => 'y', :f3 => 'z'}])
162
- matcher.matches?([]).should be_false
163
- matcher.failure_message_for_should.should =~ expected_message
164
-
165
- matcher = Slacker::QueryResultMatcher.new([{:f1 => 'x', :f2 => 'y'}, 12])
166
- matcher.matches?([]).should be_false
167
- matcher.failure_message_for_should.should =~ expected_message
168
-
169
- matcher = Slacker::QueryResultMatcher.new([12, {:f1 => 'x', :f2 => 'y'}])
170
- matcher.matches?([]).should be_false
171
- matcher.failure_message_for_should.should =~ expected_message
172
- end
173
-
174
- it 'correctly compares an empty subject with an empty golden master' do
175
- matcher = Slacker::QueryResultMatcher.new([])
176
- matcher.matches?([]).should be_true(matcher.failure_message_for_should)
177
- end
178
- end
179
-
180
- shared_examples_for 'single-value-based matcher' do
181
- it 'should correctly match the first value in the result set' do
182
- matcher = Slacker::QueryResultMatcher.new(@correct_golden_master)
183
- matcher.matches?(@subject).should be_true(matcher.failure_message_for_should)
184
- end
185
-
186
- it 'should correctly reject a non-matching first item by value' do
187
- if !@wrong_value_golden_master.nil? #Skip nil from value check when the master is nil
188
- matcher = Slacker::QueryResultMatcher.new(@wrong_value_golden_master)
189
- matcher.matches?(@subject).should be_false
190
- matcher.failure_message_for_should.should =~ /Expected value "#{@wrong_value_golden_master}", got "#{@subject[0].values[0]}"/
191
- end
192
- end
193
-
194
- it 'should correctly reject a non-matching first item by type' do
195
- matcher = Slacker::QueryResultMatcher.new(@wrong_type_golden_master)
196
- matcher.matches?(@subject).should be_false
197
- matcher.failure_message_for_should.should =~ /Expected type "#{@wrong_type_golden_master.class}", got \"#{@subject[0].values[0].class}\"/
198
- end
199
- end
200
-
201
- describe 'Integer-based golden master' do
202
- before(:each) do
203
- @subject = [{'Field 1' => 14, 'Field_2' => 'whatever'}]
204
- @correct_golden_master = 14
205
- @wrong_value_golden_master = 15
206
- @wrong_type_golden_master = 'whatever'
207
- end
208
-
209
- it_behaves_like 'single-value-based matcher'
210
- end
211
-
212
- describe 'String-based golden master' do
213
- before(:each) do
214
- @subject = [{'Field 1' => 'test string', 'Field_2' => 12}]
215
- @correct_golden_master = 'test string'
216
- @wrong_value_golden_master = 'whatever'
217
- @wrong_type_golden_master = 15
218
- end
219
-
220
- it_behaves_like 'single-value-based matcher'
221
- end
222
-
223
- describe 'Floating point-based golden master' do
224
- before(:each) do
225
- @subject = [{'Field 1' => 18.2, 'Field_2' => 12}]
226
- @correct_golden_master = 18.2
227
- @wrong_value_golden_master = 18.7
228
- @wrong_type_golden_master = 15
229
- end
230
-
231
- it_behaves_like 'single-value-based matcher'
232
- end
233
-
234
- describe 'Date-based golden master' do
235
- before(:each) do
236
- @subject = [{'Field 1' => Time.parse('1/1/2011'), 'Field_2' => 12}]
237
- @correct_golden_master = Time.parse('1/1/2011')
238
- @wrong_value_golden_master = Time.parse('2/1/2011')
239
- @wrong_type_golden_master = 'whatever'
240
- end
241
-
242
- it_behaves_like 'single-value-based matcher'
243
- end
244
-
245
- describe 'Nil-based golden master' do
246
- before(:each) do
247
- @subject = [{'Field 1' => nil, 'Field_2' => 12}]
248
- @correct_golden_master = nil
249
- @wrong_value_golden_master = nil # There is no wrong value for nil-based master
250
- @wrong_type_golden_master = 'whatever'
251
- end
252
-
253
- it_behaves_like 'single-value-based matcher'
254
- end
255
-
256
- it 'correctly matches a multi-result subject' do
257
- subject = [@subject, [{:Field_x => 12}]]
258
- matcher = Slacker::QueryResultMatcher.new(deep_copy(@subject))
259
- matcher.matches?(subject).should be_true(matcher.failure_message_for_should)
260
- end
261
-
262
- it 'correctly rejects a wrong multi-result subject' do
263
- subject = [[{:Field_x => 12}], @subject]
264
- matcher = Slacker::QueryResultMatcher.new(deep_copy(@subject))
265
- matcher.matches?(subject).should be_false
266
- matcher.failure_message_for_should.should == 'Expected 3 field(s), got 1'
267
- end
268
- end
1
+ require 'slacker'
2
+ require 'spec_helper'
3
+ require 'time'
4
+ require 'odbc'
5
+
6
+ describe Slacker::QueryResultMatcher do
7
+ def deep_copy(obj)
8
+ Marshal.load(Marshal.dump(obj))
9
+ end
10
+
11
+ before(:each) do
12
+ @subject = [{'Field 1' => 12, 'Field_2' => nil, 'b' => ''},
13
+ {'Field 1' => 'test string', 'Field_2' => ODBC::TimeStamp.new('2011-01-30'), 'b' => 8.9}]
14
+ end
15
+
16
+ shared_examples_for 'table-based matcher' do
17
+ it 'correctly rejects a non-matching query result based on wrong columns' do
18
+ one_column_too_few = deep_copy(@subject).each{|row| row.delete('b')}
19
+ one_column_too_many = deep_copy(@subject).each{|row| row['new column'] = 'val 1'}
20
+ wrong_column_name = deep_copy(@subject).each{|row| row.delete('Field_2'); row['Field_x'] = 'val x'}
21
+
22
+ @matcher.matches?(one_column_too_few).should be false
23
+ @matcher.failure_message.should == 'Expected 3 field(s), got 2'
24
+
25
+ @matcher.matches?(one_column_too_many).should be false
26
+ @matcher.failure_message.should == 'Expected 3 field(s), got 4'
27
+
28
+ @matcher.matches?(wrong_column_name).should be false
29
+ @matcher.failure_message.should == 'Expected field "Field_2", got field "b"'
30
+ end
31
+
32
+ it 'correctly rejects a non-matching query result based on number of records' do
33
+ one_row_too_few = deep_copy(@subject)
34
+ one_row_too_few.shift
35
+ one_row_too_many = deep_copy(@subject) << @subject[0]
36
+ empty_query_result = []
37
+
38
+ @matcher.matches?(one_row_too_few).should be false
39
+ @matcher.failure_message.should == 'Expected 2 record(s), got 1'
40
+
41
+ @matcher.matches?(one_row_too_many).should be false
42
+ @matcher.failure_message.should == 'Expected 2 record(s), got 3'
43
+
44
+ @matcher.matches?(empty_query_result).should be false
45
+ @matcher.failure_message.should == 'Expected 2 record(s), got 0'
46
+ end
47
+
48
+ it 'correctly rejects a non-matching query result based on a value' do
49
+ wrong_int = deep_copy(@subject)
50
+ wrong_int[0]['Field 1'] = 14
51
+
52
+ wrong_int_type = deep_copy(@subject)
53
+ wrong_int_type[1]['Field 1'] = 14
54
+
55
+ wrong_nil = deep_copy(@subject)
56
+ wrong_nil[0]['Field 1'] = nil
57
+
58
+ wrong_non_nil = deep_copy(@subject)
59
+ wrong_non_nil[0]['Field_2'] = 'whatever'
60
+
61
+ misplaced_fields = deep_copy(@subject)
62
+ misplaced_fields[0].delete('Field 1')
63
+ misplaced_fields[1].delete('Field 1')
64
+ misplaced_fields[0]['Field 1'] = 12
65
+ misplaced_fields[1]['Field 1'] = 'test string'
66
+
67
+ @matcher.matches?(wrong_int).should be false
68
+ @matcher.failure_message.should == 'Field "Field 1", Record 1: Expected value "12", got "14"'
69
+
70
+
71
+ @matcher.matches?(wrong_int_type).should be false
72
+ @matcher.failure_message.should == 'Field "Field 1", Record 2: Expected value "test string", got "14"'
73
+
74
+ @matcher.matches?(wrong_nil).should be false
75
+ @matcher.failure_message.should == 'Field "Field 1", Record 1: Expected value "12", got <NULL>'
76
+
77
+ @matcher.matches?(wrong_non_nil).should be false
78
+ @matcher.failure_message.should == 'Field "Field_2", Record 1: Expected value <NULL>, got "whatever"'
79
+
80
+ @matcher.matches?(misplaced_fields).should be false
81
+ @matcher.failure_message.should == 'Expected field "Field 1", got field "Field_2"'
82
+ end
83
+
84
+ it 'correctly compares with a query result' do
85
+ @matcher.matches?(@subject).should be true
86
+ end
87
+
88
+ it 'only accepts well formed query result subjects' do
89
+ expected_message = /Can perform query matches only against a well formed query result subject/
90
+ @matcher.matches?(1).should be false
91
+ @matcher.failure_message.should =~ expected_message
92
+
93
+ @matcher.matches?('test string').should be false
94
+ @matcher.failure_message.should =~ expected_message
95
+
96
+ @matcher.matches?(1.1).should be false
97
+ @matcher.failure_message.should =~ expected_message
98
+
99
+ @matcher.matches?(Time.now).should be false
100
+ @matcher.failure_message.should =~ expected_message
101
+
102
+ @matcher.matches?(nil).should be false
103
+ @matcher.failure_message.should =~ expected_message
104
+
105
+ @matcher.matches?([{:f1 => 'x', :f2 => 'y'}, {:f1 => 'x'}]).should be false
106
+ @matcher.failure_message.should =~ expected_message
107
+
108
+ @matcher.matches?([{:f1 => 'x', :f2 => 'y'}, {:f1 => 'x', :f3 => 'y'}]).should be false
109
+ @matcher.failure_message.should =~ expected_message
110
+
111
+ @matcher.matches?([{:f1 => 'x', :f2 => 'y'}, {:f1 => 'x', :f2 => 'y', :f3 => 'z'}]).should be false
112
+ @matcher.failure_message.should =~ expected_message
113
+
114
+ @matcher.matches?([{:f1 => 'x', :f2 => 'y'}, 12]).should be false
115
+ @matcher.failure_message.should =~ expected_message
116
+
117
+ @matcher.matches?([12, {:f1 => 'x', :f2 => 'y'}]).should be false
118
+ @matcher.failure_message.should =~ expected_message
119
+
120
+ # An array with a single empty row is not a well-formed query result
121
+ @matcher.matches?([{}]).should be false
122
+ @matcher.failure_message.should =~ expected_message
123
+ end
124
+
125
+ end
126
+
127
+ describe 'CSV-based golden master' do
128
+ before(:each) do
129
+ @matcher = Slacker::QueryResultMatcher.new(SpecHelper.load_csv('matcher/test_1.csv'))
130
+ end
131
+
132
+ it_behaves_like 'table-based matcher'
133
+
134
+ it 'correctly compares an empty subject with an empty CSV file' do
135
+ matcher = Slacker::QueryResultMatcher.new(SpecHelper.load_csv('matcher/no_rows.csv'))
136
+ matcher.matches?([]).should be true
137
+
138
+ matcher = Slacker::QueryResultMatcher.new(SpecHelper.load_csv('matcher/completely_blank.csv'))
139
+ matcher.matches?([]).should be true
140
+ end
141
+ end
142
+
143
+ describe 'Array-based golden master' do
144
+ before(:each) do
145
+ @matcher = Slacker::QueryResultMatcher.new(deep_copy(@subject))
146
+ end
147
+
148
+ it_behaves_like 'table-based matcher'
149
+
150
+ it 'only accepts well formed query result golden master' do
151
+ expected_message = /Cannot match against a non-well formed golden master array/
152
+
153
+ matcher = Slacker::QueryResultMatcher.new([{:f1 => 'x', :f2 => 'y'}, {:f1 => 'x'}])
154
+ matcher.matches?([]).should be false
155
+ matcher.failure_message.should =~ expected_message
156
+
157
+ matcher = Slacker::QueryResultMatcher.new([{:f1 => 'x', :f2 => 'y'}, {:f1 => 'x', :f3 => 'y'}])
158
+ matcher.matches?([]).should be false
159
+ matcher.failure_message.should =~ expected_message
160
+
161
+ matcher = Slacker::QueryResultMatcher.new([{:f1 => 'x', :f2 => 'y'}, {:f1 => 'x', :f2 => 'y', :f3 => 'z'}])
162
+ matcher.matches?([]).should be false
163
+ matcher.failure_message.should =~ expected_message
164
+
165
+ matcher = Slacker::QueryResultMatcher.new([{:f1 => 'x', :f2 => 'y'}, 12])
166
+ matcher.matches?([]).should be false
167
+ matcher.failure_message.should =~ expected_message
168
+
169
+ matcher = Slacker::QueryResultMatcher.new([12, {:f1 => 'x', :f2 => 'y'}])
170
+ matcher.matches?([]).should be false
171
+ matcher.failure_message.should =~ expected_message
172
+ end
173
+
174
+ it 'correctly compares an empty subject with an empty golden master' do
175
+ matcher = Slacker::QueryResultMatcher.new([])
176
+ matcher.matches?([]).should be true
177
+ end
178
+ end
179
+
180
+ shared_examples_for 'single-value-based matcher' do
181
+ it 'should correctly match the first value in the result set' do
182
+ matcher = Slacker::QueryResultMatcher.new(@correct_golden_master)
183
+ matcher.matches?(@subject).should be true
184
+ end
185
+
186
+ it 'should correctly reject a non-matching first item by value' do
187
+ if !@wrong_value_golden_master.nil? #Skip nil from value check when the master is nil
188
+ matcher = Slacker::QueryResultMatcher.new(@wrong_value_golden_master)
189
+ matcher.matches?(@subject).should be false
190
+ matcher.failure_message.should =~ /Expected value "#{@wrong_value_golden_master}", got "#{@subject[0].values[0]}"/
191
+ end
192
+ end
193
+
194
+ it 'should correctly reject a non-matching first item by type' do
195
+ matcher = Slacker::QueryResultMatcher.new(@wrong_type_golden_master)
196
+ matcher.matches?(@subject).should be false
197
+ matcher.failure_message.should =~ /Expected type "#{@wrong_type_golden_master.class}", got \"#{@subject[0].values[0].class}\"/
198
+ end
199
+ end
200
+
201
+ describe 'Integer-based golden master' do
202
+ before(:each) do
203
+ @subject = [{'Field 1' => 14, 'Field_2' => 'whatever'}]
204
+ @correct_golden_master = 14
205
+ @wrong_value_golden_master = 15
206
+ @wrong_type_golden_master = 'whatever'
207
+ end
208
+
209
+ it_behaves_like 'single-value-based matcher'
210
+ end
211
+
212
+ describe 'String-based golden master' do
213
+ before(:each) do
214
+ @subject = [{'Field 1' => 'test string', 'Field_2' => 12}]
215
+ @correct_golden_master = 'test string'
216
+ @wrong_value_golden_master = 'whatever'
217
+ @wrong_type_golden_master = 15
218
+ end
219
+
220
+ it_behaves_like 'single-value-based matcher'
221
+ end
222
+
223
+ describe 'Floating point-based golden master' do
224
+ before(:each) do
225
+ @subject = [{'Field 1' => 18.2, 'Field_2' => 12}]
226
+ @correct_golden_master = 18.2
227
+ @wrong_value_golden_master = 18.7
228
+ @wrong_type_golden_master = 15
229
+ end
230
+
231
+ it_behaves_like 'single-value-based matcher'
232
+ end
233
+
234
+ describe 'Date-based golden master' do
235
+ before(:each) do
236
+ @subject = [{'Field 1' => Time.parse('1/1/2011'), 'Field_2' => 12}]
237
+ @correct_golden_master = Time.parse('1/1/2011')
238
+ @wrong_value_golden_master = Time.parse('2/1/2011')
239
+ @wrong_type_golden_master = 'whatever'
240
+ end
241
+
242
+ it_behaves_like 'single-value-based matcher'
243
+ end
244
+
245
+ describe 'Nil-based golden master' do
246
+ before(:each) do
247
+ @subject = [{'Field 1' => nil, 'Field_2' => 12}]
248
+ @correct_golden_master = nil
249
+ @wrong_value_golden_master = nil # There is no wrong value for nil-based master
250
+ @wrong_type_golden_master = 'whatever'
251
+ end
252
+
253
+ it_behaves_like 'single-value-based matcher'
254
+ end
255
+
256
+ it 'correctly matches a multi-result subject' do
257
+ subject = [@subject, [{:Field_x => 12}]]
258
+ matcher = Slacker::QueryResultMatcher.new(deep_copy(@subject))
259
+ matcher.matches?(subject).should be true
260
+ end
261
+
262
+ it 'correctly rejects a wrong multi-result subject' do
263
+ subject = [[{:Field_x => 12}], @subject]
264
+ matcher = Slacker::QueryResultMatcher.new(deep_copy(@subject))
265
+ matcher.matches?(subject).should be false
266
+ matcher.failure_message.should == 'Expected 3 field(s), got 1'
267
+ end
268
+ end