hoptoad_notifier 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,314 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class CatcherTest < Test::Unit::TestCase
4
+
5
+ include DefinesConstants
6
+
7
+ def setup
8
+ super
9
+ reset_config
10
+ HoptoadNotifier.sender = CollectingSender.new
11
+ define_constant('RAILS_ROOT', '/path/to/rails/root')
12
+ end
13
+
14
+ def ignore(exception_class)
15
+ HoptoadNotifier.configuration.ignore << exception_class
16
+ end
17
+
18
+ def build_controller_class(&definition)
19
+ returning Class.new(ActionController::Base) do |klass|
20
+ klass.__send__(:include, HoptoadNotifier::Catcher)
21
+ klass.class_eval(&definition) if definition
22
+ define_constant('HoptoadTestController', klass)
23
+ end
24
+ end
25
+
26
+ def assert_sent_hash(hash, xpath)
27
+ hash.each do |key, value|
28
+ element_xpath = "#{xpath}/var[@key = '#{key}']"
29
+ if value.respond_to?(:to_hash)
30
+ assert_sent_hash value.to_hash, element_xpath
31
+ else
32
+ assert_sent_element value.to_s, element_xpath
33
+ end
34
+ end
35
+ end
36
+
37
+ def assert_sent_element(value, xpath)
38
+ assert_valid_node last_sent_notice_document, xpath, value
39
+ end
40
+
41
+ def assert_sent_request_info_for(request)
42
+ params = request.parameters.to_hash
43
+ assert_sent_hash params, '/notice/request/params'
44
+ assert_sent_element params['controller'], '/notice/request/component'
45
+ assert_sent_element params['action'], '/notice/request/action'
46
+ assert_sent_element url_from_request(request), '/notice/request/url'
47
+ assert_sent_hash request.env, '/notice/request/cgi-data'
48
+ end
49
+
50
+ def url_from_request(request)
51
+ url = "#{request.protocol}#{request.host}"
52
+
53
+ unless [80, 443].include?(request.port)
54
+ url << ":#{request.port}"
55
+ end
56
+
57
+ url << request.request_uri
58
+ url
59
+ end
60
+
61
+ def sender
62
+ HoptoadNotifier.sender
63
+ end
64
+
65
+ def last_sent_notice_xml
66
+ sender.collected.last
67
+ end
68
+
69
+ def last_sent_notice_document
70
+ assert_not_nil xml = last_sent_notice_xml, "No xml was sent"
71
+ Nokogiri::XML.parse(xml)
72
+ end
73
+
74
+ def process_action(opts = {}, &action)
75
+ opts[:request] ||= ActionController::TestRequest.new
76
+ opts[:response] ||= ActionController::TestResponse.new
77
+ klass = build_controller_class do
78
+ cattr_accessor :local
79
+ define_method(:index, &action)
80
+ def local_request?
81
+ local
82
+ end
83
+ end
84
+ if opts[:filters]
85
+ klass.filter_parameter_logging *opts[:filters]
86
+ end
87
+ if opts[:user_agent]
88
+ if opts[:request].respond_to?(:user_agent=)
89
+ opts[:request].user_agent = opts[:user_agent]
90
+ else
91
+ opts[:request].env["HTTP_USER_AGENT"] = opts[:user_agent]
92
+ end
93
+ end
94
+ if opts[:port]
95
+ opts[:request].port = opts[:port]
96
+ end
97
+ klass.consider_all_requests_local = opts[:all_local]
98
+ klass.local = opts[:local]
99
+ controller = klass.new
100
+ controller.stubs(:rescue_action_in_public_without_hoptoad)
101
+ opts[:request].query_parameters = opts[:request].query_parameters.merge(opts[:params] || {})
102
+ opts[:request].session = ActionController::TestSession.new(opts[:session] || {})
103
+ controller.process(opts[:request], opts[:response])
104
+ controller
105
+ end
106
+
107
+ def process_action_with_manual_notification(args = {})
108
+ process_action(args) do
109
+ notify_hoptoad(:error_message => 'fail')
110
+ # Rails will raise a template error if we don't render something
111
+ render :nothing => true
112
+ end
113
+ end
114
+
115
+ def process_action_with_automatic_notification(args = {})
116
+ process_action(args) { raise "Hello" }
117
+ end
118
+
119
+ should "deliver notices from exceptions raised in public requests" do
120
+ process_action_with_automatic_notification
121
+ assert_caught_and_sent
122
+ end
123
+
124
+ should "not deliver notices from exceptions in local requests" do
125
+ process_action_with_automatic_notification(:local => true)
126
+ assert_caught_and_not_sent
127
+ end
128
+
129
+ should "not deliver notices from exceptions when all requests are local" do
130
+ process_action_with_automatic_notification(:all_local => true)
131
+ assert_caught_and_not_sent
132
+ end
133
+
134
+ should "not deliver notices from actions that don't raise" do
135
+ controller = process_action { render :text => 'Hello' }
136
+ assert_caught_and_not_sent
137
+ assert_equal 'Hello', controller.response.body
138
+ end
139
+
140
+ should "not deliver ignored exceptions raised by actions" do
141
+ ignore(RuntimeError)
142
+ process_action_with_automatic_notification
143
+ assert_caught_and_not_sent
144
+ end
145
+
146
+ should "deliver ignored exception raised manually" do
147
+ ignore(RuntimeError)
148
+ process_action_with_manual_notification
149
+ assert_caught_and_sent
150
+ end
151
+
152
+ should "deliver manually sent notices in public requests" do
153
+ process_action_with_manual_notification
154
+ assert_caught_and_sent
155
+ end
156
+
157
+ should "not deliver manually sent notices in local requests" do
158
+ process_action_with_manual_notification(:local => true)
159
+ assert_caught_and_not_sent
160
+ end
161
+
162
+ should "not deliver manually sent notices when all requests are local" do
163
+ process_action_with_manual_notification(:all_local => true)
164
+ assert_caught_and_not_sent
165
+ end
166
+
167
+ should "continue with default behavior after delivering an exception" do
168
+ controller = process_action_with_automatic_notification(:public => true)
169
+ # TODO: can we test this without stubbing?
170
+ assert_received(controller, :rescue_action_in_public_without_hoptoad)
171
+ end
172
+
173
+ should "not create actions from Hoptoad methods" do
174
+ controller = build_controller_class.new
175
+ assert_equal [], HoptoadNotifier::Catcher.instance_methods
176
+ end
177
+
178
+ should "ignore exceptions when user agent is being ignored by regular expression" do
179
+ HoptoadNotifier.configuration.ignore_user_agent_only = [/Ignored/]
180
+ process_action_with_automatic_notification(:user_agent => 'ShouldBeIgnored')
181
+ assert_caught_and_not_sent
182
+ end
183
+
184
+ should "ignore exceptions when user agent is being ignored by string" do
185
+ HoptoadNotifier.configuration.ignore_user_agent_only = ['IgnoredUserAgent']
186
+ process_action_with_automatic_notification(:user_agent => 'IgnoredUserAgent')
187
+ assert_caught_and_not_sent
188
+ end
189
+
190
+ should "not ignore exceptions when user agent is not being ignored" do
191
+ HoptoadNotifier.configuration.ignore_user_agent_only = ['IgnoredUserAgent']
192
+ process_action_with_automatic_notification(:user_agent => 'NonIgnoredAgent')
193
+ assert_caught_and_sent
194
+ end
195
+
196
+ should "send session data for manual notifications" do
197
+ data = { 'one' => 'two' }
198
+ process_action_with_manual_notification(:session => data)
199
+ assert_sent_hash data, "/notice/request/session"
200
+ end
201
+
202
+ should "send session data for automatic notification" do
203
+ data = { 'one' => 'two' }
204
+ process_action_with_automatic_notification(:session => data)
205
+ assert_sent_hash data, "/notice/request/session"
206
+ end
207
+
208
+ should "send request data for manual notification" do
209
+ params = { 'controller' => "hoptoad_test", 'action' => "index" }
210
+ controller = process_action_with_manual_notification(:params => params)
211
+ assert_sent_request_info_for controller.request
212
+ end
213
+
214
+ should "send request data for manual notification with non-standard port" do
215
+ params = { 'controller' => "hoptoad_test", 'action' => "index" }
216
+ controller = process_action_with_manual_notification(:params => params, :port => 81)
217
+ assert_sent_request_info_for controller.request
218
+ end
219
+
220
+ should "send request data for automatic notification" do
221
+ params = { 'controller' => "hoptoad_test", 'action' => "index" }
222
+ controller = process_action_with_automatic_notification(:params => params)
223
+ assert_sent_request_info_for controller.request
224
+ end
225
+
226
+ should "send request data for automatic notification with non-standard port" do
227
+ params = { 'controller' => "hoptoad_test", 'action' => "index" }
228
+ controller = process_action_with_automatic_notification(:params => params, :port => 81)
229
+ assert_sent_request_info_for controller.request
230
+ end
231
+
232
+ should "use standard rails logging filters on params and env" do
233
+ filtered_params = { "abc" => "123",
234
+ "def" => "456",
235
+ "ghi" => "[FILTERED]" }
236
+ ENV['ghi'] = 'abc'
237
+ filtered_env = { 'ghi' => '[FILTERED]' }
238
+ filtered_cgi = { 'REQUEST_METHOD' => '[FILTERED]' }
239
+
240
+ process_action_with_automatic_notification(:filters => [:ghi, :request_method],
241
+ :params => { "abc" => "123",
242
+ "def" => "456",
243
+ "ghi" => "789" })
244
+ assert_sent_hash filtered_params, '/notice/request/params'
245
+ assert_sent_hash filtered_cgi, '/notice/request/cgi-data'
246
+ end
247
+
248
+ context "for a local error with development lookup enabled" do
249
+ setup do
250
+ HoptoadNotifier.configuration.development_lookup = true
251
+ HoptoadNotifier.stubs(:build_lookup_hash_for).returns({ :awesome => 2 })
252
+
253
+ @controller = process_action_with_automatic_notification(:local => true)
254
+ @response = @controller.response
255
+ end
256
+
257
+ should "append custom CSS and JS to response body for a local error" do
258
+ assert_match /text\/css/, @response.body
259
+ assert_match /text\/javascript/, @response.body
260
+ end
261
+
262
+ should "contain host, API key and notice JSON" do
263
+ assert_match HoptoadNotifier.configuration.host.to_json, @response.body
264
+ assert_match HoptoadNotifier.configuration.api_key.to_json, @response.body
265
+ assert_match ({ :awesome => 2 }).to_json, @response.body
266
+ end
267
+ end
268
+
269
+ context "for a local error with development lookup disabled" do
270
+ setup do
271
+ HoptoadNotifier.configuration.development_lookup = false
272
+
273
+ @controller = process_action_with_automatic_notification(:local => true)
274
+ @response = @controller.response
275
+ end
276
+
277
+ should "not append custom CSS and JS to response for a local error" do
278
+ assert_no_match /text\/css/, @response.body
279
+ assert_no_match /text\/javascript/, @response.body
280
+ end
281
+ end
282
+
283
+ should "call session.to_hash if available" do
284
+ hash_data = {:key => :value}
285
+
286
+ session = ActionController::TestSession.new
287
+ ActionController::TestSession.stubs(:new).returns(session)
288
+ session.stubs(:to_hash).returns(hash_data)
289
+
290
+ process_action_with_automatic_notification
291
+ assert_received(session, :to_hash)
292
+ assert_received(session, :data) { |expect| expect.never }
293
+ assert_caught_and_sent
294
+ end
295
+
296
+ should "call session.data if session.to_hash is undefined" do
297
+ hash_data = {:key => :value}
298
+
299
+ session = ActionController::TestSession.new
300
+ ActionController::TestSession.stubs(:new).returns(session)
301
+ session.stubs(:data).returns(hash_data)
302
+ if session.respond_to?(:to_hash)
303
+ class << session
304
+ undef to_hash
305
+ end
306
+ end
307
+
308
+ process_action_with_automatic_notification
309
+ assert_received(session, :to_hash) { |expect| expect.never }
310
+ assert_received(session, :data) { |expect| expect.at_least_once }
311
+ assert_caught_and_sent
312
+ end
313
+
314
+ end
@@ -0,0 +1,207 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class ConfigurationTest < Test::Unit::TestCase
4
+
5
+ include DefinesConstants
6
+
7
+ should "provide default values" do
8
+ assert_config_default :proxy_host, nil
9
+ assert_config_default :proxy_port, nil
10
+ assert_config_default :proxy_user, nil
11
+ assert_config_default :proxy_pass, nil
12
+ assert_config_default :project_root, nil
13
+ assert_config_default :environment_name, nil
14
+ assert_config_default :logger, nil
15
+ assert_config_default :notifier_version, HoptoadNotifier::VERSION
16
+ assert_config_default :notifier_name, 'Hoptoad Notifier'
17
+ assert_config_default :notifier_url, 'http://hoptoadapp.com'
18
+ assert_config_default :secure, false
19
+ assert_config_default :host, 'hoptoadapp.com'
20
+ assert_config_default :http_open_timeout, 2
21
+ assert_config_default :http_read_timeout, 5
22
+ assert_config_default :ignore_by_filters, []
23
+ assert_config_default :ignore_user_agent, []
24
+ assert_config_default :params_filters,
25
+ HoptoadNotifier::Configuration::DEFAULT_PARAMS_FILTERS
26
+ assert_config_default :backtrace_filters,
27
+ HoptoadNotifier::Configuration::DEFAULT_BACKTRACE_FILTERS
28
+ assert_config_default :ignore,
29
+ HoptoadNotifier::Configuration::IGNORE_DEFAULT
30
+ assert_config_default :development_lookup, true
31
+ end
32
+
33
+ should "provide default values for secure connections" do
34
+ config = HoptoadNotifier::Configuration.new
35
+ config.secure = true
36
+ assert_equal 443, config.port
37
+ assert_equal 'https', config.protocol
38
+ end
39
+
40
+ should "provide default values for insecure connections" do
41
+ config = HoptoadNotifier::Configuration.new
42
+ config.secure = false
43
+ assert_equal 80, config.port
44
+ assert_equal 'http', config.protocol
45
+ end
46
+
47
+ should "not cache inferred ports" do
48
+ config = HoptoadNotifier::Configuration.new
49
+ config.secure = false
50
+ config.port
51
+ config.secure = true
52
+ assert_equal 443, config.port
53
+ end
54
+
55
+ should "allow values to be overwritten" do
56
+ assert_config_overridable :proxy_host
57
+ assert_config_overridable :proxy_port
58
+ assert_config_overridable :proxy_user
59
+ assert_config_overridable :proxy_pass
60
+ assert_config_overridable :secure
61
+ assert_config_overridable :host
62
+ assert_config_overridable :port
63
+ assert_config_overridable :http_open_timeout
64
+ assert_config_overridable :http_read_timeout
65
+ assert_config_overridable :project_root
66
+ assert_config_overridable :notifier_version
67
+ assert_config_overridable :notifier_name
68
+ assert_config_overridable :notifier_url
69
+ assert_config_overridable :environment_name
70
+ assert_config_overridable :development_lookup
71
+ assert_config_overridable :logger
72
+ end
73
+
74
+ should "have an api key" do
75
+ assert_config_overridable :api_key
76
+ end
77
+
78
+ should "act like a hash" do
79
+ config = HoptoadNotifier::Configuration.new
80
+ hash = config.to_hash
81
+ [:api_key, :backtrace_filters, :development_environments,
82
+ :environment_name, :host, :http_open_timeout,
83
+ :http_read_timeout, :ignore, :ignore_by_filters, :ignore_user_agent,
84
+ :notifier_name, :notifier_url, :notifier_version, :params_filters,
85
+ :project_root, :port, :protocol, :proxy_host, :proxy_pass, :proxy_port,
86
+ :proxy_user, :secure, :development_lookup].each do |option|
87
+ assert_equal config[option], hash[option], "Wrong value for #{option}"
88
+ end
89
+ end
90
+
91
+ should "be mergable" do
92
+ config = HoptoadNotifier::Configuration.new
93
+ hash = config.to_hash
94
+ assert_equal hash.merge(:key => 'value'), config.merge(:key => 'value')
95
+ end
96
+
97
+ should "allow param filters to be appended" do
98
+ assert_appends_value :params_filters
99
+ end
100
+
101
+ should "warn when attempting to read environment filters" do
102
+ config = HoptoadNotifier::Configuration.new
103
+ config.
104
+ expects(:warn).
105
+ with(regexp_matches(/deprecated/i))
106
+ assert_equal [], config.environment_filters
107
+ end
108
+
109
+ should "allow ignored user agents to be appended" do
110
+ assert_appends_value :ignore_user_agent
111
+ end
112
+
113
+ should "allow backtrace filters to be appended" do
114
+ assert_appends_value(:backtrace_filters) do |config|
115
+ new_filter = lambda {}
116
+ config.filter_backtrace(&new_filter)
117
+ new_filter
118
+ end
119
+ end
120
+
121
+ should "allow ignore by filters to be appended" do
122
+ assert_appends_value(:ignore_by_filters) do |config|
123
+ new_filter = lambda {}
124
+ config.ignore_by_filter(&new_filter)
125
+ new_filter
126
+ end
127
+ end
128
+
129
+ should "allow ignored exceptions to be appended" do
130
+ config = HoptoadNotifier::Configuration.new
131
+ original_filters = config.ignore.dup
132
+ new_filter = 'hello'
133
+ config.ignore << new_filter
134
+ assert_same_elements original_filters + [new_filter], config.ignore
135
+ end
136
+
137
+ should "allow ignored exceptions to be replaced" do
138
+ assert_replaces(:ignore, :ignore_only=)
139
+ end
140
+
141
+ should "allow ignored user agents to be replaced" do
142
+ assert_replaces(:ignore_user_agent, :ignore_user_agent_only=)
143
+ end
144
+
145
+ should "use development and test as development environments by default" do
146
+ config = HoptoadNotifier::Configuration.new
147
+ assert_same_elements %w(development test cucumber), config.development_environments
148
+ end
149
+
150
+ should "be public in a public environment" do
151
+ config = HoptoadNotifier::Configuration.new
152
+ config.development_environments = %w(development)
153
+ config.environment_name = 'production'
154
+ assert config.public?
155
+ end
156
+
157
+ should "not be public in a development environment" do
158
+ config = HoptoadNotifier::Configuration.new
159
+ config.development_environments = %w(staging)
160
+ config.environment_name = 'staging'
161
+ assert !config.public?
162
+ end
163
+
164
+ should "be public without an environment name" do
165
+ config = HoptoadNotifier::Configuration.new
166
+ assert config.public?
167
+ end
168
+
169
+ should "use the assigned logger if set" do
170
+ config = HoptoadNotifier::Configuration.new
171
+ config.logger = "CUSTOM LOGGER"
172
+ assert_equal "CUSTOM LOGGER", config.logger
173
+ end
174
+
175
+ def assert_config_default(option, default_value, config = nil)
176
+ config ||= HoptoadNotifier::Configuration.new
177
+ assert_equal default_value, config.send(option)
178
+ end
179
+
180
+ def assert_config_overridable(option, value = 'a value')
181
+ config = HoptoadNotifier::Configuration.new
182
+ config.send(:"#{option}=", value)
183
+ assert_equal value, config.send(option)
184
+ end
185
+
186
+ def assert_appends_value(option, &block)
187
+ config = HoptoadNotifier::Configuration.new
188
+ original_values = config.send(option).dup
189
+ block ||= lambda do |config|
190
+ new_value = 'hello'
191
+ config.send(option) << new_value
192
+ new_value
193
+ end
194
+ new_value = block.call(config)
195
+ assert_same_elements original_values + [new_value], config.send(option)
196
+ end
197
+
198
+ def assert_replaces(option, setter)
199
+ config = HoptoadNotifier::Configuration.new
200
+ new_value = 'hello'
201
+ config.send(setter, [new_value])
202
+ assert_equal [new_value], config.send(option)
203
+ config.send(setter, new_value)
204
+ assert_equal [new_value], config.send(option)
205
+ end
206
+
207
+ end