party_foul 0.5.0 → 0.6.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.
data/README.md CHANGED
@@ -16,10 +16,10 @@ following:
16
16
  unique title, session information, and stack trace. The issue is
17
17
  tagged as a `bug`. A new comment is added with relevant data on the
18
18
  application state.
19
- 3. If an open issue is found the occurance count and time stamp is
19
+ 3. If an open issue is found the occurence count and time stamp is
20
20
  updated. A new comment is added with relevant data on the
21
21
  application state.
22
- 4. If a closed issue is found the occurance count and time stamp is
22
+ 4. If a closed issue is found the occurence count and time stamp is
23
23
  updated. The issue is reopened and a `regression` tag is
24
24
  added. A new comment is added with relevant data on the
25
25
  application state.
@@ -64,32 +64,29 @@ You need to initialize `PartyFoul`, use the following:
64
64
 
65
65
  ```ruby
66
66
  PartyFoul.configure do |config|
67
- # the collection of exceptions to be ignored by PartyFoul
67
+ # The collection of exceptions PartyFoul should not be allowed to handle
68
68
  # The constants here *must* be represented as strings
69
- config.ignored_exceptions = ['ActiveRecord::RecordNotFound', 'ActionController::RoutingError']
70
-
71
- # The names of the HTTP headers to not report
72
- config.filtered_http_headers = ['Cookie']
69
+ config.blacklisted_exceptions = ['ActiveRecord::RecordNotFound', 'ActionController::RoutingError']
73
70
 
74
71
  # The OAuth token for the account that is opening the issues on Github
75
- config.oauth_token = 'abcdefgh1234567890'
72
+ config.oauth_token = 'abcdefgh1234567890'
76
73
 
77
74
  # The API endpoint for Github. Unless you are hosting a private
78
75
  # instance of Enterprise Github you do not need to include this
79
- config.endpoint = 'https://api.github.com'
76
+ config.endpoint = 'https://api.github.com'
80
77
 
81
78
  # The Web URL for Github. Unless you are hosting a private
82
79
  # instance of Enterprise Github you do not need to include this
83
- config.web_url = 'https://github.com'
80
+ config.web_url = 'https://github.com'
84
81
 
85
82
  # The organization or user that owns the target repository
86
- config.owner = 'owner_name'
83
+ config.owner = 'owner_name'
87
84
 
88
85
  # The repository for this application
89
- config.repo = 'repo_name'
86
+ config.repo = 'repo_name'
90
87
 
91
88
  # The branch for your deployed code
92
- # config.branch = 'master'
89
+ # config.branch = 'master'
93
90
  end
94
91
  ```
95
92
 
@@ -98,8 +95,8 @@ You can generate an OAuth token from via the
98
95
  with cURL:
99
96
 
100
97
  ```bash
101
- curl -X POST -i -d "{ \"scopes\": [\"repo\"] }" \
102
- https://<github_login>:<github_password>@api.github.com/authorizations
98
+ curl -u <github_login> -i -d "{ \"scopes\": [\"repo\"] }" \
99
+ https://api.github.com/authorizations
103
100
  ```
104
101
 
105
102
  Add as the very last middleware in your production `Rack` stack.
@@ -128,25 +125,34 @@ end
128
125
 
129
126
  ```
130
127
 
131
- ### Changing How Issues Are Reported ###
128
+ ### Using PartyFoul with Sidekiq
129
+
130
+ In order to use PartyFoul for exception handling with Sidekiq you will need to create an initializer with some middleware configuration. The following example is based on using [Sidekiq with another exception notifiier server](https://github.com/bugsnag/bugsnag-ruby/blob/master/lib/bugsnag/sidekiq.rb).
132
131
 
133
- `PartyFoul` comes with default templates for what the body of issues and
134
- comments are. If you want to override these templates you simply need to
135
- add them as an option in your initializer:
132
+ File: config/initializers/partyfoul_sidekiq.rb
136
133
 
137
134
  ```ruby
138
- PartyFoul.configure do |config|
139
- config.issue_body = ':issue_title'
140
- config.comment_body = ':occurred_at'
135
+ module PartyFoul
136
+ class Sidekiq
137
+ def call(worker, msg, queue)
138
+ begin
139
+ yield
140
+ rescue => ex
141
+ PartyFoul::RacklessExceptionHandler.handle(ex, {class: worker.class.name, method: queue, params: msg})
142
+ raise
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ ::Sidekiq.configure_server do |config|
149
+ config.server_middleware do |chain|
150
+ chain.add ::PartyFoul::Sidekiq
151
+ end
141
152
  end
