slacker 1.0.7 → 1.0.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,49 +1,49 @@
1
- require 'slacker'
2
- require 'slacker/query_result_matcher'
3
- require 'csv'
4
-
5
- module Slacker
6
- module RSpecExt
7
- def query(query_string, options = {}, log_name = nil)
8
- log_name ||= Slacker.construct_log_name('query', query_string, options)
9
- sql = Slacker.sql_from_query_string(query_string, options)
10
- @results = Slacker.query_script(example, sql, log_name)
11
- if block_given?
12
- yield @results
13
- end
14
- @results
15
- end
16
-
17
- def sql
18
- Slacker.sql(self)
19
- end
20
-
21
- # Get a matcher which will compare the query results to a golden master
22
- def match(golden_master)
23
- QueryResultMatcher.new(Slacker.filter_golden_master(golden_master))
24
- end
25
-
26
- def csv(csv_file)
27
- Slacker.get_csv(csv_file)
28
- end
29
-
30
- def touch_csv(csv_file_or_object, fields, field_generators = {})
31
- Slacker.touch_csv(csv_file_or_object, fields, field_generators)
32
- end
33
-
34
- def load_csv(csv_file_or_object, table_name, log_name = nil)
35
- log_name ||= "load_csv '#{csv_file_or_object.kind_of?(CSV::Table) ? 'CSV Object' : csv_file_or_object }', 'table_name'"
36
- csv_object = case csv_file_or_object
37
- when String then Slacker.get_csv(csv_file_or_object)
38
- when CSV::Table then csv_file_or_object
39
- when Array then Slacker.hash_array_to_csv(csv_file_or_object)
40
- end
41
-
42
- Slacker.load_csv(example, csv_object, table_name, log_name)
43
- end
44
-
45
- def yes?(val)
46
- val != nil && val.downcase == 'yes'
47
- end
48
- end
49
- end
1
+ require 'slacker'
2
+ require 'slacker/query_result_matcher'
3
+ require 'csv'
4
+
5
+ module Slacker
6
+ module RSpecExt
7
+ def query(query_string, options = {}, log_name = nil)
8
+ log_name ||= Slacker.construct_log_name('query', query_string, options)
9
+ sql = Slacker.sql_from_query_string(query_string, options)
10
+ @results = Slacker.query_script(example, sql, log_name)
11
+ if block_given?
12
+ yield @results
13
+ end
14
+ @results
15
+ end
16
+
17
+ def sql
18
+ Slacker.sql(self)
19
+ end
20
+
21
+ # Get a matcher which will compare the query results to a golden master
22
+ def match(golden_master)
23
+ QueryResultMatcher.new(Slacker.filter_golden_master(golden_master))
24
+ end
25
+
26
+ def csv(csv_file)
27
+ Slacker.get_csv(csv_file)
28
+ end
29
+
30
+ def touch_csv(csv_file_or_object, fields, field_generators = {})
31
+ Slacker.touch_csv(csv_file_or_object, fields, field_generators)
32
+ end
33
+
34
+ def load_csv(csv_file_or_object, table_name, log_name = nil)
35
+ log_name ||= "load_csv '#{csv_file_or_object.kind_of?(CSV::Table) ? 'CSV Object' : csv_file_or_object }', 'table_name'"
36
+ csv_object = case csv_file_or_object
37
+ when String then Slacker.get_csv(csv_file_or_object)
38
+ when CSV::Table then csv_file_or_object
39
+ when Array then Slacker.hash_array_to_csv(csv_file_or_object)
40
+ end
41
+
42
+ Slacker.load_csv(example, csv_object, table_name, log_name)
43
+ end
44
+
45
+ def yes?(val)
46
+ val != nil && val.downcase == 'yes'
47
+ end
48
+ end
49
+ end
@@ -1,7 +1,7 @@
1
- # Monkeypatch a method to reset RSpec to reset it
2
- module RSpec
3
- def self.slacker_reset
4
- @world = nil
5
- @configuration = nil
6
- end
1
+ # Monkeypatch a method to reset RSpec to reset it
2
+ module RSpec
3
+ def self.slacker_reset
4
+ @world = nil
5
+ @configuration = nil
6
+ end
7
7
  end
