party_foul 0.0.0 → 0.1.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 +74 -4
- data/Rakefile +0 -19
- data/lib/generators/party_foul/install_generator.rb +74 -0
- data/lib/party_foul/exception_handler.rb +118 -0
- data/lib/party_foul/middleware.rb +32 -0
- data/lib/party_foul/version.rb +1 -1
- data/lib/party_foul.rb +22 -0
- data/test/party_foul/configure_test.rb +25 -0
- data/test/party_foul/exception_handler_test.rb +102 -0
- data/test/party_foul/middleware_test.rb +106 -0
- data/test/test_helper.rb +22 -11
- metadata +112 -66
- data/lib/tasks/party_foul_tasks.rake +0 -4
- data/test/dummy/README.rdoc +0 -261
- data/test/dummy/Rakefile +0 -7
- data/test/dummy/app/assets/javascripts/application.js +0 -15
- data/test/dummy/app/assets/stylesheets/application.css +0 -13
- data/test/dummy/app/controllers/application_controller.rb +0 -3
- data/test/dummy/app/helpers/application_helper.rb +0 -2
- data/test/dummy/app/views/layouts/application.html.erb +0 -14
- data/test/dummy/config/application.rb +0 -59
- data/test/dummy/config/boot.rb +0 -10
- data/test/dummy/config/database.yml +0 -25
- data/test/dummy/config/environment.rb +0 -5
- data/test/dummy/config/environments/development.rb +0 -37
- data/test/dummy/config/environments/production.rb +0 -67
- data/test/dummy/config/environments/test.rb +0 -37
- data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
- data/test/dummy/config/initializers/inflections.rb +0 -15
- data/test/dummy/config/initializers/mime_types.rb +0 -5
- data/test/dummy/config/initializers/secret_token.rb +0 -7
- data/test/dummy/config/initializers/session_store.rb +0 -8
- data/test/dummy/config/initializers/wrap_parameters.rb +0 -14
- data/test/dummy/config/locales/en.yml +0 -5
- data/test/dummy/config/routes.rb +0 -58
- data/test/dummy/config.ru +0 -4
- data/test/dummy/public/404.html +0 -26
- data/test/dummy/public/422.html +0 -26
- data/test/dummy/public/500.html +0 -25
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +0 -6
- data/test/party_foul_test.rb +0 -7
data/README.md
CHANGED
@@ -6,25 +6,96 @@
|
|
6
6
|
|
7
7
|
Rails exceptions automatically opened as issues on Github
|
8
8
|
|
9
|
+
## About ##
|
10
|
+
|
11
|
+
`PartyFoul` will capture exceptions in your application and will do the
|
12
|
+
following:
|
13
|
+
|
14
|
+
1. Will attempt to find a matching issue in your Github repo
|
15
|
+
2. If no matching issue is found an new issue will be created with a
|
16
|
+
unique title, session information, and stack trace. The issue will be
|
17
|
+
tagged as a `bug`
|
18
|
+
3. If an open issue is found the occurance count and time stamp will be
|
19
|
+
updated
|
20
|
+
4. If a closed issue is found the occurance count and time stamp will be
|
21
|
+
updated. The issue will be reopened and a `regression` tag will be
|
22
|
+
added.
|
23
|
+
5. If the issue is marked as `wontfix` the issue is not updated nor is
|
24
|
+
a new issue created.
|
25
|
+
|
9
26
|
## Installation ##
|
10
27
|
|
28
|
+
Prior to installing the gem you will need to set up an OAuth application
|
29
|
+
on Github to provide access to your repository. Once this OAuth
|
30
|
+
application is setup you will have a `ClientID` and `ClientSecret` that
|
31
|
+
you will need to complete the installation.
|
32
|
+
|
33
|
+
**Note** We highly recommend that you create a new Github account that is
|
34
|
+
a collaborator on your repository. Use this new account's credentials
|
35
|
+
for the installation below. If you use your own account you will
|
36
|
+
not receive emails when issues are created, updated, reopened, etc...
|
37
|
+
because all of the work will be done as your account.
|
38
|
+
|
11
39
|
In your Gemfile add the following:
|
12
40
|
|
13
41
|
```ruby
|
14
42
|
gem 'party_foul'
|
15
43
|
```
|
16
44
|
|
45
|
+
### Rails ###
|
46
|
+
If you are using Rails you can run the install generator.
|
17
47
|
|
18
|
-
|
48
|
+
```
|
49
|
+
rails g party_foul:install
|
50
|
+
```
|
51
|
+
|
52
|
+
This will prompt you for the Github credentials of the account that will
|
53
|
+
be opening the issues. The OAuth token for that account will be stored
|
54
|
+
in `config/initializers/party_foul.rb`. You may want to remove the token
|
55
|
+
string and store in an environment variable. It is best not to store the
|
56
|
+
token in version control.
|
57
|
+
|
58
|
+
Add as the very last middleware in your production `Rack` stack in `config/environments/production.rb`
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
config.middleware.insert_before(-1, 'PartyFoul::Middleware')
|
62
|
+
```
|
63
|
+
### Other ###
|
19
64
|
|
65
|
+
You will need to initialize `PartyFoul`, you can use the following to do
|
66
|
+
so:
|
20
67
|
|
21
|
-
|
68
|
+
```ruby
|
69
|
+
PartyFoul.configure do |config|
|
70
|
+
# the collection of exceptions to be ignored by PartyFoul
|
71
|
+
# The constants here *must* be represented as strings
|
72
|
+
config.ignored_exceptions = ['ActiveRecord::RecordNotFound']
|
73
|
+
|
74
|
+
# The OAuth token for the account that will be opening the issues on Github
|
75
|
+
config.oauth_token = 'abcdefgh1234567890'
|
76
|
+
|
77
|
+
# The API endpoint for Github. Unless you are hosting a private
|
78
|
+
# instance of Enterprise Github you do not need to include this
|
79
|
+
config.endpoint = 'https://api.github.com'
|
80
|
+
|
81
|
+
# The organization or user that owns the target repository
|
82
|
+
config.owner = 'owner_name'
|
83
|
+
|
84
|
+
# The repository for this application
|
85
|
+
config.repo = 'repo_name'
|
86
|
+
end
|
87
|
+
```
|
88
|
+
|
89
|
+
Add as the very last middleware in your production `Rack` stack.
|
90
|
+
|
91
|
+
## Usage ##
|
22
92
|
|
23
93
|
|
24
94
|
## Authors ##
|
25
95
|
|
26
96
|
[Brian Cardarella](http://twitter.com/bcardarella)
|
27
|
-
|
97
|
+
|
98
|
+
[Dan McClain](http://twitter.com/_danmcclain)
|
28
99
|
|
29
100
|
[We are very thankful for the many contributors](https://github.com/dockyard/party_foul/graphs/contributors)
|
30
101
|
|
@@ -45,4 +116,3 @@ on how to properly submit issues and pull requests.
|
|
45
116
|
[@dockyard](http://twitter.com/dockyard)
|
46
117
|
|
47
118
|
[Licensed under the MIT license](http://www.opensource.org/licenses/mit-license.php)
|
48
|
-
|
data/Rakefile
CHANGED
@@ -4,24 +4,6 @@ begin
|
|
4
4
|
rescue LoadError
|
5
5
|
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
6
|
end
|
7
|
-
begin
|
8
|
-
require 'rdoc/task'
|
9
|
-
rescue LoadError
|
10
|
-
require 'rdoc/rdoc'
|
11
|
-
require 'rake/rdoctask'
|
12
|
-
RDoc::Task = Rake::RDocTask
|
13
|
-
end
|
14
|
-
|
15
|
-
RDoc::Task.new(:rdoc) do |rdoc|
|
16
|
-
rdoc.rdoc_dir = 'rdoc'
|
17
|
-
rdoc.title = 'PartyFoul'
|
18
|
-
rdoc.options << '--line-numbers'
|
19
|
-
rdoc.rdoc_files.include('README.rdoc')
|
20
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
-
end
|
22
|
-
|
23
|
-
|
24
|
-
|
25
7
|
|
26
8
|
Bundler::GemHelper.install_tasks
|
27
9
|
|
@@ -34,5 +16,4 @@ Rake::TestTask.new(:test) do |t|
|
|
34
16
|
t.verbose = false
|
35
17
|
end
|
36
18
|
|
37
|
-
|
38
19
|
task :default => :test
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'io/console'
|
3
|
+
require 'net/http'
|
4
|
+
|
5
|
+
module PartyFoul
|
6
|
+
class InstallGenerator < Rails::Generators::Base
|
7
|
+
|
8
|
+
def create_initializer_file
|
9
|
+
puts 'A Github Application is required'
|
10
|
+
|
11
|
+
client_id = ask 'Github App Client ID:'
|
12
|
+
client_secret = ask 'Github App Client Secret:'
|
13
|
+
username = ask 'Github username:'
|
14
|
+
password = STDIN.noecho do
|
15
|
+
ask 'Github password:'
|
16
|
+
end
|
17
|
+
say ''
|
18
|
+
|
19
|
+
owner = ask 'Repository owner:'
|
20
|
+
repo = ask 'Repository name:'
|
21
|
+
auth_uri = URI("https://api.github.com/authorizations")
|
22
|
+
|
23
|
+
response = nil
|
24
|
+
Net::HTTP.start(auth_uri.host, auth_uri.port, :use_ssl => auth_uri.scheme == 'https') do |http|
|
25
|
+
request = Net::HTTP::Post.new auth_uri.request_uri
|
26
|
+
body = { :scopes => ['repo'], :client_id => client_id, :client_secret => client_secret }
|
27
|
+
request.body = body.to_json
|
28
|
+
|
29
|
+
request.basic_auth username, password
|
30
|
+
|
31
|
+
response = http.request request
|
32
|
+
end
|
33
|
+
|
34
|
+
if response.code == '201'
|
35
|
+
oauth_token = JSON.parse(response.body)['token']
|
36
|
+
|
37
|
+
File.open('config/initializers/party_foul.rb', 'w') do |f|
|
38
|
+
f.puts <<-CONTENTS
|
39
|
+
PartyFoul.configure do |config|
|
40
|
+
# the collection of exceptions to be ignored by PartyFoul
|
41
|
+
# The constants here *must* be represented as strings
|
42
|
+
config.ignored_exceptions = ['ActiveRecord::RecordNotFound']
|
43
|
+
|
44
|
+
# The OAuth token for the account that will be opening the issues on Github
|
45
|
+
config.oauth_token = '#{oauth_token}'
|
46
|
+
|
47
|
+
# The API endpoint for Github. Unless you are hosting a private
|
48
|
+
# instance of Enterprise Github you do not need to include this
|
49
|
+
config.endpoint = 'https://api.github.com'
|
50
|
+
|
51
|
+
# The organization or user that owns the target repository
|
52
|
+
config.owner = '#{owner}'
|
53
|
+
|
54
|
+
# The repository for this application
|
55
|
+
config.repo = '#{repo}'
|
56
|
+
end
|
57
|
+
CONTENTS
|
58
|
+
end
|
59
|
+
else
|
60
|
+
say 'There was an error retrieving your Github OAuth token'
|
61
|
+
end
|
62
|
+
|
63
|
+
say 'Done'
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def self.installation_message
|
69
|
+
'Generates the configuration file'
|
70
|
+
end
|
71
|
+
|
72
|
+
desc installation_message
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
class PartyFoul::ExceptionHandler
|
2
|
+
attr_accessor :exception, :env
|
3
|
+
|
4
|
+
def self.handle(exception, env)
|
5
|
+
handler = self.new(exception, env)
|
6
|
+
handler.run
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(exception, env)
|
10
|
+
self.exception = exception
|
11
|
+
self.env = env
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
if issue = find_issue
|
16
|
+
update_issue(issue)
|
17
|
+
else
|
18
|
+
create_issue
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def find_issue
|
23
|
+
unless issue = PartyFoul.github.search.issues(owner: PartyFoul.owner, repo: PartyFoul.repo, state: 'open', keyword: fingerprint).issues.first
|
24
|
+
issue = PartyFoul.github.search.issues(owner: PartyFoul.owner, repo: PartyFoul.repo, state: 'closed', keyword: fingerprint).issues.first
|
25
|
+
end
|
26
|
+
|
27
|
+
issue
|
28
|
+
end
|
29
|
+
|
30
|
+
def stack_trace
|
31
|
+
exception.backtrace.map do |line|
|
32
|
+
if matches = extract_file_name_and_line_number(line)
|
33
|
+
"<a href='../tree/master/#{matches[2]}#L#{matches[3]}'>#{line}</a>"
|
34
|
+
else
|
35
|
+
line
|
36
|
+
end
|
37
|
+
end.join("\n")
|
38
|
+
end
|
39
|
+
|
40
|
+
def create_issue
|
41
|
+
PartyFoul.github.issues.create(PartyFoul.owner, PartyFoul.repo, title: issue_title, body: issue_body, labels: ['bug'])
|
42
|
+
end
|
43
|
+
|
44
|
+
def update_issue(issue)
|
45
|
+
unless issue.key?('labels') && issue['labels'].include?('wontfix')
|
46
|
+
params = {body: update_body(issue['body']), state: 'open'}
|
47
|
+
|
48
|
+
if issue['state'] == 'closed'
|
49
|
+
params[:labels] = ['bug', 'regression']
|
50
|
+
end
|
51
|
+
|
52
|
+
PartyFoul.github.issues.edit(PartyFoul.owner, PartyFoul.repo, issue['number'], params)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def issue_title
|
57
|
+
line = exception.backtrace.select {|p| p =~ /#{app_root}/ }.first
|
58
|
+
name_and_number = extract_file_name_and_line_number(line)[1]
|
59
|
+
"#{exception} - #{name_and_number}"
|
60
|
+
end
|
61
|
+
|
62
|
+
def fingerprint
|
63
|
+
Digest::SHA1.hexdigest(issue_title)
|
64
|
+
end
|
65
|
+
|
66
|
+
def update_body(body)
|
67
|
+
begin
|
68
|
+
current_count = body.match(/<th>Count<\/th><td>(\d+)<\/td>/)[1].to_i
|
69
|
+
body.sub!("<th>Count</th><td>#{current_count}</td>", "<th>Count</th><td>#{current_count + 1}</td>")
|
70
|
+
body.sub!(/<th>Last Occurance<\/th><td>.+<\/td>/, "<th>Last Occurance</th><td>#{Time.now}</td>")
|
71
|
+
body
|
72
|
+
rescue
|
73
|
+
issue_body
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def params
|
78
|
+
if env["action_dispatch.parameter_filter"]
|
79
|
+
parameter_filter = ActionDispatch::Http::ParameterFilter.new(env["action_dispatch.parameter_filter"])
|
80
|
+
parameter_filter.filter(env['action_dispatch.request.path_parameters'])
|
81
|
+
else
|
82
|
+
env['QUERY_STRING']
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def issue_body
|
87
|
+
<<-BODY
|
88
|
+
<table>
|
89
|
+
<tr><th>Count</th><td>1</td></tr>
|
90
|
+
<tr><th>Last Occurance</th><td>#{Time.now}</td></tr>
|
91
|
+
<tr><th>Params</th><td>#{params}</td></tr>
|
92
|
+
<tr><th>Exception</th><td>#{exception}</td></tr>
|
93
|
+
</table>
|
94
|
+
|
95
|
+
## Stack Trace
|
96
|
+
<pre>#{stack_trace}</pre>
|
97
|
+
Fingerprint: `#{fingerprint}`
|
98
|
+
BODY
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def app_root
|
104
|
+
if defined?(Rails)
|
105
|
+
Rails.root
|
106
|
+
else
|
107
|
+
Dir.pwd
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def file_and_line_regex
|
112
|
+
/#{app_root}\/((.+?):(\d+))/
|
113
|
+
end
|
114
|
+
|
115
|
+
def extract_file_name_and_line_number(backtrace_line)
|
116
|
+
backtrace_line.match(file_and_line_regex)
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module PartyFoul
|
2
|
+
class Middleware
|
3
|
+
def initialize(app)
|
4
|
+
@app = app
|
5
|
+
end
|
6
|
+
|
7
|
+
def call(env)
|
8
|
+
@app.call(env)
|
9
|
+
rescue Exception => captured_exception
|
10
|
+
if allow_handling?(captured_exception)
|
11
|
+
PartyFoul::ExceptionHandler.handle(captured_exception, env)
|
12
|
+
end
|
13
|
+
raise captured_exception
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def allow_handling?(captured_exception)
|
19
|
+
!PartyFoul.ignored_exceptions.find do |ignored_exception|
|
20
|
+
names = ignored_exception.split('::')
|
21
|
+
names.shift if names.empty? || names.first.empty?
|
22
|
+
|
23
|
+
constant = Object
|
24
|
+
names.each do |name|
|
25
|
+
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
26
|
+
end
|
27
|
+
|
28
|
+
constant === captured_exception
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/party_foul/version.rb
CHANGED
data/lib/party_foul.rb
CHANGED
@@ -1,2 +1,24 @@
|
|
1
|
+
require 'github_api'
|
2
|
+
|
1
3
|
module PartyFoul
|
4
|
+
class << self
|
5
|
+
attr_accessor :github, :oauth_token, :endpoint, :owner, :repo, :ignored_exceptions
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.ignored_exceptions
|
9
|
+
@ignored_exceptions || []
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.configure(&block)
|
13
|
+
yield self
|
14
|
+
_self = self
|
15
|
+
self.github ||= Github.new do |config|
|
16
|
+
%w{endpoint oauth_token}.each do |option|
|
17
|
+
config.send("#{option}=", _self.send(option)) if !_self.send(option).nil?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
2
21
|
end
|
22
|
+
|
23
|
+
require 'party_foul/exception_handler'
|
24
|
+
require 'party_foul/middleware'
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe 'Party Foul Confg' do
|
4
|
+
|
5
|
+
after do
|
6
|
+
clean_up_party
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'sets the proper config variables' do
|
10
|
+
PartyFoul.configure do |config|
|
11
|
+
config.ignored_exceptions = ['StandardError']
|
12
|
+
config.oauth_token = 'test_token'
|
13
|
+
config.endpoint = 'test_endpoint'
|
14
|
+
config.owner = 'test_owner'
|
15
|
+
config.repo = 'test_repo'
|
16
|
+
end
|
17
|
+
|
18
|
+
PartyFoul.ignored_exceptions.must_equal ['StandardError']
|
19
|
+
PartyFoul.github.must_be_instance_of Github::Client
|
20
|
+
PartyFoul.github.oauth_token.must_equal 'test_token'
|
21
|
+
PartyFoul.github.endpoint.must_equal 'test_endpoint'
|
22
|
+
PartyFoul.owner.must_equal 'test_owner'
|
23
|
+
PartyFoul.repo.must_equal 'test_repo'
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'active_support/core_ext/object/blank'
|
3
|
+
require 'action_dispatch/http/parameter_filter'
|
4
|
+
|
5
|
+
describe 'Party Foul Exception Handler' do
|
6
|
+
before do
|
7
|
+
Time.stubs(:now).returns(Time.at(0))
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#body' do
|
11
|
+
describe 'updating issue body' do
|
12
|
+
before do
|
13
|
+
@handler = PartyFoul::ExceptionHandler.new(nil, nil)
|
14
|
+
@handler.stubs(:fingerprint).returns('abcdefg1234567890')
|
15
|
+
@handler.stubs(:stack_trace)
|
16
|
+
@handler.stubs(:params)
|
17
|
+
@handler.stubs(:exception)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'updates count and timestamp' do
|
21
|
+
body = <<-BODY
|
22
|
+
<table>
|
23
|
+
<tr><th>Count</th><td>1</td></tr>
|
24
|
+
<tr><th>Last Occurance</th><td>#{Time.now}</td></tr>
|
25
|
+
<tr><th>Params</th><td></td></tr>
|
26
|
+
<tr><th>Exception</th><td></td></tr>
|
27
|
+
</table>
|
28
|
+
|
29
|
+
## Stack Trace
|
30
|
+
<pre></pre>
|
31
|
+
Fingerprint: `abcdefg1234567890`
|
32
|
+
BODY
|
33
|
+
|
34
|
+
Time.stubs(:now).returns(Time.new(1985, 10, 25, 1, 22, 0, '-05:00'))
|
35
|
+
|
36
|
+
expected_body = <<-BODY
|
37
|
+
<table>
|
38
|
+
<tr><th>Count</th><td>2</td></tr>
|
39
|
+
<tr><th>Last Occurance</th><td>#{Time.now}</td></tr>
|
40
|
+
<tr><th>Params</th><td></td></tr>
|
41
|
+
<tr><th>Exception</th><td></td></tr>
|
42
|
+
</table>
|
43
|
+
|
44
|
+
## Stack Trace
|
45
|
+
<pre></pre>
|
46
|
+
Fingerprint: `abcdefg1234567890`
|
47
|
+
BODY
|
48
|
+
|
49
|
+
@handler.update_body(body).must_equal expected_body
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe 'empty body' do
|
54
|
+
before do
|
55
|
+
@handler = PartyFoul::ExceptionHandler.new(nil, nil)
|
56
|
+
@handler.stubs(:fingerprint).returns('abcdefg1234567890')
|
57
|
+
@handler.stubs(:stack_trace)
|
58
|
+
@handler.stubs(:params)
|
59
|
+
@handler.stubs(:exception)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'resets body' do
|
63
|
+
expected_body = <<-BODY
|
64
|
+
<table>
|
65
|
+
<tr><th>Count</th><td>1</td></tr>
|
66
|
+
<tr><th>Last Occurance</th><td>#{Time.now}</td></tr>
|
67
|
+
<tr><th>Params</th><td></td></tr>
|
68
|
+
<tr><th>Exception</th><td></td></tr>
|
69
|
+
</table>
|
70
|
+
|
71
|
+
## Stack Trace
|
72
|
+
<pre></pre>
|
73
|
+
Fingerprint: `abcdefg1234567890`
|
74
|
+
BODY
|
75
|
+
@handler.update_body(nil).must_equal expected_body
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe '#params' do
|
81
|
+
context 'with Rails' do
|
82
|
+
before do
|
83
|
+
@handler = PartyFoul::ExceptionHandler.new(nil, {'action_dispatch.parameter_filter' => ['password'], 'action_dispatch.request.path_parameters' => { 'status' => 'ok', 'password' => 'test' }, 'QUERY_STRING' => { 'status' => 'fail' } })
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'returns ok' do
|
87
|
+
@handler.params['status'].must_equal 'ok'
|
88
|
+
@handler.params['password'].must_equal '[FILTERED]'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'without Rails' do
|
93
|
+
before do
|
94
|
+
@handler = PartyFoul::ExceptionHandler.new(nil, {'QUERY_STRING' => { 'status' => 'ok' } })
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'returns ok' do
|
98
|
+
@handler.params['status'].must_equal 'ok'
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe 'Party Foul Middleware' do
|
4
|
+
include Rack::Test::Methods
|
5
|
+
|
6
|
+
after do
|
7
|
+
clean_up_party
|
8
|
+
end
|
9
|
+
|
10
|
+
def error_to_raise
|
11
|
+
Exception
|
12
|
+
end
|
13
|
+
|
14
|
+
def app
|
15
|
+
_self = self
|
16
|
+
Rack::Builder.new {
|
17
|
+
map '/' do
|
18
|
+
use PartyFoul::Middleware
|
19
|
+
run lambda { |env| raise _self.error_to_raise }
|
20
|
+
end
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
before do
|
25
|
+
PartyFoul.configure do |config|
|
26
|
+
config.oauth_token = 'abcdefg1234567890'
|
27
|
+
config.owner = 'test_owner'
|
28
|
+
config.repo = 'test_repo'
|
29
|
+
end
|
30
|
+
PartyFoul.github.stubs(:issues).returns(mock('Issues'))
|
31
|
+
PartyFoul.github.stubs(:search).returns(mock('Search'))
|
32
|
+
PartyFoul.github.issues.stubs(:create)
|
33
|
+
PartyFoul.github.issues.stubs(:edit)
|
34
|
+
PartyFoul::ExceptionHandler.any_instance.stubs(:issue_title).returns('Test Title')
|
35
|
+
PartyFoul::ExceptionHandler.any_instance.stubs(:fingerprint).returns('test_fingerprint')
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'when error is new' do
|
39
|
+
it 'will open a new error on Github' do
|
40
|
+
PartyFoul::ExceptionHandler.any_instance.stubs(:issue_body).returns('Test Body')
|
41
|
+
PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'open').returns(Hashie::Mash.new(issues: []))
|
42
|
+
PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'closed').returns(Hashie::Mash.new(issues: []))
|
43
|
+
PartyFoul.github.issues.expects(:create).with('test_owner', 'test_repo', title: 'Test Title', body: 'Test Body', :labels => ['bug'])
|
44
|
+
lambda {
|
45
|
+
get '/'
|
46
|
+
}.must_raise(Exception)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'when error is not new' do
|
51
|
+
before do
|
52
|
+
PartyFoul::ExceptionHandler.any_instance.stubs(:update_body).returns('New Body')
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'and open' do
|
56
|
+
it 'will update the issue' do
|
57
|
+
PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'open').returns(Hashie::Mash.new(issues: [{title: 'Test Title', body: 'Test Body', state: 'open', number: 1}]))
|
58
|
+
PartyFoul.github.issues.expects(:edit).with('test_owner', 'test_repo', 1, body: 'New Body', state: 'open')
|
59
|
+
lambda {
|
60
|
+
get '/'
|
61
|
+
}.must_raise(Exception)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'and closed' do
|
66
|
+
it 'will update the count on the body and re-open the issue' do
|
67
|
+
PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'open').returns(Hashie::Mash.new(issues: []))
|
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}]))
|
69
|
+
PartyFoul.github.issues.expects(:edit).with('test_owner', 'test_repo', 1, body: 'New Body', state: 'open', labels: ['bug', 'regression'])
|
70
|
+
lambda {
|
71
|
+
get '/'
|
72
|
+
}.must_raise(Exception)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'when issue is marked as "wontfix"' do
|
78
|
+
it 'does nothing' do
|
79
|
+
PartyFoul::ExceptionHandler.any_instance.stubs(:issue_body).returns('Test Body')
|
80
|
+
PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'open').returns(Hashie::Mash.new(issues: []))
|
81
|
+
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']}]))
|
82
|
+
PartyFoul.github.issues.expects(:create).never
|
83
|
+
PartyFoul.github.issues.expects(:edit).never
|
84
|
+
lambda {
|
85
|
+
get '/'
|
86
|
+
}.must_raise(Exception)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'filtering based upon exception' do
|
91
|
+
before do
|
92
|
+
PartyFoul.ignored_exceptions = ['StandardError']
|
93
|
+
self.stubs(:error_to_raise).returns(StandardError)
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'does not handle exception' do
|
97
|
+
PartyFoul::ExceptionHandler.any_instance.stubs(:issue_body).returns('Test Body')
|
98
|
+
PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'open').returns(Hashie::Mash.new(issues: []))
|
99
|
+
PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'closed').returns(Hashie::Mash.new(issues: []))
|
100
|
+
PartyFoul.github.issues.expects(:create).never
|
101
|
+
lambda {
|
102
|
+
get '/'
|
103
|
+
}.must_raise(StandardError)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -1,15 +1,26 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
require
|
5
|
-
|
1
|
+
if defined?(M)
|
2
|
+
require 'minitest/spec'
|
3
|
+
else
|
4
|
+
require 'minitest/autorun'
|
5
|
+
end
|
6
|
+
require 'rack/test'
|
7
|
+
require 'mocha/setup'
|
8
|
+
require 'bourne'
|
9
|
+
require 'debugger'
|
10
|
+
require 'party_foul'
|
6
11
|
|
7
|
-
|
12
|
+
class MiniTest::Spec
|
13
|
+
class << self
|
14
|
+
alias :context :describe
|
15
|
+
end
|
16
|
+
end
|
8
17
|
|
9
|
-
|
10
|
-
|
18
|
+
module MiniTest::Expectations
|
19
|
+
infect_an_assertion :assert_received, :must_have_received
|
20
|
+
end
|
11
21
|
|
12
|
-
|
13
|
-
|
14
|
-
|
22
|
+
def clean_up_party
|
23
|
+
%w{github oauth_token endpoint owner repo ignored_exceptions}.each do |attr|
|
24
|
+
PartyFoul.send("#{attr}=", nil)
|
25
|
+
end
|
15
26
|
end
|