hoodoo 2.1.2 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 27c91b7d6239a85430ddf6e2cb47e0461775375e6f4b1969c6bbbf9017a41243
4
- data.tar.gz: 3808aceb6a8474d1c456d25b38aa5a280df7c6da547747f1a54cce3b57bbe1ab
3
+ metadata.gz: 27f8a7c4aed490d7d8e01fb68469c393b094e1d83ab1100a700e330c2288e88d
4
+ data.tar.gz: ebf5032d0056a507db7bbaf63b51269469350e5e2189b5016d06a1012c88dd7d
5
5
  SHA512:
6
- metadata.gz: 35bea308563fef3c91d9f5f015942b3a03a198a299a18f754079d8ed1319cfc7da10165deee81d356064bcf1f479e5d20958807b9a7f90a998a3d288291d25b0
7
- data.tar.gz: 072047f7dc3c8deccb4a034de3bd793149909e99107455125313ae92f4ce85b059cf90b11ad3587af4b85a86c2199f235180f592b54bded4891ebc97a0802f4e
6
+ metadata.gz: 39ffe6dc1b31ff93b3cf5fb6424a253d107d7a9c4c1aeae789d8c04d6e6ebbec06dde7b42f9b411875b55d0722bc6571e9eb3432a148a8ac8b610dc1b96310b2
7
+ data.tar.gz: 44a537dcdcf8e7b905a4ac933a2497bd2c14598e2017b63b207baed5fcab4fdd511f17117b5a7a99e27f540f6745c5ade28eac50b4acd50d6354ab8a1c5bf5ee
@@ -57,6 +57,8 @@ module Hoodoo; module Services
57
57
  def report( e, env )
58
58
  opts = { :backtrace => Kernel.caller() }
59
59
  opts[ :rack_env ] = env unless env.nil?
60
+ e = sanitize_object(e)
61
+ opts = sanitize_object(opts)
60
62
 
61
63
  # Since an ExceptionReporter is already a "slow communicatory",
62
64
  # Hoodoo is using threads for behaviour; we don't need the async
@@ -81,9 +83,37 @@ module Hoodoo; module Services
81
83
  :environment_name => Hoodoo::Services::Middleware.environment,
82
84
  :session => user_data_for( context ) || 'unknown'
83
85
  }
86
+ e = sanitize_object(e)
87
+ opts = sanitize_object(opts)
84
88
 
85
89
  Airbrake.notify_sync( e, opts )
86
90
  end
91
+
92
+ private
93
+
94
+ # Recursive sanitisation method for deeply nested hash objects, returning
95
+ # the same object in a non frozen state.
96
+ #
97
+ # Why do I exist?
98
+ #
99
+ # Due to an airbrake-ruby issue where client arguments can be mutated when within a hash,
100
+ # a recursive sanitisation process must therefore take place before our inputs are sent
101
+ # to Airbrake, ensuring no frozen hash objects are present.
102
+ #
103
+ # https://github.com/airbrake/airbrake-ruby/issues/281
104
+ #
105
+ def sanitize_object( object )
106
+ object = ( object.dup rescue object ) if object.frozen?
107
+ return object unless object.is_a?( Hash )
108
+
109
+ sanitize_hash( object )
110
+ end
111
+
112
+ def sanitize_hash( object )
113
+ object.each do | key, value |
114
+ object[key] = sanitize_object( value )
115
+ end
116
+ end
87
117
  end
88
118
 
89
119
  end
@@ -12,11 +12,11 @@ module Hoodoo
12
12
  # The Hoodoo gem version. If this changes, be sure to re-run
13
13
  # <tt>bundle install</tt> or <tt>bundle update</tt>.
14
14
  #
15
- VERSION = '2.1.2'
15
+ VERSION = '2.2.0'
16
16
 
17
17
  # The Hoodoo gem date. If this changes, be sure to re-run
18
18
  # <tt>bundle install</tt> or <tt>bundle update</tt>.
19
19
  #
20
- DATE = '2017-11-07'
20
+ DATE = '2017-11-09'
21
21
 
22
22
  end
@@ -16,33 +16,104 @@ describe Hoodoo::Services::Middleware::ExceptionReporting::AirbrakeReporter do
16
16
  end
17
17
 
18
18
  context '#report' do
