jordan-brough-hoptoad_notifier 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/CHANGELOG +161 -0
  2. data/INSTALL +25 -0
  3. data/MIT-LICENSE +22 -0
  4. data/README.rdoc +384 -0
  5. data/Rakefile +217 -0
  6. data/SUPPORTED_RAILS_VERSIONS +9 -0
  7. data/TESTING.rdoc +8 -0
  8. data/generators/hoptoad/hoptoad_generator.rb +63 -0
  9. data/generators/hoptoad/lib/insert_commands.rb +34 -0
  10. data/generators/hoptoad/lib/rake_commands.rb +24 -0
  11. data/generators/hoptoad/templates/capistrano_hook.rb +6 -0
  12. data/generators/hoptoad/templates/hoptoad_notifier_tasks.rake +25 -0
  13. data/generators/hoptoad/templates/initializer.rb +6 -0
  14. data/lib/hoptoad_notifier.rb +148 -0
  15. data/lib/hoptoad_notifier/backtrace.rb +99 -0
  16. data/lib/hoptoad_notifier/capistrano.rb +20 -0
  17. data/lib/hoptoad_notifier/configuration.rb +232 -0
  18. data/lib/hoptoad_notifier/notice.rb +318 -0
  19. data/lib/hoptoad_notifier/rack.rb +40 -0
  20. data/lib/hoptoad_notifier/rails.rb +37 -0
  21. data/lib/hoptoad_notifier/rails/action_controller_catcher.rb +29 -0
  22. data/lib/hoptoad_notifier/rails/controller_methods.rb +63 -0
  23. data/lib/hoptoad_notifier/rails/error_lookup.rb +33 -0
  24. data/lib/hoptoad_notifier/rails3_tasks.rb +90 -0
  25. data/lib/hoptoad_notifier/railtie.rb +23 -0
  26. data/lib/hoptoad_notifier/sender.rb +63 -0
  27. data/lib/hoptoad_notifier/tasks.rb +97 -0
  28. data/lib/hoptoad_notifier/version.rb +3 -0
  29. data/lib/hoptoad_tasks.rb +44 -0
  30. data/lib/rails/generators/hoptoad/hoptoad_generator.rb +69 -0
  31. data/lib/templates/rescue.erb +91 -0
  32. data/rails/init.rb +1 -0
  33. data/script/integration_test.rb +38 -0
  34. data/test/backtrace_test.rb +118 -0
  35. data/test/catcher_test.rb +324 -0
  36. data/test/configuration_test.rb +208 -0
  37. data/test/helper.rb +239 -0
  38. data/test/hoptoad_tasks_test.rb +152 -0
  39. data/test/logger_test.rb +85 -0
  40. data/test/notice_test.rb +443 -0
  41. data/test/notifier_test.rb +222 -0
  42. data/test/rack_test.rb +58 -0
  43. data/test/rails_initializer_test.rb +36 -0
  44. data/test/sender_test.rb +123 -0
  45. metadata +205 -0
