rspec-hive 0.1.0 → 0.2.0

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