cloudtrapper 0.0.2.pre

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 (81) hide show
  1. data/CHANGELOG +823 -0
  2. data/Gemfile +12 -0
  3. data/Guardfile +6 -0
  4. data/INSTALL +20 -0
  5. data/MIT-LICENSE +22 -0
  6. data/README.md +465 -0
  7. data/README_FOR_HEROKU_ADDON.md +94 -0
  8. data/Rakefile +223 -0
  9. data/SUPPORTED_RAILS_VERSIONS +23 -0
  10. data/TESTING.md +33 -0
  11. data/cloudtrapper.gemspec +35 -0
  12. data/features/metal.feature +18 -0
  13. data/features/rack.feature +56 -0
  14. data/features/rails.feature +211 -0
  15. data/features/rails_with_js_notifier.feature +97 -0
  16. data/features/rake.feature +27 -0
  17. data/features/sinatra.feature +29 -0
  18. data/features/step_definitions/file_steps.rb +10 -0
  19. data/features/step_definitions/metal_steps.rb +23 -0
  20. data/features/step_definitions/rack_steps.rb +23 -0
  21. data/features/step_definitions/rails_application_steps.rb +433 -0
  22. data/features/step_definitions/rake_steps.rb +17 -0
  23. data/features/support/airbrake_shim.rb.template +11 -0
  24. data/features/support/env.rb +18 -0
  25. data/features/support/matchers.rb +35 -0
  26. data/features/support/rails.rb +201 -0
  27. data/features/support/rake/Rakefile +68 -0
  28. data/features/support/terminal.rb +107 -0
  29. data/features/user_informer.feature +63 -0
  30. data/generators/cloudtrapper/airbrake_generator.rb +94 -0
  31. data/generators/cloudtrapper/lib/insert_commands.rb +34 -0
  32. data/generators/cloudtrapper/lib/rake_commands.rb +24 -0
  33. data/generators/cloudtrapper/templates/capistrano_hook.rb +6 -0
  34. data/generators/cloudtrapper/templates/cloudtrapper_tasks.rake +25 -0
  35. data/generators/cloudtrapper/templates/initializer.rb +6 -0
  36. data/install.rb +1 -0
  37. data/lib/cloudtrapper/backtrace.rb +100 -0
  38. data/lib/cloudtrapper/capistrano.rb +44 -0
  39. data/lib/cloudtrapper/configuration.rb +281 -0
  40. data/lib/cloudtrapper/notice.rb +348 -0
  41. data/lib/cloudtrapper/rack.rb +55 -0
  42. data/lib/cloudtrapper/rails/action_controller_catcher.rb +30 -0
  43. data/lib/cloudtrapper/rails/controller_methods.rb +74 -0
  44. data/lib/cloudtrapper/rails/error_lookup.rb +33 -0
  45. data/lib/cloudtrapper/rails/javascript_notifier.rb +48 -0
  46. data/lib/cloudtrapper/rails/middleware/exceptions_catcher.rb +29 -0
  47. data/lib/cloudtrapper/rails.rb +40 -0
  48. data/lib/cloudtrapper/rails3_tasks.rb +85 -0
  49. data/lib/cloudtrapper/railtie.rb +48 -0
  50. data/lib/cloudtrapper/rake_handler.rb +66 -0
  51. data/lib/cloudtrapper/sender.rb +116 -0
  52. data/lib/cloudtrapper/shared_tasks.rb +36 -0
  53. data/lib/cloudtrapper/tasks.rb +83 -0
  54. data/lib/cloudtrapper/user_informer.rb +27 -0
  55. data/lib/cloudtrapper/version.rb +3 -0
  56. data/lib/cloudtrapper.rb +155 -0
  57. data/lib/cloudtrapper_tasks.rb +65 -0
  58. data/lib/rails/generators/cloudtrapper/cloudtrapper_generator.rb +100 -0
  59. data/lib/templates/javascript_notifier.erb +15 -0
  60. data/lib/templates/rescue.erb +91 -0
  61. data/rails/init.rb +1 -0
  62. data/resources/README.md +34 -0
  63. data/resources/ca-bundle.crt +3376 -0
  64. data/script/integration_test.rb +38 -0
  65. data/test/backtrace_test.rb +162 -0
  66. data/test/capistrano_test.rb +34 -0
  67. data/test/catcher_test.rb +333 -0
  68. data/test/cloudtrapper_2_2.xsd +78 -0
  69. data/test/cloudtrapper_tasks_test.rb +170 -0
  70. data/test/configuration_test.rb +221 -0
  71. data/test/helper.rb +263 -0
  72. data/test/javascript_notifier_test.rb +52 -0
  73. data/test/logger_test.rb +73 -0
  74. data/test/notice_test.rb +468 -0
  75. data/test/notifier_test.rb +246 -0
  76. data/test/rack_test.rb +58 -0
  77. data/test/rails_initializer_test.rb +36 -0
  78. data/test/recursion_test.rb +10 -0
  79. data/test/sender_test.rb +261 -0
  80. data/test/user_informer_test.rb +29 -0
  81. metadata +301 -0