142
153
  ```
143
154
 
144
- In this over-simplistic example the words that start with `:` in the
145
- templates are evaluated with the value of the corresponding named
146
- instance method on the instance of `PartyFoul::IssueRenderer`. If you
147
- want to add additional values for replacement you should open that class
148
- to add the methods. Depending upon the data point you may want o make
149
- the change in one of the [different issue renderer adapters](https://github.com/dockyard/party_foul/tree/master/lib/party_foul/issue_renderers).
155
+ This will pass the worker class name and queue as well as all worker-related parameters off to PartyFoul before passing on the exception.
150
156
 
151
157
  ## Authors ##
152
158
 
@@ -1,28 +1,25 @@
1
1
  PartyFoul.configure do |config|
2
- # the collection of exceptions to be ignored by PartyFoul
2
+ # The collection of exceptions PartyFoul should not be allowed to handle
3
3
  # The constants here *must* be represented as strings
4
- config.ignored_exceptions = ['ActiveRecord::RecordNotFound', 'ActionController::RoutingError']
5
-
6
- # The names of the HTTP headers to not report
7
- config.filtered_http_headers = ['Cookie']
4
+ config.blacklisted_exceptions = ['ActiveRecord::RecordNotFound', 'ActionController::RoutingError']
8
5
 
9
6
  # The OAuth token for the account that is opening the issues on Github
10
- config.oauth_token = '<%= @oauth_token %>'
7
+ config.oauth_token = '<%= @oauth_token %>'
11
8
 
12
9
  # The API endpoint for Github. Unless you are hosting a private
13
10
  # instance of Enterprise Github you do not need to include this
14
- config.endpoint = '<%= @endpoint %>'
11
+ config.endpoint = '<%= @endpoint %>'
15
12
 
16
13
  # The Web URL for Github. Unless you are hosting a private
17
14
  # instance of Enterprise Github you do not need to include this
18
- config.web_url = '<%= @web_url %>'
15
+ config.web_url = '<%= @web_url %>'
19
16
 
20
17
  # The organization or user that owns the target repository
21
- config.owner = '<%= @owner %>'
18
+ config.owner = '<%= @owner %>'
22
19
 
23
20
  # The repository for this application
24
- config.repo = '<%= @repo %>'
21
+ config.repo = '<%= @repo %>'
25
22
 
26
23
  # The branch for your deployed code
27
- # config.branch = 'master'
24
+ # config.branch = 'master'
28
25
  end
@@ -2,9 +2,11 @@ require 'github_api'
2
2
 
3
3
  module PartyFoul
4
4
  class << self
5
- attr_accessor :github, :oauth_token, :endpoint, :owner, :repo,
6
- :ignored_exceptions, :processor, :issue_template,
7
- :comment_template, :filtered_http_headers, :web_url, :branch
5
+ attr_accessor :github, :oauth_token, :endpoint, :owner, :repo, :blacklisted_exceptions, :processor, :web_url, :branch, :whitelisted_rack_variables
6
+ end
7
+
8
+ def self.whitelisted_rack_variables
9
+ @whitelisted_rack_variables ||= %w{GATEWAY_INTERFACE PATH_INFO REMOTE_ADDR REMOTE_HOST REQUEST_METHOD REQUEST_URI SERVER_NAME SERVER_PORT SERVER_PROTOCOL SERVER_SOFTWARE HTTP_HOST HTTP_CONNECTION HTTP_CACHE_CONTROL HTTP_ACCEPT HTTP_USER_AGENT HTTP_ACCEPT_ENCODING HTTP_ACCEPT_LANGUAGE HTTP_ACCEPT_CHARSET rack.version rack.input rack.errors rack.multithread rack.multiprocess rack.run_once rack.url_scheme HTTP_VERSION REQUEST_PATH ORIGINAL_FULLPATH action_dispatch.routes action_dispatch.parameter_filter action_dispatch.secret_token action_dispatch.show_exceptions action_dispatch.show_detailed_exceptions action_dispatch.logger action_dispatch.backtrace_cleaner action_dispatch.request_id action_dispatch.remote_ip rack.session rack.session.options rack.request.cookie_hash rack.request.cookie_string action_dispatch.cookies action_dispatch.request.unsigned_session_cookie action_dispatch.request.path_parameters action_controller.instance action_dispatch.request.request_parameters rack.request.query_string rack.request.query_hash action_dispatch.request.query_parameters action_dispatch.request.parameters action_dispatch.request.formats}
8
10
  end
9
11
 
10
12
  # The git branch that is used for linking in the stack trace
@@ -22,38 +24,20 @@ module PartyFoul
22
24
  @web_url ||= 'https://github.com'
23
25
  end
24
26
 
25
- # The template used for rendering the body of a new issue
27
+ # The api endpoint for Github. This is only interesting for Enterprise
28
+ # users
26
29
  #
27
- # @return [String]
28
- def self.issue_template
29
- @issue_template ||=
30
- <<-TEMPLATE
31
- <table>
32
- <tr><th>Exception</th><td>:exception</td></tr>
33
- <tr><th>Count</th><td>1</td></tr>
34
- <tr><th>Last Occurance</th><td>:occurred_at</td></tr>
35
- </table>
36
-
37
- ## Stack Trace
38
- <pre>:stack_trace</pre>
39
- Fingerprint: `:fingerprint`
40
- TEMPLATE
30
+ # @return [String] Defaults to 'https://api.github.com' if not set
31
+ def self.endpoint
32
+ @endpoint ||= 'https://api.github.com'
41
33
  end
42
34
 
43
- # The template used for rendering the body of a new comment
35
+ # The processor to be used when handling the exception. Defaults to a
36
+ # synchrons processor
44
37
  #
45
- # @return [String]
46
- def self.comment_template
47
- @comment_template ||=
48
- <<-TEMPLATE
49
- <table>
50
- <tr><th>Occurred at</th><td>:occurred_at</td></tr>
51
- <tr><th>Params</th><td>:params</td></tr>
52
- <tr><th>IP Address</th><td>:ip_address</td></tr>
53
- <tr><th>Session</th><td>:session</td></tr>
54
- <tr><th>HTTP Headers</th><td>:http_headers</td></tr>
55
- </table>
56
- TEMPLATE
38
+ # @return [Class] Defaults to 'PartyFoul::Processors:Sync
39
+ def self.processor
40
+ @processor ||= PartyFoul::Processors::Sync
57
41
  end
58
42
 
59
43
  # The collection of exceptions that should not be captured. Members of
@@ -67,8 +51,8 @@ Fingerprint: `:fingerprint`
67
51
  # [ActiveRecord::RecordNotFound]
68
52
  #
69
53
  # @return [Array]
70
- def self.ignored_exceptions
71
- @ignored_exceptions || []
54
+ def self.blacklisted_exceptions
55
+ @blacklisted_exceptions || []
72
56
  end
73
57
 
74
58
  # The url of the repository. Built using the {.web_url}, {.owner}, and {.repo}
@@ -87,20 +71,17 @@ Fingerprint: `:fingerprint`
87
71
  # config.oauth_token = ENV['oauth_token']
88
72
  # end
89
73
  #
74
+ # Will also setup for Github api connections
75
+ #
90
76
  # @param [Block]
91
77
  def self.configure(&block)
92
78
  yield self
93
- self.processor ||= PartyFoul::Processors::Sync
94
- _self = self
95
- self.github ||= Github.new do |config|
96
- %w{endpoint oauth_token}.each do |option|
97
- config.send("#{option}=", _self.send(option)) if !_self.send(option).nil?
98
- end
99
- end
79
+ self.github ||= Github.new oauth_token: oauth_token, endpoint: endpoint
100
80
  end
101
81
  end
102
82
 
103
83
  require 'party_foul/exception_handler'
104
- require 'party_foul/issue_renderer'
84
+ require 'party_foul/rackless_exception_handler'
85
+ require 'party_foul/issue_renderers'
105
86
  require 'party_foul/middleware'
106
87
  require 'party_foul/processors'
@@ -7,7 +7,7 @@ class PartyFoul::ExceptionHandler
7
7
  #
8
8
  # @param [Exception, Hash]
9
9
  def self.handle(exception, env)
10
- PartyFoul.processor.handle(exception, env)
10
+ PartyFoul.processor.handle(exception, clean_env(env))
11
11
  end
12
12
 
13
13
  # Makes an attempt to determine what framework is being used and will use the proper
@@ -16,9 +16,9 @@ class PartyFoul::ExceptionHandler
16
16
  # @param [Exception, Hash]
17
17
  def initialize(exception, env)
18
18
  renderer_klass = if defined?(Rails)
19
- PartyFoul::RailsIssueRenderer
19
+ PartyFoul::IssueRenderers::Rails
20
20
  else
21
- PartyFoul::RackIssueRenderer
21
+ PartyFoul::IssueRenderers::Rack
22
22
  end
23
23
 
24
24
  self.rendered_issue = renderer_klass.new(exception, env)
@@ -69,6 +69,10 @@ class PartyFoul::ExceptionHandler
69
69
 
70
70
  private
71
71
 
72
+ def self.clean_env(env)
73
+ env.select { |key, value| PartyFoul.whitelisted_rack_variables.include?(key) }
74
+ end
75
+
72
76
  def fingerprint
73
77
  rendered_issue.fingerprint
74
78
  end
@@ -0,0 +1,5 @@
1
+ module PartyFoul::IssueRenderers; end
2
+ require 'party_foul/issue_renderers/base'
3
+ require 'party_foul/issue_renderers/rack'
4
+ require 'party_foul/issue_renderers/rails'
5
+ require 'party_foul/issue_renderers/rackless'
@@ -1,4 +1,4 @@
1
- class PartyFoul::IssueRenderer
1
+ class PartyFoul::IssueRenderers::Base
2
2
  attr_accessor :exception, :env, :sha
3
3
 
4
4
  # A new renderer instance for Githug issues
@@ -16,18 +16,27 @@ class PartyFoul::IssueRenderer
16
16
  raise NotImplementedError
17
17
  end
18
18
 
19
- # Will compile the template for an issue body as defined in
20
- # {PartyFoul.issue_template}
19
+ # Renders the issue body
20
+ #
21
+ # Customize by overriding {#body_options}
21
22
  #
22
23
  # @return [String]
23
24
  def body
24
- compile_template(PartyFoul.issue_template)
25
+ <<-BODY
26
+ #{build_table_from_hash(body_options)}
27
+
28
+ ## Stack Trace
29
+ <pre>#{stack_trace}</pre>
30
+ Fingerprint: `#{fingerprint}`
31
+ BODY
25
32
  end