19
- it 'calls Airbrake correctly without an "env"' do
20
- ex = RuntimeError.new( 'A' )
21
-
22
- expect( Airbrake ).to receive( :notify_sync ).once do | e, opts |
23
- expect( e ).to be_a( Exception )
24
- expect( opts ).to be_a( Hash )
25
- expect( opts ).to have_key( :backtrace )
26
- expect( opts ).to_not have_key( :rack_env )
19
+ context 'with Airbrake mocks' do
20
+ it 'calls Airbrake correctly without an "env"' do
21
+ ex = RuntimeError.new( 'A' )
22
+
23
+ expect( Airbrake ).to receive( :notify_sync ).once do | e, opts |
24
+ expect( e ).to be_a( Exception )
25
+ expect( opts ).to be_a( Hash )
26
+ expect( opts ).to have_key( :backtrace )
27
+ expect( opts ).to_not have_key( :rack_env )
28
+ end
29
+
30
+ Hoodoo::Services::Middleware::ExceptionReporting.report( ex )
27
31
  end
28
32
 
29
- Hoodoo::Services::Middleware::ExceptionReporting.report( ex )
33
+ it 'calls Airbrake correctly with an "env"' do
34
+ ex = RuntimeError.new( 'A' )
35
+ mock_env = { 'rack' => 'request' }
36
+
37
+ expect( Airbrake ).to receive( :notify_sync ).once do | e, opts |
38
+ expect( e ).to be_a( Exception )
39
+
40
+ expect( opts ).to be_a( Hash )
41
+ expect( opts ).to have_key( :backtrace )
42
+
43
+ expect( opts[ :rack_env ] ).to eq( mock_env )
44
+ end
45
+
46
+ Hoodoo::Services::Middleware::ExceptionReporting.report( ex, mock_env )
47
+ end
30
48
  end
31
49
 
32
- it 'calls Airbrake correctly with an "env"' do
33
- ex = RuntimeError.new( 'A' )
34
- mock_env = { 'rack' => 'request' }
50
+ context 'without Airbrake mocks' do
51
+
52
+ # Airbrake does not allow the default notifier to be reconfigured, so we
53
+ # must set some dummy values here just once within this Airbrake-specific
54
+ # integration test. Without this, non-mocked tests do not run much of the
55
+ # Airbrake code that, over time, we have discovered should be tested.
56
+ #
57
+ before :all do
58
+ Airbrake.configure do | config |
59
+ config.project_id = '123456'
60
+ config.project_key = Hoodoo::UUID.generate()
61
+ end
62
+ end
35
63
 
36
- expect( Airbrake ).to receive( :notify_sync ).once do | e, opts |
37
- expect( e ).to be_a( Exception )
64
+ before :each do
65
+ WebMock.enable!
38
66
 
39
- expect( opts ).to be_a( Hash )
40
- expect( opts ).to have_key( :backtrace )
67
+ stub_request( :post, /airbrake\.io\/api/ ).
68
+ to_return( :body => "{}",
69
+ :status => 201,
70
+ :headers => { 'Content-Length' => 2 } )
71
+ end
72
+
73
+ after :each do
74
+ WebMock.reset!
75
+ WebMock.disable!
76
+ end
77
+
78
+ it 'can send frozen exceptions' do
79
+ ex = RuntimeError.new( 'A' )
80
+
81
+ # Be sure that the adaptero called Airbrake and that Airbrake did try
82
+ # to internally send the message (which we'll catch with WebMock via
83
+ # the "before :each" filter above).
84
+ #
85
+ expect( Airbrake ).to receive( :notify_sync ).once.and_call_original
86
+ expect_any_instance_of( Airbrake::SyncSender ).to receive( :send ).once.and_call_original
41
87
 
42
- expect( opts[ :rack_env ] ).to eq( mock_env )
88
+ # There shouldn't be any need to handle exceptions inside the
89
+ # communicator pool underneath the adaptor.
90
+ #
91
+ expect_any_instance_of( Hoodoo::Communicators::Pool ).to_not receive( :handle_exception )
92
+
93
+ Hoodoo::Services::Middleware::ExceptionReporting.report( ex.freeze() )
94
+ Hoodoo::Services::Middleware::ExceptionReporting.wait()
43
95
  end
44
96
 
