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.
- data/Gemfile +4 -4
- data/README.markdown +22 -22
- data/Rakefile +11 -11
- data/lib/slacker.rb +175 -175
- data/lib/slacker/application.rb +206 -206
- data/lib/slacker/command_line_formatter.rb +59 -59
- data/lib/slacker/configuration.rb +59 -59
- data/lib/slacker/formatter.rb +19 -19
- data/lib/slacker/query_result_matcher.rb +178 -178
- data/lib/slacker/rspec_ext.rb +49 -49
- data/lib/slacker/rspec_monkey.rb +6 -6
- data/lib/slacker/sql.rb +39 -39
- data/lib/slacker/string_helper.rb +16 -16
- data/lib/slacker/version.rb +1 -1
- data/lib/slacker_new/project/debug/failed_examples/example_001.sql +0 -0
- data/lib/slacker_new/project/debug/passed_examples/example_001.sql +0 -0
- data/lib/slacker_new/project/sql/sample_1/my_table_on_power.sql.erb +1 -1
- data/lib/slacker_new/project/sql/sample_1/play_with_numbers.sql.erb +16 -16
- data/slacker.gemspec +3 -3
- data/spec/application_spec.rb +13 -13
- data/spec/query_result_matcher_spec.rb +268 -268
- data/spec/slacker_spec.rb +59 -59
- data/spec/spec_helper.rb +9 -9
- data/spec/test_files/test_slacker_project/sql/nest/nested_1.sql.erb +1 -1
- data/spec/test_files/test_slacker_project/sql/params.sql.erb +1 -1
- metadata +8 -7
- data/Gemfile.lock +0 -28
data/lib/slacker/rspec_ext.rb
CHANGED
@@ -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
|
data/lib/slacker/rspec_monkey.rb
CHANGED
@@ -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
|
data/lib/slacker/sql.rb
CHANGED
@@ -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
|
data/lib/slacker/version.rb
CHANGED
File without changes
|
File without changes
|
@@ -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;
|
data/slacker.gemspec
CHANGED
@@ -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'
|
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', '
|
23
|
+
s.add_dependency 'bundler', '>= 1.0.15'
|
24
24
|
s.add_dependency 'ruby-odbc', '= 0.99994'
|
25
|
-
s.add_dependency 'rspec', '
|
25
|
+
s.add_dependency 'rspec', '>= 2.5.0'
|
26
26
|
end
|
data/spec/application_spec.rb
CHANGED
@@ -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
|