@@ -1,39 +1,39 @@
1
- require 'slacker'
2
- require 'slacker/rspec_ext'
3
-
4
- module Slacker
5
- class Sql < BasicObject
6
- attr_accessor :base_folder, :rspec_ext
7
-
8
- def initialize(base_folder, rspec_ext)
9
- @base_folder = base_folder
10
- @rspec_ext = rspec_ext
11
- end
12
-
13
- def method_missing(method_name, *params, &block)
14
- ::Kernel.raise "Slacker::Sql.rspec_ext not initialized" if rspec_ext.nil?
15
- ::Kernel.raise "Missing folder #{base_folder}" if !::File.directory?(base_folder)
16
-
17
- method_name = method_name.to_s
18
-
19
- if ::File.directory?(::File.join(base_folder, method_name))
20
- ::Slacker::Sql.new(::File.join(base_folder, method_name), rspec_ext)
21
- else
22
- sql_file = ::Slacker.sql_file_from_method_name(base_folder, method_name)
23
- case sql_file
24
- when nil
25
- ::Kernel.raise "No SQL file found corresponding to method '#{method_name}' in folder #{base_folder}"
26
- else
27
- rspec_ext.query sql_file, *params, &block
28
- end
29
- end
30
- end
31
-
32
- def respond_to?(method_name)
33
- method_name = method_name.to_s
34
- ::Kernel.raise "Slacker::Sql.rspec_ext not initialized" if rspec_ext.nil?
35
- ::File.directory?(::File.join(base_folder, method_name)) ||
36
- !(::Slacker.sql_file_from_method_name(base_folder, method_name).nil?)
37
- end
38
- end
39
- end
1
+ require 'slacker'
2
+ require 'slacker/rspec_ext'
3
+
4
+ module Slacker
5
+ class Sql < BasicObject
6
+ attr_accessor :base_folder, :rspec_ext
7
+
8
+ def initialize(base_folder, rspec_ext)
9
+ @base_folder = base_folder
10
+ @rspec_ext = rspec_ext
11
+ end
12
+
13
+ def method_missing(method_name, *params, &block)
14
+ ::Kernel.raise "Slacker::Sql.rspec_ext not initialized" if rspec_ext.nil?
15
+ ::Kernel.raise "Missing folder #{base_folder}" if !::File.directory?(base_folder)
16
+
17
+ method_name = method_name.to_s
18
+
19
+ if ::File.directory?(::File.join(base_folder, method_name))
20
+ ::Slacker::Sql.new(::File.join(base_folder, method_name), rspec_ext)
21
+ else
22
+ sql_file = ::Slacker.sql_file_from_method_name(base_folder, method_name)
23
+ case sql_file
24
+ when nil
25
+ ::Kernel.raise "No SQL file found corresponding to method '#{method_name}' in folder #{base_folder}"
26
+ else
27
+ rspec_ext.query sql_file, *params, &block
28
+ end
29
+ end
30
+ end
31
+
32
+ def respond_to?(method_name)
33
+ method_name = method_name.to_s
34
+ ::Kernel.raise "Slacker::Sql.rspec_ext not initialized" if rspec_ext.nil?
35
+ ::File.directory?(::File.join(base_folder, method_name)) ||
36
+ !(::Slacker.sql_file_from_method_name(base_folder, method_name).nil?)
37
+ end
38
+ end
39
+ end
@@ -1,17 +1,17 @@
1
- module Slacker
2
- module StringHelper
3
- class << self
4
- def camelize(lower_case_and_underscored_word)
5
- lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
6
- end
7
-
8
- def constantize(camel_cased_word)
9
- unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word
10
- raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!"
11
- end
12
-
13
- Object.module_eval("::#{$1}", __FILE__, __LINE__)
14
- end
15
- end
16
- end
1
+ module Slacker
2
+ module StringHelper
3
+ class << self
4
+ def camelize(lower_case_and_underscored_word)
5
+ lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
6
+ end
7
+
8
+ def constantize(camel_cased_word)
9
+ unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word
10
+ raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!"
11
+ end
12
+
13
+ Object.module_eval("::#{$1}", __FILE__, __LINE__)
14
+ end
15
+ end
16
+ end
17
17
  end
@@ -1,3 +1,3 @@
1
1
  module Slacker
2
- VERSION = "1.0.7"
2
+ VERSION = "1.0.8"
3
3
  end
@@ -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;
@@ -13,14 +13,14 @@ Gem::Specification.new do |s|
13
13
 
14
14
  s.rubyforge_project = "slacker"
15
15
 
16
- s.files = ['README.markdown', 'Rakefile', 'Gemfile', 'slacker.gemspec', 'Gemfile.lock'] + Dir.glob("{bin,lib,spec}/**/*")
16
+ s.files = ['README.markdown', 'Rakefile', 'Gemfile', 'slacker.gemspec'] + Dir.glob("{bin,lib,spec}/**/*")
17
17
  s.test_files = Dir.glob("spec/**/*")
18
18
  s.executables = ['slacker', 'slacker_new']
19
19
  s.require_paths = ["lib"]
20
20
 
21
21
  s.required_ruby_version = '>= 1.9.2'
22
22
 
23
- s.add_dependency 'bundler', '~> 1.0.15'
23
+ s.add_dependency 'bundler', '>= 1.0.15'
24
24
  s.add_dependency 'ruby-odbc', '= 0.99994'
25
- s.add_dependency 'rspec', '~> 2.5.0'
25
+ s.add_dependency 'rspec', '>= 2.5.0'
26
26
  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', '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_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