honeybadger 1.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.
Files changed (65) hide show
  1. data/Gemfile +13 -0
  2. data/Gemfile.lock +114 -0
  3. data/Guardfile +5 -0
  4. data/MIT-LICENSE +22 -0
  5. data/README.md +271 -0
  6. data/Rakefile +261 -0
  7. data/SUPPORTED_RAILS_VERSIONS +26 -0
  8. data/TESTING.md +33 -0
  9. data/features/metal.feature +18 -0
  10. data/features/rack.feature +56 -0
  11. data/features/rails.feature +211 -0
  12. data/features/rake.feature +27 -0
  13. data/features/sinatra.feature +29 -0
  14. data/features/step_definitions/file_steps.rb +10 -0
  15. data/features/step_definitions/metal_steps.rb +23 -0
  16. data/features/step_definitions/rack_steps.rb +23 -0
  17. data/features/step_definitions/rails_application_steps.rb +394 -0
  18. data/features/step_definitions/rake_steps.rb +17 -0
  19. data/features/support/env.rb +17 -0
  20. data/features/support/honeybadger_shim.rb.template +8 -0
  21. data/features/support/rails.rb +201 -0
  22. data/features/support/rake/Rakefile +68 -0
  23. data/features/support/terminal.rb +107 -0
  24. data/generators/honeybadger/honeybadger_generator.rb +94 -0
  25. data/generators/honeybadger/lib/insert_commands.rb +34 -0
  26. data/generators/honeybadger/lib/rake_commands.rb +24 -0
  27. data/generators/honeybadger/templates/capistrano_hook.rb +6 -0
  28. data/generators/honeybadger/templates/honeybadger_tasks.rake +25 -0
  29. data/generators/honeybadger/templates/initializer.rb +6 -0
  30. data/honeybadger.gemspec +109 -0
  31. data/lib/honeybadger.rb +162 -0
  32. data/lib/honeybadger/backtrace.rb +123 -0
  33. data/lib/honeybadger/capistrano.rb +43 -0
  34. data/lib/honeybadger/configuration.rb +273 -0
  35. data/lib/honeybadger/notice.rb +314 -0
  36. data/lib/honeybadger/rack.rb +55 -0
  37. data/lib/honeybadger/rails.rb +34 -0
  38. data/lib/honeybadger/rails/action_controller_catcher.rb +30 -0
  39. data/lib/honeybadger/rails/controller_methods.rb +69 -0
  40. data/lib/honeybadger/rails/middleware/exceptions_catcher.rb +29 -0
  41. data/lib/honeybadger/rails3_tasks.rb +84 -0
  42. data/lib/honeybadger/railtie.rb +45 -0
  43. data/lib/honeybadger/rake_handler.rb +65 -0
  44. data/lib/honeybadger/sender.rb +120 -0
  45. data/lib/honeybadger/shared_tasks.rb +36 -0
  46. data/lib/honeybadger/tasks.rb +82 -0
  47. data/lib/honeybadger_tasks.rb +65 -0
  48. data/lib/rails/generators/honeybadger/honeybadger_generator.rb +99 -0
  49. data/rails/init.rb +1 -0
  50. data/resources/README.md +34 -0
  51. data/resources/ca-bundle.crt +3376 -0
  52. data/script/integration_test.rb +38 -0
  53. data/test/test_helper.rb +143 -0
  54. data/test/unit/backtrace_test.rb +180 -0
  55. data/test/unit/capistrano_test.rb +34 -0
  56. data/test/unit/configuration_test.rb +201 -0
  57. data/test/unit/honeybadger_tasks_test.rb +163 -0
  58. data/test/unit/logger_test.rb +72 -0
  59. data/test/unit/notice_test.rb +406 -0
  60. data/test/unit/notifier_test.rb +245 -0
  61. data/test/unit/rack_test.rb +56 -0
  62. data/test/unit/rails/action_controller_catcher_test.rb +300 -0
  63. data/test/unit/rails_test.rb +35 -0
  64. data/test/unit/sender_test.rb +257 -0
  65. metadata +315 -0
