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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +0 -1
  3. data/.rubocop.yml +13 -1
  4. data/.rubocop_u2i.yml +9 -1
  5. data/.travis.yml +19 -0
  6. data/Guardfile +12 -1
  7. data/README.md +3 -1
  8. data/Rakefile +11 -2
  9. data/examples/{query.rb → lib/query.rb} +2 -5
  10. data/examples/{hive_tests_config.yml.example → rspec-hive.yml.example} +0 -0
  11. data/examples/spec/query_spec.rb +103 -0
  12. data/examples/spec/spec_helper.rb +22 -0
  13. data/lib/rspec/hive.rb +7 -2
  14. data/lib/rspec/hive/connection_delegator.rb +17 -5
  15. data/lib/rspec/hive/connector.rb +0 -1
  16. data/lib/rspec/hive/exponential_backoff.rb +15 -0
  17. data/lib/rspec/hive/matchers.rb +29 -0
  18. data/lib/rspec/hive/query_builder.rb +83 -0
  19. data/lib/rspec/hive/query_builder/null_strategy.rb +11 -0
  20. data/lib/rspec/hive/query_builder/row_transformer.rb +63 -0
  21. data/lib/rspec/hive/query_builder/type_faker.rb +36 -0
  22. data/lib/rspec/hive/query_builder/value_by_type_strategy.rb +13 -0
  23. data/lib/rspec/hive/query_builder_helper.rb +24 -0
  24. data/lib/rspec/{rake_tasks → hive}/railtie.rb +2 -2
  25. data/lib/rspec/{rake_tasks → hive/rake_tasks}/docker.rake +0 -0
  26. data/lib/rspec/hive/version.rb +1 -1
  27. data/lib/rspec/hive/with_hive_connection.rb +16 -10
  28. data/rspec-hive.gemspec +6 -4
  29. data/spec/.rubocop.yml +4 -0
  30. data/spec/lib/rspec/hive/configuration_spec.rb +9 -6
  31. data/spec/lib/rspec/hive/connection_delegator_spec.rb +1 -1
  32. data/spec/lib/rspec/hive/connector_spec.rb +1 -1
  33. data/spec/lib/rspec/hive/db_name_spec.rb +1 -1
  34. data/spec/lib/rspec/hive/matchers_spec.rb +94 -0
  35. data/spec/lib/rspec/hive/query_builder/row_transformer_spec.rb +37 -0
  36. data/spec/lib/rspec/hive/query_builder_spec.rb +143 -0
  37. data/spec/lib/rspec/hive_spec.rb +1 -1
  38. data/spec/spec_helper.rb +2 -62
  39. metadata +68 -24
  40. data/examples/config_helper.rb +0 -1
  41. data/examples/query_spec.rb +0 -41
@@ -0,0 +1,11 @@
1
+ module RSpec
2
+ module Hive
3
+ class QueryBuilder
4
+ class NullStrategy
5
+ def missing(_column)
6
+ '\N'
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -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,13 @@
1
+ require_relative 'type_faker'
2
+
3
+ module RSpec
4
+ module Hive
5
+ class QueryBuilder
6
+ class ValueByTypeStrategy
7
+ def missing(column)
8
+ RSpec::Hive::QueryBuilder::TypeFaker.fake(column.type)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ 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
@@ -2,9 +2,9 @@ require 'rails'
2
2
 
3
3
  module RSpec
4
4
  module Hive
5
- class Railtie < Rails::Railtie
5
+ class Railtie < ::Rails::Railtie
6
6
  rake_tasks do
7
- load 'rspec/rake_tasks/docker.rake'
7
+ load 'rspec/hive/rake_tasks/docker.rake'
8
8
  end
9
9
  end
10
10
  end
@@ -1,5 +1,5 @@
1
1
  module RSpec
2
2
  module Hive
3
- VERSION = '0.1.0'
3
+ VERSION = '0.2.0'.freeze
4
4
  end
5
5
  end
@@ -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
- connection
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
- hive.stop_connection(connection) unless hive && connection
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 'rbhive', '~> 0.6.0'
29
- spec.add_development_dependency 'rubocop', '~> 0.34'
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
@@ -0,0 +1,4 @@
1
+ inherit_from: "../.rubocop_u2i.yml"
2
+
3
+ Metrics/LineLength:
4
+ Enabled: false
@@ -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
- allow_any_instance_of(described_class).to receive(:mac?) { true }
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
- allow_any_instance_of(described_class).to receive(:mac?) { false }
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 parametres required and optional' do
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::ConnectionDelegator do
3
+ RSpec.describe RSpec::Hive::ConnectionDelegator do
4
4
  describe '#load_into_table' do
5
5
  let(:host_shared_directory_path) { '/tmp/host' }
6
6
  let(:docker_file_path) { '/tmp/docked/test_file' }
@@ -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) }
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe RSpec::Hive::DbName do
3
+ RSpec.describe RSpec::Hive::DbName do
4
4
  describe '.random_name' do
5
5
  subject { described_class.random_name }
6
6
 
@@ -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