slacker 1.0.7 → 1.0.8
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -1,59 +1,59 @@
|
|
1
|
-
require 'slacker/formatter'
|
2
|
-
require 'rspec/core/formatters/progress_formatter'
|
3
|
-
|
4
|
-
module Slacker
|
5
|
-
class CommandLineFormatter < RSpec::Core::Formatters::ProgressFormatter
|
6
|
-
include Slacker::Formatter
|
7
|
-
|
8
|
-
def initialize(output)
|
9
|
-
super(output)
|
10
|
-
@failed_examples_count = 0
|
11
|
-
@passed_examples_count = 0
|
12
|
-
end
|
13
|
-
|
14
|
-
def example_passed(example)
|
15
|
-
process_example_debug_output(example, false)
|
16
|
-
super(example)
|
17
|
-
end
|
18
|
-
|
19
|
-
def example_failed(example)
|
20
|
-
process_example_debug_output(example, true)
|
21
|
-
super(example)
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
def process_example_debug_output(example, example_failed)
|
27
|
-
if example_failed
|
28
|
-
@failed_examples_count += 1
|
29
|
-
debug_output(example, Slacker.configuration.expand_path('debug/failed_examples'), @failed_examples_count, example_failed)
|
30
|
-
else
|
31
|
-
@passed_examples_count += 1
|
32
|
-
debug_output(example, Slacker.configuration.expand_path('debug/passed_examples'), @passed_examples_count, example_failed)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def debug_output(example, out_folder, file_number, example_failed)
|
37
|
-
# Write out the SQL
|
38
|
-
File.open("#{out_folder}/example_#{'%03d' % file_number}.sql", 'w') do |out_file|
|
39
|
-
out_file.write(get_formatted_example_sql(example, example_failed))
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def get_formatted_example_sql(example, example_failed)
|
44
|
-
sql = <<EOF
|
45
|
-
-- Example "#{example.metadata[:full_description]}"
|
46
|
-
-- #{example.metadata[:location]}
|
47
|
-
-- Executed at #{example.metadata[:execution_result][:started_at]}
|
48
|
-
|
49
|
-
#{example.metadata[:sql]}
|
50
|
-
|
51
|
-
-- SLACKER RESULTS
|
52
|
-
-- *******************************************
|
53
|
-
#{example_failed ? example_failure_text(example).split("\n").collect{|line| '-- ' + line}.join("\n") : '-- Example Passed OK'}
|
54
|
-
-- *******************************************
|
55
|
-
EOF
|
56
|
-
sql.strip
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
1
|
+
require 'slacker/formatter'
|
2
|
+
require 'rspec/core/formatters/progress_formatter'
|
3
|
+
|
4
|
+
module Slacker
|
5
|
+
class CommandLineFormatter < RSpec::Core::Formatters::ProgressFormatter
|
6
|
+
include Slacker::Formatter
|
7
|
+
|
8
|
+
def initialize(output)
|
9
|
+
super(output)
|
10
|
+
@failed_examples_count = 0
|
11
|
+
@passed_examples_count = 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def example_passed(example)
|
15
|
+
process_example_debug_output(example, false)
|
16
|
+
super(example)
|
17
|
+
end
|
18
|
+
|
19
|
+
def example_failed(example)
|
20
|
+
process_example_debug_output(example, true)
|
21
|
+
super(example)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def process_example_debug_output(example, example_failed)
|
27
|
+
if example_failed
|
28
|
+
@failed_examples_count += 1
|
29
|
+
debug_output(example, Slacker.configuration.expand_path('debug/failed_examples'), @failed_examples_count, example_failed)
|
30
|
+
else
|
31
|
+
@passed_examples_count += 1
|
32
|
+
debug_output(example, Slacker.configuration.expand_path('debug/passed_examples'), @passed_examples_count, example_failed)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def debug_output(example, out_folder, file_number, example_failed)
|
37
|
+
# Write out the SQL
|
38
|
+
File.open("#{out_folder}/example_#{'%03d' % file_number}.sql", 'w') do |out_file|
|
39
|
+
out_file.write(get_formatted_example_sql(example, example_failed))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_formatted_example_sql(example, example_failed)
|
44
|
+
sql = <<EOF
|
45
|
+
-- Example "#{example.metadata[:full_description]}"
|
46
|
+
-- #{example.metadata[:location]}
|
47
|
+
-- Executed at #{example.metadata[:execution_result][:started_at]}
|
48
|
+
|
49
|
+
#{example.metadata[:sql]}
|
50
|
+
|
51
|
+
-- SLACKER RESULTS
|
52
|
+
-- *******************************************
|
53
|
+
#{example_failed ? example_failure_text(example).split("\n").collect{|line| '-- ' + line}.join("\n") : '-- Example Passed OK'}
|
54
|
+
-- *******************************************
|
55
|
+
EOF
|
56
|
+
sql.strip
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -1,59 +1,59 @@
|
|
1
|
-
module Slacker
|
2
|
-
class Configuration
|
3
|
-
attr_accessor :base_dir, :error_stream, :output_stream, :formatter, :db_server, :db_name, :db_user, :db_password, :console_enabled
|
4
|
-
|
5
|
-
def initialize
|
6
|
-
@console_enabled = true
|
7
|
-
@base_dir = Dir.pwd
|
8
|
-
@error_stream = nil
|
9
|
-
@output_stream = nil
|
10
|
-
@rspec_args = nil
|
11
|
-
@formatter = nil
|
12
|
-
@db_server = nil
|
13
|
-
@db_name = nil
|
14
|
-
@db_user = nil
|
15
|
-
@db_password = nil
|
16
|
-
end
|
17
|
-
|
18
|
-
def expand_path(path)
|
19
|
-
File.expand_path("#{@base_dir}/#{path}")
|
20
|
-
end
|
21
|
-
|
22
|
-
def console_enabled
|
23
|
-
@console_enabled
|
24
|
-
end
|
25
|
-
|
26
|
-
def console_enabled=(value)
|
27
|
-
@console_enabled = value
|
28
|
-
if @console_enabled
|
29
|
-
@error_stream = $stderr
|
30
|
-
@output_stream = $stdout
|
31
|
-
@rspec_args = ARGV
|
32
|
-
else
|
33
|
-
@error_stream = nil
|
34
|
-
@output_stream = nil
|
35
|
-
@rspec_args = []
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def rspec_args
|
40
|
-
if @rspec_args.nil? || @rspec_args.empty?
|
41
|
-
Dir.glob(expand_path("spec/**/*.rb"))
|
42
|
-
else
|
43
|
-
@rspec_args
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def rspec_args=(value)
|
48
|
-
@rspec_args = value
|
49
|
-
end
|
50
|
-
|
51
|
-
def dsn_string
|
52
|
-
"Driver={SQL Server};Server=#{@db_server};Database=#{@db_name};Uid=#{@db_user};Pwd=#{@db_password}"
|
53
|
-
end
|
54
|
-
|
55
|
-
def db_config
|
56
|
-
{:adapter => 'sqlserver', :mode => 'odbc', :dsn => dsn_string}
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
1
|
+
module Slacker
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :base_dir, :error_stream, :output_stream, :formatter, :db_server, :db_name, :db_user, :db_password, :console_enabled
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@console_enabled = true
|
7
|
+
@base_dir = Dir.pwd
|
8
|
+
@error_stream = nil
|
9
|
+
@output_stream = nil
|
10
|
+
@rspec_args = nil
|
11
|
+
@formatter = nil
|
12
|
+
@db_server = nil
|
13
|
+
@db_name = nil
|
14
|
+
@db_user = nil
|
15
|
+
@db_password = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def expand_path(path)
|
19
|
+
File.expand_path("#{@base_dir}/#{path}")
|
20
|
+
end
|
21
|
+
|
22
|
+
def console_enabled
|
23
|
+
@console_enabled
|
24
|
+
end
|
25
|
+
|
26
|
+
def console_enabled=(value)
|
27
|
+
@console_enabled = value
|
28
|
+
if @console_enabled
|
29
|
+
@error_stream = $stderr
|
30
|
+
@output_stream = $stdout
|
31
|
+
@rspec_args = ARGV
|
32
|
+
else
|
33
|
+
@error_stream = nil
|
34
|
+
@output_stream = nil
|
35
|
+
@rspec_args = []
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def rspec_args
|
40
|
+
if @rspec_args.nil? || @rspec_args.empty?
|
41
|
+
Dir.glob(expand_path("spec/**/*.rb"))
|
42
|
+
else
|
43
|
+
@rspec_args
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def rspec_args=(value)
|
48
|
+
@rspec_args = value
|
49
|
+
end
|
50
|
+
|
51
|
+
def dsn_string
|
52
|
+
"Driver={SQL Server};Server=#{@db_server};Database=#{@db_name};Uid=#{@db_user};Pwd=#{@db_password}"
|
53
|
+
end
|
54
|
+
|
55
|
+
def db_config
|
56
|
+
{:adapter => 'sqlserver', :mode => 'odbc', :dsn => dsn_string}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/slacker/formatter.rb
CHANGED
@@ -1,19 +1,19 @@
|
|
1
|
-
require 'slacker'
|
2
|
-
|
3
|
-
module Slacker
|
4
|
-
module Formatter
|
5
|
-
def example_failure_text(example)
|
6
|
-
text = ''
|
7
|
-
exception = example.execution_result[:exception]
|
8
|
-
text << "Failure/Error: #{read_failed_line(exception, example).strip}\n"
|
9
|
-
text << "#{long_padding}#{exception.class.name << ":"}\n" unless exception.class.name =~ /RSpec/
|
10
|
-
exception.message.split("\n").each { |line| text << "#{line}\n" }
|
11
|
-
|
12
|
-
format_backtrace(example.execution_result[:exception].backtrace, example).each do |backtrace_info|
|
13
|
-
text << "# #{backtrace_info}\n"
|
14
|
-
end
|
15
|
-
|
16
|
-
text
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
1
|
+
require 'slacker'
|
2
|
+
|
3
|
+
module Slacker
|
4
|
+
module Formatter
|
5
|
+
def example_failure_text(example)
|
6
|
+
text = ''
|
7
|
+
exception = example.execution_result[:exception]
|
8
|
+
text << "Failure/Error: #{read_failed_line(exception, example).strip}\n"
|
9
|
+
text << "#{long_padding}#{exception.class.name << ":"}\n" unless exception.class.name =~ /RSpec/
|
10
|
+
exception.message.split("\n").each { |line| text << "#{line}\n" }
|
11
|
+
|
12
|
+
format_backtrace(example.execution_result[:exception].backtrace, example).each do |backtrace_info|
|
13
|
+
text << "# #{backtrace_info}\n"
|
14
|
+
end
|
15
|
+
|
16
|
+
text
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -1,178 +1,178 @@
|
|
1
|
-
require 'csv'
|
2
|
-
|
3
|
-
module Slacker
|
4
|
-
DATE_FORMAT = "%m/%d/%Y"
|
5
|
-
|
6
|
-
class QueryResultMatcher
|
7
|
-
def initialize(golden_master)
|
8
|
-
@golden_master = golden_master
|
9
|
-
@failure_message = ''
|
10
|
-
end
|
11
|
-
|
12
|
-
def matches?(subject)
|
13
|
-
does_match = false
|
14
|
-
|
15
|
-
subject = normalize_query_result_subject(subject)
|
16
|
-
|
17
|
-
catch :no_match do
|
18
|
-
test_type_match(subject)
|
19
|
-
test_value_match(subject)
|
20
|
-
does_match = true
|
21
|
-
end
|
22
|
-
|
23
|
-
does_match
|
24
|
-
end
|
25
|
-
|
26
|
-
def failure_message_for_should
|
27
|
-
@failure_message
|
28
|
-
end
|
29
|
-
|
30
|
-
private
|
31
|
-
|
32
|
-
#test if the subject type is consistent with the golden master
|
33
|
-
def test_type_match(subject)
|
34
|
-
if !is_well_formed_query_result?(subject)
|
35
|
-
throw_no_match "Can perform query matches only against a well formed query result subject"
|
36
|
-
end
|
37
|
-
|
38
|
-
if (@golden_master.kind_of? Array) && !is_well_formed_query_result?(@golden_master)
|
39
|
-
throw_no_match "Cannot match against a non-well formed golden master array"
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def test_value_match(subject)
|
44
|
-
case @golden_master
|
45
|
-
when CSV::Table
|
46
|
-
test_csv_table_match(subject)
|
47
|
-
when Array
|
48
|
-
test_array_match(subject)
|
49
|
-
else
|
50
|
-
test_single_value_match(subject)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
# Compare the golden master CSV table with the subject query result
|
55
|
-
def test_csv_table_match(subject)
|
56
|
-
# Compare the fields
|
57
|
-
if !subject.empty?
|
58
|
-
subject_fields = subject[0].keys
|
59
|
-
master_fields = @golden_master.headers
|
60
|
-
|
61
|
-
if subject_fields.count != master_fields.count
|
62
|
-
throw_no_match "Expected #{master_fields.count} field(s), got #{subject_fields.count}"
|
63
|
-
end
|
64
|
-
|
65
|
-
master_fields.each_with_index do |column, index|
|
66
|
-
if column != subject_fields[index]
|
67
|
-
throw_no_match "Expected field \"#{column}\", got field \"#{subject_fields[index]}\""
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
# Compare the number of records
|
73
|
-
subject_record_count = subject.count
|
74
|
-
master_record_count = @golden_master.inject(0){|count| count += 1}
|
75
|
-
if subject_record_count != master_record_count
|
76
|
-
throw_no_match "Expected #{master_record_count} record(s), got #{subject_record_count}"
|
77
|
-
end
|
78
|
-
|
79
|
-
# Compare the values of the golden master with the subject
|
80
|
-
current_row = 0
|
81
|
-
@golden_master.each do |row|
|
82
|
-
row.each do |field, master_string|
|
83
|
-
subject_value = subject[current_row][field]
|
84
|
-
if !match_values?(master_string, subject_value)
|
85
|
-
throw_no_match "Field \"#{field}\", Record #{current_row + 1}: Expected value #{master_string.nil? ? '<NULL>' : "\"#{master_string}\""}, got #{subject_value.nil? ? '<NULL>' : "\"#{subject_value}\""}"
|
86
|
-
end
|
87
|
-
end
|
88
|
-
current_row += 1
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def match_types?(master_val, subject_val)
|
93
|
-
subject_val.kind_of? master_val.class
|
94
|
-
end
|
95
|
-
|
96
|
-
def match_values?(master_val, subject_val)
|
97
|
-
if master_val.nil?
|
98
|
-
master_val == subject_val
|
99
|
-
elsif master_val.kind_of?(String)
|
100
|
-
case subject_val
|
101
|
-
when ODBC::TimeStamp
|
102
|
-
(!!Time.strptime(master_val, DATE_FORMAT) rescue false) && Time.strptime(master_val, DATE_FORMAT) == ODBC::to_time(subject_val)
|
103
|
-
when Float
|
104
|
-
(!!Float(master_val) rescue false) && Float(master_val) == subject_val
|
105
|
-
else
|
106
|
-
subject_val.to_s == master_val.to_s
|
107
|
-
end
|
108
|
-
else
|
109
|
-
subject_val.to_s == master_val.to_s
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def test_array_match(subject)
|
114
|
-
# Compare the fields
|
115
|
-
if !(subject.empty? || @golden_master.empty?)
|
116
|
-
subject_fields = subject[0].keys
|
117
|
-
master_fields = @golden_master[0].keys
|
118
|
-
|
119
|
-
if subject_fields.count != master_fields.count
|
120
|
-
throw_no_match "Expected #{master_fields.count} field(s), got #{subject_fields.count}"
|
121
|
-
end
|
122
|
-
|
123
|
-
master_fields.each_with_index do |column, index|
|
124
|
-
if column != subject_fields[index]
|
125
|
-
throw_no_match "Expected field \"#{column}\", got field \"#{subject_fields[index]}\""
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
# Compare the number of records
|
131
|
-
subject_record_count = subject.count
|
132
|
-
master_record_count = @golden_master.count
|
133
|
-
if subject_record_count != master_record_count
|
134
|
-
throw_no_match "Expected #{master_record_count} record(s), got #{subject_record_count}"
|
135
|
-
end
|
136
|
-
|
137
|
-
# Compare the values of the golden master with the subject
|
138
|
-
current_row = 0
|
139
|
-
@golden_master.each do |row|
|
140
|
-
row.each do |field, master_value|
|
141
|
-
subject_value = subject[current_row][field]
|
142
|
-
if !match_values?(master_value, subject_value)
|
143
|
-
throw_no_match "Field \"#{field}\", Record #{current_row + 1}: Expected value #{master_value.nil? ? '<NULL>' : "\"#{master_value}\""}, got #{subject_value.nil? ? '<NULL>' : "\"#{subject_value}\""}"
|
144
|
-
end
|
145
|
-
end
|
146
|
-
current_row += 1
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
def is_well_formed_query_result?(arr)
|
151
|
-
return false unless arr.kind_of? Array
|
152
|
-
header =[]
|
153
|
-
arr.find{|row| !row.kind_of?(Hash) || (header = header.empty? ? row.keys : header) != row.keys || row.keys.empty?}.nil?
|
154
|
-
end
|
155
|
-
|
156
|
-
def test_single_value_match(subject)
|
157
|
-
subject_value = subject[0].values[0]
|
158
|
-
subject_field = subject[0].keys[0]
|
159
|
-
if !match_types?(@golden_master, subject_value)
|
160
|
-
throw_no_match "Field \"#{subject_field}\", Record 1: Expected type \"#{@golden_master.class}\", got \"#{subject_value.class}\""
|
161
|
-
end
|
162
|
-
|
163
|
-
if !match_values?(@golden_master, subject_value)
|
164
|
-
throw_no_match "Field \"#{subject_field}\", Record 1: Expected value #{@golden_master.nil? ? '<NULL>' : "\"#{@golden_master}\""}, got #{subject_value.nil? ? '<NULL>' : "\"#{subject_value}\""}"
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
# In case of a multi-resultset subject, extract the first result set
|
169
|
-
def normalize_query_result_subject(subject)
|
170
|
-
subject.kind_of?(Array) && !subject.empty? && is_well_formed_query_result?(subject[0]) ? subject[0] : subject
|
171
|
-
end
|
172
|
-
|
173
|
-
def throw_no_match(message)
|
174
|
-
@failure_message = message
|
175
|
-
throw :no_match
|
176
|
-
end
|
177
|
-
end
|
178
|
-
end
|
1
|
+
require 'csv'
|
2
|
+
|
3
|
+
module Slacker
|
4
|
+
DATE_FORMAT = "%m/%d/%Y"
|
5
|
+
|
6
|
+
class QueryResultMatcher
|
7
|
+
def initialize(golden_master)
|
8
|
+
@golden_master = golden_master
|
9
|
+
@failure_message = ''
|
10
|
+
end
|
11
|
+
|
12
|
+
def matches?(subject)
|
13
|
+
does_match = false
|
14
|
+
|
15
|
+
subject = normalize_query_result_subject(subject)
|
16
|
+
|
17
|
+
catch :no_match do
|
18
|
+
test_type_match(subject)
|
19
|
+
test_value_match(subject)
|
20
|
+
does_match = true
|
21
|
+
end
|
22
|
+
|
23
|
+
does_match
|
24
|
+
end
|
25
|
+
|
26
|
+
def failure_message_for_should
|
27
|
+
@failure_message
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
#test if the subject type is consistent with the golden master
|
33
|
+
def test_type_match(subject)
|
34
|
+
if !is_well_formed_query_result?(subject)
|
35
|
+
throw_no_match "Can perform query matches only against a well formed query result subject"
|
36
|
+
end
|
37
|
+
|
38
|
+
if (@golden_master.kind_of? Array) && !is_well_formed_query_result?(@golden_master)
|
39
|
+
throw_no_match "Cannot match against a non-well formed golden master array"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_value_match(subject)
|
44
|
+
case @golden_master
|
45
|
+
when CSV::Table
|
46
|
+
test_csv_table_match(subject)
|
47
|
+
when Array
|
48
|
+
test_array_match(subject)
|
49
|
+
else
|
50
|
+
test_single_value_match(subject)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Compare the golden master CSV table with the subject query result
|
55
|
+
def test_csv_table_match(subject)
|
56
|
+
# Compare the fields
|
57
|
+
if !subject.empty?
|
58
|
+
subject_fields = subject[0].keys
|
59
|
+
master_fields = @golden_master.headers
|
60
|
+
|
61
|
+
if subject_fields.count != master_fields.count
|
62
|
+
throw_no_match "Expected #{master_fields.count} field(s), got #{subject_fields.count}"
|
63
|
+
end
|
64
|
+
|
65
|
+
master_fields.each_with_index do |column, index|
|
66
|
+
if column != subject_fields[index]
|
67
|
+
throw_no_match "Expected field \"#{column}\", got field \"#{subject_fields[index]}\""
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Compare the number of records
|
73
|
+
subject_record_count = subject.count
|
74
|
+
master_record_count = @golden_master.inject(0){|count| count += 1}
|
75
|
+
if subject_record_count != master_record_count
|
76
|
+
throw_no_match "Expected #{master_record_count} record(s), got #{subject_record_count}"
|
77
|
+
end
|
78
|
+
|
79
|
+
# Compare the values of the golden master with the subject
|
80
|
+
current_row = 0
|
81
|
+
@golden_master.each do |row|
|
82
|
+
row.each do |field, master_string|
|
83
|
+
subject_value = subject[current_row][field]
|
84
|
+
if !match_values?(master_string, subject_value)
|
85
|
+
throw_no_match "Field \"#{field}\", Record #{current_row + 1}: Expected value #{master_string.nil? ? '<NULL>' : "\"#{master_string}\""}, got #{subject_value.nil? ? '<NULL>' : "\"#{subject_value}\""}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
current_row += 1
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def match_types?(master_val, subject_val)
|
93
|
+
subject_val.kind_of? master_val.class
|
94
|
+
end
|
95
|
+
|
96
|
+
def match_values?(master_val, subject_val)
|
97
|
+
if master_val.nil?
|
98
|
+
master_val == subject_val
|
99
|
+
elsif master_val.kind_of?(String)
|
100
|
+
case subject_val
|
101
|
+
when ODBC::TimeStamp
|
102
|
+
(!!Time.strptime(master_val, DATE_FORMAT) rescue false) && Time.strptime(master_val, DATE_FORMAT) == ODBC::to_time(subject_val)
|
103
|
+
when Float
|
104
|
+
(!!Float(master_val) rescue false) && Float(master_val) == subject_val
|
105
|
+
else
|
106
|
+
subject_val.to_s == master_val.to_s
|
107
|
+
end
|
108
|
+
else
|
109
|
+
subject_val.to_s == master_val.to_s
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_array_match(subject)
|
114
|
+
# Compare the fields
|
115
|
+
if !(subject.empty? || @golden_master.empty?)
|
116
|
+
subject_fields = subject[0].keys
|
117
|
+
master_fields = @golden_master[0].keys
|
118
|
+
|
119
|
+
if subject_fields.count != master_fields.count
|
120
|
+
throw_no_match "Expected #{master_fields.count} field(s), got #{subject_fields.count}"
|
121
|
+
end
|
122
|
+
|
123
|
+
master_fields.each_with_index do |column, index|
|
124
|
+
if column != subject_fields[index]
|
125
|
+
throw_no_match "Expected field \"#{column}\", got field \"#{subject_fields[index]}\""
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Compare the number of records
|
131
|
+
subject_record_count = subject.count
|
132
|
+
master_record_count = @golden_master.count
|
133
|
+
if subject_record_count != master_record_count
|
134
|
+
throw_no_match "Expected #{master_record_count} record(s), got #{subject_record_count}"
|
135
|
+
end
|
136
|
+
|
137
|
+
# Compare the values of the golden master with the subject
|
138
|
+
current_row = 0
|
139
|
+
@golden_master.each do |row|
|
140
|
+
row.each do |field, master_value|
|
141
|
+
subject_value = subject[current_row][field]
|
142
|
+
if !match_values?(master_value, subject_value)
|
143
|
+
throw_no_match "Field \"#{field}\", Record #{current_row + 1}: Expected value #{master_value.nil? ? '<NULL>' : "\"#{master_value}\""}, got #{subject_value.nil? ? '<NULL>' : "\"#{subject_value}\""}"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
current_row += 1
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def is_well_formed_query_result?(arr)
|
151
|
+
return false unless arr.kind_of? Array
|
152
|
+
header =[]
|
153
|
+
arr.find{|row| !row.kind_of?(Hash) || (header = header.empty? ? row.keys : header) != row.keys || row.keys.empty?}.nil?
|
154
|
+
end
|
155
|
+
|
156
|
+
def test_single_value_match(subject)
|
157
|
+
subject_value = subject[0].values[0]
|
158
|
+
subject_field = subject[0].keys[0]
|
159
|
+
if !match_types?(@golden_master, subject_value)
|
160
|
+
throw_no_match "Field \"#{subject_field}\", Record 1: Expected type \"#{@golden_master.class}\", got \"#{subject_value.class}\""
|
161
|
+
end
|
162
|
+
|
163
|
+
if !match_values?(@golden_master, subject_value)
|
164
|
+
throw_no_match "Field \"#{subject_field}\", Record 1: Expected value #{@golden_master.nil? ? '<NULL>' : "\"#{@golden_master}\""}, got #{subject_value.nil? ? '<NULL>' : "\"#{subject_value}\""}"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# In case of a multi-resultset subject, extract the first result set
|
169
|
+
def normalize_query_result_subject(subject)
|
170
|
+
subject.kind_of?(Array) && !subject.empty? && is_well_formed_query_result?(subject[0]) ? subject[0] : subject
|
171
|
+
end
|
172
|
+
|
173
|
+
def throw_no_match(message)
|
174
|
+
@failure_message = message
|
175
|
+
throw :no_match
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|