@@ -0,0 +1,245 @@
1
+ require 'test_helper'
2
+
3
+ class NotifierTest < Honeybadger::UnitTest
4
+ class OriginalException < Exception
5
+ end
6
+
7
+ class ContinuedException < Exception
8
+ end
9
+
10
+ include DefinesConstants
11
+
12
+ def setup
13
+ super
14
+ reset_config
15
+ end
16
+
17
+ def assert_sent(notice, notice_args)
18
+ assert_received(Honeybadger::Notice, :new) {|expect| expect.with(has_entries(notice_args)) }
19
+ assert_received(notice, :to_json)
20
+ assert_received(Honeybadger.sender, :send_to_honeybadger) {|expect| expect.with(notice.to_json) }
21
+ end
22
+
23
+ def set_public_env
24
+ Honeybadger.configure { |config| config.environment_name = 'production' }
25
+ end
26
+
27
+ def set_development_env
28
+ Honeybadger.configure { |config| config.environment_name = 'development' }
29
+ end
30
+
31
+ should "yield and save a configuration when configuring" do
32
+ yielded_configuration = nil
33
+ Honeybadger.configure do |config|
34
+ yielded_configuration = config
35
+ end
36
+
37
+ assert_kind_of Honeybadger::Configuration, yielded_configuration
38
+ assert_equal yielded_configuration, Honeybadger.configuration
39
+ end
40
+
41
+ should "not remove existing config options when configuring twice" do
42
+ first_config = nil
43
+ Honeybadger.configure do |config|
44
+ first_config = config
45
+ end
46
+ Honeybadger.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
+ Honeybadger::Sender.stubs(:new => sender)
54
+ configuration = nil
55
+
56
+ Honeybadger.configure { |yielded_config| configuration = yielded_config }
57
+
58
+ assert_received(Honeybadger::Sender, :new) { |expect| expect.with(configuration) }
59
+ assert_equal sender, Honeybadger.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
+ Honeybadger.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
+ Honeybadger.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
+ Honeybadger.notify(notice_args)
91
+
92
+ assert_received(Honeybadger::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
+ Honeybadger.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
+ Honeybadger.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
+ Honeybadger.notify(build_exception)
125
+ Honeybadger.notify_or_ignore(build_exception)
126
+
127
+ assert_received(sender, :send_to_honeybadger) {|expect| expect.never }
128
+ end
129
+
130
+ should "not deliver an ignored exception when notifying implicitly" do
131
+ set_public_env
132
+ exception = build_exception
133
+ sender = stub_sender!
134
+ notice = stub_notice!
135
+ notice.stubs(:ignore? => true)
136
+
137
+ Honeybadger.notify_or_ignore(exception)
138
+
139
+ assert_received(sender, :send_to_honeybadger) {|expect| expect.never }
140
+ end
141
+
142
+ should "deliver an ignored exception when notifying manually" do
143
+ set_public_env
144
+ exception = build_exception
145
+ sender = stub_sender!
146
+ notice = stub_notice!
147
+ notice.stubs(:ignore? => true)
148
+
149
+ Honeybadger.notify(exception)
150
+
151
+ assert_sent(notice, :exception => exception)
152
+ end
153
+
154
+ should "pass config to created notices" do
155
+ exception = build_exception
156
+ config_opts = { 'one' => 'two', 'three' => 'four' }
157
+ notice = stub_notice!
158
+ stub_sender!
159
+ Honeybadger.configuration = stub('config', :merge => config_opts, :public? => true)
160
+
161
+ Honeybadger.notify(exception)
162
+
163
+ assert_received(Honeybadger::Notice, :new) do |expect|
164
+ expect.with(has_entries(config_opts))
165
+ end
166
+ end
167
+
168
+ context "building notice JSON for an exception" do
169
+ setup do
170
+ @params = { :controller => "users", :action => "create" }
171
+ @exception = build_exception
172
+ @hash = Honeybadger.build_lookup_hash_for(@exception, @params)
173
+ end
174
+
175
+ should "set action" do
176
+ assert_equal @params[:action], @hash[:action]
177
+ end
178
+
179
+ should "set controller" do
180
+ assert_equal @params[:controller], @hash[:component]
181
+ end
182
+
183
+ should "set line number" do
184
+ assert @hash[:line_number] =~ /\d+/
185
+ end
186
+
187
+ should "set file" do
188
+ assert_match /honeybadger\/rack_test\.rb$/, @hash[:file]
189
+ end
190
+
191
+ should "set rails_env to production" do
192
+ assert_equal 'production', @hash[:environment_name]
193
+ end
194
+
195
+ should "set error class" do
196
+ assert_equal @exception.class.to_s, @hash[:error_class]
197
+ end
198
+
199
+ should "not set file or line number with no backtrace" do
200
+ @exception.stubs(:backtrace).returns([])
201
+
202
+ @hash = Honeybadger.build_lookup_hash_for(@exception)
203
+
204
+ assert_nil @hash[:line_number]
205
+ assert_nil @hash[:file]
206
+ end
207
+
208
+ should "not set action or controller when not provided" do
209
+ @hash = Honeybadger.build_lookup_hash_for(@exception)
210
+
211
+ assert_nil @hash[:action]
212
+ assert_nil @hash[:controller]
213
+ end
214
+
215
+ context "when an exception that provides #original_exception is raised" do
216
+ setup do
217
+ @exception.stubs(:original_exception).returns(begin
218
+ raise NotifierTest::OriginalException.new
219
+ rescue Exception => e
220
+ e
221
+ end)
222
+ end
223
+
224
+ should "unwrap exceptions that provide #original_exception" do
225
+ @hash = Honeybadger.build_lookup_hash_for(@exception)
226
+ assert_equal "NotifierTest::OriginalException", @hash[:error_class]
227
+ end
228
+ end
229
+
230
+ context "when an exception that provides #continued_exception is raised" do
231
+ setup do
232
+ @exception.stubs(:continued_exception).returns(begin
233
+ raise NotifierTest::ContinuedException.new
234
+ rescue Exception => e
235
+ e
236
+ end)
237
+ end
238
+
239
+ should "unwrap exceptions that provide #continued_exception" do
240
+ @hash = Honeybadger.build_lookup_hash_for(@exception)
241
+ assert_equal "NotifierTest::ContinuedException", @hash[:error_class]
242
+ end
243
+ end
244
+ end
245
+ end
@@ -0,0 +1,56 @@
1
+ require 'test_helper'
2
+
3
+ class RackTest < Honeybadger::UnitTest
4
+ should "call the upstream app with the environment" do
5
+ environment = { 'key' => 'value' }
6
+ app = lambda { |env| ['response', {}, env] }
7
+ stack = Honeybadger::Rack.new(app)
8
+
9
+ response = stack.call(environment)
10
+
11
+ assert_equal ['response', {}, environment], response
12
+ end
13
+
14
+ should "deliver an exception raised while calling an upstream app" do
15
+ Honeybadger.stubs(:notify_or_ignore)
16
+
17
+ exception = build_exception
18
+ environment = { 'key' => 'value' }
19
+ app = lambda do |env|
20
+ raise exception
21
+ end
22
+
23
+ begin
24
+ stack = Honeybadger::Rack.new(app)
25
+ stack.call(environment)
26
+ rescue Exception => raised
27
+ assert_equal exception, raised
28
+ else
29
+ flunk "Didn't raise an exception"
30
+ end
31
+
32
+ assert_received(Honeybadger, :notify_or_ignore) do |expect|
33
+ expect.with(exception, :rack_env => environment)
34
+ end
35
+ end
36
+
37
+ should "deliver an exception in rack.exception" do
38
+ Honeybadger.stubs(:notify_or_ignore)
39
+ exception = build_exception
40
+ environment = { 'key' => 'value' }
41
+
42
+ response = [200, {}, ['okay']]
43
+ app = lambda do |env|
44
+ env['rack.exception'] = exception
45
+ response
46
+ end
47
+ stack = Honeybadger::Rack.new(app)
48
+
49
+ actual_response = stack.call(environment)
50
+
51
+ assert_equal response, actual_response
52
+ assert_received(Honeybadger, :notify_or_ignore) do |expect|
53
+ expect.with(exception, :rack_env => environment)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,300 @@
1
+ require 'test_helper'
2
+ require 'honeybadger/rails'
3
+
4
+ class ActionControllerCatcherTest < Honeybadger::UnitTest
5
+ include DefinesConstants
6
+
7
+ def setup
8
+ super
9
+ reset_config
10
+ Honeybadger.sender = CollectingSender.new
11
+ define_constant('RAILS_ROOT', '/path/to/rails/root')
12
+ end
13
+
14
+ def ignore(exception_class)
15
+ Honeybadger.configuration.ignore << exception_class
16
+ end
17
+
18
+ def build_controller_class(&definition)
19
+ Class.new(ActionController::Base).tap do |klass|
20
+ klass.__send__(:include, Honeybadger::Rails::ActionControllerCatcher)
21
+ klass.class_eval(&definition) if definition
22
+ define_constant('HoneybadgerTestController', klass)
23
+ end
24
+ end
25
+
26
+ def assert_sent_hash(hash, &block)
27
+ hash.each do |key, value|
28
+ next if key.match(/^honeybadger\./) # We added this key.
29
+
30
+ new_block = Proc.new {
31
+ block.call(last_sent_notice_payload)[key]
32
+ }
33
+
34
+ if value.respond_to?(:to_hash)
35
+ assert_sent_hash(value.to_hash, &new_block)
36
+ else
37
+ assert_sent_element(value, &new_block)
38
+ end
39
+ end
40
+ end
41
+
42
+ def assert_sent_element(value, &block)
43
+ assert_equal yield(last_sent_notice_payload), stringify_array_elements(value)
44
+ end
45
+
46
+ def stringify_array_elements(data)
47
+ if data.is_a?(Array)
48
+ data.collect do |value|
49
+ stringify_array_elements(value)
50
+ end
51
+ else
52
+ data.to_s
53
+ end
54
+ end
55
+
56
+ def assert_sent_request_info_for(request)
57
+ params = request.parameters.to_hash
58
+ assert_sent_hash(params) { |h| h['request']['params'] }
59
+ assert_sent_element(params['controller']) { |h| h['request']['component'] }
60
+ assert_sent_element(params['action']) { |h| h['request']['action'] }
61
+ assert_sent_element(url_from_request(request)) { |h| h['request']['url'] }
62
+ assert_sent_hash(request.env) { |h| h['request']['cgi_data'] }
63
+ end
64
+
65
+ def url_from_request(request)
66
+ url = "#{request.protocol}#{request.host}"
67
+
68
+ unless [80, 443].include?(request.port)
69
+ url << ":#{request.port}"
70
+ end
71
+
72
+ url << request.request_uri
73
+ url
74
+ end
75
+
76
+ def sender
77
+ Honeybadger.sender
78
+ end
79
+
80
+ def last_sent_notice_json
81
+ sender.collected.last
82
+ end
83
+
84
+ def last_sent_notice_payload
85
+ assert_not_nil xml = last_sent_notice_json, "No json was sent"
86
+ JSON.parse(last_sent_notice_json)
87
+ end
88
+
89
+ def process_action(opts = {}, &action)
90
+ opts[:request] ||= ActionController::TestRequest.new
91
+ opts[:response] ||= ActionController::TestResponse.new
92
+ klass = build_controller_class do
93
+ cattr_accessor :local
94
+ define_method(:index, &action)
95
+ def local_request?
96
+ local
97
+ end
98
+ end
99
+ if opts[:filters]
100
+ klass.filter_parameter_logging *opts[:filters]
101
+ end
102
+ if opts[:user_agent]
103
+ if opts[:request].respond_to?(:user_agent=)
104
+ opts[:request].user_agent = opts[:user_agent]
105
+ else
106
+ opts[:request].env["HTTP_USER_AGENT"] = opts[:user_agent]
107
+ end
108
+ end
109
+ if opts[:port]
110
+ opts[:request].port = opts[:port]
111
+ end
112
+ klass.consider_all_requests_local = opts[:all_local]
113
+ klass.local = opts[:local]
114
+ controller = klass.new
115
+ controller.stubs(:rescue_action_in_public_without_honeybadger)
116
+ opts[:request].query_parameters = opts[:request].query_parameters.merge(opts[:params] || {})
117
+ opts[:request].session = ActionController::TestSession.new(opts[:session] || {})
118
+ # Prevents request.fullpath from crashing Rails in tests
119
+ opts[:request].env['REQUEST_URI'] = opts[:request].request_uri
120
+ controller.process(opts[:request], opts[:response])
121
+ controller
122
+ end
123
+
124
+ def process_action_with_manual_notification(args = {})
125
+ process_action(args) do
126
+ notify_honeybadger(:error_message => 'fail')
127
+ # Rails will raise a template error if we don't render something
128
+ render :nothing => true
129
+ end
130
+ end
131
+
132
+ def process_action_with_automatic_notification(args = {})
133
+ process_action(args) { raise "Hello" }
134
+ end
135
+
136
+ should "deliver notices from exceptions raised in public requests" do
137
+ process_action_with_automatic_notification
138
+ assert_caught_and_sent
139
+ end
140
+
141
+ should "not deliver notices from exceptions in local requests" do
142
+ process_action_with_automatic_notification(:local => true)
143
+ assert_caught_and_not_sent
144
+ end
145
+
146
+ should "not deliver notices from exceptions when all requests are local" do
147
+ process_action_with_automatic_notification(:all_local => true)
148
+ assert_caught_and_not_sent
149
+ end
150
+
151
+ should "not deliver notices from actions that don't raise" do
152
+ controller = process_action { render :text => 'Hello' }
153
+ assert_caught_and_not_sent
154
+ assert_equal 'Hello', controller.response.body
155
+ end
156
+
157
+ should "not deliver ignored exceptions raised by actions" do
158
+ ignore(RuntimeError)
159
+ process_action_with_automatic_notification
160
+ assert_caught_and_not_sent
161
+ end
162
+
163
+ should "deliver ignored exception raised manually" do
164
+ ignore(RuntimeError)
165
+ process_action_with_manual_notification
166
+ assert_caught_and_sent
167
+ end
168
+
169
+ should "deliver manually sent notices in public requests" do
170
+ process_action_with_manual_notification
171
+ assert_caught_and_sent
172
+ end
173
+
174
+ should "not deliver manually sent notices in local requests" do
175
+ process_action_with_manual_notification(:local => true)
176
+ assert_caught_and_not_sent
177
+ end
178
+
179
+ should "not deliver manually sent notices when all requests are local" do
180
+ process_action_with_manual_notification(:all_local => true)
181
+ assert_caught_and_not_sent
182
+ end
183
+
184
+ should "continue with default behavior after delivering an exception" do
185
+ controller = process_action_with_automatic_notification(:public => true)
186
+ # TODO: can we test this without stubbing?
187
+ assert_received(controller, :rescue_action_in_public_without_honeybadger)
188
+ end
189
+
190
+ should "not create actions from Honeybadger methods" do
191
+ controller = build_controller_class.new
192
+ assert_equal [], Honeybadger::Rails::ActionControllerCatcher.instance_methods
193
+ end
194
+
195
+ should "ignore exceptions when user agent is being ignored by regular expression" do
196
+ Honeybadger.configuration.ignore_user_agent_only = [/Ignored/]
197
+ process_action_with_automatic_notification(:user_agent => 'ShouldBeIgnored')
198
+ assert_caught_and_not_sent
199
+ end
200
+
201
+ should "ignore exceptions when user agent is being ignored by string" do
202
+ Honeybadger.configuration.ignore_user_agent_only = ['IgnoredUserAgent']
203
+ process_action_with_automatic_notification(:user_agent => 'IgnoredUserAgent')
204
+ assert_caught_and_not_sent
205
+ end
206
+
207
+ should "not ignore exceptions when user agent is not being ignored" do
208
+ Honeybadger.configuration.ignore_user_agent_only = ['IgnoredUserAgent']
209
+ process_action_with_automatic_notification(:user_agent => 'NonIgnoredAgent')
210
+ assert_caught_and_sent
211
+ end
212
+
213
+ should "send session data for manual notifications" do
214
+ data = { 'one' => 'two' }
215
+ process_action_with_manual_notification(:session => data)
216
+ assert_sent_hash(data) { |h| h['request']['session'] }
217
+ end
218
+
219
+ should "send session data for automatic notification" do
220
+ data = { 'one' => 'two' }
221
+ process_action_with_automatic_notification(:session => data)
222
+ assert_sent_hash(data) { |h| h['request']['session'] }
223
+ end
224
+
225
+ should "send request data for manual notification" do
226
+ params = { 'controller' => "honeybadger_test", 'action' => "index" }
227
+ controller = process_action_with_manual_notification(:params => params)
228
+ assert_sent_request_info_for controller.request
229
+ end
230
+
231
+ should "send request data for manual notification with non-standard port" do
232
+ params = { 'controller' => "honeybadger_test", 'action' => "index" }
233
+ controller = process_action_with_manual_notification(:params => params, :port => 81)
234
+ assert_sent_request_info_for controller.request
235
+ end
236
+
237
+ should "send request data for automatic notification" do
238
+ params = { 'controller' => "honeybadger_test", 'action' => "index" }
239
+ controller = process_action_with_automatic_notification(:params => params)
240
+ assert_sent_request_info_for controller.request
241
+ end
242
+
243
+ should "send request data for automatic notification with non-standard port" do
244
+ params = { 'controller' => "honeybadger_test", 'action' => "index" }
245
+ controller = process_action_with_automatic_notification(:params => params, :port => 81)
246
+ assert_sent_request_info_for controller.request
247
+ end
248
+
249
+ should "use standard rails logging filters on params and session and env" do
250
+ filtered_params = { "abc" => "123",
251
+ "def" => "456",
252
+ "ghi" => "[FILTERED]" }
253
+ filtered_session = { "abc" => "123",
254
+ "ghi" => "[FILTERED]" }
255
+ ENV['ghi'] = 'abc'
256
+ filtered_env = { 'ghi' => '[FILTERED]' }
257
+ filtered_cgi = { 'REQUEST_METHOD' => '[FILTERED]' }
258
+
259
+ process_action_with_automatic_notification(:filters => [:ghi, :request_method],
260
+ :params => { "abc" => "123",
261
+ "def" => "456",
262
+ "ghi" => "789" },
263
+ :session => { "abc" => "123",
264
+ "ghi" => "789" })
265
+ assert_sent_hash(filtered_params) { |h| h['request']['params'] }
266
+ assert_sent_hash(filtered_cgi) { |h| h['request']['cgi_data'] }
267
+ assert_sent_hash(filtered_session) { |h| h['request']['session'] }
268
+ end
269
+
270
+ should "call session.to_hash if available" do
271
+ hash_data = {:key => :value}
272
+
273
+ session = ActionController::TestSession.new
274
+ ActionController::TestSession.stubs(:new).returns(session)
275
+ session.stubs(:to_hash).returns(hash_data)
276
+
277
+ process_action_with_automatic_notification
278
+ assert_received(session, :to_hash)
279
+ assert_received(session, :data) { |expect| expect.never }
280
+ assert_caught_and_sent
281
+ end
282
+
283
+ should "call session.data if session.to_hash is undefined" do
284
+ hash_data = {:key => :value}
285
+
286
+ session = ActionController::TestSession.new
287
+ ActionController::TestSession.stubs(:new).returns(session)
288
+ session.stubs(:data).returns(hash_data)
289
+ if session.respond_to?(:to_hash)
290
+ class << session
291
+ undef to_hash
292
+ end
293
+ end
294
+
295
+ process_action_with_automatic_notification
296
+ assert_received(session, :to_hash) { |expect| expect.never }
297
+ assert_received(session, :data) { |expect| expect.at_least_once }
298
+ assert_caught_and_sent
299
+ end
300
+ end