slacker 1.0.7 → 1.0.8

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.
@@ -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