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,276 @@
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(Pulse::Notice, :new) {|expect| expect.with(has_entries(notice_args)) }
20
+ assert_received(Pulse.sender, :send_to_pulse) {|expect| expect.with(notice) }
21
+ end
22
+
23
+ def set_public_env
24
+ Pulse.configure { |config| config.environment_name = 'production' }
25
+ end
26
+
27
+ def set_development_env
28
+ Pulse.configure { |config| config.environment_name = 'development' }
29
+ end
30
+
31
+ should "yield and save a configuration when configuring" do
32
+ yielded_configuration = nil
33
+ Pulse.configure do |config|
34
+ yielded_configuration = config
35
+ end
36
+
37
+ assert_kind_of Pulse::Configuration, yielded_configuration
38
+ assert_equal yielded_configuration, Pulse.configuration
39
+ end
40
+
41
+ should "not remove existing config options when configuring twice" do
42
+ first_config = nil
43
+ Pulse.configure do |config|
44
+ first_config = config
45
+ end
46
+ Pulse.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
+ Pulse::Sender.stubs(:new => sender)
54
+ configuration = nil
55
+
56
+ Pulse.configure { |yielded_config| configuration = yielded_config }
57
+
58
+ assert_received(Pulse::Sender, :new) { |expect| expect.with(configuration) }
59
+ assert_equal sender, Pulse.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
+ Pulse.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
+ Pulse.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
+ Pulse.notify(notice_args)
91
+
92
+ assert_received(Pulse::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
+ Pulse.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
+ Pulse.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
+ Pulse.notify(build_exception)
125
+ Pulse.notify_or_ignore(build_exception)
126
+
127
+ assert_received(sender, :send_to_pulse) {|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
+ Pulse.notify_or_ignore(exception)
138
+
139
+ assert_received(sender, :send_to_pulse) {|expect| expect.never }
140
+ end
141
+
142
+ should "deliver exception in async-mode" do
143
+ Pulse.configure do |config|
144
+ config.environment_name = 'production'
145
+ config.async do |notice|
146
+ Pulse.sender.send_to_pulse(notice)
147
+ end
148
+ end
149
+ exception = build_exception
150
+ sender = stub_sender!
151
+ notice = stub_notice!
152
+
153
+ Pulse.notify(exception)
154
+
155
+ assert_sent(notice, :exception => exception)
156
+ end
157
+
158
+ should "pass notice in async-mode" do
159
+ received_notice = nil
160
+ Pulse.configure do |config|
161
+ config.environment_name = 'production'
162
+ config.async {|notice| received_notice = notice}
163
+ end
164
+ exception = build_exception
165
+ sender = stub_sender!
166
+ notice = stub_notice!
167
+
168
+ Pulse.notify(exception)
169
+
170
+ assert_equal received_notice, notice
171
+ end
172
+
173
+ should "deliver an ignored exception when notifying manually" do
174
+ set_public_env
175
+ exception = build_exception
176
+ sender = stub_sender!
177
+ notice = stub_notice!
178
+ notice.stubs(:ignore? => true)
179
+
180
+ Pulse.notify(exception)
181
+
182
+ assert_sent(notice, :exception => exception)
183
+ end
184
+
185
+ should "pass config to created notices" do
186
+ exception = build_exception
187
+ config_opts = { 'one' => 'two', 'three' => 'four' }
188
+ notice = stub_notice!
189
+ stub_sender!
190
+ Pulse.configuration = stub('config', :merge => config_opts, :public? => true,:async? => nil)
191
+
192
+ Pulse.notify(exception)
193
+
194
+ assert_received(Pulse::Notice, :new) do |expect|
195
+ expect.with(has_entries(config_opts))
196
+ end
197
+ end
198
+
199
+ context "building notice JSON for an exception" do
200
+ setup do
201
+ @params = { :controller => "users", :action => "create" }
202
+ @exception = build_exception
203
+ @hash = Pulse.build_lookup_hash_for(@exception, @params)
204
+ end
205
+
206
+ should "set action" do
207
+ assert_equal @params[:action], @hash[:action]
208
+ end
209
+
210
+ should "set controller" do
211
+ assert_equal @params[:controller], @hash[:component]
212
+ end
213
+
214
+ should "set line number" do
215
+ assert @hash[:line_number] =~ /\d+/
216
+ end
217
+
218
+ should "set file" do
219
+ assert_match /test\/helper\.rb$/, @hash[:file]
220
+ end
221
+
222
+ should "set rails_env to production" do
223
+ assert_equal 'production', @hash[:environment_name]
224
+ end
225
+
226
+ should "set error class" do
227
+ assert_equal @exception.class.to_s, @hash[:error_class]
228
+ end
229
+
230
+ should "not set file or line number with no backtrace" do
231
+ @exception.stubs(:backtrace).returns([])
232
+
233
+ @hash = Pulse.build_lookup_hash_for(@exception)
234
+
235
+ assert_nil @hash[:line_number]
236
+ assert_nil @hash[:file]
237
+ end
238
+
239
+ should "not set action or controller when not provided" do
240
+ @hash = Pulse.build_lookup_hash_for(@exception)
241
+
242
+ assert_nil @hash[:action]
243
+ assert_nil @hash[:controller]
244
+ end
245
+
246
+ context "when an exception that provides #original_exception is raised" do
247
+ setup do
248
+ @exception.stubs(:original_exception).returns(begin
249
+ raise NotifierTest::OriginalException.new
250
+ rescue Exception => e
251
+ e
252
+ end)
253
+ end
254
+
255
+ should "unwrap exceptions that provide #original_exception" do
256
+ @hash = Pulse.build_lookup_hash_for(@exception)
257
+ assert_equal "NotifierTest::OriginalException", @hash[:error_class]
258
+ end
259
+ end
260
+
261
+ context "when an exception that provides #continued_exception is raised" do
262
+ setup do
263
+ @exception.stubs(:continued_exception).returns(begin
264
+ raise NotifierTest::ContinuedException.new
265
+ rescue Exception => e
266
+ e
267
+ end)
268
+ end
269
+
270
+ should "unwrap exceptions that provide #continued_exception" do
271
+ @hash = Pulse.build_lookup_hash_for(@exception)
272
+ assert_equal "NotifierTest::ContinuedException", @hash[:error_class]
273
+ end
274
+ end
275
+ end
276
+ end
@@ -0,0 +1,170 @@
1
+ require File.expand_path '../helper', __FILE__
2
+ require 'rubygems'
3
+
4
+ require File.dirname(__FILE__) + '/../lib/pulse_tasks'
5
+ require 'fakeweb'
6
+
7
+ FakeWeb.allow_net_connect = false
8
+
9
+ class PulseTasksTest < Test::Unit::TestCase
10
+ def successful_response(body = "")
11
+ response = Net::HTTPSuccess.new('1.2', '200', 'OK')
12
+ response.stubs(:body).returns(body)
13
+ return response
14
+ end
15
+
16
+ def unsuccessful_response(body = "")
17
+ response = Net::HTTPClientError.new('1.2', '200', 'OK')
18
+ response.stubs(:body).returns(body)
19
+ return response
20
+ end
21
+
22
+ context "being quiet" do
23
+ setup { PulseTasks.stubs(:puts) }
24
+
25
+ context "in a configured project" do
26
+ setup { Pulse.configure { |config| config.api_key = "1234123412341234" } }
27
+
28
+ context "on deploy({})" do
29
+ setup { @output = PulseTasks.deploy({}) }
30
+
31
+ before_should "complain about missing rails env" do
32
+ PulseTasks.expects(:puts).with(regexp_matches(/rails environment/i))
33
+ end
34
+
35
+ should "return false" do
36
+ assert !@output
37
+ end
38
+ end
39
+
40
+ context "given an optional HTTP proxy and valid options" do
41
+ setup do
42
+ @response = stub("response", :body => "stub body")
43
+ @http_proxy = stub("proxy", :request => @response)
44
+ @http_proxy_class = stub("proxy_class", :new => @http_proxy)
45
+ @post = stub("post", :set_form_data => nil)
46
+
47
+ Net::HTTP.expects(:Proxy).
48
+ with(Pulse.configuration.proxy_host,
49
+ Pulse.configuration.proxy_port,
50
+ Pulse.configuration.proxy_user,
51
+ Pulse.configuration.proxy_pass).
52
+ returns(@http_proxy_class)
53
+ Net::HTTP::Post.expects(:new).with("/deploys.txt").returns(@post)
54
+
55
+ @options = { :rails_env => "staging", :dry_run => false }
56
+ end
57
+
58
+ context "performing a dry run" do
59
+ setup { @output = PulseTasks.deploy(@options.merge(:dry_run => true)) }
60
+
61
+ should "return true without performing any actual request" do
62
+ assert_equal true, @output
63
+ assert_received(@http_proxy, :request) do |expects|
64
+ expects.never
65
+ end
66
+ end
67
+ end
68
+
69
+ context "on deploy(options)" do
70
+ setup do
71
+ @output = PulseTasks.deploy(@options)
72
+ end
73
+
74
+ before_should "post to http://errors.projectlocker.com:80/deploys.txt" do
75
+ @http_proxy_class.expects(:new).with("errors.projectlocker.com", 80).returns(@http_proxy)
76
+ @post.expects(:set_form_data).with(kind_of(Hash))
77
+ @http_proxy.expects(:request).with(any_parameters).returns(successful_response)
78
+ end
79
+
80
+ before_should "use the project api key" do
81
+ @post.expects(:set_form_data).
82
+ with(has_entries('api_key' => "1234123412341234"))
83
+ end
84
+
85
+ before_should "use send the rails_env param" do
86
+ @post.expects(:set_form_data).
87
+ with(has_entries("deploy[rails_env]" => "staging"))
88
+ end
89
+
90
+ [:local_username, :scm_repository, :scm_revision].each do |key|
91
+ before_should "use send the #{key} param if it's passed in." do
92
+ @options[key] = "value"
93
+ @post.expects(:set_form_data).
94
+ with(has_entries("deploy[#{key}]" => "value"))
95
+ end
96
+ end
97
+
98
+ before_should "use the :api_key param if it's passed in." do
99
+ @options[:api_key] = "value"
100
+ @post.expects(:set_form_data).
101
+ with(has_entries("api_key" => "value"))
102
+ end
103
+
104
+ before_should "puts the response body on success" do
105
+ PulseTasks.expects(:puts).with("body")
106
+ @http_proxy.expects(:request).with(any_parameters).returns(successful_response('body'))
107
+ end
108
+
109
+ before_should "puts the response body on failure" do
110
+ PulseTasks.expects(:puts).with("body")
111
+ @http_proxy.expects(:request).with(any_parameters).returns(unsuccessful_response('body'))
112
+ end
113
+
114
+ should "return false on failure", :before => lambda {
115
+ @http_proxy.expects(:request).with(any_parameters).returns(unsuccessful_response('body'))
116
+ } do
117
+ assert !@output
118
+ end
119
+
120
+ should "return true on success", :before => lambda {
121
+ @http_proxy.expects(:request).with(any_parameters).returns(successful_response('body'))
122
+ } do
123
+ assert @output
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ context "in a configured project with custom host" do
130
+ setup do
131
+ Pulse.configure do |config|
132
+ config.api_key = "1234123412341234"
133
+ config.host = "custom.host"
134
+ end
135
+ end
136
+
137
+ context "on deploy(:rails_env => 'staging')" do
138
+ setup { @output = PulseTasks.deploy(:rails_env => "staging") }
139
+
140
+ before_should "post to the custom host" do
141
+ @post = stub("post", :set_form_data => nil)
142
+ @http_proxy = stub("proxy", :request => @response)
143
+
144
+ @http_proxy_class = stub("proxy_class", :new => @http_proxy)
145
+ @http_proxy_class.expects(:new).with("custom.host", 80).returns(@http_proxy)
146
+ Net::HTTP.expects(:Proxy).with(any_parameters).returns(@http_proxy_class)
147
+ Net::HTTP::Post.expects(:new).with("/deploys.txt").returns(@post)
148
+ @post.expects(:set_form_data).with(kind_of(Hash))
149
+ @http_proxy.expects(:request).with(any_parameters).returns(successful_response)
150
+ end
151
+ end
152
+ end
153
+
154
+ context "when not configured" do
155
+ setup { Pulse.configure { |config| config.api_key = "" } }
156
+
157
+ context "on deploy(:rails_env => 'staging')" do
158
+ setup { @output = PulseTasks.deploy(:rails_env => "staging") }
159
+
160
+ before_should "complain about missing api key" do
161
+ PulseTasks.expects(:puts).with(regexp_matches(/api key/i))
162
+ end
163
+
164
+ should "return false" do
165
+ assert !@output
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
data/test/pulse.xsd ADDED
@@ -0,0 +1,88 @@
1
+ <?xml version="1.0"?>
2
+ <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3
+
4
+ <xs:element name="notice">
5
+ <xs:complexType>
6
+ <xs:all>
7
+ <xs:element name="api-key" type="xs:string"/>
8
+ <xs:element name="notifier" type="notifier"/>
9
+ <xs:element name="error" type="error"/>
10
+ <xs:element name="request" type="request" minOccurs="0"/>
11
+ <xs:element name="server-environment" type="serverEnvironment"/>
12
+ <xs:element name="current-user" type="current-user" minOccurs="0"/>
13
+ </xs:all>
14
+ <xs:attribute name="version" type="xs:string" use="required"/>
15
+ </xs:complexType>
16
+ </xs:element>
17
+
18
+ <xs:complexType name="notifier">
19
+ <xs:all>
20
+ <xs:element name="name" type="xs:string"/>
21
+ <xs:element name="version" type="xs:string"/>
22
+ <xs:element name="url" type="xs:string"/>
23
+ </xs:all>
24
+ </xs:complexType>
25
+
26
+ <xs:complexType name="error">
27
+ <xs:all>
28
+ <xs:element name="class" type="xs:string"/>
29
+ <xs:element name="message" type="xs:string" minOccurs="0"/>
30
+ <xs:element name="backtrace" type="backtrace"/>
31
+ </xs:all>
32
+ </xs:complexType>
33
+
34
+ <xs:complexType name="backtrace">
35
+ <xs:sequence>
36
+ <xs:element name="line" maxOccurs="unbounded" minOccurs="0">
37
+ <xs:complexType>
38
+ <xs:attribute name="file" type="xs:string" use="required"/>
39
+ <xs:attribute name="number" type="xs:string" use="required"/>
40
+ <xs:attribute name="method" type="xs:string" use="optional"/>
41
+ </xs:complexType>
42
+ </xs:element>
43
+ </xs:sequence>
44
+ </xs:complexType>
45
+
46
+ <xs:complexType name="request">
47
+ <xs:all>
48
+ <xs:element name="url" type="xs:string"/>
49
+ <xs:element name="component" type="xs:string"/>
50
+ <xs:element name="action" type="xs:string" minOccurs="0"/>
51
+ <xs:element name="params" type="varList" minOccurs="0"/>
52
+ <xs:element name="session" type="varList" minOccurs="0"/>
53
+ <xs:element name="cgi-data" type="varList" minOccurs="0"/>
54
+ </xs:all>
55
+ </xs:complexType>
56
+
57
+ <xs:complexType name="varList">
58
+ <xs:sequence>
59
+ <xs:element name="var" type="var" maxOccurs="unbounded"/>
60
+ </xs:sequence>
61
+ </xs:complexType>
62
+
63
+ <xs:complexType name="var" mixed="true">
64
+ <xs:sequence>
65
+ <xs:element name="var" type="var" minOccurs="0" maxOccurs="unbounded"/>
66
+ </xs:sequence>
67
+ <xs:attribute name="key" type="xs:string" use="required"/>
68
+ </xs:complexType>
69
+
70
+ <xs:complexType name="serverEnvironment">
71
+ <xs:sequence>
72
+ <xs:element name="project-root" type="xs:string" minOccurs="0"/>
73
+ <xs:element name="environment-name" type="xs:string"/>
74
+ <xs:element name="app-version" type="xs:string" minOccurs="0"/>
75
+ <xs:element name="hostname" type="xs:string" minOccurs="0"/>
76
+ </xs:sequence>
77
+ </xs:complexType>
78
+
79
+ <xs:complexType name="current-user">
80
+ <xs:all>
81
+ <xs:element name="id" type="xs:string"/>
82
+ <xs:element name="name" type="xs:string" minOccurs="0"/>
83
+ <xs:element name="email" type="xs:string" minOccurs="0"/>
84
+ <xs:element name="username" type="xs:string" minOccurs="0"/>
85
+ </xs:all>
86
+ </xs:complexType>
87
+
88
+ </xs:schema>
data/test/rack_test.rb ADDED
@@ -0,0 +1,58 @@
1
+ require File.expand_path '../helper', __FILE__
2
+
3
+ class RackTest < Test::Unit::TestCase
4
+
5
+ should "call the upstream app with the environment" do
6
+ environment = { 'key' => 'value' }
7
+ app = lambda { |env| ['response', {}, env] }
8
+ stack = Pulse::Rack.new(app)
9
+
10
+ response = stack.call(environment)
11
+
12
+ assert_equal ['response', {}, environment], response
13
+ end
14
+
15
+ should "deliver an exception raised while calling an upstream app" do
16
+ Pulse.stubs(:notify_or_ignore)
17
+
18
+ exception = build_exception
19
+ environment = { 'key' => 'value' }
20
+ app = lambda do |env|
21
+ raise exception
22
+ end
23
+
24
+ begin
25
+ stack = Pulse::Rack.new(app)
26
+ stack.call(environment)
27
+ rescue Exception => raised
28
+ assert_equal exception, raised
29
+ else
30
+ flunk "Didn't raise an exception"
31
+ end
32
+
33
+ assert_received(Pulse, :notify_or_ignore) do |expect|
34
+ expect.with(exception, :rack_env => environment)
35
+ end
36
+ end
37
+
38
+ should "deliver an exception in rack.exception" do
39
+ Pulse.stubs(:notify_or_ignore)
40
+ exception = build_exception
41
+ environment = { 'key' => 'value' }
42
+
43
+ response = [200, {}, ['okay']]
44
+ app = lambda do |env|
45
+ env['rack.exception'] = exception
46
+ response
47
+ end
48
+ stack = Pulse::Rack.new(app)
49
+
50
+ actual_response = stack.call(environment)
51
+
52
+ assert_equal response, actual_response
53
+ assert_received(Pulse, :notify_or_ignore) do |expect|
54
+ expect.with(exception, :rack_env => environment)
55
+ end
56
+ end
57
+
58
+ end
@@ -0,0 +1,36 @@
1
+ require File.expand_path '../helper', __FILE__
2
+
3
+ require 'pulse/rails'
4
+
5
+ class RailsInitializerTest < Test::Unit::TestCase
6
+ include DefinesConstants
7
+
8
+ should "trigger use of Rails' logger if logger isn't set and Rails' logger exists" do
9
+ rails = Module.new do
10
+ def self.logger
11
+ "RAILS LOGGER"
12
+ end
13
+ end
14
+ define_constant("Rails", rails)
15
+ Pulse::Rails.initialize
16
+ assert_equal "RAILS LOGGER", Pulse.logger
17
+ end
18
+
19
+ should "trigger use of Rails' default logger if logger isn't set and Rails.logger doesn't exist" do
20
+ define_constant("RAILS_DEFAULT_LOGGER", "RAILS DEFAULT LOGGER")
21
+
22
+ Pulse::Rails.initialize
23
+ assert_equal "RAILS DEFAULT LOGGER", Pulse.logger
24
+ end
25
+
26
+ should "allow overriding of the logger if already assigned" do
27
+ define_constant("RAILS_DEFAULT_LOGGER", "RAILS DEFAULT LOGGER")
28
+ Pulse::Rails.initialize
29
+
30
+ Pulse.configure(true) do |config|
31
+ config.logger = "OVERRIDDEN LOGGER"
32
+ end
33
+
34
+ assert_equal "OVERRIDDEN LOGGER", Pulse.logger
35
+ end
36
+ 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 = Pulse::Notice.new(:parameters => hash)
8
+ assert_equal "[possible infinite recursion halted]", notice.parameters[:hash]
9
+ end
10
+ end