hoodoo 1.2.3 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|