rspec-hive 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +0 -1
- data/.rubocop.yml +13 -1
- data/.rubocop_u2i.yml +9 -1
- data/.travis.yml +19 -0
- data/Guardfile +12 -1
- data/README.md +3 -1
- data/Rakefile +11 -2
- data/examples/{query.rb → lib/query.rb} +2 -5
- data/examples/{hive_tests_config.yml.example → rspec-hive.yml.example} +0 -0
- data/examples/spec/query_spec.rb +103 -0
- data/examples/spec/spec_helper.rb +22 -0
- data/lib/rspec/hive.rb +7 -2
- data/lib/rspec/hive/connection_delegator.rb +17 -5
- data/lib/rspec/hive/connector.rb +0 -1
- data/lib/rspec/hive/exponential_backoff.rb +15 -0
- data/lib/rspec/hive/matchers.rb +29 -0
- data/lib/rspec/hive/query_builder.rb +83 -0
- data/lib/rspec/hive/query_builder/null_strategy.rb +11 -0
- data/lib/rspec/hive/query_builder/row_transformer.rb +63 -0
- data/lib/rspec/hive/query_builder/type_faker.rb +36 -0
- data/lib/rspec/hive/query_builder/value_by_type_strategy.rb +13 -0
- data/lib/rspec/hive/query_builder_helper.rb +24 -0
- data/lib/rspec/{rake_tasks → hive}/railtie.rb +2 -2
- data/lib/rspec/{rake_tasks → hive/rake_tasks}/docker.rake +0 -0
- data/lib/rspec/hive/version.rb +1 -1
- data/lib/rspec/hive/with_hive_connection.rb +16 -10
- data/rspec-hive.gemspec +6 -4
- data/spec/.rubocop.yml +4 -0
- data/spec/lib/rspec/hive/configuration_spec.rb +9 -6
- data/spec/lib/rspec/hive/connection_delegator_spec.rb +1 -1
- data/spec/lib/rspec/hive/connector_spec.rb +1 -1
- data/spec/lib/rspec/hive/db_name_spec.rb +1 -1
- data/spec/lib/rspec/hive/matchers_spec.rb +94 -0
- data/spec/lib/rspec/hive/query_builder/row_transformer_spec.rb +37 -0
- data/spec/lib/rspec/hive/query_builder_spec.rb +143 -0
- data/spec/lib/rspec/hive_spec.rb +1 -1
- data/spec/spec_helper.rb +2 -62
- metadata +68 -24
- data/examples/config_helper.rb +0 -1
- data/examples/query_spec.rb +0 -41
@@ -0,0 +1,63 @@
|
|
1
|
+
require_relative 'type_faker'
|
2
|
+
|
3
|
+
module RSpec
|
4
|
+
module Hive
|
5
|
+
class QueryBuilder
|
6
|
+
class RowTransformer
|
7
|
+
def initialize(schema, missing_column_strategy)
|
8
|
+
@schema = schema
|
9
|
+
@strategy = missing_column_strategy
|
10
|
+
end
|
11
|
+
|
12
|
+
def transform(row)
|
13
|
+
if row.respond_to?(:each_pair)
|
14
|
+
mock_hive_row(row)
|
15
|
+
elsif row.respond_to?(:each)
|
16
|
+
array_row(row)
|
17
|
+
else
|
18
|
+
raise ArgumentError, 'Array or Hash required!'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :schema, :strategy
|
25
|
+
|
26
|
+
HIVE_NIL = '\N'.freeze
|
27
|
+
|
28
|
+
def array_row(row)
|
29
|
+
size = schema.columns.size
|
30
|
+
missing = size - row.size
|
31
|
+
if missing > 0
|
32
|
+
row_with_missing_columns(row)
|
33
|
+
else
|
34
|
+
row
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def row_with_missing_columns(row)
|
39
|
+
schema.columns.map.with_index do |column, index|
|
40
|
+
if index > row.size
|
41
|
+
strategy.missing(column)
|
42
|
+
else
|
43
|
+
row[index]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def mock_hive_row(partial_row)
|
49
|
+
symbolized_row = Hash[partial_row.map { |k, v| [k.to_sym, v] }]
|
50
|
+
|
51
|
+
schema.columns.map do |column|
|
52
|
+
value = symbolized_row.fetch(column.name.to_sym) { strategy.missing(column) }
|
53
|
+
nil_to_null(value)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def nil_to_null(value)
|
58
|
+
value.nil? ? HIVE_NIL : value
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'faker'
|
2
|
+
|
3
|
+
module RSpec
|
4
|
+
module Hive
|
5
|
+
class QueryBuilder
|
6
|
+
class TypeFaker
|
7
|
+
class << self
|
8
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
9
|
+
def fake(type)
|
10
|
+
case type
|
11
|
+
when :int
|
12
|
+
Faker::Number.number(9)
|
13
|
+
when :smallint
|
14
|
+
Faker::Number.number(4)
|
15
|
+
when :tinyint
|
16
|
+
Faker::Number.number(1)
|
17
|
+
when :bigint
|
18
|
+
Faker::Number.number(12)
|
19
|
+
when :float
|
20
|
+
Faker::Number.decimal(4, 4)
|
21
|
+
when :double
|
22
|
+
Faker::Number.decimal(8, 8)
|
23
|
+
when :boolean
|
24
|
+
Faker::Boolean.boolean
|
25
|
+
when :string
|
26
|
+
Faker::Lorem.word
|
27
|
+
else
|
28
|
+
raise ArgumentError, "Unsupported type: #{type}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative 'query_builder'
|
2
|
+
|
3
|
+
module RSpec
|
4
|
+
module Hive
|
5
|
+
module QueryBuilderHelper
|
6
|
+
def into_hive(schema)
|
7
|
+
hive_connection_guard!
|
8
|
+
::RSpec::Hive::QueryBuilder.new(schema, connection)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def hive_connection_present?
|
14
|
+
respond_to?(:connection) &&
|
15
|
+
(connection.is_a?(RBHive::TCLIConnection) ||
|
16
|
+
connection.is_a?(RSpec::Hive::ConnectionDelegator))
|
17
|
+
end
|
18
|
+
|
19
|
+
def hive_connection_guard!
|
20
|
+
raise 'Include WithHiveConnection' unless hive_connection_present?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
File without changes
|
data/lib/rspec/hive/version.rb
CHANGED
@@ -1,17 +1,13 @@
|
|
1
|
+
require_relative 'exponential_backoff'
|
2
|
+
|
1
3
|
module RSpec
|
2
4
|
module Hive
|
3
5
|
module WithHiveConnection
|
4
|
-
def hive
|
5
|
-
Hive.connector
|
6
|
-
end
|
7
|
-
|
8
|
-
def connection
|
9
|
-
@connection ||= hive.start_connection
|
10
|
-
end
|
11
|
-
|
12
6
|
def self.included(mod)
|
13
7
|
mod.before(:all) do
|
14
|
-
|
8
|
+
ExponentialBackoff.retryable(on: Thrift::TransportException) do
|
9
|
+
connection
|
10
|
+
end
|
15
11
|
end
|
16
12
|
|
17
13
|
mod.before(:each) do
|
@@ -19,9 +15,19 @@ module RSpec
|
|
19
15
|
end
|
20
16
|
|
21
17
|
mod.after(:all) do
|
22
|
-
|
18
|
+
hive_connector.stop_connection(connection) if hive_connector && @connection
|
23
19
|
end
|
24
20
|
end
|
21
|
+
|
22
|
+
def connection
|
23
|
+
@connection ||= hive_connector.start_connection
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def hive_connector
|
29
|
+
::RSpec::Hive.connector
|
30
|
+
end
|
25
31
|
end
|
26
32
|
end
|
27
33
|
end
|
data/rspec-hive.gemspec
CHANGED
@@ -21,13 +21,15 @@ Gem::Specification.new do |spec|
|
|
21
21
|
|
22
22
|
spec.add_dependency 'rake', '~> 10.0'
|
23
23
|
spec.add_dependency 'colorize', '~> 0.7'
|
24
|
+
spec.add_dependency 'faker', '~> 1.6'
|
25
|
+
spec.add_dependency 'retryable', '~> 2.0.3'
|
26
|
+
spec.add_dependency 'rspec', '~> 3.4'
|
27
|
+
spec.add_dependency 'rbhive-u2i', '~> 1.0.0'
|
24
28
|
|
25
29
|
spec.add_development_dependency 'bundler', '~> 1.7'
|
26
|
-
spec.add_development_dependency 'rspec', '~> 3.4'
|
27
30
|
spec.add_development_dependency 'rspec-its', '~> 1.2'
|
28
|
-
spec.add_development_dependency '
|
29
|
-
spec.add_development_dependency 'rubocop', '~>
|
30
|
-
spec.add_development_dependency 'rubocop-rspec', '~> 1.3'
|
31
|
+
spec.add_development_dependency 'rubocop', '~> 0.39'
|
32
|
+
spec.add_development_dependency 'rubocop-rspec', '~> 1.4'
|
31
33
|
spec.add_development_dependency 'guard', '~> 2.6'
|
32
34
|
spec.add_development_dependency 'guard-rspec', '~> 4.3'
|
33
35
|
spec.add_development_dependency 'guard-rubocop', '~> 1.2'
|
data/spec/.rubocop.yml
ADDED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'tempfile'
|
3
3
|
|
4
|
-
describe RSpec::Hive::Configuration do
|
4
|
+
RSpec.describe RSpec::Hive::Configuration do
|
5
5
|
RSpec.shared_examples('config') do
|
6
6
|
its(:host) do
|
7
7
|
is_expected.to eq(expected_host)
|
@@ -53,20 +53,21 @@ describe RSpec::Hive::Configuration do
|
|
53
53
|
|
54
54
|
context 'when no configuration file is provided' do
|
55
55
|
let(:expected_port) { 10000 }
|
56
|
+
let!(:original_host_os) { RbConfig::CONFIG['host_os'] }
|
56
57
|
|
57
58
|
before { allow(Dir).to receive(:mktmpdir) { mock_tmpdir } }
|
58
59
|
|
59
|
-
subject { described_class.new }
|
60
|
-
|
61
60
|
context 'when on Mac' do
|
62
61
|
let(:mock_tmpdir) { '/Users/Shared/test/' }
|
63
62
|
let(:expected_host) { '192.168.99.100' }
|
64
63
|
let(:expected_host_shared_directory_path) { '/Users/Shared/test/spec-tmp-files' }
|
65
64
|
|
66
65
|
before do
|
67
|
-
|
66
|
+
RbConfig::CONFIG['host_os'] = 'mac os'
|
68
67
|
end
|
69
68
|
|
69
|
+
after { RbConfig::CONFIG['host_os'] = original_host_os }
|
70
|
+
|
70
71
|
include_examples('config')
|
71
72
|
end
|
72
73
|
|
@@ -76,9 +77,11 @@ describe RSpec::Hive::Configuration do
|
|
76
77
|
let(:expected_host_shared_directory_path) { '/tmp/test/spec-tmp-files' }
|
77
78
|
|
78
79
|
before do
|
79
|
-
|
80
|
+
RbConfig::CONFIG['host_os'] = 'linux'
|
80
81
|
end
|
81
82
|
|
83
|
+
after { RbConfig::CONFIG['host_os'] = original_host_os }
|
84
|
+
|
82
85
|
include_examples('config')
|
83
86
|
end
|
84
87
|
end
|
@@ -136,7 +139,7 @@ describe RSpec::Hive::Configuration do
|
|
136
139
|
include_examples('config')
|
137
140
|
end
|
138
141
|
|
139
|
-
context 'where there are some
|
142
|
+
context 'where there are some parameters required and optional' do
|
140
143
|
let(:yaml_hash) do
|
141
144
|
{
|
142
145
|
'hive' =>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe RSpec::Hive::Connector do
|
3
|
+
RSpec.describe RSpec::Hive::Connector do
|
4
4
|
describe '#start_connection' do
|
5
5
|
let(:tcli_connection) { double(RBHive::TCLIConnection) }
|
6
6
|
let(:connection_delegator) { double(RSpec::Hive::ConnectionDelegator) }
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rspec/hive/query_builder_helper'
|
3
|
+
|
4
|
+
RSpec.describe 'match_result_set' do
|
5
|
+
include RSpec::Hive::QueryBuilderHelper
|
6
|
+
|
7
|
+
let(:john) { {first_name: 'John', last_name: 'Lennon', age: 40} }
|
8
|
+
let(:paul) { {first_name: 'Paul', last_name: 'McCartney', age: 73} }
|
9
|
+
|
10
|
+
let(:full_match) { expect(actual_rows).to match_result_set(expected_rows) }
|
11
|
+
let(:partial_match) { expect(actual_rows).to match_result_set(expected_rows).partially }
|
12
|
+
let(:full_match_fails) { expect(actual_rows).not_to match_result_set(expected_rows) }
|
13
|
+
let(:partial_match_fails) { expect(actual_rows).not_to match_result_set(expected_rows).partially }
|
14
|
+
|
15
|
+
context 'when the expected set has only one row' do
|
16
|
+
context 'but the actual set has more rows' do
|
17
|
+
let(:actual_rows) { [john, paul] }
|
18
|
+
|
19
|
+
context 'when the row is given as an array' do
|
20
|
+
let(:expected_rows) { [john.values] }
|
21
|
+
|
22
|
+
specify { full_match_fails }
|
23
|
+
specify { partial_match_fails }
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'when the row is given as a hash' do
|
27
|
+
let(:expected_rows) { [john] }
|
28
|
+
|
29
|
+
specify { full_match_fails }
|
30
|
+
specify { partial_match_fails }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'and the actual set has one row' do
|
35
|
+
let(:actual_rows) { [john] }
|
36
|
+
|
37
|
+
context 'when the row is given as an array' do
|
38
|
+
context 'when the number of columns differs' do
|
39
|
+
let(:expected_rows) { [john.values << 'yoko'] }
|
40
|
+
|
41
|
+
specify { full_match_fails }
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'when the actual and expected have are different' do
|
45
|
+
let(:expected_rows) { [paul.values] }
|
46
|
+
|
47
|
+
specify { full_match_fails }
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'when the actual and expected rows are equal' do
|
51
|
+
let(:expected_rows) { [john.values] }
|
52
|
+
|
53
|
+
specify { full_match }
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'when the actual and expected rows are equal with rspec matchers' do
|
57
|
+
let(:expected_rows) { [[a_string_matching('John'), a_string_matching(/lennon/i), 40]] }
|
58
|
+
|
59
|
+
specify { full_match }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'when the row is given as a hash' do
|
64
|
+
context 'when the number of columns differs' do
|
65
|
+
let(:expected_rows) { [john.dup.tap { |j| j[:ono] = 'yoko' }] }
|
66
|
+
|
67
|
+
specify { full_match_fails }
|
68
|
+
specify { partial_match_fails }
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'when the actual and expected have are different' do
|
72
|
+
let(:expected_rows) { [john.dup.tap { |j| j[:first_name] = 'yoko' }] }
|
73
|
+
|
74
|
+
specify { full_match_fails }
|
75
|
+
specify { partial_match_fails }
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'when the actual and expected rows are equal' do
|
79
|
+
let(:expected_rows) { [john] }
|
80
|
+
|
81
|
+
specify { full_match }
|
82
|
+
specify { partial_match }
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'when matching a subset of columns' do
|
86
|
+
let(:expected_rows) { [{first_name: john[:first_name]}] }
|
87
|
+
|
88
|
+
specify { full_match_fails }
|
89
|
+
specify { partial_match }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe RSpec::Hive::QueryBuilder::RowTransformer do
|
4
|
+
let(:transformer) { described_class.new(schema, missing_column_strategy) }
|
5
|
+
let(:schema) do
|
6
|
+
RBHive::TableSchema.new('table_name', nil) do
|
7
|
+
column :col1, :string
|
8
|
+
column :col2, :string
|
9
|
+
end
|
10
|
+
end
|
11
|
+
let(:column) { schema.instance_variable_get(:@columns).last }
|
12
|
+
let(:partition) { double }
|
13
|
+
let(:missing_column_strategy) { double }
|
14
|
+
|
15
|
+
describe '#transform' do
|
16
|
+
subject { transformer.transform(row) }
|
17
|
+
|
18
|
+
let(:row) { {col1: real_value} }
|
19
|
+
let(:real_value) { 'col1' }
|
20
|
+
let(:fake_value) { 'lorem' }
|
21
|
+
let(:expected_row) { [real_value, fake_value] }
|
22
|
+
|
23
|
+
before { allow(missing_column_strategy).to receive(:missing).with(column).and_return(fake_value) }
|
24
|
+
|
25
|
+
it 'fills missing fields' do
|
26
|
+
expect(subject[1]).to eq(fake_value)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'uses defined fields' do
|
30
|
+
expect(subject[0]).to eq(real_value)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'returns valid Rows' do
|
34
|
+
expect(subject).to eq(expected_row)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|