26
33
 
27
- # Will compile the template for a comment body as defined in
28
- # {PartyFoul.comment_template}
34
+ # Renderes the issue comment
35
+ #
36
+ # Customize by overriding {#comment_options}
37
+ #
29
38
  def comment
30
- compile_template(PartyFoul.comment_template)
39
+ build_table_from_hash(comment_options)
31
40
  end
32
41
 
33
42
  # Compiles the stack trace for use in the issue body. Lines in the
@@ -67,21 +76,13 @@ class PartyFoul::IssueRenderer
67
76
  begin
68
77
  current_count = old_body.match(/<th>Count<\/th><td>(\d+)<\/td>/)[1].to_i
69
78
  old_body.sub!("<th>Count</th><td>#{current_count}</td>", "<th>Count</th><td>#{current_count + 1}</td>")
70
- old_body.sub!(/<th>Last Occurance<\/th><td>.+<\/td>/, "<th>Last Occurance</th><td>#{occurred_at}</td>")
79
+ old_body.sub!(/<th>Last Occurrence<\/th><td>.+<\/td>/, "<th>Last Occurrence</th><td>#{occurred_at}</td>")
71
80
  old_body
72
81
  rescue
73
82
  self.body
74
83
  end
75
84
  end
76
85
 
77
- # The params hash at the time the exception occurred. This method is
78
- # overriden for each framework adapter. It should return a hash.
79
- #
80
- # @return [NotImplementedError]
81
- def params
82
- raise NotImplementedError
83
- end
84
-
85
86
  # The timestamp when the exception occurred.
86
87
  #
87
88
  # @return [String]
@@ -89,48 +90,45 @@ class PartyFoul::IssueRenderer
89
90
  Time.now.strftime('%B %d, %Y %H:%M:%S %z')
90
91
  end
91
92
 
92
- # IP address of the client who triggered the exception
93
+ # The hash used for building the table in issue body
93
94
  #
94
- # @return [String]
95
- def ip_address
96
- env['REMOTE_ADDR']
95
+ # @return [Hash]
96
+ def body_options(count = 0)
97
+ { Exception: exception, 'Last Occurrence' => occurred_at, Count: count + 1 }
97
98
  end
98
99
 
99
- # The session hash for the client at the time of the exception
100
+ # The hash used for building the table in the comment body
100
101
  #
101
102
  # @return [Hash]
102
- def session
103
- env['rack.session']
103
+ def comment_options
104
+ { 'Occurred At' => occurred_at }
104
105
  end
105
106
 
106
- # HTTP Headers hash from the request. Headers can be filtered out by
107
- # adding matching key names to {PartyFoul.filtered_http_headers}
107
+
108
+ # Builds an HTML table from hash
108
109
  #
109
- # @return [Hash]
110
- def http_headers
111
- env.keys.select { |key| key =~ /^HTTP_(\w+)/ && !(PartyFoul.filtered_http_headers || []).include?($1.split('_').map(&:capitalize).join('-')) }.sort.inject({}) do |hash, key|
112
- hash[key.split('HTTP_').last.split('_').map(&:capitalize).join('-')] = env[key]
113
- hash
114
- end
110
+ # @return [String]
111
+ def build_table_from_hash(hash)
112
+ "<table>#{rows_for_table_from_hash(hash)}</table>"
115
113
  end
116
114
 
117
- def compile_template(template)
118
- template.gsub(/:\w+/) do |method|
119
- value = self.send(method.split(':').last)
120
- if value.kind_of?(Hash)
121
- hash_as_table(value)
122
- else
123
- value
115
+ # Builds the rows of an HTML table from hash.
116
+ # Keys as Headers cells and Values as Data cells
117
+ # If the Value is a Hash it will be rendered as a table
118
+ #
119
+ # @return [String]
120
+ def rows_for_table_from_hash(hash)
121
+ hash.inject('') do |rows, row|
122
+ key, value = row
123
+ if row[1].kind_of?(Hash)
124
+ value = build_table_from_hash(row[1])
124
125
  end
126
+ rows << "<tr><th>#{key}</th><td>#{value}</td></tr>"
125
127
  end
126
128
  end
127
129
 
128
130
  private
129
131
 
130
- def hash_as_table(value)
131
- "<table>#{value.map {|key, value| "<tr><th>#{key}</th><td>#{value}</td></tr>"}.join}</table>"
132
- end
133
-
134
132
  def app_root
135
133
  Dir.pwd
136
134
  end
