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