hoodoo 1.2.3 → 1.3.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.
- checksums.yaml +8 -8
- data/lib/hoodoo/services/middleware/exception_reporting/base_reporter.rb +84 -1
- data/lib/hoodoo/services/middleware/exception_reporting/exception_reporting.rb +23 -1
- data/lib/hoodoo/services/middleware/exception_reporting/reporters/airbrake_reporter.rb +27 -4
- data/lib/hoodoo/services/middleware/exception_reporting/reporters/raygun_reporter.rb +18 -2
- data/lib/hoodoo/services/middleware/middleware.rb +5 -1
- data/lib/hoodoo/version.rb +1 -1
- data/spec/services/middleware/exception_reporting/base_reporter_spec.rb +94 -1
- data/spec/services/middleware/exception_reporting/exception_reporting_spec.rb +70 -0
- data/spec/services/middleware/exception_reporting/reporters/airbrake_reporter_spec.rb +83 -6
- data/spec/services/middleware/exception_reporting/reporters/raygun_reporter_spec.rb +71 -5
- data/spec/services/middleware/middleware_spec.rb +23 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
N2VjMWU3NjI1YmZmODQ1NDQ3OTYxMTg1MTk3YzRhNmQzM2U3YzZhNw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NzFkZjIyZTk0YmUzNTVhY2IxZWMxMThiMWU0ZmZiMzhmOGNkYjk5Yw==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZjViZWUyYzM2NmY3NmNjZjFlNTQ1ZjkyZWY1ZGE1N2VmNmQ0MDA3N2ExNmNj
|
10
|
+
M2RlNDZjYTlhOTU5NjM0NWU4NjQ5MjhhODVkMTk2YjE0MjM0NTM4ZDgzYjI3
|
11
|
+
ODRiYTVkNzE2OTQxY2IzYjE4NzA1ZGJkNGUxZDUzYmZjMGViNDk=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ZGI3OTZlM2QzYmFhNGVjZGIzYTJlNGM0NDZmZDYwN2MxOTNiODE1ZWEwYjEw
|
14
|
+
MDRmMzQxMWMwNGM1ZDJjM2RlMDk0NzZiMzg5MTBiYTg1MDJmMWFlMjFkNzQw
|
15
|
+
MGU5NGI3ZGIyYmI5OWExZjUxNDZlODljZmY4YTMxYmQzN2IzZGE=
|
@@ -84,6 +84,30 @@ module Hoodoo; module Services
|
|
84
84
|
raise( 'Subclasses must implement #report' )
|
85
85
|
end
|
86
86
|
|
87
|
+
# Similar to #report, with the same caveats; but has more information
|
88
|
+
# available.
|
89
|
+
#
|
90
|
+
# Subclasses report an exception for errors that occur within a fully
|
91
|
+
# handled Rack request context, with a high level processed Hoodoo
|
92
|
+
# representation available.
|
93
|
+
#
|
94
|
+
# Through the protected #user_data_for method, subclasses can, if the
|
95
|
+
# exception reporting backend supports it, include detailed request
|
96
|
+
# information with their contextual exception reports.
|
97
|
+
#
|
98
|
+
# Implementation is optional. If not available, the system falls back
|
99
|
+
# to the less detailed #report method. If called, all parameters must
|
100
|
+
# be provided; none are optional.
|
101
|
+
#
|
102
|
+
# +e+:: Exception (or subclass) instance to be reported.
|
103
|
+
#
|
104
|
+
# +context+:: Hoodoo::Services::Context instance describing an
|
105
|
+
# in-flight request/response cycle.
|
106
|
+
#
|
107
|
+
def contextual_report( e, context )
|
108
|
+
raise( 'Subclasses may implement #contextual_report' )
|
109
|
+
end
|
110
|
+
|
87
111
|
# Subclasses *MUST* *NOT* override this method, which is part of the
|
88
112
|
# base class implementation and implements
|
89
113
|
# Hoodoo::Communicators::Slow#communicate. It calls through to the
|
@@ -95,7 +119,66 @@ module Hoodoo; module Services
|
|
95
119
|
# instance.
|
96
120
|
#
|
97
121
|
def communicate( object )
|
98
|
-
|
122
|
+
|
123
|
+
env = object.rack_env || ( object.context.owning_interaction.rack_request.env rescue nil )
|
124
|
+
|
125
|
+
# The 'instance_methods( false )' call pulls only instance methods
|
126
|
+
# strictly defined in 'self' instance, not in any superclasses.
|
127
|
+
# Thus we don't see the base implementation of 'contextual_report'
|
128
|
+
# in this source file; only an overriding implementation in a real
|
129
|
+
# reporter subclass.
|
130
|
+
#
|
131
|
+
# http://ruby-doc.org/core-2.1.8/Module.html#method-i-instance_methods
|
132
|
+
#
|
133
|
+
subclass_methods = self.class.instance_methods( false )
|
134
|
+
|
135
|
+
if object.context && subclass_methods.include?( :contextual_report )
|
136
|
+
self.contextual_report( object.exception, object.context )
|
137
|
+
else
|
138
|
+
self.report( object.exception, env )
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
protected
|
143
|
+
|
144
|
+
# When passed a request context, extracts information that can be given
|
145
|
+
# as "user data" (or similar) to a exception reporting endpoint, if it
|
146
|
+
# supports such a concept.
|
147
|
+
#
|
148
|
+
# +context+:: Hoodoo::Services::Context instance describing an
|
149
|
+
# in-flight request/response cycle.
|
150
|
+
#
|
151
|
+
def user_data_for( context )
|
152
|
+
begin
|
153
|
+
hash = {
|
154
|
+
:interaction_id => context.owning_interaction.interaction_id,
|
155
|
+
:action => ( context.owning_interaction.requested_action || '(unknown)' ).to_s,
|
156
|
+
:resource => ( context.owning_interaction.target_interface.resource || '(unknown)' ).to_s,
|
157
|
+
:version => context.owning_interaction.target_interface.version,
|
158
|
+
:request => {
|
159
|
+
:locale => context.request.locale,
|
160
|
+
:uri_path_components => context.request.uri_path_components,
|
161
|
+
:uri_path_extension => context.request.uri_path_extension,
|
162
|
+
:embeds => context.request.embeds,
|
163
|
+
:references => context.request.references,
|
164
|
+
:headers => context.request.headers,
|
165
|
+
:list => {
|
166
|
+
:offset => context.request.list.offset,
|
167
|
+
:limit => context.request.list.limit,
|
168
|
+
:sort_data => context.request.list.sort_data,
|
169
|
+
:search_data => context.request.list.search_data,
|
170
|
+
:filter_data => context.request.list.filter_data
|
171
|
+
}
|
172
|
+
}
|
173
|
+
}
|
174
|
+
|
175
|
+
hash[ :session ] = context.session.to_h unless context.session.nil?
|
176
|
+
return hash
|
177
|
+
|
178
|
+
rescue
|
179
|
+
return nil
|
180
|
+
|
181
|
+
end
|
99
182
|
end
|
100
183
|
end
|
101
184
|
|
@@ -75,6 +75,21 @@ module Hoodoo; module Services
|
|
75
75
|
@@reporter_pool.communicate( payload )
|
76
76
|
end
|
77
77
|
|
78
|
+
# Call all added exception reporters (see ::add) to report an exception
|
79
|
+
# based on the context of an in-flight request/response cycle. Reporters
|
80
|
+
# need to support the contextual reporting mechanism. If any do not, the
|
81
|
+
# simpler ::report mechanism is used as a fallback.
|
82
|
+
#
|
83
|
+
# +exception+:: Exception or Exception subclass instance to report.
|
84
|
+
#
|
85
|
+
# +context+:: Hoodoo::Services::Context instance describing the
|
86
|
+
# in-flight request/response cycle.
|
87
|
+
#
|
88
|
+
def self.contextual_report( exception, context )
|
89
|
+
payload = Payload.new( exception: exception, context: context )
|
90
|
+
@@reporter_pool.communicate( payload )
|
91
|
+
end
|
92
|
+
|
78
93
|
# Wait for all executing reporter threads to catch up before continuing.
|
79
94
|
#
|
80
95
|
# +timeout+:: Optional timeout wait delay *for* *each* *thread*. Default
|
@@ -99,14 +114,21 @@ module Hoodoo; module Services
|
|
99
114
|
#
|
100
115
|
attr_accessor :rack_env
|
101
116
|
|
117
|
+
# A Hoodoo::Services::Context instance describing the in-flight
|
118
|
+
# request/response cycle, if there is one. May be +nil+.
|
119
|
+
#
|
120
|
+
attr_accessor :context
|
121
|
+
|
102
122
|
# Initialize this instance with named parameters:
|
103
123
|
#
|
104
124
|
# +exception+:: Exception (or Exception subclass) instance. Mandatory.
|
105
125
|
# +rack_env+:: Rack environment hash. Optional.
|
126
|
+
# +context+:: Hoodoo::Services::Context instance. Optional.
|
106
127
|
#
|
107
|
-
def initialize( exception:, rack_env: nil )
|
128
|
+
def initialize( exception:, rack_env: nil, context: nil )
|
108
129
|
@exception = exception
|
109
130
|
@rack_env = rack_env
|
131
|
+
@context = context
|
110
132
|
end
|
111
133
|
end
|
112
134
|
end
|
@@ -48,13 +48,36 @@ module Hoodoo; module Services
|
|
48
48
|
#
|
49
49
|
# +e+:: Exception (or subclass) instance to be reported.
|
50
50
|
#
|
51
|
-
# +env+:: Optional Rack environment hash for the inbound request,
|
52
|
-
# exception reports made in the context of Rack request
|
51
|
+
# +env+:: Optional Rack environment hash for the inbound request,
|
52
|
+
# for exception reports made in the context of Rack request
|
53
53
|
# handling. In the case of Airbrake, the call may just hang
|
54
54
|
# unless a Rack environment is provided.
|
55
55
|
#
|
56
|
-
def report( e, env
|
57
|
-
|
56
|
+
def report( e, env )
|
57
|
+
opts = { :backtrace => Kernel.caller() }
|
58
|
+
opts[ :rack_env ] = env unless env.nil?
|
59
|
+
|
60
|
+
Airbrake.notify_or_ignore( e, opts )
|
61
|
+
end
|
62
|
+
|
63
|
+
# Report an exception for errors that occur within a fully handled Rack
|
64
|
+
# request context, with a high level processed Hoodoo representation
|
65
|
+
# available.
|
66
|
+
#
|
67
|
+
# +e+:: Exception (or subclass) instance to be reported.
|
68
|
+
#
|
69
|
+
# +context+:: Hoodoo::Services::Context instance describing an
|
70
|
+
# in-flight request/response cycle.
|
71
|
+
#
|
72
|
+
def contextual_report( e, context )
|
73
|
+
opts = {
|
74
|
+
:rack_env => context.owning_interaction.rack_request.env,
|
75
|
+
:backtrace => Kernel.caller(),
|
76
|
+
:environment_name => Hoodoo::Services::Middleware.environment,
|
77
|
+
:session => user_data_for( context ) || 'unknown'
|
78
|
+
}
|
79
|
+
|
80
|
+
Airbrake.notify_or_ignore( e, opts )
|
58
81
|
end
|
59
82
|
end
|
60
83
|
|
@@ -48,13 +48,29 @@ module Hoodoo; module Services
|
|
48
48
|
#
|
49
49
|
# +e+:: Exception (or subclass) instance to be reported.
|
50
50
|
#
|
51
|
-
# +env+:: Optional Rack environment hash for the inbound request,
|
52
|
-
# exception reports made in the context of Rack request
|
51
|
+
# +env+:: Optional Rack environment hash for the inbound request,
|
52
|
+
# for exception reports made in the context of Rack request
|
53
53
|
# handling.
|
54
54
|
#
|
55
55
|
def report( e, env = nil )
|
56
56
|
Raygun.track_exception( e, env )
|
57
57
|
end
|
58
|
+
|
59
|
+
# Report an exception for errors that occur within a fully handled Rack
|
60
|
+
# request context, with a high level processed Hoodoo representation
|
61
|
+
# available.
|
62
|
+
#
|
63
|
+
# +e+:: Exception (or subclass) instance to be reported.
|
64
|
+
#
|
65
|
+
# +context+:: Hoodoo::Services::Context instance describing an
|
66
|
+
# in-flight request/response cycle.
|
67
|
+
#
|
68
|
+
def contextual_report( e, context )
|
69
|
+
hash = context.owning_interaction.rack_request.env rescue {}
|
70
|
+
hash = hash.merge( :custom_data => user_data_for( context ) || { 'user_data' => 'unknown' } )
|
71
|
+
|
72
|
+
Raygun.track_exception( e, hash )
|
73
|
+
end
|
58
74
|
end
|
59
75
|
|
60
76
|
end
|
@@ -498,8 +498,12 @@ module Hoodoo; module Services
|
|
498
498
|
|
499
499
|
rescue => exception
|
500
500
|
begin
|
501
|
+
if interaction && interaction.context
|
502
|
+
ExceptionReporting.contextual_report( exception, interaction.context )
|
503
|
+
else
|
504
|
+
ExceptionReporting.report( exception, env )
|
505
|
+
end
|
501
506
|
|
502
|
-
ExceptionReporting.report( exception, env )
|
503
507
|
record_exception( interaction, exception )
|
504
508
|
|
505
509
|
return respond_for( interaction )
|
data/lib/hoodoo/version.rb
CHANGED
@@ -4,6 +4,8 @@ describe Hoodoo::Services::Middleware::ExceptionReporting::BaseReporter do
|
|
4
4
|
class TestERBase < described_class
|
5
5
|
end
|
6
6
|
|
7
|
+
# The #communicate method is checked via exception_reporting_spec.rb.
|
8
|
+
|
7
9
|
it 'is a singleton' do
|
8
10
|
expect( TestERBase.instance ).to be_a( TestERBase )
|
9
11
|
end
|
@@ -11,6 +13,97 @@ describe Hoodoo::Services::Middleware::ExceptionReporting::BaseReporter do
|
|
11
13
|
it 'provides a reporting example' do
|
12
14
|
expect {
|
13
15
|
TestERBase.instance.report( RuntimeError.new )
|
14
|
-
}.to raise_exception( RuntimeError )
|
16
|
+
}.to raise_exception( RuntimeError, 'Subclasses must implement #report' )
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'provides a contextual reporting example' do
|
20
|
+
expect {
|
21
|
+
TestERBase.instance.contextual_report( RuntimeError.new, nil )
|
22
|
+
}.to raise_exception( RuntimeError, 'Subclasses may implement #contextual_report' )
|
23
|
+
end
|
24
|
+
|
25
|
+
context '#user_data_for' do
|
26
|
+
|
27
|
+
class UserDataImplementation < Hoodoo::Services::Implementation
|
28
|
+
end
|
29
|
+
|
30
|
+
class UserDataInterface < Hoodoo::Services::Interface
|
31
|
+
interface :UserData do
|
32
|
+
version 2
|
33
|
+
endpoint :user_data, UserDataImplementation
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
before :each do
|
38
|
+
|
39
|
+
# This requires a great big heap of setup to avoid lots of mocking
|
40
|
+
# and keep the test reasonably meaningful.
|
41
|
+
|
42
|
+
session = Hoodoo::Services::Middleware.test_session()
|
43
|
+
request = Hoodoo::Services::Request.new
|
44
|
+
|
45
|
+
request.locale = 'en-nz'
|
46
|
+
request.uri_path_components = [ 'path', 'subpath' ]
|
47
|
+
request.uri_path_extension = 'tar.gz'
|
48
|
+
request.embeds = [ 'e1', 'e2' ]
|
49
|
+
request.references = [ 'r1', 'r2' ]
|
50
|
+
request.headers = { 'HTTP_X_EXAMPLE' => '42' }
|
51
|
+
request.list.offset = 0
|
52
|
+
request.list.limit = 50
|
53
|
+
request.list.sort_data = { 'created_at' => 'desc' }
|
54
|
+
request.list.search_data = { 'example' => '42' }
|
55
|
+
request.list.filter_data = { 'unexample' => '24' }
|
56
|
+
|
57
|
+
@mock_iid = Hoodoo::UUID.generate()
|
58
|
+
mock_env = { 'HTTP_X_INTERACTION_ID' => @mock_iid }
|
59
|
+
owning_interaction = Hoodoo::Services::Middleware::Interaction.new(
|
60
|
+
mock_env,
|
61
|
+
nil,
|
62
|
+
session
|
63
|
+
)
|
64
|
+
|
65
|
+
owning_interaction.target_interface = UserDataInterface
|
66
|
+
|
67
|
+
@context = Hoodoo::Services::Context.new(
|
68
|
+
session,
|
69
|
+
request,
|
70
|
+
nil,
|
71
|
+
owning_interaction
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'works' do
|
76
|
+
hash = TestERBase.instance.send( :user_data_for, @context )
|
77
|
+
expect( hash ).to eq(
|
78
|
+
{
|
79
|
+
:interaction_id => @mock_iid,
|
80
|
+
:action => '(unknown)',
|
81
|
+
:resource => 'UserData',
|
82
|
+
:version => 2,
|
83
|
+
:request => {
|
84
|
+
:locale => 'en-nz',
|
85
|
+
:uri_path_components => [ 'path', 'subpath' ],
|
86
|
+
:uri_path_extension => 'tar.gz',
|
87
|
+
:embeds => [ 'e1', 'e2' ],
|
88
|
+
:references => [ 'r1', 'r2' ],
|
89
|
+
:headers => { 'HTTP_X_EXAMPLE' => '42' },
|
90
|
+
:list => {
|
91
|
+
:offset => 0,
|
92
|
+
:limit => 50,
|
93
|
+
:sort_data => { 'created_at' => 'desc' },
|
94
|
+
:search_data => { 'example' => '42' },
|
95
|
+
:filter_data => { 'unexample' => '24' }
|
96
|
+
}
|
97
|
+
},
|
98
|
+
:session => Hoodoo::Services::Middleware.test_session().to_h()
|
99
|
+
}
|
100
|
+
)
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'returns "nil" for bad contexts' do
|
104
|
+
@context.owning_interaction.target_interface = nil
|
105
|
+
hash = TestERBase.instance.send( :user_data_for, @context )
|
106
|
+
expect( hash ).to be_nil
|
107
|
+
end
|
15
108
|
end
|
16
109
|
end
|
@@ -22,11 +22,22 @@ describe Hoodoo::Services::Middleware::ExceptionReporting do
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
+
class TestReporterD < Hoodoo::Services::Middleware::ExceptionReporting::BaseReporter
|
26
|
+
def report( e, env = nil )
|
27
|
+
expectable_hook_d1( e, env )
|
28
|
+
end
|
29
|
+
|
30
|
+
def contextual_report( e, context )
|
31
|
+
expectable_hook_d2( e, context )
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
25
35
|
after :each do
|
26
36
|
Hoodoo::Services::Middleware::ExceptionReporting.wait()
|
27
37
|
Hoodoo::Services::Middleware::ExceptionReporting.remove( TestReporterA )
|
28
38
|
Hoodoo::Services::Middleware::ExceptionReporting.remove( TestReporterB )
|
29
39
|
Hoodoo::Services::Middleware::ExceptionReporting.remove( TestReporterC )
|
40
|
+
Hoodoo::Services::Middleware::ExceptionReporting.remove( TestReporterD )
|
30
41
|
end
|
31
42
|
|
32
43
|
it 'lets me add and remove handlers' do
|
@@ -89,4 +100,63 @@ describe Hoodoo::Services::Middleware::ExceptionReporting do
|
|
89
100
|
expect( TestReporterA.instance ).to receive( :expectable_hook_a ).once.with( ex, ha )
|
90
101
|
Hoodoo::Services::Middleware::ExceptionReporting.report( ex, ha )
|
91
102
|
end
|
103
|
+
|
104
|
+
context 'with contextual reporting' do
|
105
|
+
before :each do
|
106
|
+
Hoodoo::Services::Middleware::ExceptionReporting.add( TestReporterD )
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'supports normal behaviour' do
|
110
|
+
ex = RuntimeError.new( 'D' )
|
111
|
+
ha = { :foo => :bar }
|
112
|
+
|
113
|
+
expect( TestReporterD.instance ).to receive( :expectable_hook_d1 ).once.with( ex, ha )
|
114
|
+
Hoodoo::Services::Middleware::ExceptionReporting.report( ex, ha )
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'supports contextual behaviour' do
|
118
|
+
ex = RuntimeError.new( 'D' )
|
119
|
+
co = { :bar => :baz }
|
120
|
+
|
121
|
+
expect( TestReporterD.instance ).to receive( :expectable_hook_d2 ).once.with( ex, co )
|
122
|
+
Hoodoo::Services::Middleware::ExceptionReporting.contextual_report( ex, co )
|
123
|
+
end
|
124
|
+
|
125
|
+
context 'falling back to normal' do
|
126
|
+
before :each do
|
127
|
+
Hoodoo::Services::Middleware::ExceptionReporting.add( TestReporterA )
|
128
|
+
end
|
129
|
+
|
130
|
+
# When falling back, it should extract the Rack env (mocked here) from
|
131
|
+
# the context and pass that to the normal reporter.
|
132
|
+
#
|
133
|
+
it 'extracts the Rack "env"' do
|
134
|
+
ex = RuntimeError.new( 'D' )
|
135
|
+
ha = { :foo => :bar }
|
136
|
+
co = OpenStruct.new
|
137
|
+
|
138
|
+
co.owning_interaction = OpenStruct.new
|
139
|
+
co.owning_interaction.rack_request = OpenStruct.new
|
140
|
+
co.owning_interaction.rack_request.env = ha
|
141
|
+
|
142
|
+
expect( TestReporterD.instance ).to receive( :expectable_hook_d2 ).once.with( ex, co )
|
143
|
+
expect( TestReporterA.instance ).to receive( :expectable_hook_a ).once.with( ex, ha )
|
144
|
+
|
145
|
+
Hoodoo::Services::Middleware::ExceptionReporting.contextual_report( ex, co )
|
146
|
+
end
|
147
|
+
|
148
|
+
# If it can't extract the Rack request from the context when falling back,
|
149
|
+
# the normal reporter should just be called with a "nil" second parameter.
|
150
|
+
#
|
151
|
+
it 'recovers from bad contexts' do
|
152
|
+
ex = RuntimeError.new( 'D' )
|
153
|
+
co = { :bar => :baz }
|
154
|
+
|
155
|
+
expect( TestReporterD.instance ).to receive( :expectable_hook_d2 ).once.with( ex, co )
|
156
|
+
expect( TestReporterA.instance ).to receive( :expectable_hook_a ).once.with( ex, nil )
|
157
|
+
|
158
|
+
Hoodoo::Services::Middleware::ExceptionReporting.contextual_report( ex, co )
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
92
162
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require '
|
2
|
+
require 'airbrake'
|
3
3
|
|
4
4
|
# This doesn't test the Airbrake gem / configuration itself - just check that
|
5
5
|
# the appropriate Airbrake method gets called.
|
@@ -15,10 +15,87 @@ describe Hoodoo::Services::Middleware::ExceptionReporting::AirbrakeReporter do
|
|
15
15
|
Hoodoo::Services::Middleware::ExceptionReporting.remove( described_class )
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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_or_ignore ).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 )
|
27
|
+
end
|
28
|
+
|
29
|
+
Hoodoo::Services::Middleware::ExceptionReporting.report( ex )
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'calls Airbrake correctly with an "env"' do
|
33
|
+
ex = RuntimeError.new( 'A' )
|
34
|
+
mock_env = { 'rack' => 'request' }
|
35
|
+
|
36
|
+
expect( Airbrake ).to receive( :notify_or_ignore ).once do | e, opts |
|
37
|
+
expect( e ).to be_a( Exception )
|
38
|
+
|
39
|
+
expect( opts ).to be_a( Hash )
|
40
|
+
expect( opts ).to have_key( :backtrace )
|
41
|
+
|
42
|
+
expect( opts[ :rack_env ] ).to eq( mock_env )
|
43
|
+
end
|
44
|
+
|
45
|
+
Hoodoo::Services::Middleware::ExceptionReporting.report( ex, mock_env )
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context '#contextual_report' do
|
50
|
+
it 'calls Airbrake correctly' do
|
51
|
+
ex = RuntimeError.new( 'A' )
|
52
|
+
co = OpenStruct.new
|
53
|
+
mock_user_data = { :foo => :bar }
|
54
|
+
mock_env = { 'rack' => 'request' }
|
55
|
+
|
56
|
+
co.owning_interaction = OpenStruct.new
|
57
|
+
co.owning_interaction.rack_request = OpenStruct.new
|
58
|
+
co.owning_interaction.rack_request.env = mock_env
|
59
|
+
|
60
|
+
expect( described_class.instance ).to receive( :user_data_for ).once.and_return( mock_user_data )
|
61
|
+
|
62
|
+
expect( Airbrake ).to receive( :notify_or_ignore ).once do | e, opts |
|
63
|
+
expect( e ).to be_a( Exception )
|
64
|
+
|
65
|
+
expect( opts ).to be_a( Hash )
|
66
|
+
expect( opts ).to have_key( :backtrace )
|
67
|
+
|
68
|
+
expect( opts[ :rack_env ] ).to eq( mock_env )
|
69
|
+
expect( opts[ :environment_name ] ).to eq( 'test' )
|
70
|
+
expect( opts[ :session ] ).to eq( mock_user_data )
|
71
|
+
end
|
72
|
+
|
73
|
+
Hoodoo::Services::Middleware::ExceptionReporting.contextual_report( ex, co )
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'has special case handling for user data recovery failure' do
|
77
|
+
ex = RuntimeError.new( 'A' )
|
78
|
+
co = OpenStruct.new
|
79
|
+
mock_env = { 'rack' => 'request' }
|
80
|
+
|
81
|
+
co.owning_interaction = OpenStruct.new
|
82
|
+
co.owning_interaction.rack_request = OpenStruct.new
|
83
|
+
co.owning_interaction.rack_request.env = mock_env
|
84
|
+
|
85
|
+
expect( described_class.instance ).to receive( :user_data_for ).once.and_return( nil )
|
86
|
+
|
87
|
+
expect( Airbrake ).to receive( :notify_or_ignore ).once do | e, opts |
|
88
|
+
expect( e ).to be_a( Exception )
|
89
|
+
|
90
|
+
expect( opts ).to be_a( Hash )
|
91
|
+
expect( opts ).to have_key( :backtrace )
|
92
|
+
|
93
|
+
expect( opts[ :rack_env ] ).to eq( mock_env )
|
94
|
+
expect( opts[ :environment_name ] ).to eq( 'test' )
|
95
|
+
expect( opts[ :session ] ).to eq( 'unknown' )
|
96
|
+
end
|
97
|
+
|
98
|
+
Hoodoo::Services::Middleware::ExceptionReporting.contextual_report( ex, co )
|
99
|
+
end
|
23
100
|
end
|
24
101
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require '
|
2
|
+
require 'raygun4ruby'
|
3
3
|
|
4
4
|
# This doesn't test the Raygun gem / configuration itself - just check that
|
5
5
|
# the appropriate Raygun method gets called.
|
@@ -15,9 +15,75 @@ describe Hoodoo::Services::Middleware::ExceptionReporting::RaygunReporter do
|
|
15
15
|
Hoodoo::Services::Middleware::ExceptionReporting.remove( described_class )
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
context '#report' do
|
19
|
+
it 'calls Raygun correctly without an "env"' do
|
20
|
+
ex = RuntimeError.new( 'A' )
|
21
|
+
|
22
|
+
expect( Raygun ).to receive( :track_exception ).once do | e, opts |
|
23
|
+
expect( e ).to be_a( Exception )
|
24
|
+
expect( opts ).to be_nil
|
25
|
+
end
|
26
|
+
|
27
|
+
Hoodoo::Services::Middleware::ExceptionReporting.report( ex )
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'calls Raygun correctly with an "env"' do
|
31
|
+
ex = RuntimeError.new( 'A' )
|
32
|
+
mock_env = { 'rack' => 'request' }
|
33
|
+
|
34
|
+
expect( Raygun ).to receive( :track_exception ).once do | e, opts |
|
35
|
+
expect( e ).to be_a( Exception )
|
36
|
+
|
37
|
+
expect( opts ).to be_a( Hash )
|
38
|
+
expect( opts ).to eq( mock_env )
|
39
|
+
end
|
40
|
+
|
41
|
+
Hoodoo::Services::Middleware::ExceptionReporting.report( ex, mock_env )
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context '#contextual_report' do
|
46
|
+
it 'calls Raygun correctly' do
|
47
|
+
ex = RuntimeError.new( 'A' )
|
48
|
+
co = OpenStruct.new
|
49
|
+
mock_user_data = { :foo => :bar }
|
50
|
+
mock_env = { 'rack' => 'request' }
|
51
|
+
|
52
|
+
co.owning_interaction = OpenStruct.new
|
53
|
+
co.owning_interaction.rack_request = OpenStruct.new
|
54
|
+
co.owning_interaction.rack_request.env = mock_env
|
55
|
+
|
56
|
+
expect( described_class.instance ).to receive( :user_data_for ).once.and_return( mock_user_data )
|
57
|
+
|
58
|
+
expect( Raygun ).to receive( :track_exception ).once do | e, opts |
|
59
|
+
expect( e ).to be_a( Exception )
|
60
|
+
|
61
|
+
expect( opts ).to be_a( Hash )
|
62
|
+
expect( opts ).to eq( mock_env.merge( :custom_data => mock_user_data ) )
|
63
|
+
end
|
64
|
+
|
65
|
+
Hoodoo::Services::Middleware::ExceptionReporting.contextual_report( ex, co )
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'has special case handling for user data recovery failure' do
|
69
|
+
ex = RuntimeError.new( 'A' )
|
70
|
+
co = OpenStruct.new
|
71
|
+
mock_env = { 'rack' => 'request' }
|
72
|
+
|
73
|
+
co.owning_interaction = OpenStruct.new
|
74
|
+
co.owning_interaction.rack_request = OpenStruct.new
|
75
|
+
co.owning_interaction.rack_request.env = mock_env
|
76
|
+
|
77
|
+
expect( described_class.instance ).to receive( :user_data_for ).once.and_return( nil )
|
78
|
+
|
79
|
+
expect( Raygun ).to receive( :track_exception ).once do | e, opts |
|
80
|
+
expect( e ).to be_a( Exception )
|
81
|
+
|
82
|
+
expect( opts ).to be_a( Hash )
|
83
|
+
expect( opts ).to eq( mock_env.merge( :custom_data => { 'user_data' => 'unknown' } ) )
|
84
|
+
end
|
85
|
+
|
86
|
+
Hoodoo::Services::Middleware::ExceptionReporting.contextual_report( ex, co )
|
87
|
+
end
|
22
88
|
end
|
23
89
|
end
|
@@ -367,7 +367,7 @@ describe Hoodoo::Services::Middleware do
|
|
367
367
|
|
368
368
|
expect(Hoodoo::Services::Middleware.environment).to receive(:test?).once.and_return(false)
|
369
369
|
expect(Hoodoo::Services::Middleware.environment).to receive(:development?).and_return(false)
|
370
|
-
expect(Hoodoo::Services::Middleware::ExceptionReporting).to receive(:
|
370
|
+
expect(Hoodoo::Services::Middleware::ExceptionReporting).to receive(:contextual_report).and_raise("boo!")
|
371
371
|
|
372
372
|
# Route through to the unimplemented "list" call, so the subclass raises
|
373
373
|
# an exception. This is tested independently elsewhere too. This causes
|
@@ -381,6 +381,28 @@ describe Hoodoo::Services::Middleware do
|
|
381
381
|
expect(last_response.body).to eq('Middleware exception in exception handler')
|
382
382
|
end
|
383
383
|
|
384
|
+
it 'a matching endpoint should fall back to the basic reporting API if context is not available' do
|
385
|
+
|
386
|
+
# See previous test for details.
|
387
|
+
|
388
|
+
expect(Hoodoo::Services::Middleware.environment).to receive(:test?).once.and_return(true)
|
389
|
+
expect(Hoodoo::Services::Middleware.environment).to receive(:test?).once.and_return(false)
|
390
|
+
expect(Hoodoo::Services::Middleware.environment).to receive(:development?).and_return(false)
|
391
|
+
|
392
|
+
expect(Hoodoo::Services::Middleware::Interaction).to receive(:new) do
|
393
|
+
raise("boo!")
|
394
|
+
end
|
395
|
+
|
396
|
+
# All we care about is seeing a call to #report instead of
|
397
|
+
# #contextual_report as in the previous test. Things will probably
|
398
|
+
# fail and fall to the fallback handler eventually anyway given that
|
399
|
+
# we broke the attempt to create an Interaction instance deliberately,
|
400
|
+
# so don't worry about examining the 'get' results.
|
401
|
+
#
|
402
|
+
expect(Hoodoo::Services::Middleware::ExceptionReporting).to receive(:report)
|
403
|
+
get '/v2/rspec_test_service_stub', nil, { 'CONTENT_TYPE' => 'application/json; charset=utf-8' }
|
404
|
+
end
|
405
|
+
|
384
406
|
# -------------------------------------------------------------------------
|
385
407
|
|
386
408
|
describe 'service implementation #before and #after' do
|
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: 1.
|
4
|
+
version: 1.3.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: 2016-02-
|
11
|
+
date: 2016-02-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: kgio
|