@@ -139,6 +137,3 @@ class PartyFoul::IssueRenderer
139
137
  backtrace_line.match(/#{app_root}\/((.+?):(\d+))/)
140
138
  end
141
139
  end
142
-
143
- require 'party_foul/issue_renderers/rack'
144
- require 'party_foul/issue_renderers/rails'
@@ -1,19 +1,45 @@
1
- require 'party_foul/issue_renderer'
1
+ class PartyFoul::IssueRenderers::Rack < PartyFoul::IssueRenderers::Base
2
+ # Title for the issue comprised of (exception) "message"
3
+ #
4
+ # @return [String]
5
+ def title
6
+ %{(#{exception.class}) "#{exception.message}"}
7
+ end
8
+
9
+ def comment_options
10
+ super.merge(URL: url, Params: params, Session: session, 'IP Address' => ip_address, 'HTTP Headers' => http_headers)
11
+ end
12
+
13
+ # Rack params
14
+ #
15
+ # @return [Hash]
16
+ def params
17
+ env['QUERY_STRING']
18
+ end
2
19
 
3
- module PartyFoul
4
- class RackIssueRenderer < IssueRenderer
5
- # Rack params
6
- #
7
- # @return [Hash]
8
- def params
9
- env['QUERY_STRING']
10
- end
20
+ # IP address of the client who triggered the exception
21
+ #
22
+ # @return [String]
23
+ def ip_address
24
+ env['REMOTE_ADDR']
25
+ end
26
+
27
+ def url
28
+ "[#{env['REQUEST_METHOD']}] #{env['REQUEST_URI']}"
29
+ end
30
+
31
+ # The session hash for the client at the time of the exception
32
+ #
33
+ # @return [Hash]
34
+ def session
35
+ env['rack.session']
36
+ end
11
37
 
12
- # Title for the issue comprised of (exception) "message"
13
- #
14
- # @return [String]
15
- def title
16
- %{(#{exception.class}) "#{exception.message}"}
17
- end
38
+ # HTTP Headers hash from the request. Headers can be filtered out by
39
+ # adding matching key names to {PartyFoul.blacklisted_headers}
40
+ #
41
+ # @return [Hash]
42
+ def http_headers
43
+ { Version: env['HTTP_VERSION'], 'User Agent' => env['HTTP_USER_AGENT'], 'Accept Encoding' => env['HTTP_ACCEPT_ENCODING'], Accept: env['HTTP_ACCEPT'] }
18
44
  end
19
45
  end
@@ -0,0 +1,26 @@
1
+ require 'party_foul/issue_renderers/base'
2
+
3
+ class PartyFoul::IssueRenderers::Rackless < PartyFoul::IssueRenderers::Base
4
+ # env in a rackless environment is expected to contain three keys:
5
+ # class: name of the class that raised the exception
6
+ # method: name of the method that raised the exception
7
+ # params: parameters passed to the method that raised the exception
8
+
9
+ # Rails params hash. Filtered parms are respected.
10
+ #
11
+ # @return [Hash]
12
+ def params
13
+ env[:params]
14
+ end
15
+
16
+ # Title for the issue comprised of Controller#action (exception) "message"
17
+ #
18
+ # @return [String]
19
+ def title
20
+ %{#{env[:class]}##{env[:method]} (#{exception.class}) "#{exception.message}"}
21
+ end
22
+
23
+ def comment_options
24
+ super.merge(Params: params)
25
+ end
26
+ end
@@ -1,26 +1,30 @@
1
- require 'party_foul/issue_renderer'
1
+ class PartyFoul::IssueRenderers::Rails < PartyFoul::IssueRenderers::Rack
2
+ # Title for the issue comprised of Controller#action (exception) "message"
3
+ #
4
+ # @return [String]
5
+ def title
6
+ %{#{env['action_controller.instance'].class}##{env['action_dispatch.request.path_parameters']['action']} (#{exception.class}) "#{exception.message}"}
7
+ end
2
8
 
3
- module PartyFoul
4
- class RailsIssueRenderer < IssueRenderer
5
- # Rails params hash. Filtered parms are respected.
6
- #
7
- # @return [Hash]
8
- def params
9
- parameter_filter = ActionDispatch::Http::ParameterFilter.new(env["action_dispatch.parameter_filter"])
10
- parameter_filter.filter(env['action_dispatch.request.path_parameters'])
11
- end
9
+ # Rails params hash. Filtered parms are respected.
10
+ #
11
+ # @return [Hash]
12
+ def params
13
+ parameter_filter = ActionDispatch::Http::ParameterFilter.new(env["action_dispatch.parameter_filter"])
14
+ parameter_filter.filter(env['action_dispatch.request.path_parameters'])
15
+ end
12
16
 
13
- # Title for the issue comprised of Controller#action (exception) "message"
14
- #
15
- # @return [String]
16
- def title
17
- %{#{env['action_controller.instance'].class}##{env['action_dispatch.request.path_parameters']['action']} (#{exception.class}) "#{exception.message}"}
18
- end
17
+ # Rails session hash. Filtered parms are respected.
18
+ #
19
+ # @return [Hash]
20
+ def session
21
+ parameter_filter = ActionDispatch::Http::ParameterFilter.new(env['action_dispatch.parameter_filter'])
22
+ parameter_filter.filter(env['rack.session'])
23
+ end
19
24
 
20
- private
25
+ private
21
26
 
22
- def app_root
23
- Rails.root
24
- end
27
+ def app_root
28
+ Rails.root
25
29
  end
26
30
  end
@@ -16,8 +16,8 @@ module PartyFoul
16
16
  private
17
17
 
18
18
  def allow_handling?(captured_exception)
19
- !PartyFoul.ignored_exceptions.find do |ignored_exception|
20
- names = ignored_exception.split('::')
19
+ !PartyFoul.blacklisted_exceptions.find do |blacklisted_exception|
20
+ names = blacklisted_exception.split('::')
21
21
  names.shift if names.empty? || names.first.empty?
22
22
 
23
23
  constant = Object
@@ -1,4 +1,2 @@
1
- module PartyFoul::Processors
2
- end
3
-
1
+ module PartyFoul::Processors; end
4
2
  require 'party_foul/processors/sync'
@@ -0,0 +1,11 @@
1
+ class PartyFoul::Processors::Base
2
+ # Passes the exception and rack env data to the ExceptionHandler and
3
+ # runs everything synchronously. This base class method must be
4
+ # overriden by any inheriting class.
5
+ #
6
+ # @param [Exception, Hash]
7
+ # @return [NotImplementedError]
8
+ def self.handle(exception, env)
9
+ raise NotImplementedError
10
+ end
11
+ end
@@ -1,4 +1,6 @@
1
- class PartyFoul::Processors::Sync
1
+ require 'party_foul/processors/base'
2
+
3
+ class PartyFoul::Processors::Sync < PartyFoul::Processors::Base
2
4
  # Passes the exception and rack env data to the ExceptionHandler and
3
5
  # runs everything synchronously.
4
6
  #
@@ -0,0 +1,17 @@
1
+ class PartyFoul::RacklessExceptionHandler < PartyFoul::ExceptionHandler
2
+ # This handler will pass the exception and working environment from Rack off to a processor.
3
+ # The default PartyFoul processor will work synchronously. Processor adapters can be written
4
+ # to push this logic to a background job if desired.
5
+ #
6
+ # @param [Exception, Hash]
7
+ def self.handle(exception, env)
8
+ self.new(exception, clean_env(env)).run
9
+ end
10
+
11
+ # Uses the Rackless IssueRenderer for a rackless environment
12
+ #
13
+ # @param [Exception, Hash]
14
+ def initialize(exception, env)
15
+ self.rendered_issue = PartyFoul::IssueRenderers::Rackless.new(exception, env)
16
+ end
17
+ end
@@ -1,3 +1,3 @@
1
1
  module PartyFoul
2
- VERSION = '0.5.0'
2
+ VERSION = '0.6.0'
3
3
  end
@@ -8,7 +8,7 @@ describe 'Party Foul Confg' do
8
8
 
9
9
  it 'sets the proper config variables' do
10
10
  PartyFoul.configure do |config|
11
- config.ignored_exceptions = ['StandardError']
11
+ config.blacklisted_exceptions = ['StandardError']
12
12
  config.oauth_token = 'test_token'
13
13
  config.web_url = 'http://example.com'
14
14
  config.endpoint = 'http://api.example.com'
@@ -17,7 +17,7 @@ describe 'Party Foul Confg' do
17
17
  config.branch = 'master'
18
18
  end
19
19
 
20
- PartyFoul.ignored_exceptions.must_equal ['StandardError']
20
+ PartyFoul.blacklisted_exceptions.must_equal ['StandardError']
21
21
  PartyFoul.github.must_be_instance_of Github::Client
22
22
  PartyFoul.github.oauth_token.must_equal 'test_token'
23
23
  PartyFoul.github.endpoint.must_equal 'http://api.example.com'
@@ -16,14 +16,14 @@ describe 'Party Foul Exception Handler' do
16
16
  PartyFoul.github.issues.stubs(:edit)
17
17
  PartyFoul.github.issues.stubs(:comments).returns(mock('Comments'))
18
18
  PartyFoul.github.issues.comments.stubs(:create)
19
- PartyFoul::RailsIssueRenderer.any_instance.stubs(:title).returns('Test Title')
20
- PartyFoul::RailsIssueRenderer.any_instance.stubs(:fingerprint).returns('test_fingerprint')
19
+ PartyFoul::IssueRenderers::Rails.any_instance.stubs(:title).returns('Test Title')
20
+ PartyFoul::IssueRenderers::Rails.any_instance.stubs(:fingerprint).returns('test_fingerprint')
21
21
  end
22
22
 
23
23
  context 'when error is new' do
24
24
  it 'will open a new error on Github' do
25
- PartyFoul::RailsIssueRenderer.any_instance.stubs(:body).returns('Test Body')
26
- PartyFoul::RailsIssueRenderer.any_instance.stubs(:comment).returns('Test Comment')
25
+ PartyFoul::IssueRenderers::Rails.any_instance.stubs(:body).returns('Test Body')
26
+ PartyFoul::IssueRenderers::Rails.any_instance.stubs(:comment).returns('Test Comment')
27
27
  PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'open').returns(Hashie::Mash.new(issues: []))
28
28
  PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'closed').returns(Hashie::Mash.new(issues: []))
29
29
  PartyFoul.github.issues.expects(:create).with('test_owner', 'test_repo', title: 'Test Title', body: 'Test Body', :labels => ['bug']).returns(Hashie::Mash.new('number' => 1))
@@ -35,8 +35,8 @@ describe 'Party Foul Exception Handler' do
35
35
 
36
36
  context 'when error is not new' do
37
37
  before do
38
- PartyFoul::RailsIssueRenderer.any_instance.stubs(:update_body).returns('New Body')
39
- PartyFoul::RailsIssueRenderer.any_instance.stubs(:comment).returns('Test Comment')
38
+ PartyFoul::IssueRenderers::Rails.any_instance.stubs(:update_body).returns('New Body')
39
+ PartyFoul::IssueRenderers::Rails.any_instance.stubs(:comment).returns('Test Comment')
40
40
  end
41
41
 
42
42
  context 'and open' do
@@ -63,7 +63,7 @@ describe 'Party Foul Exception Handler' do
63
63
 
64
64
  context 'when issue is marked as "wontfix"' do
65
65
  it 'does nothing' do
66
- PartyFoul::RailsIssueRenderer.any_instance.stubs(:body).returns('Test Body')
66
+ PartyFoul::IssueRenderers::Rails.any_instance.stubs(:body).returns('Test Body')
67
67
  PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'open').returns(Hashie::Mash.new(issues: []))
68
68
  PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'closed').returns(Hashie::Mash.new(issues: [{title: 'Test Title', body: 'Test Body', state: 'closed', number: 1, 'labels' => ['wontfix']}]))
69
69
  PartyFoul.github.issues.expects(:create).never
@@ -0,0 +1,91 @@
1
+ require 'test_helper'
2
+ require 'active_support/core_ext/object/blank'
3
+ require 'action_dispatch/http/parameter_filter'
4
+
5
+ describe 'Party Foul Issue Renderer Base' do
6
+ before do
7
+ Time.stubs(:now).returns(Time.new(1970, 1, 1, 0, 0, 0, '-05:00'))
8
+ end
9
+
10
+ after do
11
+ clean_up_party
12
+ end
13
+
14
+ describe '#body' do
15
+ describe 'updating issue body' do
16
+ before do
17
+ @rendered_issue = PartyFoul::IssueRenderers::Base.new(nil, nil)
18
+ @rendered_issue.stubs(:exception).returns('Test Exception')
19
+ @rendered_issue.stubs(:fingerprint).returns('abcdefg1234567890')
20
+ @rendered_issue.stubs(:stack_trace)
21
+ end
22
+
23
+ it 'updates count and timestamp' do
24
+ body = <<-BODY
25
+ <table>
26
+ <tr><th>Exception</th><td>Test Exception</td></tr>
27
+ <tr><th>Count</th><td>1</td></tr>
28
+ <tr><th>Last Occurrence</th><td>January 01, 1970 00:00:00 -0500</td></tr>
29
+ </table>
30
+
31
+ ## Stack Trace
32
+ <pre></pre>
33
+ Fingerprint: `abcdefg1234567890`
34
+ BODY
35
+
36
+ Time.stubs(:now).returns(Time.new(1985, 10, 25, 1, 22, 0, '-05:00'))
37
+
38
+ expected_body = <<-BODY
39
+ <table>
40
+ <tr><th>Exception</th><td>Test Exception</td></tr>
41
+ <tr><th>Count</th><td>2</td></tr>
42
+ <tr><th>Last Occurrence</th><td>October 25, 1985 01:22:00 -0500</td></tr>
43
+ </table>
44
+
45
+ ## Stack Trace
46
+ <pre></pre>
47
+ Fingerprint: `abcdefg1234567890`
48
+ BODY
49
+
50
+ @rendered_issue.update_body(body).must_equal expected_body
51
+ end
52
+ end
53
+
54
+ describe 'empty body' do
55
+ before do
56
+ @rendered_issue = PartyFoul::IssueRenderers::Base.new(nil, nil)
57
+ @rendered_issue.stubs(:exception).returns('Test Exception')
58
+ @rendered_issue.stubs(:fingerprint).returns('abcdefg1234567890')
59
+ @rendered_issue.stubs(:stack_trace)
60
+ end
61
+
62
+ it 'resets body' do
63
+ expected_body = <<-BODY
64
+ <table><tr><th>Exception</th><td>Test Exception</td></tr><tr><th>Last Occurrence</th><td>January 01, 1970 00:00:00 -0500</td></tr><tr><th>Count</th><td>1</td></tr></table>
65
+
66
+ ## Stack Trace
67
+ <pre></pre>
68
+ Fingerprint: `abcdefg1234567890`
69
+ BODY
70
+ @rendered_issue.update_body(nil).must_equal expected_body
71
+ end
72
+ end
73
+ end
74
+
75
+ describe '#build_table_from_hash' do
76
+ it 'builds an HTML table from a hash' do
77
+ rendered_issue = PartyFoul::IssueRenderers::Base.new(nil, nil)
78
+ hash = { 'Value 1' => 'abc', 'Value 2' => { 'Value A' => 123, 'Value B' => 456 } }
79
+ expected = '<table><tr><th>Value 1</th><td>abc</td></tr><tr><th>Value 2</th><td><table><tr><th>Value A</th><td>123</td></tr><tr><th>Value B</th><td>456</td></tr></table></td></tr></table>'
80
+ rendered_issue.build_table_from_hash(hash).must_equal expected
81
+ end
82
+ end
83
+
84
+ describe '#fingerprint' do
85
+ it 'SHA1s the title' do
86
+ rendered_issue = PartyFoul::IssueRenderers::Base.new(nil, nil)
87
+ rendered_issue.stubs(:title).returns('abcdefg1234567890')
88
+ rendered_issue.fingerprint.must_equal Digest::SHA1.hexdigest(rendered_issue.title)
89
+ end
90
+ end
91
+ end
@@ -3,7 +3,7 @@ require 'test_helper'
3
3
  describe 'Rack Issue Renderer' do
4
4
  describe '#params' do
5
5
  before do
6
- @rendered_issue = PartyFoul::RackIssueRenderer.new(nil, {'QUERY_STRING' => { 'status' => 'ok' } })
6
+ @rendered_issue = PartyFoul::IssueRenderers::Rack.new(nil, {'QUERY_STRING' => { 'status' => 'ok' } })
7
7
  end
8
8
 
9
9
  it 'returns ok' do
@@ -17,7 +17,7 @@ describe 'Rack Issue Renderer' do
17
17
  end
18
18
 
19
19
  it 'constructs the title with the class and instance method' do
20
- @rendered_issue = PartyFoul::RackIssueRenderer.new(@exception, {})
20
+ @rendered_issue = PartyFoul::IssueRenderers::Rack.new(@exception, {})
21
21
  @rendered_issue.title.must_equal %{(Exception) "message"}
22
22
  end
23
23
  end
@@ -0,0 +1,29 @@
1
+ require 'test_helper'
2
+
3
+ describe 'Rackless Issue Renderer' do
4
+ before do
5
+ @env = { :params => { :val1 => '1', :val2 => '2' }, :class => 'Worker', :method => 'perform' }
6
+ end
7
+
8
+ describe '#params' do
9
+ before do
10
+ @rendered_issue = PartyFoul::IssueRenderers::Rackless.new(nil, @env)
11
+ end
12
+
13
+ it 'returns the parameters' do
14
+ @rendered_issue.params[:val1].must_equal '1'
15
+ @rendered_issue.params[:val2].must_equal '2'
16
+ end
17
+ end
18
+
19
+ describe '#title' do
20
+ before do
21
+ @exception = Exception.new('message')
22
+ @rendered_issue = PartyFoul::IssueRenderers::Rackless.new(@exception, @env)
23
+ end
24
+
25
+ it 'constructs the title with the controller and action' do
26
+ @rendered_issue.title.must_equal %{Worker#perform (Exception) "message"}
27
+ end
28
+ end
29
+ end
@@ -3,7 +3,7 @@ require 'test_helper'
3
3
  describe 'Rails Issue Renderer' do
4
4
  describe '#params' do
5
5
  before do
6
- @rendered_issue = PartyFoul::RailsIssueRenderer.new(nil, {'action_dispatch.parameter_filter' => ['password'], 'action_dispatch.request.path_parameters' => { 'status' => 'ok', 'password' => 'test' }, 'QUERY_STRING' => { 'status' => 'fail' } })
6
+ @rendered_issue = PartyFoul::IssueRenderers::Rails.new(nil, {'action_dispatch.parameter_filter' => ['password'], 'action_dispatch.request.path_parameters' => { 'status' => 'ok', 'password' => 'test' }, 'QUERY_STRING' => { 'status' => 'fail' } })
7
7
  end
8
8
 
9
9
  it 'returns ok' do
@@ -12,6 +12,17 @@ describe 'Rails Issue Renderer' do
12
12
  end
13
13
  end
14
14
 
15
+ describe '#session' do
16
+ before do
17
+ @rendered_issue = PartyFoul::IssueRenderers::Rails.new(nil, {'action_dispatch.parameter_filter' => ['password'], 'rack.session' => { 'status' => 'ok', 'password' => 'test' }, 'QUERY_STRING' => { 'status' => 'fail' } })
18
+ end
19
+
20
+ it 'returns ok' do
21
+ @rendered_issue.session['status'].must_equal 'ok'
22
+ @rendered_issue.session['password'].must_equal '[FILTERED]'
23
+ end
24
+ end
25
+
15
26
  describe '#title' do
16
27
  before do
17
28
  @exception = Exception.new('message')
@@ -21,7 +32,7 @@ describe 'Rails Issue Renderer' do
21
32
  'action_dispatch.request.path_parameters' => { 'controller' => 'landing', 'action' => 'index' },
22
33
  'action_controller.instance' => controller_instance
23
34
  }
24
- @rendered_issue = PartyFoul::RailsIssueRenderer.new(@exception, env)
35
+ @rendered_issue = PartyFoul::IssueRenderers::Rails.new(@exception, env)
25
36
  end
26
37
 
27
38
  it 'constructs the title with the controller and action' do
@@ -33,7 +33,7 @@ describe 'Party Foul Middleware' do
33
33
 
34
34
  context 'filtering based upon exception' do
35
35
  before do
36
- PartyFoul.ignored_exceptions = ['StandardError']
36
+ PartyFoul.blacklisted_exceptions = ['StandardError']
37
37
  self.stubs(:error_to_raise).returns(StandardError)
38
38
  end
39
39
 
@@ -0,0 +1,25 @@
1
+ require 'test_helper'
2
+
3
+ describe 'Party Foul Rackless Exception Handler' do
4
+ before do
5
+ PartyFoul.configure do |config|
6
+ config.oauth_token = 'abcdefg1234567890'
7
+ config.owner = 'test_owner'
8
+ config.repo = 'test_repo'
9
+ end
10
+ end
11
+
12
+ describe '#handle' do
13
+ it 'will call run on itself' do
14
+ PartyFoul::RacklessExceptionHandler.any_instance.expects(:run)
15
+ PartyFoul::RacklessExceptionHandler.handle(nil, {})
16
+ end
17
+ end
18
+
19
+ describe '#initialize' do
20
+ it 'should use PartyFoul::IssueRenderers::Rackless for rendering issues' do
21
+ issue_renderer = PartyFoul::RacklessExceptionHandler.new(nil, {}).rendered_issue
22
+ assert_kind_of(PartyFoul::IssueRenderers::Rackless, issue_renderer)
23
+ end
24
+ end
25
+ end
@@ -1,13 +1,16 @@
1
+ require 'bundler/setup'
2
+
1
3
  if defined?(M)
2
4
  require 'minitest/spec'
3
5
  else
4
6
  require 'minitest/autorun'
5
7
  end
6
- require 'rack/test'
7
- require 'mocha/setup'
8
- unless ENV['CI']
8
+ begin
9
9
  require 'debugger'
10
+ rescue LoadError
10
11
  end
12
+ require 'rack/test'
13
+ require 'mocha/setup'
11
14
  require 'party_foul'
12
15
 
13
16
  class MiniTest::Spec
@@ -21,7 +24,7 @@ module MiniTest::Expectations
21
24
  end
22
25
 
23
26
  def clean_up_party
24
- %w{github oauth_token endpoint owner repo ignored_exceptions processor issue_template comment_template filtered_http_headers web_url branch}.each do |attr|
27
+ %w{github oauth_token endpoint owner repo blacklisted_exceptions processor web_url branch}.each do |attr|
25
28
  PartyFoul.send("#{attr}=", nil)
26
29
  end
27
30
  end
@@ -1,28 +1,25 @@
1
1
  PartyFoul.configure do |config|
2
- # the collection of exceptions to be ignored by PartyFoul
2
+ # The collection of exceptions PartyFoul should not be allowed to handle
3
3
  # The constants here *must* be represented as strings
4
- config.ignored_exceptions = ['ActiveRecord::RecordNotFound', 'ActionController::RoutingError']
5
-
6
- # The names of the HTTP headers to not report
7
- config.filtered_http_headers = ['Cookie']
4
+ config.blacklisted_exceptions = ['ActiveRecord::RecordNotFound', 'ActionController::RoutingError']
8
5
 
9
6
  # The OAuth token for the account that is opening the issues on Github
10
- config.oauth_token = 'test_token'
7
+ config.oauth_token = 'test_token'
11
8
 
12
9
  # The API endpoint for Github. Unless you are hosting a private
13
10
  # instance of Enterprise Github you do not need to include this
14
- config.endpoint = 'http://api.example.com'
11
+ config.endpoint = 'http://api.example.com'
15
12
 
16
13
  # The Web URL for Github. Unless you are hosting a private
17
14
  # instance of Enterprise Github you do not need to include this
18
- config.web_url = 'http://example.com'
15
+ config.web_url = 'http://example.com'
19
16
 
20
17
  # The organization or user that owns the target repository
21
- config.owner = 'test_owner'
18
+ config.owner = 'test_owner'
22
19
 
23
20
  # The repository for this application
24
- config.repo = 'test_repo'
21
+ config.repo = 'test_repo'
25
22
 
26
23
  # The branch for your deployed code
27
- # config.branch = 'master'
24
+ # config.branch = 'master'
28
25
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: party_foul
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-01-28 00:00:00.000000000 Z
13
+ date: 2013-02-04 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: github_api
@@ -28,6 +28,22 @@ dependencies:
28
28
  - - ~>
29
29
  - !ruby/object:Gem::Version
30
30
  version: 0.8.8
31
+ - !ruby/object:Gem::Dependency
32
+ name: io-console
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ~>
37
+ - !ruby/object:Gem::Version
38
+ version: 0.3.0
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: 0.3.0
31
47
  - !ruby/object:Gem::Dependency
32
48
  name: actionpack
33
49
  requirement: !ruby/object:Gem::Requirement
@@ -167,12 +183,16 @@ files:
167
183
  - lib/generators/party_foul/install_generator.rb
168
184
  - lib/generators/party_foul/templates/party_foul.rb
169
185
  - lib/party_foul/exception_handler.rb
170
- - lib/party_foul/issue_renderer.rb
186
+ - lib/party_foul/issue_renderers/base.rb
171
187
  - lib/party_foul/issue_renderers/rack.rb
188
+ - lib/party_foul/issue_renderers/rackless.rb
172
189
  - lib/party_foul/issue_renderers/rails.rb
190
+ - lib/party_foul/issue_renderers.rb
173
191
  - lib/party_foul/middleware.rb
192
+ - lib/party_foul/processors/base.rb
174
193
  - lib/party_foul/processors/sync.rb
175
194
  - lib/party_foul/processors.rb
195
+ - lib/party_foul/rackless_exception_handler.rb
176
196
  - lib/party_foul/version.rb
177
197
  - lib/party_foul.rb
178
198
  - Rakefile
@@ -180,10 +200,12 @@ files:
180
200
  - test/generator_test.rb
181
201
  - test/party_foul/configure_test.rb
182
202
  - test/party_foul/exception_handler_test.rb
183
- - test/party_foul/issue_renderer_test.rb
203
+ - test/party_foul/issue_renderers/base_test.rb
184
204
  - test/party_foul/issue_renderers/rack_test.rb
205
+ - test/party_foul/issue_renderers/rackless_test.rb
185
206
  - test/party_foul/issue_renderers/rails_test.rb
186
207
  - test/party_foul/middleware_test.rb
208
+ - test/party_foul/rackless_exception_handler_test.rb
187
209
  - test/test_helper.rb
188
210
  - test/tmp/config/initializers/party_foul.rb
189
211
  homepage: https://github.com/dockyard/party_foul
@@ -200,7 +222,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
200
222
  version: '0'
201
223
  segments:
202
224
  - 0
203
- hash: -549281873562555121
225
+ hash: -845978866647605957
204
226
  required_rubygems_version: !ruby/object:Gem::Requirement
205
227
  none: false
206
228
  requirements:
@@ -209,7 +231,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
209
231
  version: '0'
210
232
  segments:
211
233
  - 0
212
- hash: -549281873562555121
234
+ hash: -845978866647605957
213
235
  requirements: []
214
236
  rubyforge_project:
215
237
  rubygems_version: 1.8.23
@@ -220,9 +242,11 @@ test_files:
220
242
  - test/generator_test.rb
221
243
  - test/party_foul/configure_test.rb
222
244
  - test/party_foul/exception_handler_test.rb
223
- - test/party_foul/issue_renderer_test.rb
245
+ - test/party_foul/issue_renderers/base_test.rb
224
246
  - test/party_foul/issue_renderers/rack_test.rb
247
+ - test/party_foul/issue_renderers/rackless_test.rb
225
248
  - test/party_foul/issue_renderers/rails_test.rb
226
249
  - test/party_foul/middleware_test.rb
250
+ - test/party_foul/rackless_exception_handler_test.rb
227
251
  - test/test_helper.rb
228
252
  - test/tmp/config/initializers/party_foul.rb
@@ -1,139 +0,0 @@
1
- require 'test_helper'
2
- require 'active_support/core_ext/object/blank'
3
- require 'action_dispatch/http/parameter_filter'
4
-
5
- describe 'Party Foul Issue Renderer' do
6
- before do
7
- Time.stubs(:now).returns(Time.new(1970, 1, 1, 0, 0, 0, '-05:00'))
8
- end
9
-
10
- after do
11
- clean_up_party
12
- end
13
-
14
- describe '#body' do
15
- describe 'updating issue body' do
16
- before do
17
- @rendered_issue = PartyFoul::IssueRenderer.new(nil, nil)
18
- @rendered_issue.stubs(:exception).returns('Test Exception')
19
- @rendered_issue.stubs(:fingerprint).returns('abcdefg1234567890')
20
- @rendered_issue.stubs(:stack_trace)
21
- end
22
-
23
- it 'updates count and timestamp' do
24
- body = <<-BODY
25
- <table>
26
- <tr><th>Exception</th><td>Test Exception</td></tr>
27
- <tr><th>Count</th><td>1</td></tr>
28
- <tr><th>Last Occurance</th><td>January 01, 1970 00:00:00 -0500</td></tr>
29
- </table>
30
-
31
- ## Stack Trace
32
- <pre></pre>
33
- Fingerprint: `abcdefg1234567890`
34
- BODY
35
-
36
- Time.stubs(:now).returns(Time.new(1985, 10, 25, 1, 22, 0, '-05:00'))
37
-
38
- expected_body = <<-BODY
39
- <table>
40
- <tr><th>Exception</th><td>Test Exception</td></tr>
41
- <tr><th>Count</th><td>2</td></tr>
42
- <tr><th>Last Occurance</th><td>October 25, 1985 01:22:00 -0500</td></tr>
43
- </table>
44
-
45
- ## Stack Trace
46
- <pre></pre>
47
- Fingerprint: `abcdefg1234567890`
48
- BODY
49
-
50
- @rendered_issue.update_body(body).must_equal expected_body
51
- end
52
- end
53
-
54
- describe 'empty body' do
55
- before do
56
- @rendered_issue = PartyFoul::IssueRenderer.new(nil, nil)
57
- @rendered_issue.stubs(:exception).returns('Test Exception')
58
- @rendered_issue.stubs(:fingerprint).returns('abcdefg1234567890')
59
- @rendered_issue.stubs(:stack_trace)
60
- end
61
-
62
- it 'resets body' do
63
- expected_body = <<-BODY
64
- <table>
65
- <tr><th>Exception</th><td>Test Exception</td></tr>
66
- <tr><th>Count</th><td>1</td></tr>
67
- <tr><th>Last Occurance</th><td>January 01, 1970 00:00:00 -0500</td></tr>
68
- </table>
69
-
70
- ## Stack Trace
71
- <pre></pre>
72
- Fingerprint: `abcdefg1234567890`
73
- BODY
74
- @rendered_issue.update_body(nil).must_equal expected_body
75
- end
76
- end
77
- end
78
-
79
- describe '#issue_comment' do
80
- before do
81
- env = {
82
- 'REQUEST_URI' => 'http://example.com/',
83
- 'HTTP_USER_AGENT' => 'test_user_agent',
84
- 'REMOTE_ADDR' => '127.0.0.1',
85
- 'HTTP_HOST' => 'localhost:3000',
86
- 'rack.session' => { :id => 1 }
87
- }
88
- @rendered_issue = PartyFoul::IssueRenderer.new(nil, env)
89
- @rendered_issue.stubs(:params).returns({})
90
- end
91
-
92
- it 'renders a new comment' do
93
- expected_comment = <<-COMMENT
94
- <table>
95
- <tr><th>Occurred at</th><td>January 01, 1970 00:00:00 -0500</td></tr>
96
- <tr><th>Params</th><td><table></table></td></tr>
97
- <tr><th>IP Address</th><td>127.0.0.1</td></tr>
98
- <tr><th>Session</th><td><table><tr><th>id</th><td>1</td></tr></table></td></tr>
99
- <tr><th>HTTP Headers</th><td><table><tr><th>Host</th><td>localhost:3000</td></tr><tr><th>User-Agent</th><td>test_user_agent</td></tr></table></td></tr>
100
- </table>
101
- COMMENT
102
-
103
- @rendered_issue.comment.must_equal expected_comment
104
- end
105
- end
106
-
107
- describe '#compile_template' do
108
- it 'it parses the tags and inserts proper data' do
109
- template = '<span>:data1</span><div>:data2</div>'
110
- @rendered_issue = PartyFoul::IssueRenderer.new(nil, nil)
111
- @rendered_issue.stubs(:data1).returns('123')
112
- @rendered_issue.stubs(:data2).returns('abc')
113
- @rendered_issue.compile_template(template).must_equal '<span>123</span><div>abc</div>'
114
- end
115
- end
116
-
117
- describe '#http_headers' do
118
- before do
119
- PartyFoul.filtered_http_headers = ['Cookie']
120
- env = {
121
- 'HTTP_USER_AGENT' => 'test_user_agent',
122
- 'HTTP_COOKIE' => 'test_cookie',
123
- }
124
- @rendered_issue = PartyFoul::IssueRenderer.new(nil, env)
125
- end
126
-
127
- it 'ignored Cookie' do
128
- @rendered_issue.http_headers.must_equal('User-Agent' => 'test_user_agent')
129
- end
130
- end
131
-
132
- describe '#fingerprint' do
133
- it 'SHA1s the title' do
134
- rendered_issue = PartyFoul::IssueRenderer.new(nil, nil)
135
- rendered_issue.stubs(:title).returns('abcdefg1234567890')
136
- rendered_issue.fingerprint.must_equal Digest::SHA1.hexdigest(rendered_issue.title)
137
- end
138
- end
139
- end