party_foul 0.0.0 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|