@@ -0,0 +1,73 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class LoggerTest < Test::Unit::TestCase
4
+ def stub_http(response, body = nil)
5
+ response.stubs(:body => body) if body
6
+ @http = stub(:post => response,
7
+ :read_timeout= => nil,
8
+ :open_timeout= => nil,
9
+ :use_ssl= => nil)
10
+ Net::HTTP.stubs(:new).returns(@http)
11
+ end
12
+
13
+ def send_notice
14
+ Cloudtrapper.sender.send_to_cloudtrapper('data')
15
+ end
16
+
17
+ def stub_verbose_log
18
+ Cloudtrapper.stubs(:write_verbose_log)
19
+ end
20
+
21
+ def configure
22
+ Cloudtrapper.configure { |config| }
23
+ end
24
+
25
+ should "report that notifier is ready when configured" do
26
+ stub_verbose_log
27
+ configure
28
+ assert_logged /Notifier (.*) ready/
29
+ end
30
+
31
+ should "not report that notifier is ready when internally configured" do
32
+ stub_verbose_log
33
+ Cloudtrapper.configure(true) { |config| }
34
+ assert_not_logged /.*/
35
+ end
36
+
37
+ should "print environment info a successful notification without a body" do
38
+ reset_config
39
+ stub_verbose_log
40
+ stub_http(Net::HTTPSuccess)
41
+ send_notice
42
+ assert_logged /Environment Info:/
43
+ assert_not_logged /Response from Cloudtrapper:/
44
+ end
45
+
46
+ should "print environment info on a failed notification without a body" do
47
+ reset_config
48
+ stub_verbose_log
49
+ stub_http(Net::HTTPError)
50
+ send_notice
51
+ assert_logged /Environment Info:/
52
+ assert_not_logged /Response from Cloudtrapper:/
53
+ end
54
+
55
+ should "print environment info and response on a success with a body" do
56
+ reset_config
57
+ stub_verbose_log
58
+ stub_http(Net::HTTPSuccess, 'test')
59
+ send_notice
60
+ assert_logged /Environment Info:/
61
+ assert_logged /Response from Cloudtrapper:/
62
+ end
63
+
64
+ should "print environment info and response on a failure with a body" do
65
+ reset_config
66
+ stub_verbose_log
67
+ stub_http(Net::HTTPError, 'test')
68
+ send_notice
69
+ assert_logged /Environment Info:/
70
+ assert_logged /Response from Cloudtrapper:/
71
+ end
72
+
73
+ end
@@ -0,0 +1,468 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class NoticeTest < Test::Unit::TestCase
4
+
5
+ include DefinesConstants
6
+
7
+ def configure
8
+ Cloudtrapper::Configuration.new.tap do |config|
9
+ config.api_key = 'abc123def456'
10
+ end
11
+ end
12
+
13
+ def build_notice(args = {})
14
+ configuration = args.delete(:configuration) || configure
15
+ Cloudtrapper::Notice.new(configuration.merge(args))
16
+ end
17
+
18
+ def stub_request(attrs = {})
19
+ stub('request', { :parameters => { 'one' => 'two' },
20
+ :protocol => 'http',
21
+ :host => 'some.host',
22
+ :request_uri => '/some/uri',
23
+ :session => { :to_hash => { 'a' => 'b' } },
24
+ :env => { 'three' => 'four' } }.update(attrs))
25
+ end
26
+
27
+ should "set the api key" do
28
+ api_key = 'key'
29
+ notice = build_notice(:api_key => api_key)
30
+ assert_equal api_key, notice.api_key
31
+ end
32
+
33
+ should "accept a project root" do
34
+ project_root = '/path/to/project'
35
+ notice = build_notice(:project_root => project_root)
36
+ assert_equal project_root, notice.project_root
37
+ end
38
+
39
+ should "accept a component" do
40
+ assert_equal 'users_controller', build_notice(:component => 'users_controller').controller
41
+ end
42
+
43
+ should "alias the component as controller" do
44
+ assert_equal 'users_controller', build_notice(:controller => 'users_controller').component
45
+ assert_equal 'users_controller', build_notice(:component => 'users_controller').controller
46
+ end
47
+
48
+ should "accept a action" do
49
+ assert_equal 'index', build_notice(:action => 'index').action
50
+ end
51
+
52
+ should "accept a url" do
53
+ url = 'http://some.host/uri'
54
+ notice = build_notice(:url => url)
55
+ assert_equal url, notice.url
56
+ end
57
+
58
+ should "set the host name" do
59
+ notice = build_notice
60
+ assert_equal hostname, notice.hostname
61
+ end
62
+
63
+ should "accept a backtrace from an exception or hash" do
64
+ array = ["user.rb:34:in `crazy'"]
65
+ exception = build_exception
66
+ exception.set_backtrace array
67
+ backtrace = Cloudtrapper::Backtrace.parse(array)
68
+ notice_from_exception = build_notice(:exception => exception)
69
+
70
+
71
+ assert_equal backtrace,
72
+ notice_from_exception.backtrace,
73
+ "backtrace was not correctly set from an exception"
74
+
75
+ notice_from_hash = build_notice(:backtrace => array)
76
+ assert_equal backtrace,
77
+ notice_from_hash.backtrace,
78
+ "backtrace was not correctly set from a hash"
79
+ end
80
+
81
+ should "pass its backtrace filters for parsing" do
82
+ backtrace_array = ['my/file/backtrace:3']
83
+ exception = build_exception
84
+ exception.set_backtrace(backtrace_array)
85
+ Cloudtrapper::Backtrace.expects(:parse).with(backtrace_array, {:filters => 'foo'})
86
+
87
+ notice = Cloudtrapper::Notice.new({:exception => exception, :backtrace_filters => 'foo'})
88
+ end
89
+
90
+ should "set the error class from an exception or hash" do
91
+ assert_accepts_exception_attribute :error_class do |exception|
92
+ exception.class.name
93
+ end
94
+ end
95
+
96
+ should "set the error message from an exception or hash" do
97
+ assert_accepts_exception_attribute :error_message do |exception|
98
+ "#{exception.class.name}: #{exception.message}"
99
+ end
100
+ end
101
+
102
+ should "accept parameters from a request or hash" do
103
+ parameters = { 'one' => 'two' }
104
+ notice_from_hash = build_notice(:parameters => parameters)
105
+ assert_equal notice_from_hash.parameters, parameters
106
+ end
107
+
108
+ should "accept session data from a session[:data] hash" do
109
+ data = { 'one' => 'two' }
110
+ notice = build_notice(:session => { :data => data })
111
+ assert_equal data, notice.session_data
112
+ end
113
+
114
+ should "accept session data from a session_data hash" do
115
+ data = { 'one' => 'two' }
116
+ notice = build_notice(:session_data => data)
117
+ assert_equal data, notice.session_data
118
+ end
119
+
120
+ should "accept an environment name" do
121
+ assert_equal 'development', build_notice(:environment_name => 'development').environment_name
122
+ end
123
+
124
+ should "accept CGI data from a hash" do
125
+ data = { 'string' => 'value' }
126
+ notice = build_notice(:cgi_data => data)
127
+ assert_equal data, notice.cgi_data, "should take CGI data from a hash"
128
+ end
129
+
130
+ should "accept notifier information" do
131
+ params = { :notifier_name => 'a name for a notifier',
132
+ :notifier_version => '1.0.5',
133
+ :notifier_url => 'http://notifiers.r.us/download' }
134
+ notice = build_notice(params)
135
+ assert_equal params[:notifier_name], notice.notifier_name
136
+ assert_equal params[:notifier_version], notice.notifier_version
137
+ assert_equal params[:notifier_url], notice.notifier_url
138
+ end
139
+
140
+ should "set sensible defaults without an exception" do
141
+ backtrace = Cloudtrapper::Backtrace.parse(build_backtrace_array)
142
+ notice = build_notice(:backtrace => build_backtrace_array)
143
+
144
+ assert_equal 'Notification', notice.error_message
145
+ assert_array_starts_with backtrace.lines, notice.backtrace.lines
146
+ assert_equal({}, notice.parameters)
147
+ assert_equal({}, notice.session_data)
148
+ end
149
+
150
+ should "use the caller as the backtrace for an exception without a backtrace" do
151
+ filters = Cloudtrapper::Configuration.new.backtrace_filters
152
+ backtrace = Cloudtrapper::Backtrace.parse(caller, :filters => filters)
153
+ notice = build_notice(:exception => StandardError.new('error'), :backtrace => nil)
154
+
155
+ assert_array_starts_with backtrace.lines, notice.backtrace.lines
156
+ end
157
+
158
+ should "convert unserializable objects to strings" do
159
+ assert_serializes_hash(:parameters)
160
+ assert_serializes_hash(:cgi_data)
161
+ assert_serializes_hash(:session_data)
162
+ end
163
+
164
+ should "filter parameters" do
165
+ assert_filters_hash(:parameters)
166
+ end
167
+
168
+ should "filter cgi data" do
169
+ assert_filters_hash(:cgi_data)
170
+ end
171
+
172
+ should "filter session" do
173
+ assert_filters_hash(:session_data)
174
+ end
175
+
176
+ should "remove rack.request.form_vars" do
177
+ original = {
178
+ "rack.request.form_vars" => "story%5Btitle%5D=The+TODO+label",
179
+ "abc" => "123"
180
+ }
181
+
182
+ notice = build_notice(:cgi_data => original)
183
+ assert_equal({"abc" => "123"}, notice.cgi_data)
184
+ end
185
+
186
+ context "a Notice turned into XML" do
187
+ setup do
188
+ Cloudtrapper.configure do |config|
189
+ config.api_key = "1234567890"
190
+ end
191
+
192
+ @exception = build_exception
193
+
194
+ @notice = build_notice({
195
+ :notifier_name => 'a name',
196
+ :notifier_version => '1.2.3',
197
+ :notifier_url => 'http://some.url/path',
198
+ :exception => @exception,
199
+ :controller => "controller",
200
+ :action => "action",
201
+ :url => "http://url.com",
202
+ :parameters => { "paramskey" => "paramsvalue",
203
+ "nestparentkey" => { "nestkey" => "nestvalue" } },
204
+ :session_data => { "sessionkey" => "sessionvalue" },
205
+ :cgi_data => { "cgikey" => "cgivalue" },
206
+ :project_root => "RAILS_ROOT",
207
+ :environment_name => "RAILS_ENV"
208
+ })
209
+
210
+ @xml = @notice.to_xml
211
+
212
+ @document = Nokogiri::XML::Document.parse(@xml)
213
+ end
214
+
215
+ should "validate against the XML schema" do
216
+ assert_valid_notice_document @document
217
+ end
218
+
219
+ should "serialize a Notice to XML when sent #to_xml" do
220
+ assert_valid_node(@document, "//api-key", @notice.api_key)
221
+
222
+ assert_valid_node(@document, "//notifier/name", @notice.notifier_name)
223
+ assert_valid_node(@document, "//notifier/version", @notice.notifier_version)
224
+ assert_valid_node(@document, "//notifier/url", @notice.notifier_url)
225
+
226
+ assert_valid_node(@document, "//error/class", @notice.error_class)
227
+ assert_valid_node(@document, "//error/message", @notice.error_message)
228
+
229
+ assert_valid_node(@document, "//error/backtrace/line/@number", @notice.backtrace.lines.first.number)
230
+ assert_valid_node(@document, "//error/backtrace/line/@file", @notice.backtrace.lines.first.file)
231
+ assert_valid_node(@document, "//error/backtrace/line/@method", @notice.backtrace.lines.first.method)
232
+
233
+ assert_valid_node(@document, "//request/url", @notice.url)
234
+ assert_valid_node(@document, "//request/component", @notice.controller)
235
+ assert_valid_node(@document, "//request/action", @notice.action)
236
+
237
+ assert_valid_node(@document, "//request/params/var/@key", "paramskey")
238
+ assert_valid_node(@document, "//request/params/var", "paramsvalue")
239
+ assert_valid_node(@document, "//request/params/var/@key", "nestparentkey")
240
+ assert_valid_node(@document, "//request/params/var/var/@key", "nestkey")
241
+ assert_valid_node(@document, "//request/params/var/var", "nestvalue")
242
+ assert_valid_node(@document, "//request/session/var/@key", "sessionkey")
243
+ assert_valid_node(@document, "//request/session/var", "sessionvalue")
244
+ assert_valid_node(@document, "//request/cgi-data/var/@key", "cgikey")
245
+ assert_valid_node(@document, "//request/cgi-data/var", "cgivalue")
246
+
247
+ assert_valid_node(@document, "//server-environment/project-root", "RAILS_ROOT")
248
+ assert_valid_node(@document, "//server-environment/environment-name", "RAILS_ENV")
249
+ assert_valid_node(@document, "//server-environment/hostname", hostname)
250
+ end
251
+ end
252
+
253
+ should "not send empty request data" do
254
+ notice = build_notice
255
+ assert_nil notice.url
256
+ assert_nil notice.controller
257
+ assert_nil notice.action
258
+
259
+ xml = notice.to_xml
260
+ document = Nokogiri::XML.parse(xml)
261
+ assert_nil document.at('//request/url')
262
+ assert_nil document.at('//request/component')
263
+ assert_nil document.at('//request/action')
264
+
265
+ assert_valid_notice_document document
266
+ end
267
+
268
+ %w(url controller action).each do |var|
269
+ should "send a request if #{var} is present" do
270
+ notice = build_notice(var.to_sym => 'value')
271
+ xml = notice.to_xml
272
+ document = Nokogiri::XML.parse(xml)
273
+ assert_not_nil document.at('//request')
274
+ end
275
+ end
276
+
277
+ %w(parameters cgi_data session_data).each do |var|
278
+ should "send a request if #{var} is present" do
279
+ notice = build_notice(var.to_sym => { 'key' => 'value' })
280
+ xml = notice.to_xml
281
+ document = Nokogiri::XML.parse(xml)
282
+ assert_not_nil document.at('//request')
283
+ end
284
+ end
285
+
286
+ should "not ignore an exception not matching ignore filters" do
287
+ notice = build_notice(:error_class => 'ArgumentError',
288
+ :ignore => ['Argument'],
289
+ :ignore_by_filters => [lambda { |notice| false }])
290
+ assert !notice.ignore?
291
+ end
292
+
293
+ should "ignore an exception with a matching error class" do
294
+ notice = build_notice(:error_class => 'ArgumentError',
295
+ :ignore => [ArgumentError])
296
+ assert notice.ignore?
297
+ end
298
+
299
+ should "ignore an exception with a matching error class name" do
300
+ notice = build_notice(:error_class => 'ArgumentError',
301
+ :ignore => ['ArgumentError'])
302
+ assert notice.ignore?
303
+ end
304
+
305
+ should "ignore an exception with a matching filter" do
306
+ filter = lambda {|notice| notice.error_class == 'ArgumentError' }
307
+ notice = build_notice(:error_class => 'ArgumentError',
308
+ :ignore_by_filters => [filter])
309
+ assert notice.ignore?
310
+ end
311
+
312
+ should "not raise without an ignore list" do
313
+ notice = build_notice(:ignore => nil, :ignore_by_filters => nil)
314
+ assert_nothing_raised do
315
+ notice.ignore?
316
+ end
317
+ end
318
+
319
+ ignored_error_classes = %w(
320
+ ActiveRecord::RecordNotFound
321
+ AbstractController::ActionNotFound
322
+ ActionController::RoutingError
323
+ ActionController::InvalidAuthenticityToken
324
+ CGI::Session::CookieStore::TamperedWithCookie
325
+ ActionController::UnknownAction
326
+ )
327
+
328
+ ignored_error_classes.each do |ignored_error_class|
329
+ should "ignore #{ignored_error_class} error by default" do
330
+ notice = build_notice(:error_class => ignored_error_class)
331
+ assert notice.ignore?
332
+ end
333
+ end
334
+
335
+ should "act like a hash" do
336
+ notice = build_notice(:error_message => 'some message')
337
+ assert_equal notice.error_message, notice[:error_message]
338
+ end
339
+
340
+ should "return params on notice[:request][:params]" do
341
+ params = { 'one' => 'two' }
342
+ notice = build_notice(:parameters => params)
343
+ assert_equal params, notice[:request][:params]
344
+ end
345
+
346
+ should "ensure #to_hash is called on objects that support it" do
347
+ assert_nothing_raised do
348
+ build_notice(:session => { :object => stub(:to_hash => {}) })
349
+ end
350
+ end
351
+
352
+
353
+ should "ensure #to_ary is called on objects that support it" do
354
+ assert_nothing_raised do
355
+ build_notice(:session => { :object => stub(:to_ary => {}) })
356
+ end
357
+ end
358
+
359
+ should "extract data from a rack environment hash" do
360
+ url = "https://subdomain.happylane.com:100/test/file.rb?var=value&var2=value2"
361
+ parameters = { 'var' => 'value', 'var2' => 'value2' }
362
+ env = Rack::MockRequest.env_for(url)
363
+
364
+ notice = build_notice(:rack_env => env)
365
+
366
+ assert_equal url, notice.url
367
+ assert_equal parameters, notice.parameters
368
+ assert_equal 'GET', notice.cgi_data['REQUEST_METHOD']
369
+ end
370
+
371
+ should "extract data from a rack environment hash with action_dispatch info" do
372
+ params = { 'controller' => 'users', 'action' => 'index', 'id' => '7' }
373
+ env = Rack::MockRequest.env_for('/', { 'action_dispatch.request.parameters' => params })
374
+
375
+ notice = build_notice(:rack_env => env)
376
+
377
+ assert_equal params, notice.parameters
378
+ assert_equal params['controller'], notice.component
379
+ assert_equal params['action'], notice.action
380
+ end
381
+
382
+ should "extract session data from a rack environment" do
383
+ session_data = { 'something' => 'some value' }
384
+ env = Rack::MockRequest.env_for('/', 'rack.session' => session_data)
385
+
386
+ notice = build_notice(:rack_env => env)
387
+
388
+ assert_equal session_data, notice.session_data
389
+ end
390
+
391
+ should "prefer passed session data to rack session data" do
392
+ session_data = { 'something' => 'some value' }
393
+ env = Rack::MockRequest.env_for('/')
394
+
395
+ notice = build_notice(:rack_env => env, :session_data => session_data)
396
+
397
+ assert_equal session_data, notice.session_data
398
+ end
399
+
400
+ def assert_accepts_exception_attribute(attribute, args = {}, &block)
401
+ exception = build_exception
402
+ block ||= lambda { exception.send(attribute) }
403
+ value = block.call(exception)
404
+
405
+ notice_from_exception = build_notice(args.merge(:exception => exception))
406
+
407
+ assert_equal notice_from_exception.send(attribute),
408
+ value,
409
+ "#{attribute} was not correctly set from an exception"
410
+
411
+ notice_from_hash = build_notice(args.merge(attribute => value))
412
+ assert_equal notice_from_hash.send(attribute),
413
+ value,
414
+ "#{attribute} was not correctly set from a hash"
415
+ end
416
+
417
+ def assert_serializes_hash(attribute)
418
+ [File.open(__FILE__), Proc.new { puts "boo!" }, Module.new].each do |object|
419
+ hash = {
420
+ :strange_object => object,
421
+ :sub_hash => {
422
+ :sub_object => object
423
+ },
424
+ :array => [object]
425
+ }
426
+ notice = build_notice(attribute => hash)
427
+ hash = notice.send(attribute)
428
+ assert_equal object.to_s, hash[:strange_object], "objects should be serialized"
429
+ assert_kind_of Hash, hash[:sub_hash], "subhashes should be kept"
430
+ assert_equal object.to_s, hash[:sub_hash][:sub_object], "subhash members should be serialized"
431
+ assert_kind_of Array, hash[:array], "arrays should be kept"
432
+ assert_equal object.to_s, hash[:array].first, "array members should be serialized"
433
+ end
434
+ end
435
+
436
+ def assert_valid_notice_document(document)
437
+ xsd_path = File.join(File.dirname(__FILE__), "cloudtrapper_2_2.xsd")
438
+ schema = Nokogiri::XML::Schema.new(IO.read(xsd_path))
439
+ errors = schema.validate(document)
440
+ assert errors.empty?, errors.collect{|e| e.message }.join
441
+ end
442
+
443
+ def assert_filters_hash(attribute)
444
+ filters = ["abc", :def]
445
+ original = { 'abc' => "123", 'def' => "456", 'ghi' => "789", 'nested' => { 'abc' => '100' },
446
+ 'something_with_abc' => 'match the entire string'}
447
+ filtered = { 'abc' => "[FILTERED]",
448
+ 'def' => "[FILTERED]",
449
+ 'something_with_abc' => "match the entire string",
450
+ 'ghi' => "789",
451
+ 'nested' => { 'abc' => '[FILTERED]' } }
452
+
453
+ notice = build_notice(:params_filters => filters, attribute => original)
454
+
455
+ assert_equal(filtered,
456
+ notice.send(attribute))
457
+ end
458
+
459
+ def build_backtrace_array
460
+ ["app/models/user.rb:13:in `magic'",
461
+ "app/controllers/users_controller.rb:8:in `index'"]
462
+ end
463
+
464
+ def hostname
465
+ `hostname`.chomp
466
+ end
467
+
468
+ end