hydraulic_brake 0.0.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.
@@ -0,0 +1,79 @@
1
+ require File.expand_path '../helper', __FILE__
2
+
3
+ class LoggerTest < Test::Unit::TestCase
4
+ def stub_http(response, body = nil)
5
+ response.stubs(:body => body) if body
6
+ @http = stub(:post => response,
7
+ :read_timeout= => nil,
8
+ :open_timeout= => nil,
9
+ :use_ssl= => nil)
10
+ Net::HTTP.stubs(:new).returns(@http)
11
+ end
12
+
13
+ def send_notice
14
+ HydraulicBrake.sender.send_to_airbrake({'foo' => "bar"})
15
+ end
16
+
17
+ def stub_verbose_log
18
+ HydraulicBrake.stubs(:write_verbose_log)
19
+ end
20
+
21
+ def configure
22
+ HydraulicBrake.configure { |config| }
23
+ end
24
+
25
+ should "report that notifier is ready when configured" do
26
+ stub_verbose_log
27
+ configure
28
+ assert_logged /Notifier (.*) ready/
29
+ end
30
+
31
+ should "not report that notifier is ready when internally configured" do
32
+ stub_verbose_log
33
+ HydraulicBrake.configure(true) { |config| }
34
+ assert_not_logged /.*/
35
+ end
36
+
37
+ should "print environment info a successful notification without a body" do
38
+ reset_config
39
+ stub_verbose_log
40
+ stub_http(Net::HTTPSuccess)
41
+ send_notice
42
+ assert_logged /Environment Info:/
43
+ assert_not_logged /Response from Airbrake:/
44
+ end
45
+
46
+ should "print environment info on a failed notification without a body" do
47
+ reset_config
48
+ stub_verbose_log
49
+ stub_http(Net::HTTPError)
50
+ send_notice
51
+ assert_logged /Environment Info:/
52
+ assert_not_logged /Response from Airbrake:/
53
+ end
54
+
55
+ should "print environment info and response on a success with a body" do
56
+ reset_config
57
+ stub_verbose_log
58
+ stub_http(Net::HTTPSuccess, 'test')
59
+ send_notice
60
+ assert_logged /Environment Info:/
61
+ assert_logged /Response from Airbrake:/
62
+ end
63
+
64
+ should "print environment info and response on a failure with a body" do
65
+ reset_config
66
+ stub_verbose_log
67
+ stub_http(Net::HTTPError, 'test')
68
+ send_notice
69
+ assert_logged /Environment Info:/
70
+ assert_logged /Response from Airbrake:/
71
+ end
72
+
73
+ should "print information about the notice when Airbrake server fails" do
74
+ stub_verbose_log
75
+ stub_http(Net::HTTPError, "test")
76
+ send_notice
77
+ assert_logged /Notice details:/
78
+ end
79
+ end
@@ -0,0 +1,324 @@
1
+ require File.expand_path '../helper', __FILE__
2
+
3
+ class NoticeTest < Test::Unit::TestCase
4
+
5
+ include DefinesConstants
6
+
7
+ def configure
8
+ HydraulicBrake::Configuration.new.tap do |config|
9
+ config.api_key = 'abc123def456'
10
+ end
11
+ end
12
+
13
+ def build_notice(args = {})
14
+ configuration = args.delete(:configuration) || configure
15
+ HydraulicBrake::Notice.new(configuration.merge(args))
16
+ end
17
+
18
+ def stub_request(attrs = {})
19
+ stub('request', { :parameters => { 'one' => 'two' },
20
+ :protocol => 'http',
21
+ :host => 'some.host',
22
+ :request_uri => '/some/uri',
23
+ :session => { :to_hash => { 'a' => 'b' } },
24
+ :env => { 'three' => 'four' } }.update(attrs))
25
+ end
26
+
27
+ def assert_accepts_exception_attribute(attribute, args = {}, &block)
28
+ exception = build_exception
29
+ block ||= lambda { exception.send(attribute) }
30
+ value = block.call(exception)
31
+
32
+ notice_from_exception = build_notice(args.merge(:exception => exception))
33
+
34
+ assert_equal notice_from_exception.send(attribute),
35
+ value,
36
+ "#{attribute} was not correctly set from an exception"
37
+
38
+ notice_from_hash = build_notice(args.merge(attribute => value))
39
+ assert_equal notice_from_hash.send(attribute),
40
+ value,
41
+ "#{attribute} was not correctly set from a hash"
42
+ end
43
+
44
+ def assert_valid_notice_document(document)
45
+ xsd_path = File.join(File.dirname(__FILE__), "airbrake_2_3.xsd")
46
+ schema = Nokogiri::XML::Schema.new(IO.read(xsd_path))
47
+ errors = schema.validate(document)
48
+ assert errors.empty?, errors.collect{|e| e.message }.join
49
+ end
50
+
51
+ def build_backtrace_array
52
+ ["app/models/user.rb:13:in `magic'",
53
+ "app/controllers/users_controller.rb:8:in `index'"]
54
+ end
55
+
56
+ def hostname
57
+ `hostname`.chomp
58
+ end
59
+
60
+ def user
61
+ Struct.new(:email,:id,:name).
62
+ new("darth@vader.com",1,"Anakin Skywalker")
63
+ end
64
+
65
+ should "set the api key" do
66
+ api_key = 'key'
67
+ notice = build_notice(:api_key => api_key)
68
+ assert_equal api_key, notice.api_key
69
+ end
70
+
71
+ should "accept a project root" do
72
+ project_root = '/path/to/project'
73
+ notice = build_notice(:project_root => project_root)
74
+ assert_equal project_root, notice.project_root
75
+ end
76
+
77
+ should "accept a component" do
78
+ assert_equal 'users_controller', build_notice(:component => 'users_controller').controller
79
+ end
80
+
81
+ should "alias the component as controller" do
82
+ assert_equal 'users_controller', build_notice(:controller => 'users_controller').component
83
+ assert_equal 'users_controller', build_notice(:component => 'users_controller').controller
84
+ end
85
+
86
+ should "accept a action" do
87
+ assert_equal 'index', build_notice(:action => 'index').action
88
+ end
89
+
90
+ should "accept a url" do
91
+ url = 'http://some.host/uri'
92
+ notice = build_notice(:url => url)
93
+ assert_equal url, notice.url
94
+ end
95
+
96
+ should "set the host name" do
97
+ notice = build_notice
98
+ assert_equal hostname, notice.hostname
99
+ end
100
+
101
+ should "accept a backtrace from an exception or hash" do
102
+ array = ["user.rb:34:in `crazy'"]
103
+ exception = build_exception
104
+ exception.set_backtrace array
105
+ backtrace = HydraulicBrake::Backtrace.parse(array)
106
+ notice_from_exception = build_notice(:exception => exception)
107
+
108
+
109
+ assert_equal backtrace,
110
+ notice_from_exception.backtrace,
111
+ "backtrace was not correctly set from an exception"
112
+
113
+ notice_from_hash = build_notice(:backtrace => array)
114
+ assert_equal backtrace,
115
+ notice_from_hash.backtrace,
116
+ "backtrace was not correctly set from a hash"
117
+ end
118
+
119
+ should "accept user" do
120
+ assert_equal user.id, build_notice(:user => user).user.id
121
+ assert_equal user.email, build_notice(:user => user).user.email
122
+ assert_equal user.name, build_notice(:user => user).user.name
123
+ end
124
+
125
+ should "pass its backtrace filters for parsing" do
126
+ backtrace_array = ['my/file/backtrace:3']
127
+ exception = build_exception
128
+ exception.set_backtrace(backtrace_array)
129
+ HydraulicBrake::Backtrace.expects(:parse).with(backtrace_array, {:filters => 'foo'})
130
+
131
+ notice = HydraulicBrake::Notice.new({:exception => exception, :backtrace_filters => 'foo'})
132
+ end
133
+
134
+ should "set the error class from an exception or hash" do
135
+ assert_accepts_exception_attribute :error_class do |exception|
136
+ exception.class.name
137
+ end
138
+ end
139
+
140
+ should "set the error message from an exception or hash" do
141
+ assert_accepts_exception_attribute :error_message do |exception|
142
+ "#{exception.class.name}: #{exception.message}"
143
+ end
144
+ end
145
+
146
+ should "accept parameters from a request or hash" do
147
+ parameters = { 'one' => 'two' }
148
+ notice_from_hash = build_notice(:parameters => parameters)
149
+ assert_equal notice_from_hash.parameters, parameters
150
+ end
151
+
152
+ should "accept session data from a session_data hash" do
153
+ data = { 'one' => 'two' }
154
+ notice = build_notice(:session_data => data)
155
+ assert_equal data, notice.session_data
156
+ end
157
+
158
+ should "accept an environment name" do
159
+ assert_equal 'development', build_notice(:environment_name => 'development').environment_name
160
+ end
161
+
162
+ should "accept CGI data from a hash" do
163
+ data = { 'string' => 'value' }
164
+ notice = build_notice(:cgi_data => data)
165
+ assert_equal data, notice.cgi_data, "should take CGI data from a hash"
166
+ end
167
+
168
+ should "accept notifier information" do
169
+ params = { :notifier_name => 'a name for a notifier',
170
+ :notifier_version => '1.0.5',
171
+ :notifier_url => 'http://notifiers.r.us/download' }
172
+ notice = build_notice(params)
173
+ assert_equal params[:notifier_name], notice.notifier_name
174
+ assert_equal params[:notifier_version], notice.notifier_version
175
+ assert_equal params[:notifier_url], notice.notifier_url
176
+ end
177
+
178
+ should "set sensible defaults without an exception" do
179
+ backtrace = HydraulicBrake::Backtrace.parse(build_backtrace_array)
180
+ notice = build_notice(:backtrace => build_backtrace_array)
181
+
182
+ assert_equal 'Notification', notice.error_message
183
+ assert_array_starts_with backtrace.lines, notice.backtrace.lines
184
+ assert_equal({}, notice.parameters)
185
+ assert_equal({}, notice.session_data)
186
+ end
187
+
188
+ should "use the caller as the backtrace for an exception without a backtrace" do
189
+ filters = HydraulicBrake::Configuration.new.backtrace_filters
190
+ backtrace = HydraulicBrake::Backtrace.parse(caller, :filters => filters)
191
+ notice = build_notice(:exception => StandardError.new('error'), :backtrace => nil)
192
+
193
+ assert_array_starts_with backtrace.lines, notice.backtrace.lines
194
+ end
195
+
196
+ context "a Notice turned into XML" do
197
+ setup do
198
+ HydraulicBrake.configure do |config|
199
+ config.api_key = "1234567890"
200
+ end
201
+
202
+ @exception = build_exception
203
+
204
+ @notice = build_notice({
205
+ :notifier_name => 'a name',
206
+ :notifier_version => '1.2.3',
207
+ :notifier_url => 'http://some.url/path',
208
+ :exception => @exception,
209
+ :controller => "controller",
210
+ :action => "action",
211
+ :url => "http://url.com",
212
+ :parameters => { "paramskey" => "paramsvalue",
213
+ "nestparentkey" => { "nestkey" => "nestvalue" } },
214
+ :session_data => { "sessionkey" => "sessionvalue" },
215
+ :cgi_data => { "cgikey" => "cgivalue" },
216
+ :project_root => "RAILS_ROOT",
217
+ :environment_name => "RAILS_ENV"
218
+ })
219
+
220
+ @xml = @notice.to_xml
221
+
222
+ @document = Nokogiri::XML::Document.parse(@xml)
223
+ end
224
+
225
+ should "validate against the XML schema" do
226
+ assert_valid_notice_document @document
227
+ end
228
+
229
+ should "serialize a Notice to XML when sent #to_xml" do
230
+ assert_valid_node(@document, "//api-key", @notice.api_key)
231
+
232
+ assert_valid_node(@document, "//notifier/name", @notice.notifier_name)
233
+ assert_valid_node(@document, "//notifier/version", @notice.notifier_version)
234
+ assert_valid_node(@document, "//notifier/url", @notice.notifier_url)
235
+
236
+ assert_valid_node(@document, "//error/class", @notice.error_class)
237
+ assert_valid_node(@document, "//error/message", @notice.error_message)
238
+
239
+ assert_valid_node(@document, "//error/backtrace/line/@number", @notice.backtrace.lines.first.number)
240
+ assert_valid_node(@document, "//error/backtrace/line/@file", @notice.backtrace.lines.first.file)
241
+ assert_valid_node(@document, "//error/backtrace/line/@method", @notice.backtrace.lines.first.method)
242
+
243
+ assert_valid_node(@document, "//request/url", @notice.url)
244
+ assert_valid_node(@document, "//request/component", @notice.controller)
245
+ assert_valid_node(@document, "//request/action", @notice.action)
246
+
247
+ assert_valid_node(@document, "//request/params/var/@key", "paramskey")
248
+ assert_valid_node(@document, "//request/params/var", "paramsvalue")
249
+ assert_valid_node(@document, "//request/params/var/@key", "nestparentkey")
250
+ assert_valid_node(@document, "//request/params/var/var/@key", "nestkey")
251
+ assert_valid_node(@document, "//request/params/var/var", "nestvalue")
252
+ assert_valid_node(@document, "//request/session/var/@key", "sessionkey")
253
+ assert_valid_node(@document, "//request/session/var", "sessionvalue")
254
+ assert_valid_node(@document, "//request/cgi-data/var/@key", "cgikey")
255
+ assert_valid_node(@document, "//request/cgi-data/var", "cgivalue")
256
+
257
+ assert_valid_node(@document, "//server-environment/project-root", "RAILS_ROOT")
258
+ assert_valid_node(@document, "//server-environment/environment-name", "RAILS_ENV")
259
+ assert_valid_node(@document, "//server-environment/hostname", hostname)
260
+ end
261
+ end
262
+
263
+ should "not send empty request data" do
264
+ notice = build_notice
265
+ assert_nil notice.url
266
+ assert_nil notice.controller
267
+ assert_nil notice.action
268
+
269
+ xml = notice.to_xml
270
+ document = Nokogiri::XML.parse(xml)
271
+ assert_nil document.at('//request/url')
272
+ assert_nil document.at('//request/component')
273
+ assert_nil document.at('//request/action')
274
+
275
+ assert_valid_notice_document document
276
+ end
277
+
278
+ %w(url controller action).each do |var|
279
+ should "send a request if #{var} is present" do
280
+ notice = build_notice(var.to_sym => 'value')
281
+ xml = notice.to_xml
282
+ document = Nokogiri::XML.parse(xml)
283
+ assert_not_nil document.at('//request')
284
+ end
285
+ end
286
+
287
+ %w(parameters cgi_data session_data).each do |var|
288
+ should "send a request if #{var} is present" do
289
+ notice = build_notice(var.to_sym => { 'key' => 'value' })
290
+ xml = notice.to_xml
291
+ document = Nokogiri::XML.parse(xml)
292
+ assert_not_nil document.at('//request')
293
+ end
294
+ end
295
+
296
+ should "act like a hash" do
297
+ notice = build_notice(:error_message => 'some message')
298
+ assert_equal notice.error_message, notice[:error_message]
299
+ end
300
+
301
+ should "return params on notice[:request][:params]" do
302
+ params = { 'one' => 'two' }
303
+ notice = build_notice(:parameters => params)
304
+ assert_equal params, notice[:request][:params]
305
+ end
306
+
307
+ should "ensure #to_hash is called on objects that support it" do
308
+ assert_nothing_raised do
309
+ build_notice(:session => { :object => stub(:to_hash => {}) })
310
+ end
311
+ end
312
+
313
+ should "ensure #to_ary is called on objects that support it" do
314
+ assert_nothing_raised do
315
+ build_notice(:session => { :object => stub(:to_ary => {}) })
316
+ end
317
+ end
318
+
319
+ should "prefer passed error_message to exception message" do
320
+ exception = build_exception
321
+ notice = build_notice(:exception => exception,:error_message => "Random ponies")
322
+ assert_equal "BacktracedException: Random ponies", notice.error_message
323
+ end
324
+ end
@@ -0,0 +1,220 @@
1
+ require File.expand_path '../helper', __FILE__
2
+
3
+ class NotifierTest < Test::Unit::TestCase
4
+
5
+ class OriginalException < Exception
6
+ end
7
+
8
+ class ContinuedException < Exception
9
+ end
10
+
11
+ include DefinesConstants
12
+
13
+ def setup
14
+ super
15
+ reset_config
16
+ end
17
+
18
+ def assert_sent(notice, notice_args)
19
+ assert_received(HydraulicBrake::Notice, :new) {|expect| expect.with(has_entries(notice_args)) }
20
+ assert_received(HydraulicBrake.sender, :send_to_airbrake) {|expect| expect.with(notice) }
21
+ end
22
+
23
+ def set_public_env
24
+ HydraulicBrake.configure { |config| config.environment_name = 'production' }
25
+ end
26
+
27
+ def set_development_env
28
+ HydraulicBrake.configure { |config| config.environment_name = 'development' }
29
+ end
30
+
31
+ should "yield and save a configuration when configuring" do
32
+ yielded_configuration = nil
33
+ HydraulicBrake.configure do |config|
34
+ yielded_configuration = config
35
+ end
36
+
37
+ assert_kind_of HydraulicBrake::Configuration, yielded_configuration
38
+ assert_equal yielded_configuration, HydraulicBrake.configuration
39
+ end
40
+
41
+ should "not remove existing config options when configuring twice" do
42
+ first_config = nil
43
+ HydraulicBrake.configure do |config|
44
+ first_config = config
45
+ end
46
+ HydraulicBrake.configure do |config|
47
+ assert_equal first_config, config
48
+ end
49
+ end
50
+
51
+ should "configure the sender" do
52
+ sender = stub_sender
53
+ HydraulicBrake::Sender.stubs(:new => sender)
54
+ configuration = nil
55
+
56
+ HydraulicBrake.configure { |yielded_config| configuration = yielded_config }
57
+
58
+ assert_received(HydraulicBrake::Sender, :new) { |expect| expect.with(configuration) }
59
+ assert_equal sender, HydraulicBrake.sender
60
+ end
61
+
62
+ should "create and send a notice for an exception" do
63
+ set_public_env
64
+ exception = build_exception
65
+ stub_sender!
66
+ notice = stub_notice!
67
+
68
+ HydraulicBrake.notify(exception)
69
+
70
+ assert_sent notice, :exception => exception
71
+ end
72
+
73
+ should "create and send a notice for a hash" do
74
+ set_public_env
75
+ notice = stub_notice!
76
+ notice_args = { :error_message => 'uh oh' }
77
+ stub_sender!
78
+
79
+ HydraulicBrake.notify(notice_args)
80
+
81
+ assert_sent(notice, notice_args)
82
+ end
83
+
84
+ should "not pass the hash as an exception when sending a notice for it" do
85
+ set_public_env
86
+ notice = stub_notice!
87
+ notice_args = { :error_message => 'uh oh' }
88
+ stub_sender!
89
+
90
+ HydraulicBrake.notify(notice_args)
91
+
92
+ assert_received(HydraulicBrake::Notice, :new) {|expect| expect.with(Not(has_key(:exception))) }
93
+ end
94
+
95
+ should "create and send a notice for an exception that responds to to_hash" do
96
+ set_public_env
97
+ exception = build_exception
98
+ notice = stub_notice!
99
+ notice_args = { :error_message => 'uh oh' }
100
+ exception.stubs(:to_hash).returns(notice_args)
101
+ stub_sender!
102
+
103
+ HydraulicBrake.notify(exception)
104
+
105
+ assert_sent(notice, notice_args.merge(:exception => exception))
106
+ end
107
+
108
+ should "create and sent a notice for an exception and hash" do
109
+ set_public_env
110
+ exception = build_exception
111
+ notice = stub_notice!
112
+ notice_args = { :error_message => 'uh oh' }
113
+ stub_sender!
114
+
115
+ HydraulicBrake.notify(exception, notice_args)
116
+
117
+ assert_sent(notice, notice_args.merge(:exception => exception))
118
+ end
119
+
120
+ should "not create a notice in a development environment" do
121
+ set_development_env
122
+ sender = stub_sender!
123
+
124
+ HydraulicBrake.notify(build_exception)
125
+
126
+ assert_received(sender, :send_to_airbrake) {|expect| expect.never }
127
+ end
128
+
129
+ should "pass config to created notices" do
130
+ exception = build_exception
131
+ config_opts = { 'one' => 'two', 'three' => 'four' }
132
+ stub_notice!
133
+ stub_sender!
134
+ HydraulicBrake.configuration = stub('config', :merge => config_opts, :public? => true)
135
+
136
+ HydraulicBrake.notify(exception)
137
+
138
+ assert_received(HydraulicBrake::Notice, :new) do |expect|
139
+ expect.with(has_entries(config_opts))
140
+ end
141
+ end
142
+
143
+ context "building notice JSON for an exception" do
144
+ setup do
145
+ @params = { :controller => "users", :action => "create" }
146
+ @exception = build_exception
147
+ @hash = HydraulicBrake.build_lookup_hash_for(@exception, @params)
148
+ end
149
+
150
+ should "set action" do
151
+ assert_equal @params[:action], @hash[:action]
152
+ end
153
+
154
+ should "set controller" do
155
+ assert_equal @params[:controller], @hash[:component]
156
+ end
157
+
158
+ should "set line number" do
159
+ assert @hash[:line_number] =~ /\d+/
160
+ end
161
+
162
+ should "set file" do
163
+ assert_match(/test\/helper\.rb$/, @hash[:file])
164
+ end
165
+
166
+ should "set env to production" do
167
+ assert_equal 'production', @hash[:environment_name]
168
+ end
169
+
170
+ should "set error class" do
171
+ assert_equal @exception.class.to_s, @hash[:error_class]
172
+ end
173
+
174
+ should "not set file or line number with no backtrace" do
175
+ @exception.stubs(:backtrace).returns([])
176
+
177
+ @hash = HydraulicBrake.build_lookup_hash_for(@exception)
178
+
179
+ assert_nil @hash[:line_number]
180
+ assert_nil @hash[:file]
181
+ end
182
+
183
+ should "not set action or controller when not provided" do
184
+ @hash = HydraulicBrake.build_lookup_hash_for(@exception)
185
+
186
+ assert_nil @hash[:action]
187
+ assert_nil @hash[:controller]
188
+ end
189
+
190
+ context "when an exception that provides #original_exception is raised" do
191
+ setup do
192
+ @exception.stubs(:original_exception).returns(begin
193
+ raise NotifierTest::OriginalException.new
194
+ rescue Exception => e
195
+ e
196
+ end)
197
+ end
198
+
199
+ should "unwrap exceptions that provide #original_exception" do
200
+ @hash = HydraulicBrake.build_lookup_hash_for(@exception)
201
+ assert_equal "NotifierTest::OriginalException", @hash[:error_class]
202
+ end
203
+ end
204
+
205
+ context "when an exception that provides #continued_exception is raised" do
206
+ setup do
207
+ @exception.stubs(:continued_exception).returns(begin
208
+ raise NotifierTest::ContinuedException.new
209
+ rescue Exception => e
210
+ e
211
+ end)
212
+ end
213
+
214
+ should "unwrap exceptions that provide #continued_exception" do
215
+ @hash = HydraulicBrake.build_lookup_hash_for(@exception)
216
+ assert_equal "NotifierTest::ContinuedException", @hash[:error_class]
217
+ end
218
+ end
219
+ end
220
+ end
@@ -0,0 +1,10 @@
1
+ require File.expand_path '../helper', __FILE__
2
+
3
+ class RecursionTest < Test::Unit::TestCase
4
+ should "not allow infinite recursion" do
5
+ hash = {:a => :a}
6
+ hash[:hash] = hash
7
+ notice = HydraulicBrake::Notice.new(:parameters => hash)
8
+ assert_equal "[possible infinite recursion halted]", notice.parameters[:hash]
9
+ end
10
+ end