45
- Hoodoo::Services::Middleware::ExceptionReporting.report( ex, mock_env )
97
+ it 'can send frozen data large enough to require truncation' do
98
+ ex = RuntimeError.new( 'A' )
99
+ mock_env = { 'rack' => 'request' }
100
+
101
+ 1.upto( Airbrake::Notice::PAYLOAD_MAX_SIZE + 10 ) do | i |
102
+ mock_env[ Hoodoo::UUID.generate() ] = i
103
+ end
104
+
105
+ # See previous test (above) for an explanation of the expectations
106
+ # below.
107
+
108
+ expect( Airbrake ).to receive( :notify_sync ).once.and_call_original
109
+ expect_any_instance_of( Airbrake::Truncator ).to receive( :truncate_object ).at_least( :once ).and_call_original
110
+ expect_any_instance_of( Airbrake::SyncSender ).to receive( :send ).once.and_call_original
111
+
112
+ expect_any_instance_of( Hoodoo::Communicators::Pool ).to_not receive( :handle_exception )
113
+
114
+ Hoodoo::Services::Middleware::ExceptionReporting.report( ex, mock_env.freeze() )
115
+ Hoodoo::Services::Middleware::ExceptionReporting.wait()
116
+ end
46
117
  end
47
118
  end
48
119
 
data/spec/spec_helper.rb CHANGED
@@ -19,6 +19,16 @@ end
19
19
 
20
20
  require 'byebug'
21
21
 
22
+ # Stubbing Net::HTTP with WebMock - disabled by default; only enable for
23
+ # tests where you need it, as real Net::HTTP connections are used in many
24
+ # other tests and must not be mocked.
25
+ #
26
+ # https://github.com/bblimke/webmock
27
+
28
+ require 'webmock/rspec'
29
+
30
+ WebMock.disable!
31
+
22
32
  # The ActiveRecord extensions need testing in the context of a database. I
23
33
  # did consider NullDB - https://github.com/nulldb/nulldb - but this was too
24
34
  # far from 'the real thing' for my liking. Instead, we use SQLite in memory
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hoodoo
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.2
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Loyalty New Zealand
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-07 00:00:00.000000000 Z
11
+ date: 2017-11-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dalli
@@ -122,6 +122,20 @@ dependencies:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
124
  version: '3.5'
125
+ - !ruby/object:Gem::Dependency
126
+ name: webmock
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '3.1'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '3.1'
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: activerecord
127
141
  requirement: !ruby/object:Gem::Requirement
@@ -212,28 +226,28 @@ dependencies:
212
226
  requirements:
213
227
  - - "~>"
214
228
  - !ruby/object:Gem::Version
215
- version: '1.1'
229
+ version: '2.6'
216
230
  type: :development
217
231
  prerelease: false
218
232
  version_requirements: !ruby/object:Gem::Requirement
219
233
  requirements:
220
234
  - - "~>"
221
235
  - !ruby/object:Gem::Version
222
- version: '1.1'
236
+ version: '2.6'
223
237
  - !ruby/object:Gem::Dependency
224
238
  name: airbrake
225
239
  requirement: !ruby/object:Gem::Requirement
226
240
  requirements:
227
241
  - - "~>"
228
242
  - !ruby/object:Gem::Version
229
- version: '6.2'
243
+ version: '7.1'
230
244
  type: :development
231
245
  prerelease: false
232
246
  version_requirements: !ruby/object:Gem::Requirement
233
247
  requirements:
234
248
  - - "~>"
235
249
  - !ruby/object:Gem::Version
236
- version: '6.2'
250
+ version: '7.1'
237
251
  - !ruby/object:Gem::Dependency
238
252
  name: le
239
253
  requirement: !ruby/object:Gem::Requirement
@@ -518,7 +532,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
518
532
  requirements:
519
533
  - - ">="
520
534
  - !ruby/object:Gem::Version
521
- version: 2.2.7
535
+ version: 2.2.8
522
536
  required_rubygems_version: !ruby/object:Gem::Requirement
523
537
  requirements:
524
538
  - - ">="
@@ -526,7 +540,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
526
540
  version: '0'
527
541
  requirements: []
528
542
  rubyforge_project:
529
- rubygems_version: 2.7.1
543
+ rubygems_version: 2.7.2
530
544
  signing_key:
531
545
  specification_version: 4
532
546
  summary: Opinionated APIs