@@ -0,0 +1,208 @@
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
+ assert_config_default :framework, 'Standalone'
32
+ end
33
+
34
+ should "provide default values for secure connections" do
35
+ config = HoptoadNotifier::Configuration.new
36
+ config.secure = true
37
+ assert_equal 443, config.port
38
+ assert_equal 'https', config.protocol
39
+ end
40
+
41
+ should "provide default values for insecure connections" do
42
+ config = HoptoadNotifier::Configuration.new
43
+ config.secure = false
44
+ assert_equal 80, config.port
45
+ assert_equal 'http', config.protocol
46
+ end
47
+
48
+ should "not cache inferred ports" do
49
+ config = HoptoadNotifier::Configuration.new
50
+ config.secure = false
51
+ config.port
52
+ config.secure = true
53
+ assert_equal 443, config.port
54
+ end
55
+
56
+ should "allow values to be overwritten" do
57
+ assert_config_overridable :proxy_host
58
+ assert_config_overridable :proxy_port
59
+ assert_config_overridable :proxy_user
60
+ assert_config_overridable :proxy_pass
61
+ assert_config_overridable :secure
62
+ assert_config_overridable :host
63
+ assert_config_overridable :port
64
+ assert_config_overridable :http_open_timeout
65
+ assert_config_overridable :http_read_timeout
66
+ assert_config_overridable :project_root
67
+ assert_config_overridable :notifier_version
68
+ assert_config_overridable :notifier_name
69
+ assert_config_overridable :notifier_url
70
+ assert_config_overridable :environment_name
71
+ assert_config_overridable :development_lookup
72
+ assert_config_overridable :logger
73
+ end
74
+
75
+ should "have an api key" do
76
+ assert_config_overridable :api_key
77
+ end
78
+
79
+ should "act like a hash" do
80
+ config = HoptoadNotifier::Configuration.new
81
+ hash = config.to_hash
82
+ [:api_key, :backtrace_filters, :development_environments,
83
+ :environment_name, :host, :http_open_timeout,
84
+ :http_read_timeout, :ignore, :ignore_by_filters, :ignore_user_agent,
85
+ :notifier_name, :notifier_url, :notifier_version, :params_filters,
86
+ :project_root, :port, :protocol, :proxy_host, :proxy_pass, :proxy_port,
87
+ :proxy_user, :secure, :development_lookup].each do |option|
88
+ assert_equal config[option], hash[option], "Wrong value for #{option}"
89
+ end
90
+ end
91
+
92
+ should "be mergable" do
93
+ config = HoptoadNotifier::Configuration.new
94
+ hash = config.to_hash
95
+ assert_equal hash.merge(:key => 'value'), config.merge(:key => 'value')
96
+ end
97
+
98
+ should "allow param filters to be appended" do
99
+ assert_appends_value :params_filters
100
+ end
101
+
102
+ should "warn when attempting to read environment filters" do
103
+ config = HoptoadNotifier::Configuration.new
104
+ config.
105
+ expects(:warn).
106
+ with(regexp_matches(/deprecated/i))
107
+ assert_equal [], config.environment_filters
108
+ end
109
+
110
+ should "allow ignored user agents to be appended" do
111
+ assert_appends_value :ignore_user_agent
112
+ end
113
+
114
+ should "allow backtrace filters to be appended" do
115
+ assert_appends_value(:backtrace_filters) do |config|
116
+ new_filter = lambda {}
117
+ config.filter_backtrace(&new_filter)
118
+ new_filter
119
+ end
120
+ end
121
+
122
+ should "allow ignore by filters to be appended" do
123
+ assert_appends_value(:ignore_by_filters) do |config|
124
+ new_filter = lambda {}
125
+ config.ignore_by_filter(&new_filter)
126
+ new_filter
127
+ end
128
+ end
129
+
130
+ should "allow ignored exceptions to be appended" do
131
+ config = HoptoadNotifier::Configuration.new
132
+ original_filters = config.ignore.dup
133
+ new_filter = 'hello'
134
+ config.ignore << new_filter
135
+ assert_same_elements original_filters + [new_filter], config.ignore
136
+ end
137
+
138
+ should "allow ignored exceptions to be replaced" do
139
+ assert_replaces(:ignore, :ignore_only=)
140
+ end
141
+
142
+ should "allow ignored user agents to be replaced" do
143
+ assert_replaces(:ignore_user_agent, :ignore_user_agent_only=)
144
+ end
145
+
146
+ should "use development and test as development environments by default" do
147
+ config = HoptoadNotifier::Configuration.new
148
+ assert_same_elements %w(development test cucumber), config.development_environments
149
+ end
150
+
151
+ should "be public in a public environment" do
152
+ config = HoptoadNotifier::Configuration.new
153
+ config.development_environments = %w(development)
154
+ config.environment_name = 'production'
155
+ assert config.public?
156
+ end
157
+
158
+ should "not be public in a development environment" do
159
+ config = HoptoadNotifier::Configuration.new
160
+ config.development_environments = %w(staging)
161
+ config.environment_name = 'staging'
162
+ assert !config.public?
163
+ end
164
+
165
+ should "be public without an environment name" do
166
+ config = HoptoadNotifier::Configuration.new
167
+ assert config.public?
168
+ end
169
+
170
+ should "use the assigned logger if set" do
171
+ config = HoptoadNotifier::Configuration.new
172
+ config.logger = "CUSTOM LOGGER"
173
+ assert_equal "CUSTOM LOGGER", config.logger
174
+ end
175
+
176
+ def assert_config_default(option, default_value, config = nil)
177
+ config ||= HoptoadNotifier::Configuration.new
178
+ assert_equal default_value, config.send(option)
179
+ end
180
+
181
+ def assert_config_overridable(option, value = 'a value')
182
+ config = HoptoadNotifier::Configuration.new
183
+ config.send(:"#{option}=", value)
184
+ assert_equal value, config.send(option)
185
+ end
186
+
187
+ def assert_appends_value(option, &block)
188
+ config = HoptoadNotifier::Configuration.new
189
+ original_values = config.send(option).dup
190
+ block ||= lambda do |config|
191
+ new_value = 'hello'
192
+ config.send(option) << new_value
193
+ new_value
194
+ end
195
+ new_value = block.call(config)
196
+ assert_same_elements original_values + [new_value], config.send(option)
197
+ end
198
+
199
+ def assert_replaces(option, setter)
200
+ config = HoptoadNotifier::Configuration.new
201
+ new_value = 'hello'
202
+ config.send(setter, [new_value])
203
+ assert_equal [new_value], config.send(option)
204
+ config.send(setter, new_value)
205
+ assert_equal [new_value], config.send(option)
206
+ end
207
+
208
+ end
@@ -0,0 +1,239 @@
1
+ require 'test/unit'
2
+ require 'rubygems'
3
+
4
+ gem 'jferris-mocha', '>= 0.9.5.0.1241126838'
5
+
6
+ $LOAD_PATH << File.join(File.dirname(__FILE__), *%w[.. vendor ginger lib])
7
+ $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))
8
+
9
+ require 'shoulda'
10
+ require 'mocha'
11
+
12
+ require 'ginger'
13
+
14
+ require 'action_controller'
15
+ require 'action_controller/test_process'
16
+ require 'active_record'
17
+ require 'active_record/base'
18
+ require 'active_support'
19
+ require 'nokogiri'
20
+ require 'rack'
21
+
22
+ require "hoptoad_notifier"
23
+
24
+ begin require 'redgreen'; rescue LoadError; end
25
+
26
+ module TestMethods
27
+ def rescue_action e
28
+ raise e
29
+ end
30
+
31
+ def do_raise
32
+ raise "Hoptoad"
33
+ end
34
+
35
+ def do_not_raise
36
+ render :text => "Success"
37
+ end
38
+
39
+ def do_raise_ignored
40
+ raise ActiveRecord::RecordNotFound.new("404")
41
+ end
42
+
43
+ def do_raise_not_ignored
44
+ raise ActiveRecord::StatementInvalid.new("Statement invalid")
45
+ end
46
+
47
+ def manual_notify
48
+ notify_hoptoad(Exception.new)
49
+ render :text => "Success"
50
+ end
51
+
52
+ def manual_notify_ignored
53
+ notify_hoptoad(ActiveRecord::RecordNotFound.new("404"))
54
+ render :text => "Success"
55
+ end
56
+ end
57
+
58
+ class HoptoadController < ActionController::Base
59
+ include TestMethods
60
+ end
61
+
62
+ class Test::Unit::TestCase
63
+ def request(action = nil, method = :get, user_agent = nil, params = {})
64
+ @request = ActionController::TestRequest.new
65
+ @request.action = action ? action.to_s : ""
66
+
67
+ if user_agent
68
+ if @request.respond_to?(:user_agent=)
69
+ @request.user_agent = user_agent
70
+ else
71
+ @request.env["HTTP_USER_AGENT"] = user_agent
72
+ end
73
+ end
74
+ @request.query_parameters = @request.query_parameters.merge(params)
75
+ @response = ActionController::TestResponse.new
76
+ @controller.process(@request, @response)
77
+ end
78
+
79
+ # Borrowed from ActiveSupport 2.3.2
80
+ def assert_difference(expression, difference = 1, message = nil, &block)
81
+ b = block.send(:binding)
82
+ exps = Array.wrap(expression)
83
+ before = exps.map { |e| eval(e, b) }
84
+
85
+ yield
86
+
87
+ exps.each_with_index do |e, i|
88
+ error = "#{e.inspect} didn't change by #{difference}"
89
+ error = "#{message}.\n#{error}" if message
90
+ assert_equal(before[i] + difference, eval(e, b), error)
91
+ end
92
+ end
93
+
94
+ def assert_no_difference(expression, message = nil, &block)
95
+ assert_difference expression, 0, message, &block
96
+ end
97
+
98
+ def stub_sender
99
+ stub('sender', :send_to_hoptoad => nil)
100
+ end
101
+
102
+ def stub_sender!
103
+ HoptoadNotifier.sender = stub_sender
104
+ end
105
+
106
+ def stub_notice
107
+ stub('notice', :to_xml => 'some yaml', :ignore? => false)
108
+ end
109
+
110
+ def stub_notice!
111
+ returning stub_notice do |notice|
112
+ HoptoadNotifier::Notice.stubs(:new => notice)
113
+ end
114
+ end
115
+
116
+ def create_dummy
117
+ HoptoadNotifier::DummySender.new
118
+ end
119
+
120
+ def reset_config
121
+ HoptoadNotifier.configuration = nil
122
+ HoptoadNotifier.configure do |config|
123
+ config.api_key = 'abc123'
124
+ end
125
+ end
126
+
127
+ def clear_backtrace_filters
128
+ HoptoadNotifier.configuration.backtrace_filters.clear
129
+ end
130
+
131
+ def build_exception
132
+ raise
133
+ rescue => caught_exception
134
+ caught_exception
135
+ end
136
+
137
+ def build_notice_data(exception = nil)
138
+ exception ||= build_exception
139
+ {
140
+ :api_key => 'abc123',
141
+ :error_class => exception.class.name,
142
+ :error_message => "#{exception.class.name}: #{exception.message}",
143
+ :backtrace => exception.backtrace,
144
+ :environment => { 'PATH' => '/bin', 'REQUEST_URI' => '/users/1' },
145
+ :request => {
146
+ :params => { 'controller' => 'users', 'action' => 'show', 'id' => '1' },
147
+ :rails_root => '/path/to/application',
148
+ :url => "http://test.host/users/1"
149
+ },
150
+ :session => {
151
+ :key => '123abc',
152
+ :data => { 'user_id' => '5', 'flash' => { 'notice' => 'Logged in successfully' } }
153
+ }
154
+ }
155
+ end
156
+
157
+ def assert_caught_and_sent
158
+ assert !HoptoadNotifier.sender.collected.empty?
159
+ end
160
+
161
+ def assert_caught_and_not_sent
162
+ assert HoptoadNotifier.sender.collected.empty?
163
+ end
164
+
165
+ def assert_array_starts_with(expected, actual)
166
+ assert_respond_to actual, :to_ary
167
+ array = actual.to_ary.reverse
168
+ expected.reverse.each_with_index do |value, i|
169
+ assert_equal value, array[i]
170
+ end
171
+ end
172
+
173
+ def assert_valid_node(document, xpath, content)
174
+ nodes = document.xpath(xpath)
175
+ assert nodes.any?{|node| node.content == content },
176
+ "Expected xpath #{xpath} to have content #{content}, " +
177
+ "but found #{nodes.map { |n| n.content }} in #{nodes.size} matching nodes." +
178
+ "Document:\n#{document.to_s}"
179
+ end
180
+ end
181
+
182
+ module DefinesConstants
183
+ def setup
184
+ @defined_constants = []
185
+ end
186
+
187
+ def teardown
188
+ @defined_constants.each do |constant|
189
+ Object.__send__(:remove_const, constant)
190
+ end
191
+ end
192
+
193
+ def define_constant(name, value)
194
+ Object.const_set(name, value)
195
+ @defined_constants << name
196
+ end
197
+ end
198
+
199
+ # Also stolen from AS 2.3.2
200
+ class Array
201
+ # Wraps the object in an Array unless it's an Array. Converts the
202
+ # object to an Array using #to_ary if it implements that.
203
+ def self.wrap(object)
204
+ case object
205
+ when nil
206
+ []
207
+ when self
208
+ object
209
+ else
210
+ if object.respond_to?(:to_ary)
211
+ object.to_ary
212
+ else
213
+ [object]
214
+ end
215
+ end
216
+ end
217
+
218
+ end
219
+
220
+ class CollectingSender
221
+ attr_reader :collected
222
+
223
+ def initialize
224
+ @collected = []
225
+ end
226
+
227
+ def send_to_hoptoad(data)
228
+ @collected << data
229
+ end
230
+ end
231
+
232
+ class FakeLogger
233
+ def info(*args); end
234
+ def debug(*args); end
235
+ def warn(*args); end
236
+ def error(*args); end
237
+ def fatal(*args); end
238
+ end
239
+
@@ -0,0 +1,152 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+ require 'rubygems'
3
+
4
+ require File.dirname(__FILE__) + '/../lib/hoptoad_tasks'
5
+ require 'fakeweb'
6
+
7
+ FakeWeb.allow_net_connect = false
8
+
9
+ class HoptoadTasksTest < 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 { HoptoadTasks.stubs(:puts) }
24
+
25
+ context "in a configured project" do
26
+ setup { HoptoadNotifier.configure { |config| config.api_key = "1234123412341234" } }
27
+
28
+ context "on deploy({})" do
29
+ setup { @output = HoptoadTasks.deploy({}) }
30
+
31
+ before_should "complain about missing rails env" do
32
+ HoptoadTasks.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", :post_form => @response)
44
+
45
+ Net::HTTP.expects(:Proxy).
46
+ with(HoptoadNotifier.configuration.proxy_host,
47
+ HoptoadNotifier.configuration.proxy_port,
48
+ HoptoadNotifier.configuration.proxy_user,
49
+ HoptoadNotifier.configuration.proxy_pass).
50
+ returns(@http_proxy)
51
+
52
+ @options = { :rails_env => "staging" }
53
+ end
54
+
55
+ context "on deploy(options)" do
56
+ setup do
57
+ @output = HoptoadTasks.deploy(@options)
58
+ end
59
+
60
+ before_should "post to http://hoptoadapp.com/deploys.txt" do
61
+ URI.stubs(:parse).with('http://hoptoadapp.com/deploys.txt').returns(:uri)
62
+ @http_proxy.expects(:post_form).with(:uri, kind_of(Hash)).returns(successful_response)
63
+ end
64
+
65
+ before_should "use the project api key" do
66
+ @http_proxy.expects(:post_form).
67
+ with(kind_of(URI), has_entries('api_key' => "1234123412341234")).
68
+ returns(successful_response)
69
+ end
70
+
71
+ before_should "use send the rails_env param" do
72
+ @http_proxy.expects(:post_form).
73
+ with(kind_of(URI), has_entries("deploy[rails_env]" => "staging")).
74
+ returns(successful_response)
75
+ end
76
+
77
+ [:local_username, :scm_repository, :scm_revision].each do |key|
78
+ before_should "use send the #{key} param if it's passed in." do
79
+ @options[key] = "value"
80
+ @http_proxy.expects(:post_form).
81
+ with(kind_of(URI), has_entries("deploy[#{key}]" => "value")).
82
+ returns(successful_response)
83
+ end
84
+ end
85
+
86
+ before_should "use the :api_key param if it's passed in." do
87
+ @options[:api_key] = "value"
88
+ @http_proxy.expects(:post_form).
89
+ with(kind_of(URI), has_entries("api_key" => "value")).
90
+ returns(successful_response)
91
+ end
92
+
93
+ before_should "puts the response body on success" do
94
+ HoptoadTasks.expects(:puts).with("body")
95
+ @http_proxy.expects(:post_form).with(any_parameters).returns(successful_response('body'))
96
+ end
97
+
98
+ before_should "puts the response body on failure" do
99
+ HoptoadTasks.expects(:puts).with("body")
100
+ @http_proxy.expects(:post_form).with(any_parameters).returns(unsuccessful_response('body'))
101
+ end
102
+
103
+ should "return false on failure", :before => lambda {
104
+ @http_proxy.expects(:post_form).with(any_parameters).returns(unsuccessful_response('body'))
105
+ } do
106
+ assert !@output
107
+ end
108
+
109
+ should "return true on success", :before => lambda {
110
+ @http_proxy.expects(:post_form).with(any_parameters).returns(successful_response('body'))
111
+ } do
112
+ assert @output
113
+ end
114
+ end
115
+ end
116
+ end
117
+
118
+ context "in a configured project with custom host" do
119
+ setup do
120
+ HoptoadNotifier.configure do |config|
121
+ config.api_key = "1234123412341234"
122
+ config.host = "custom.host"
123
+ end
124
+ end
125
+
126
+ context "on deploy(:rails_env => 'staging')" do
127
+ setup { @output = HoptoadTasks.deploy(:rails_env => "staging") }
128
+
129
+ before_should "post to the custom host" do
130
+ URI.stubs(:parse).with('http://custom.host/deploys.txt').returns(:uri)
131
+ Net::HTTP.expects(:post_form).with(:uri, kind_of(Hash)).returns(successful_response)
132
+ end
133
+ end
134
+ end
135
+
136
+ context "when not configured" do
137
+ setup { HoptoadNotifier.configure { |config| config.api_key = "" } }
138
+
139
+ context "on deploy(:rails_env => 'staging')" do
140
+ setup { @output = HoptoadTasks.deploy(:rails_env => "staging") }
141
+
142
+ before_should "complain about missing api key" do
143
+ HoptoadTasks.expects(:puts).with(regexp_matches(/api key/i))
144
+ end
145
+
146
+ should "return false" do
147
+ assert !@output
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end