projectlocker_pulse 0.2.1

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 (82) hide show
  1. data/CHANGELOG +26 -0
  2. data/Gemfile +3 -0
  3. data/Guardfile +6 -0
  4. data/INSTALL +20 -0
  5. data/MIT-LICENSE +23 -0
  6. data/README.md +439 -0
  7. data/README_FOR_HEROKU_ADDON.md +89 -0
  8. data/Rakefile +223 -0
  9. data/SUPPORTED_RAILS_VERSIONS +38 -0
  10. data/TESTING.md +41 -0
  11. data/features/metal.feature +18 -0
  12. data/features/rack.feature +60 -0
  13. data/features/rails.feature +272 -0
  14. data/features/rails_with_js_notifier.feature +97 -0
  15. data/features/rake.feature +27 -0
  16. data/features/sinatra.feature +29 -0
  17. data/features/step_definitions/file_steps.rb +10 -0
  18. data/features/step_definitions/metal_steps.rb +23 -0
  19. data/features/step_definitions/rack_steps.rb +23 -0
  20. data/features/step_definitions/rails_application_steps.rb +478 -0
  21. data/features/step_definitions/rake_steps.rb +17 -0
  22. data/features/support/env.rb +18 -0
  23. data/features/support/matchers.rb +35 -0
  24. data/features/support/projectlocker_pulse_shim.rb.template +16 -0
  25. data/features/support/rails.rb +201 -0
  26. data/features/support/rake/Rakefile +68 -0
  27. data/features/support/terminal.rb +107 -0
  28. data/features/user_informer.feature +63 -0
  29. data/generators/pulse/lib/insert_commands.rb +34 -0
  30. data/generators/pulse/lib/rake_commands.rb +24 -0
  31. data/generators/pulse/pulse_generator.rb +94 -0
  32. data/generators/pulse/templates/capistrano_hook.rb +6 -0
  33. data/generators/pulse/templates/initializer.rb +6 -0
  34. data/generators/pulse/templates/pulse_tasks.rake +25 -0
  35. data/install.rb +1 -0
  36. data/lib/projectlocker_pulse.rb +159 -0
  37. data/lib/pulse/backtrace.rb +108 -0
  38. data/lib/pulse/capistrano.rb +43 -0
  39. data/lib/pulse/configuration.rb +305 -0
  40. data/lib/pulse/notice.rb +390 -0
  41. data/lib/pulse/rack.rb +54 -0
  42. data/lib/pulse/rails/action_controller_catcher.rb +30 -0
  43. data/lib/pulse/rails/controller_methods.rb +85 -0
  44. data/lib/pulse/rails/error_lookup.rb +33 -0
  45. data/lib/pulse/rails/javascript_notifier.rb +47 -0
  46. data/lib/pulse/rails/middleware/exceptions_catcher.rb +33 -0
  47. data/lib/pulse/rails.rb +40 -0
  48. data/lib/pulse/rails3_tasks.rb +99 -0
  49. data/lib/pulse/railtie.rb +49 -0
  50. data/lib/pulse/rake_handler.rb +65 -0
  51. data/lib/pulse/sender.rb +128 -0
  52. data/lib/pulse/shared_tasks.rb +47 -0
  53. data/lib/pulse/tasks.rb +83 -0
  54. data/lib/pulse/user_informer.rb +27 -0
  55. data/lib/pulse/utils/blank.rb +53 -0
  56. data/lib/pulse/version.rb +3 -0
  57. data/lib/pulse_tasks.rb +64 -0
  58. data/lib/rails/generators/pulse/pulse_generator.rb +100 -0
  59. data/lib/templates/javascript_notifier.erb +15 -0
  60. data/lib/templates/rescue.erb +91 -0
  61. data/pulse.gemspec +39 -0
  62. data/rails/init.rb +1 -0
  63. data/resources/README.md +34 -0
  64. data/resources/ca-bundle.crt +3376 -0
  65. data/script/integration_test.rb +38 -0
  66. data/test/backtrace_test.rb +162 -0
  67. data/test/capistrano_test.rb +34 -0
  68. data/test/catcher_test.rb +333 -0
  69. data/test/configuration_test.rb +236 -0
  70. data/test/helper.rb +263 -0
  71. data/test/javascript_notifier_test.rb +51 -0
  72. data/test/logger_test.rb +79 -0
  73. data/test/notice_test.rb +490 -0
  74. data/test/notifier_test.rb +276 -0
  75. data/test/projectlocker_pulse_tasks_test.rb +170 -0
  76. data/test/pulse.xsd +88 -0
  77. data/test/rack_test.rb +58 -0
  78. data/test/rails_initializer_test.rb +36 -0
  79. data/test/recursion_test.rb +10 -0
  80. data/test/sender_test.rb +288 -0
  81. data/test/user_informer_test.rb +29 -0
  82. metadata +432 -0
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'logger'
4
+ require 'fileutils'
5
+
6
+ RAILS_ENV = "production"
7
+ RAILS_ROOT = FileUtils.pwd
8
+ RAILS_DEFAULT_LOGGER = Logger.new(STDOUT)
9
+
10
+ $: << File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
11
+ require 'projectlocker_pulse'
12
+ require 'rails/init'
13
+
14
+ fail "Please supply an API Key as the first argument" if ARGV.empty?
15
+
16
+ host = ARGV[1]
17
+ host ||= "errors.projectlocker.com"
18
+
19
+ secure = (ARGV[2] == "secure")
20
+
21
+ exception = begin
22
+ raise "Testing Pulse notifier with secure = #{secure}. If you can see this, it works."
23
+ rescue => foo
24
+ foo
25
+ end
26
+
27
+ Pulse.configure do |config|
28
+ config.secure = secure
29
+ config.host = host
30
+ config.api_key = ARGV.first
31
+ end
32
+ puts "Configuration:"
33
+ Pulse.configuration.to_hash.each do |key, value|
34
+ puts sprintf("%25s: %s", key.to_s, value.inspect.slice(0, 55))
35
+ end
36
+ puts "Sending #{secure ? "" : "in"}secure notification to project with key #{ARGV.first}"
37
+ Pulse.notify(exception)
38
+
@@ -0,0 +1,162 @@
1
+ require File.expand_path '../helper', __FILE__
2
+
3
+ class BacktraceTest < Test::Unit::TestCase
4
+
5
+ should "parse a backtrace into lines" do
6
+ array = [
7
+ "app/models/user.rb:13:in `magic'",
8
+ "app/controllers/users_controller.rb:8:in `index'"
9
+ ]
10
+
11
+ backtrace = Pulse::Backtrace.parse(array)
12
+
13
+ line = backtrace.lines.first
14
+ assert_equal '13', line.number
15
+ assert_equal 'app/models/user.rb', line.file
16
+ assert_equal 'magic', line.method
17
+
18
+ line = backtrace.lines.last
19
+ assert_equal '8', line.number
20
+ assert_equal 'app/controllers/users_controller.rb', line.file
21
+ assert_equal 'index', line.method
22
+ end
23
+
24
+ should "parse a windows backtrace into lines" do
25
+ array = [
26
+ "C:/Program Files/Server/app/models/user.rb:13:in `magic'",
27
+ "C:/Program Files/Server/app/controllers/users_controller.rb:8:in `index'"
28
+ ]
29
+
30
+ backtrace = Pulse::Backtrace.parse(array)
31
+
32
+ line = backtrace.lines.first
33
+ assert_equal '13', line.number
34
+ assert_equal 'C:/Program Files/Server/app/models/user.rb', line.file
35
+ assert_equal 'magic', line.method
36
+
37
+ line = backtrace.lines.last
38
+ assert_equal '8', line.number
39
+ assert_equal 'C:/Program Files/Server/app/controllers/users_controller.rb', line.file
40
+ assert_equal 'index', line.method
41
+ end
42
+
43
+ should "be equal with equal lines" do
44
+ one = build_backtrace_array
45
+ two = one.dup
46
+
47
+ assert_equal Pulse::Backtrace.parse(one), Pulse::Backtrace.parse(two)
48
+ end
49
+
50
+ should "parse massive one-line exceptions into multiple lines" do
51
+ original_backtrace = Pulse::Backtrace.
52
+ parse(["one:1:in `one'\n two:2:in `two'\n three:3:in `three`"])
53
+ expected_backtrace = Pulse::Backtrace.
54
+ parse(["one:1:in `one'", "two:2:in `two'", "three:3:in `three`"])
55
+
56
+ assert_equal expected_backtrace, original_backtrace
57
+ end
58
+
59
+ context "with a project root" do
60
+ setup do
61
+ @project_root = '/some/path'
62
+ Pulse.configure {|config| config.project_root = @project_root }
63
+ end
64
+
65
+ teardown do
66
+ reset_config
67
+ end
68
+
69
+ should "filter out the project root" do
70
+ backtrace_with_root = Pulse::Backtrace.parse(
71
+ ["#{@project_root}/app/models/user.rb:7:in `latest'",
72
+ "#{@project_root}/app/controllers/users_controller.rb:13:in `index'",
73
+ "/lib/something.rb:41:in `open'"],
74
+ :filters => default_filters)
75
+ backtrace_without_root = Pulse::Backtrace.parse(
76
+ ["[PROJECT_ROOT]/app/models/user.rb:7:in `latest'",
77
+ "[PROJECT_ROOT]/app/controllers/users_controller.rb:13:in `index'",
78
+ "/lib/something.rb:41:in `open'"])
79
+
80
+ assert_equal backtrace_without_root, backtrace_with_root
81
+ end
82
+ end
83
+
84
+ context "with a project root equals to a part of file name" do
85
+ setup do
86
+ # Heroku-like
87
+ @project_root = '/app'
88
+ Pulse.configure {|config| config.project_root = @project_root }
89
+ end
90
+
91
+ teardown do
92
+ reset_config
93
+ end
94
+
95
+ should "filter out the project root" do
96
+ backtrace_with_root = Pulse::Backtrace.parse(
97
+ ["#{@project_root}/app/models/user.rb:7:in `latest'",
98
+ "#{@project_root}/app/controllers/users_controller.rb:13:in `index'",
99
+ "/lib/something.rb:41:in `open'"],
100
+ :filters => default_filters)
101
+ backtrace_without_root = Pulse::Backtrace.parse(
102
+ ["[PROJECT_ROOT]/app/models/user.rb:7:in `latest'",
103
+ "[PROJECT_ROOT]/app/controllers/users_controller.rb:13:in `index'",
104
+ "/lib/something.rb:41:in `open'"])
105
+
106
+ assert_equal backtrace_without_root, backtrace_with_root
107
+ end
108
+ end
109
+
110
+ context "with a blank project root" do
111
+ setup do
112
+ Pulse.configure {|config| config.project_root = '' }
113
+ end
114
+
115
+ teardown do
116
+ reset_config
117
+ end
118
+
119
+ should "not filter line numbers with respect to any project root" do
120
+ backtrace = ["/app/models/user.rb:7:in `latest'",
121
+ "/app/controllers/users_controller.rb:13:in `index'",
122
+ "/lib/something.rb:41:in `open'"]
123
+
124
+ backtrace_with_root =
125
+ Pulse::Backtrace.parse(backtrace, :filters => default_filters)
126
+
127
+ backtrace_without_root =
128
+ Pulse::Backtrace.parse(backtrace)
129
+
130
+ assert_equal backtrace_without_root, backtrace_with_root
131
+ end
132
+ end
133
+
134
+ should "remove notifier trace" do
135
+ inside_notifier = ['lib/pulse.rb:13:in `voodoo`']
136
+ outside_notifier = ['users_controller:8:in `index`']
137
+
138
+ without_inside = Pulse::Backtrace.parse(outside_notifier)
139
+ with_inside = Pulse::Backtrace.parse(inside_notifier + outside_notifier,
140
+ :filters => default_filters)
141
+
142
+ assert_equal without_inside, with_inside
143
+ end
144
+
145
+ should "run filters on the backtrace" do
146
+ filters = [lambda { |line| line.sub('foo', 'bar') }]
147
+ input = Pulse::Backtrace.parse(["foo:13:in `one'", "baz:14:in `two'"],
148
+ :filters => filters)
149
+ expected = Pulse::Backtrace.parse(["bar:13:in `one'", "baz:14:in `two'"])
150
+ assert_equal expected, input
151
+ end
152
+
153
+ def build_backtrace_array
154
+ ["app/models/user.rb:13:in `magic'",
155
+ "app/controllers/users_controller.rb:8:in `index'"]
156
+ end
157
+
158
+ def default_filters
159
+ Pulse::Configuration::DEFAULT_BACKTRACE_FILTERS
160
+ end
161
+
162
+ end
@@ -0,0 +1,34 @@
1
+ require File.expand_path '../helper', __FILE__
2
+
3
+ require 'capistrano/configuration'
4
+ require 'pulse/capistrano'
5
+
6
+ class CapistranoTest < Test::Unit::TestCase
7
+ def setup
8
+ super
9
+ reset_config
10
+
11
+ @configuration = Capistrano::Configuration.new
12
+ Pulse::Capistrano.load_into(@configuration)
13
+ @configuration.dry_run = true
14
+ end
15
+
16
+ should "define pulse:deploy task" do
17
+ assert_not_nil @configuration.find_task('pulse:deploy')
18
+ end
19
+
20
+ should "log when calling pulse:deploy task" do
21
+ @configuration.set(:current_revision, '084505b1c0e0bcf1526e673bb6ac99fbcb18aecc')
22
+ @configuration.set(:repository, 'repository')
23
+ @configuration.set(:current_release, '/home/deploy/rails_app/hoptoad')
24
+ io = StringIO.new
25
+ logger = Capistrano::Logger.new(:output => io)
26
+ logger.level = Capistrano::Logger::MAX_LEVEL
27
+
28
+ @configuration.logger = logger
29
+ @configuration.find_and_execute_task('pulse:deploy')
30
+
31
+ assert io.string.include?('** Notifying Pulse of Deploy')
32
+ assert io.string.include?('** Pulse Notification Complete')
33
+ end
34
+ end
@@ -0,0 +1,333 @@
1
+ require File.expand_path '../helper', __FILE__
2
+
3
+ class ActionControllerCatcherTest < Test::Unit::TestCase
4
+
5
+ include DefinesConstants
6
+
7
+ def setup
8
+ super
9
+ reset_config
10
+ Pulse.sender = CollectingSender.new
11
+ define_constant('RAILS_ROOT', '/path/to/rails/root')
12
+ end
13
+
14
+ def ignore(exception_class)
15
+ Pulse.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, Pulse::Rails::ActionControllerCatcher)
21
+ klass.class_eval(&definition) if definition
22
+ define_constant('PulseTestController', klass)
23
+ end
24
+ end
25
+
26
+ def assert_sent_hash(hash, xpath)
27
+ hash.each do |key, value|
28
+ next if key.match(/^pulse\./) # We added this key.
29
+
30
+ element_xpath = "#{xpath}/var[@key = '#{key}']"
31
+ if value.respond_to?(:to_hash)
32
+ assert_sent_hash value.to_hash, element_xpath
33
+ else
34
+ assert_sent_element value, element_xpath
35
+ end
36
+ end
37
+ end
38
+
39
+ def assert_sent_element(value, xpath)
40
+ assert_valid_node last_sent_notice_document, xpath, stringify_array_elements(value).to_s
41
+ end
42
+
43
+ def stringify_array_elements(data)
44
+ if data.respond_to?(:to_ary)
45
+ data.collect do |value|
46
+ stringify_array_elements(value)
47
+ end
48
+ else
49
+ data.to_s
50
+ end
51
+ end
52
+
53
+ def assert_sent_request_info_for(request)
54
+ params = request.parameters.to_hash
55
+ assert_sent_hash params, '/notice/request/params'
56
+ assert_sent_element params['controller'], '/notice/request/component'
57
+ assert_sent_element params['action'], '/notice/request/action'
58
+ assert_sent_element url_from_request(request), '/notice/request/url'
59
+ assert_sent_hash request.env, '/notice/request/cgi-data'
60
+ end
61
+
62
+ def url_from_request(request)
63
+ url = "#{request.protocol}#{request.host}"
64
+
65
+ unless [80, 443].include?(request.port)
66
+ url << ":#{request.port}"
67
+ end
68
+
69
+ url << request.request_uri
70
+ url
71
+ end
72
+
73
+ def sender
74
+ Pulse.sender
75
+ end
76
+
77
+ def last_sent_notice_xml
78
+ sender.collected.last.to_xml
79
+ end
80
+
81
+ def last_sent_notice_document
82
+ assert_not_nil xml = last_sent_notice_xml, "No xml was sent"
83
+ Nokogiri::XML.parse(xml)
84
+ end
85
+
86
+ def process_action(opts = {}, &action)
87
+ opts[:request] ||= ActionController::TestRequest.new
88
+ opts[:response] ||= ActionController::TestResponse.new
89
+ klass = build_controller_class do
90
+ cattr_accessor :local
91
+ define_method(:index, &action)
92
+ def local_request?
93
+ local
94
+ end
95
+ end
96
+ if opts[:filters]
97
+ klass.filter_parameter_logging *opts[:filters]
98
+ end
99
+ if opts[:user_agent]
100
+ if opts[:request].respond_to?(:user_agent=)
101
+ opts[:request].user_agent = opts[:user_agent]
102
+ else
103
+ opts[:request].env["HTTP_USER_AGENT"] = opts[:user_agent]
104
+ end
105
+ end
106
+ if opts[:port]
107
+ opts[:request].port = opts[:port]
108
+ end
109
+ klass.consider_all_requests_local = opts[:all_local]
110
+ klass.local = opts[:local]
111
+ controller = klass.new
112
+ controller.stubs(:rescue_action_in_public_without_pulse)
113
+ opts[:request].query_parameters = opts[:request].query_parameters.merge(opts[:params] || {})
114
+ opts[:request].session = ActionController::TestSession.new(opts[:session] || {})
115
+ # Prevents request.fullpath from crashing Rails in tests
116
+ opts[:request].env['REQUEST_URI'] = opts[:request].request_uri
117
+ controller.process(opts[:request], opts[:response])
118
+ controller
119
+ end
120
+
121
+ def process_action_with_manual_notification(args = {})
122
+ process_action(args) do
123
+ notify_pulse(:error_message => 'fail')
124
+ # Rails will raise a template error if we don't render something
125
+ render :nothing => true
126
+ end
127
+ end
128
+
129
+ def process_action_with_automatic_notification(args = {})
130
+ process_action(args) { raise "Hello" }
131
+ end
132
+
133
+ should "deliver notices from exceptions raised in public requests" do
134
+ process_action_with_automatic_notification
135
+ assert_caught_and_sent
136
+ end
137
+
138
+ should "not deliver notices from exceptions in local requests" do
139
+ process_action_with_automatic_notification(:local => true)
140
+ assert_caught_and_not_sent
141
+ end
142
+
143
+ should "not deliver notices from exceptions when all requests are local" do
144
+ process_action_with_automatic_notification(:all_local => true)
145
+ assert_caught_and_not_sent
146
+ end
147
+
148
+ should "not deliver notices from actions that don't raise" do
149
+ controller = process_action { render :text => 'Hello' }
150
+ assert_caught_and_not_sent
151
+ assert_equal 'Hello', controller.response.body
152
+ end
153
+
154
+ should "not deliver ignored exceptions raised by actions" do
155
+ ignore(RuntimeError)
156
+ process_action_with_automatic_notification
157
+ assert_caught_and_not_sent
158
+ end
159
+
160
+ should "deliver ignored exception raised manually" do
161
+ ignore(RuntimeError)
162
+ process_action_with_manual_notification
163
+ assert_caught_and_sent
164
+ end
165
+
166
+ should "deliver manually sent notices in public requests" do
167
+ process_action_with_manual_notification
168
+ assert_caught_and_sent
169
+ end
170
+
171
+ should "not deliver manually sent notices in local requests" do
172
+ process_action_with_manual_notification(:local => true)
173
+ assert_caught_and_not_sent
174
+ end
175
+
176
+ should "not deliver manually sent notices when all requests are local" do
177
+ process_action_with_manual_notification(:all_local => true)
178
+ assert_caught_and_not_sent
179
+ end
180
+
181
+ should "continue with default behavior after delivering an exception" do
182
+ controller = process_action_with_automatic_notification(:public => true)
183
+ # TODO: can we test this without stubbing?
184
+ assert_received(controller, :rescue_action_in_public_without_pulse)
185
+ end
186
+
187
+ should "not create actions from Pulse methods" do
188
+ controller = build_controller_class.new
189
+ assert_equal [], Pulse::Rails::ActionControllerCatcher.instance_methods
190
+ end
191
+
192
+ should "ignore exceptions when user agent is being ignored by regular expression" do
193
+ Pulse.configuration.ignore_user_agent_only = [/Ignored/]
194
+ process_action_with_automatic_notification(:user_agent => 'ShouldBeIgnored')
195
+ assert_caught_and_not_sent
196
+ end
197
+
198
+ should "ignore exceptions when user agent is being ignored by string" do
199
+ Pulse.configuration.ignore_user_agent_only = ['IgnoredUserAgent']
200
+ process_action_with_automatic_notification(:user_agent => 'IgnoredUserAgent')
201
+ assert_caught_and_not_sent
202
+ end
203
+
204
+ should "not ignore exceptions when user agent is not being ignored" do
205
+ Pulse.configuration.ignore_user_agent_only = ['IgnoredUserAgent']
206
+ process_action_with_automatic_notification(:user_agent => 'NonIgnoredAgent')
207
+ assert_caught_and_sent
208
+ end
209
+
210
+ should "send session data for manual notifications" do
211
+ data = { 'one' => 'two' }
212
+ process_action_with_manual_notification(:session => data)
213
+ assert_sent_hash data, "/notice/request/session"
214
+ end
215
+
216
+ should "send session data for automatic notification" do
217
+ data = { 'one' => 'two' }
218
+ process_action_with_automatic_notification(:session => data)
219
+ assert_sent_hash data, "/notice/request/session"
220
+ end
221
+
222
+ should "send request data for manual notification" do
223
+ params = { 'controller' => "pulse_test", 'action' => "index" }
224
+ controller = process_action_with_manual_notification(:params => params)
225
+ assert_sent_request_info_for controller.request
226
+ end
227
+
228
+ should "send request data for manual notification with non-standard port" do
229
+ params = { 'controller' => "pulse_test", 'action' => "index" }
230
+ controller = process_action_with_manual_notification(:params => params, :port => 81)
231
+ assert_sent_request_info_for controller.request
232
+ end
233
+
234
+ should "send request data for automatic notification" do
235
+ params = { 'controller' => "pulse_test", 'action' => "index" }
236
+ controller = process_action_with_automatic_notification(:params => params)
237
+ assert_sent_request_info_for controller.request
238
+ end
239
+
240
+ should "send request data for automatic notification with non-standard port" do
241
+ params = { 'controller' => "pulse_test", 'action' => "index" }
242
+ controller = process_action_with_automatic_notification(:params => params, :port => 81)
243
+ assert_sent_request_info_for controller.request
244
+ end
245
+
246
+ should "use standard rails logging filters on params and session and env" do
247
+ filtered_params = { "abc" => "123",
248
+ "def" => "456",
249
+ "ghi" => "[FILTERED]" }
250
+ filtered_session = { "abc" => "123",
251
+ "ghi" => "[FILTERED]" }
252
+ ENV['ghi'] = 'abc'
253
+ filtered_env = { 'ghi' => '[FILTERED]' }
254
+ filtered_cgi = { 'REQUEST_METHOD' => '[FILTERED]' }
255
+
256
+ process_action_with_automatic_notification(:filters => [:ghi, :request_method],
257
+ :params => { "abc" => "123",
258
+ "def" => "456",
259
+ "ghi" => "789" },
260
+ :session => { "abc" => "123",
261
+ "ghi" => "789" })
262
+ assert_sent_hash filtered_params, '/notice/request/params'
263
+ assert_sent_hash filtered_cgi, '/notice/request/cgi-data'
264
+ assert_sent_hash filtered_session, '/notice/request/session'
265
+ end
266
+
267
+ context "for a local error with development lookup enabled" do
268
+ setup do
269
+ Pulse.configuration.development_lookup = true
270
+ Pulse.stubs(:build_lookup_hash_for).returns({ :awesome => 2 })
271
+
272
+ @controller = process_action_with_automatic_notification(:local => true)
273
+ @response = @controller.response
274
+ end
275
+
276
+ should "append custom CSS and JS to response body for a local error" do
277
+ assert_match /text\/css/, @response.body
278
+ assert_match /text\/javascript/, @response.body
279
+ end
280
+
281
+ should "contain host, API key and notice JSON" do
282
+ assert_match Pulse.configuration.host.to_json, @response.body
283
+ assert_match Pulse.configuration.api_key.to_json, @response.body
284
+ assert_match ({ :awesome => 2 }).to_json, @response.body
285
+ end
286
+ end
287
+
288
+ context "for a local error with development lookup disabled" do
289
+ setup do
290
+ Pulse.configuration.development_lookup = false
291
+
292
+ @controller = process_action_with_automatic_notification(:local => true)
293
+ @response = @controller.response
294
+ end
295
+
296
+ should "not append custom CSS and JS to response for a local error" do
297
+ assert_no_match /text\/css/, @response.body
298
+ assert_no_match /text\/javascript/, @response.body
299
+ end
300
+ end
301
+
302
+ should "call session.to_hash if available" do
303
+ hash_data = {:key => :value}
304
+
305
+ session = ActionController::TestSession.new
306
+ ActionController::TestSession.stubs(:new).returns(session)
307
+ session.stubs(:to_hash).returns(hash_data)
308
+
309
+ process_action_with_automatic_notification
310
+ assert_received(session, :to_hash)
311
+ assert_received(session, :data) { |expect| expect.never }
312
+ assert_caught_and_sent
313
+ end
314
+
315
+ should "call session.data if session.to_hash is undefined" do
316
+ hash_data = {:key => :value}
317
+
318
+ session = ActionController::TestSession.new
319
+ ActionController::TestSession.stubs(:new).returns(session)
320
+ session.stubs(:data).returns(hash_data)
321
+ if session.respond_to?(:to_hash)
322
+ class << session
323
+ undef to_hash
324
+ end
325
+ end
326
+
327
+ process_action_with_automatic_notification
328
+ assert_received(session, :to_hash) { |expect| expect.never }
329
+ assert_received(session, :data) { |expect| expect.at_least_once }
330
+ assert_caught_and_sent
331
+ end
332
+
333
+ end