Commit 4d5a1bf84d42431b434ace49aca1272f6418375f
1 parent
00791678
Exists in
master
and in
1 other branch
refactored model to embed Errs into Problems so that they can be merged
Showing
53 changed files
with
989 additions
and
858 deletions
Show diff stats
app/controllers/apps_controller.rb
| 1 | class AppsController < InheritedResources::Base | 1 | class AppsController < InheritedResources::Base |
| 2 | - | ||
| 3 | before_filter :require_admin!, :except => [:index, :show] | 2 | before_filter :require_admin!, :except => [:index, :show] |
| 4 | before_filter :parse_email_at_notices_or_set_default, :only => [:create, :update] | 3 | before_filter :parse_email_at_notices_or_set_default, :only => [:create, :update] |
| 5 | respond_to :html | 4 | respond_to :html |
| 6 | - | 5 | + |
| 6 | + | ||
| 7 | def show | 7 | def show |
| 8 | respond_to do |format| | 8 | respond_to do |format| |
| 9 | format.html do | 9 | format.html do |
| 10 | @all_errs = !!params[:all_errs] | 10 | @all_errs = !!params[:all_errs] |
| 11 | 11 | ||
| 12 | - @errs = resource.errs | ||
| 13 | - @errs = @errs.unresolved unless @all_errs | ||
| 14 | - @errs = @errs.in_env(params[:environment]).ordered.paginate(:page => params[:page], :per_page => current_user.per_page) | 12 | + @problems = resource.problems |
| 13 | + @problems = @problems.unresolved unless @all_errs | ||
| 14 | + @problems = @problems.in_env(params[:environment]).ordered.paginate(:page => params[:page], :per_page => current_user.per_page) | ||
| 15 | 15 | ||
| 16 | - @selected_errs = params[:errs] || [] | 16 | + @selected_problems = params[:problems] || [] |
| 17 | @deploys = @app.deploys.order_by(:created_at.desc).limit(5) | 17 | @deploys = @app.deploys.order_by(:created_at.desc).limit(5) |
| 18 | end | 18 | end |
| 19 | format.atom do | 19 | format.atom do |
| 20 | - @errs = resource.errs.unresolved.ordered | 20 | + @problems = resource.problems.unresolved.ordered |
| 21 | end | 21 | end |
| 22 | end | 22 | end |
| 23 | end | 23 | end |
| 24 | - | 24 | + |
| 25 | def create | 25 | def create |
| 26 | @app = App.new(params[:app]) | 26 | @app = App.new(params[:app]) |
| 27 | initialize_subclassed_issue_tracker | 27 | initialize_subclassed_issue_tracker |
| 28 | create! | 28 | create! |
| 29 | end | 29 | end |
| 30 | - | 30 | + |
| 31 | def update | 31 | def update |
| 32 | @app = resource | 32 | @app = resource |
| 33 | initialize_subclassed_issue_tracker | 33 | initialize_subclassed_issue_tracker |
| 34 | update! | 34 | update! |
| 35 | end | 35 | end |
| 36 | - | 36 | + |
| 37 | def new | 37 | def new |
| 38 | plug_params(build_resource) | 38 | plug_params(build_resource) |
| 39 | new! | 39 | new! |
| 40 | end | 40 | end |
| 41 | - | 41 | + |
| 42 | def edit | 42 | def edit |
| 43 | plug_params(resource) | 43 | plug_params(resource) |
| 44 | edit! | 44 | edit! |
| 45 | end | 45 | end |
| 46 | - | 46 | + |
| 47 | protected | 47 | protected |
| 48 | def collection | 48 | def collection |
| 49 | # Sort apps by number of unresolved errs, descending. | 49 | # Sort apps by number of unresolved errs, descending. |
| 50 | # Caches the unresolved err counts while performing the sort. | 50 | # Caches the unresolved err counts while performing the sort. |
| 51 | @unresolved_counts = {} | 51 | @unresolved_counts = {} |
| 52 | @apps ||= end_of_association_chain.all.sort{|a,b| | 52 | @apps ||= end_of_association_chain.all.sort{|a,b| |
| 53 | - [a,b].each{|app| @unresolved_counts[app.id] ||= app.errs.unresolved.count } | 53 | + [a,b].each{|app| @unresolved_counts[app.id] ||= app.problems.unresolved.count } |
| 54 | @unresolved_counts[b.id] <=> @unresolved_counts[a.id] | 54 | @unresolved_counts[b.id] <=> @unresolved_counts[a.id] |
| 55 | } | 55 | } |
| 56 | end | 56 | end |
app/controllers/errs_controller.rb
| @@ -2,17 +2,18 @@ class ErrsController < ApplicationController | @@ -2,17 +2,18 @@ class ErrsController < ApplicationController | ||
| 2 | include ActionView::Helpers::TextHelper | 2 | include ActionView::Helpers::TextHelper |
| 3 | 3 | ||
| 4 | before_filter :find_app, :except => [:index, :all, :destroy_several, :resolve_several, :unresolve_several] | 4 | before_filter :find_app, :except => [:index, :all, :destroy_several, :resolve_several, :unresolve_several] |
| 5 | - before_filter :find_err, :except => [:index, :all, :destroy_several, :resolve_several, :unresolve_several] | ||
| 6 | - before_filter :find_selected_errs, :only => [:destroy_several, :resolve_several, :unresolve_several] | 5 | + before_filter :find_problem, :except => [:index, :all, :destroy_several, :resolve_several, :unresolve_several] |
| 6 | + before_filter :find_selected_problems, :only => [:destroy_several, :resolve_several, :unresolve_several] | ||
| 7 | + | ||
| 7 | 8 | ||
| 8 | 9 | ||
| 9 | def index | 10 | def index |
| 10 | app_scope = current_user.admin? ? App.all : current_user.apps | 11 | app_scope = current_user.admin? ? App.all : current_user.apps |
| 11 | - @errs = Err.for_apps(app_scope).in_env(params[:environment]).unresolved.ordered | ||
| 12 | - @selected_errs = params[:errs] || [] | 12 | + @problems = Problem.for_apps(app_scope).in_env(params[:environment]).unresolved.ordered |
| 13 | + @selected_problems = params[:problems] || [] | ||
| 13 | respond_to do |format| | 14 | respond_to do |format| |
| 14 | format.html do | 15 | format.html do |
| 15 | - @errs = @errs.paginate(:page => params[:page], :per_page => current_user.per_page) | 16 | + @problems = @problems.paginate(:page => params[:page], :per_page => current_user.per_page) |
| 16 | end | 17 | end |
| 17 | format.atom | 18 | format.atom |
| 18 | end | 19 | end |
| @@ -21,15 +22,15 @@ class ErrsController < ApplicationController | @@ -21,15 +22,15 @@ class ErrsController < ApplicationController | ||
| 21 | 22 | ||
| 22 | def all | 23 | def all |
| 23 | app_scope = current_user.admin? ? App.all : current_user.apps | 24 | app_scope = current_user.admin? ? App.all : current_user.apps |
| 24 | - @selected_errs = params[:errs] || [] | ||
| 25 | - @errs = Err.for_apps(app_scope).ordered.paginate(:page => params[:page], :per_page => current_user.per_page) | 25 | + @problems = Problem.for_apps(app_scope).ordered.paginate(:page => params[:page], :per_page => current_user.per_page) |
| 26 | + @selected_problems = params[:problems] || [] | ||
| 26 | end | 27 | end |
| 27 | 28 | ||
| 28 | 29 | ||
| 29 | def show | 30 | def show |
| 30 | - page = (params[:notice] || @err.notices_count) | 31 | + page = (params[:notice] || @problem.notices_count) |
| 31 | page = 1 if page.to_i.zero? | 32 | page = 1 if page.to_i.zero? |
| 32 | - @notices = @err.notices.ordered.paginate(:page => page, :per_page => 1) | 33 | + @notices = @problem.notices.paginate(:page => page, :per_page => 1) |
| 33 | @notice = @notices.first | 34 | @notice = @notices.first |
| 34 | @comment = Comment.new | 35 | @comment = Comment.new |
| 35 | end | 36 | end |
| @@ -39,32 +40,30 @@ class ErrsController < ApplicationController | @@ -39,32 +40,30 @@ class ErrsController < ApplicationController | ||
| 39 | set_tracker_params | 40 | set_tracker_params |
| 40 | 41 | ||
| 41 | if @app.issue_tracker | 42 | if @app.issue_tracker |
| 42 | - @app.issue_tracker.create_issue @err | 43 | + @app.issue_tracker.create_issue @problem |
| 43 | else | 44 | else |
| 44 | flash[:error] = "This app has no issue tracker setup." | 45 | flash[:error] = "This app has no issue tracker setup." |
| 45 | end | 46 | end |
| 46 | - redirect_to app_err_path(@app, @err) | 47 | + redirect_to app_err_path(@app, @problem) |
| 47 | rescue ActiveResource::ConnectionError => e | 48 | rescue ActiveResource::ConnectionError => e |
| 48 | Rails.logger.error e.to_s | 49 | Rails.logger.error e.to_s |
| 49 | flash[:error] = "There was an error during issue creation. Check your tracker settings or try again later." | 50 | flash[:error] = "There was an error during issue creation. Check your tracker settings or try again later." |
| 50 | - redirect_to app_err_path(@app, @err) | 51 | + redirect_to app_err_path(@app, @problem) |
| 51 | end | 52 | end |
| 52 | 53 | ||
| 53 | 54 | ||
| 54 | def unlink_issue | 55 | def unlink_issue |
| 55 | - @err.update_attribute :issue_link, nil | ||
| 56 | - redirect_to app_err_path(@app, @err) | 56 | + @problem.update_attribute :issue_link, nil |
| 57 | + redirect_to app_err_path(@app, @problem) | ||
| 57 | end | 58 | end |
| 58 | 59 | ||
| 59 | 60 | ||
| 60 | def resolve | 61 | def resolve |
| 61 | # Deal with bug in mongoid where find is returning an Enumberable obj | 62 | # Deal with bug in mongoid where find is returning an Enumberable obj |
| 62 | - @err = @err.first if @err.respond_to?(:first) | ||
| 63 | - | ||
| 64 | - @err.resolve! | 63 | + @problem = @problem.first if @problem.respond_to?(:first) |
| 65 | 64 | ||
| 65 | + @problem.resolve! | ||
| 66 | flash[:success] = 'Great news everyone! The err has been resolved.' | 66 | flash[:success] = 'Great news everyone! The err has been resolved.' |
| 67 | - | ||
| 68 | redirect_to :back | 67 | redirect_to :back |
| 69 | rescue ActionController::RedirectBackError | 68 | rescue ActionController::RedirectBackError |
| 70 | redirect_to app_path(@app) | 69 | redirect_to app_path(@app) |
| @@ -74,13 +73,13 @@ class ErrsController < ApplicationController | @@ -74,13 +73,13 @@ class ErrsController < ApplicationController | ||
| 74 | def create_comment | 73 | def create_comment |
| 75 | @comment = Comment.new(params[:comment].merge(:user_id => current_user.id)) | 74 | @comment = Comment.new(params[:comment].merge(:user_id => current_user.id)) |
| 76 | if @comment.valid? | 75 | if @comment.valid? |
| 77 | - @err.comments << @comment | ||
| 78 | - @err.save | 76 | + @problem.comments << @comment |
| 77 | + @problem.save | ||
| 79 | flash[:success] = "Comment saved!" | 78 | flash[:success] = "Comment saved!" |
| 80 | else | 79 | else |
| 81 | flash[:error] = "I'm sorry, your comment was blank! Try again?" | 80 | flash[:error] = "I'm sorry, your comment was blank! Try again?" |
| 82 | end | 81 | end |
| 83 | - redirect_to app_err_path(@app, @err) | 82 | + redirect_to app_err_path(@app, @problem) |
| 84 | end | 83 | end |
| 85 | 84 | ||
| 86 | 85 | ||
| @@ -91,27 +90,27 @@ class ErrsController < ApplicationController | @@ -91,27 +90,27 @@ class ErrsController < ApplicationController | ||
| 91 | else | 90 | else |
| 92 | flash[:error] = "Sorry, I couldn't delete your comment for some reason. I hope you don't have any sensitive information in there!" | 91 | flash[:error] = "Sorry, I couldn't delete your comment for some reason. I hope you don't have any sensitive information in there!" |
| 93 | end | 92 | end |
| 94 | - redirect_to app_err_path(@app, @err) | 93 | + redirect_to app_err_path(@app, @problem) |
| 95 | end | 94 | end |
| 96 | 95 | ||
| 97 | 96 | ||
| 98 | def resolve_several | 97 | def resolve_several |
| 99 | - @selected_errs.each(&:resolve!) | ||
| 100 | - flash[:success] = "Great news everyone! #{pluralize(@selected_errs.count, 'err has', 'errs have')} been resolved." | 98 | + @selected_problems.each(&:resolve!) |
| 99 | + flash[:success] = "Great news everyone! #{pluralize(@selected_problems.count, 'err has', 'errs have')} been resolved." | ||
| 101 | redirect_to :back | 100 | redirect_to :back |
| 102 | end | 101 | end |
| 103 | 102 | ||
| 104 | 103 | ||
| 105 | def unresolve_several | 104 | def unresolve_several |
| 106 | - @selected_errs.each(&:unresolve!) | ||
| 107 | - flash[:success] = "#{pluralize(@selected_errs.count, 'err has', 'errs have')} been unresolved." | 105 | + @selected_problems.each(&:unresolve!) |
| 106 | + flash[:success] = "#{pluralize(@selected_problems.count, 'err has', 'errs have')} been unresolved." | ||
| 108 | redirect_to :back | 107 | redirect_to :back |
| 109 | end | 108 | end |
| 110 | 109 | ||
| 111 | 110 | ||
| 112 | def destroy_several | 111 | def destroy_several |
| 113 | - @selected_errs.each(&:destroy) | ||
| 114 | - flash[:notice] = "#{pluralize(@selected_errs.count, 'err has', 'errs have')} been deleted." | 112 | + @selected_problems.each(&:destroy) |
| 113 | + flash[:notice] = "#{pluralize(@selected_problems.count, 'err has', 'errs have')} been deleted." | ||
| 115 | redirect_to :back | 114 | redirect_to :back |
| 116 | end | 115 | end |
| 117 | 116 | ||
| @@ -128,8 +127,11 @@ protected | @@ -128,8 +127,11 @@ protected | ||
| 128 | end | 127 | end |
| 129 | 128 | ||
| 130 | 129 | ||
| 131 | - def find_err | ||
| 132 | - @err = @app.errs.find(params[:id]) | 130 | + def find_problem |
| 131 | + @problem = @app.problems.find(params[:id]) | ||
| 132 | + | ||
| 133 | + # Deal with bug in mogoid where find is returning an Enumberable obj | ||
| 134 | + @problem = @problem.first if @problem.respond_to?(:first) | ||
| 133 | end | 135 | end |
| 134 | 136 | ||
| 135 | 137 | ||
| @@ -140,13 +142,13 @@ protected | @@ -140,13 +142,13 @@ protected | ||
| 140 | end | 142 | end |
| 141 | 143 | ||
| 142 | 144 | ||
| 143 | - def find_selected_errs | ||
| 144 | - err_ids = (params[:errs] || []).compact | 145 | + def find_selected_problems |
| 146 | + err_ids = (params[:problems] || []).compact | ||
| 145 | if err_ids.empty? | 147 | if err_ids.empty? |
| 146 | flash[:notice] = "You have not selected any errors" | 148 | flash[:notice] = "You have not selected any errors" |
| 147 | redirect_to :back | 149 | redirect_to :back |
| 148 | else | 150 | else |
| 149 | - @selected_errs = Array(Err.find(err_ids)) | 151 | + @selected_problems = Array(Problem.find(err_ids)) |
| 150 | end | 152 | end |
| 151 | end | 153 | end |
| 152 | 154 |
app/helpers/errs_helper.rb
| 1 | module ErrsHelper | 1 | module ErrsHelper |
| 2 | - | ||
| 3 | - def last_notice_at err | ||
| 4 | - err.last_notice_at || err.created_at | 2 | + |
| 3 | + def last_notice_at(problem) | ||
| 4 | + problem.last_notice_at || problem.created_at | ||
| 5 | end | 5 | end |
| 6 | - | 6 | + |
| 7 | def err_confirm | 7 | def err_confirm |
| 8 | Errbit::Config.confirm_resolve_err === false ? nil : 'Seriously?' | 8 | Errbit::Config.confirm_resolve_err === false ? nil : 'Seriously?' |
| 9 | end | 9 | end |
| 10 | - | ||
| 11 | - def link_to_github app, line, text=nil | 10 | + |
| 11 | + def link_to_github(app, line, text=nil) | ||
| 12 | file_name = line['file'].split('/').last | 12 | file_name = line['file'].split('/').last |
| 13 | file_path = line['file'].gsub('[PROJECT_ROOT]', '') | 13 | file_path = line['file'].gsub('[PROJECT_ROOT]', '') |
| 14 | line_number = line['number'] | 14 | line_number = line['number'] |
| 15 | link_to(text || file_name, "#{app.github_url_to_file(file_path)}#L#{line_number}", :target => '_blank') | 15 | link_to(text || file_name, "#{app.github_url_to_file(file_path)}#L#{line_number}", :target => '_blank') |
| 16 | end | 16 | end |
| 17 | - | 17 | + |
| 18 | end | 18 | end |
| 19 | \ No newline at end of file | 19 | \ No newline at end of file |
app/mailers/mailer.rb
| @@ -4,21 +4,21 @@ require Rails.root.join('config/routes.rb') | @@ -4,21 +4,21 @@ require Rails.root.join('config/routes.rb') | ||
| 4 | 4 | ||
| 5 | class Mailer < ActionMailer::Base | 5 | class Mailer < ActionMailer::Base |
| 6 | default :from => Errbit::Config.email_from | 6 | default :from => Errbit::Config.email_from |
| 7 | - | 7 | + |
| 8 | def err_notification(notice) | 8 | def err_notification(notice) |
| 9 | @notice = notice | 9 | @notice = notice |
| 10 | - @app = notice.err.app | ||
| 11 | - | 10 | + @app = notice.app |
| 11 | + | ||
| 12 | mail({ | 12 | mail({ |
| 13 | :to => @app.notification_recipients, | 13 | :to => @app.notification_recipients, |
| 14 | - :subject => "[#{@app.name}][#{@notice.err.environment}] #{@notice.err.message}" | 14 | + :subject => "[#{@app.name}][#{@notice.environment_name}] #{@notice.message}" |
| 15 | }) | 15 | }) |
| 16 | end | 16 | end |
| 17 | - | 17 | + |
| 18 | def deploy_notification(deploy) | 18 | def deploy_notification(deploy) |
| 19 | @deploy = deploy | 19 | @deploy = deploy |
| 20 | - @app = deploy.app | ||
| 21 | - | 20 | + @app = deploy.app |
| 21 | + | ||
| 22 | mail({ | 22 | mail({ |
| 23 | :to => @app.notification_recipients, | 23 | :to => @app.notification_recipients, |
| 24 | :subject => "[#{@app.name}] Deployed to #{@deploy.environment} by #{@deploy.username}" | 24 | :subject => "[#{@app.name}] Deployed to #{@deploy.environment} by #{@deploy.username}" |
app/models/app.rb
| 1 | class App | 1 | class App |
| 2 | include Mongoid::Document | 2 | include Mongoid::Document |
| 3 | include Mongoid::Timestamps | 3 | include Mongoid::Timestamps |
| 4 | - | 4 | + |
| 5 | field :name, :type => String | 5 | field :name, :type => String |
| 6 | field :api_key | 6 | field :api_key |
| 7 | field :github_url | 7 | field :github_url |
| @@ -10,53 +10,65 @@ class App | @@ -10,53 +10,65 @@ class App | ||
| 10 | field :notify_on_errs, :type => Boolean, :default => true | 10 | field :notify_on_errs, :type => Boolean, :default => true |
| 11 | field :notify_on_deploys, :type => Boolean, :default => false | 11 | field :notify_on_deploys, :type => Boolean, :default => false |
| 12 | field :email_at_notices, :type => Array, :default => Errbit::Config.email_at_notices | 12 | field :email_at_notices, :type => Array, :default => Errbit::Config.email_at_notices |
| 13 | - | 13 | + |
| 14 | # Some legacy apps may have string as key instead of BSON::ObjectID | 14 | # Some legacy apps may have string as key instead of BSON::ObjectID |
| 15 | identity :type => String | 15 | identity :type => String |
| 16 | + | ||
| 16 | # There seems to be a Mongoid bug making it impossible to use String identity with references_many feature: | 17 | # There seems to be a Mongoid bug making it impossible to use String identity with references_many feature: |
| 17 | # https://github.com/mongoid/mongoid/issues/703 | 18 | # https://github.com/mongoid/mongoid/issues/703 |
| 18 | # Using 32 character string as a workaround. | 19 | # Using 32 character string as a workaround. |
| 19 | before_create do |r| | 20 | before_create do |r| |
| 20 | r.id = ActiveSupport::SecureRandom.hex | 21 | r.id = ActiveSupport::SecureRandom.hex |
| 21 | end | 22 | end |
| 22 | - | 23 | + |
| 23 | embeds_many :watchers | 24 | embeds_many :watchers |
| 24 | embeds_many :deploys | 25 | embeds_many :deploys |
| 25 | embeds_one :issue_tracker | 26 | embeds_one :issue_tracker |
| 26 | - has_many :errs, :inverse_of => :app, :dependent => :destroy | ||
| 27 | - | 27 | + has_many :problems, :inverse_of => :app, :dependent => :destroy |
| 28 | + | ||
| 28 | before_validation :generate_api_key, :on => :create | 29 | before_validation :generate_api_key, :on => :create |
| 29 | before_save :normalize_github_url | 30 | before_save :normalize_github_url |
| 30 | - | 31 | + |
| 31 | validates_presence_of :name, :api_key | 32 | validates_presence_of :name, :api_key |
| 32 | validates_uniqueness_of :name, :allow_blank => true | 33 | validates_uniqueness_of :name, :allow_blank => true |
| 33 | validates_uniqueness_of :api_key, :allow_blank => true | 34 | validates_uniqueness_of :api_key, :allow_blank => true |
| 34 | validates_associated :watchers | 35 | validates_associated :watchers |
| 35 | validate :check_issue_tracker | 36 | validate :check_issue_tracker |
| 36 | - | 37 | + |
| 37 | accepts_nested_attributes_for :watchers, :allow_destroy => true, | 38 | accepts_nested_attributes_for :watchers, :allow_destroy => true, |
| 38 | :reject_if => proc { |attrs| attrs[:user_id].blank? && attrs[:email].blank? } | 39 | :reject_if => proc { |attrs| attrs[:user_id].blank? && attrs[:email].blank? } |
| 39 | accepts_nested_attributes_for :issue_tracker, :allow_destroy => true, | 40 | accepts_nested_attributes_for :issue_tracker, :allow_destroy => true, |
| 40 | :reject_if => proc { |attrs| !IssueTracker.subclasses.map(&:to_s).include?(attrs[:type].to_s) } | 41 | :reject_if => proc { |attrs| !IssueTracker.subclasses.map(&:to_s).include?(attrs[:type].to_s) } |
| 41 | - | 42 | + |
| 43 | + | ||
| 44 | + | ||
| 45 | + def find_or_create_err!(attrs) | ||
| 46 | + Err.where(attrs).first || problems.create!.errs.create!(attrs) | ||
| 47 | + end | ||
| 48 | + | ||
| 49 | + | ||
| 50 | + | ||
| 51 | + # Mongoid Bug: find(id) on association proxies returns an Enumerator | ||
| 42 | def self.find_by_id!(app_id) | 52 | def self.find_by_id!(app_id) |
| 43 | find app_id | 53 | find app_id |
| 44 | end | 54 | end |
| 45 | - | 55 | + |
| 46 | def self.find_by_api_key!(key) | 56 | def self.find_by_api_key!(key) |
| 47 | where(:api_key => key).first || raise(Mongoid::Errors::DocumentNotFound.new(self,key)) | 57 | where(:api_key => key).first || raise(Mongoid::Errors::DocumentNotFound.new(self,key)) |
| 48 | end | 58 | end |
| 49 | - | 59 | + |
| 50 | def last_deploy_at | 60 | def last_deploy_at |
| 51 | deploys.last && deploys.last.created_at | 61 | deploys.last && deploys.last.created_at |
| 52 | end | 62 | end |
| 53 | - | 63 | + |
| 64 | + | ||
| 54 | # Legacy apps don't have notify_on_errs and notify_on_deploys params | 65 | # Legacy apps don't have notify_on_errs and notify_on_deploys params |
| 55 | def notify_on_errs | 66 | def notify_on_errs |
| 56 | !(self[:notify_on_errs] == false) | 67 | !(self[:notify_on_errs] == false) |
| 57 | end | 68 | end |
| 58 | alias :notify_on_errs? :notify_on_errs | 69 | alias :notify_on_errs? :notify_on_errs |
| 59 | - | 70 | + |
| 71 | + | ||
| 60 | def notify_on_deploys | 72 | def notify_on_deploys |
| 61 | !(self[:notify_on_deploys] == false) | 73 | !(self[:notify_on_deploys] == false) |
| 62 | end | 74 | end |
| @@ -117,4 +129,3 @@ class App | @@ -117,4 +129,3 @@ class App | ||
| 117 | end | 129 | end |
| 118 | 130 | ||
| 119 | end | 131 | end |
| 120 | - |
app/models/deploy.rb
| 1 | class Deploy | 1 | class Deploy |
| 2 | include Mongoid::Document | 2 | include Mongoid::Document |
| 3 | include Mongoid::Timestamps | 3 | include Mongoid::Timestamps |
| 4 | - | 4 | + |
| 5 | field :username | 5 | field :username |
| 6 | field :repository | 6 | field :repository |
| 7 | field :environment | 7 | field :environment |
| 8 | field :revision | 8 | field :revision |
| 9 | field :message | 9 | field :message |
| 10 | - | 10 | + |
| 11 | index :created_at, Mongo::DESCENDING | 11 | index :created_at, Mongo::DESCENDING |
| 12 | - | 12 | + |
| 13 | embedded_in :app, :inverse_of => :deploys | 13 | embedded_in :app, :inverse_of => :deploys |
| 14 | - | 14 | + |
| 15 | after_create :deliver_notification, :if => :should_notify? | 15 | after_create :deliver_notification, :if => :should_notify? |
| 16 | after_create :resolve_app_errs, :if => :should_resolve_app_errs? | 16 | after_create :resolve_app_errs, :if => :should_resolve_app_errs? |
| 17 | - | 17 | + |
| 18 | validates_presence_of :username, :environment | 18 | validates_presence_of :username, :environment |
| 19 | - | 19 | + |
| 20 | def deliver_notification | 20 | def deliver_notification |
| 21 | Mailer.deploy_notification(self).deliver | 21 | Mailer.deploy_notification(self).deliver |
| 22 | end | 22 | end |
| 23 | - | 23 | + |
| 24 | def resolve_app_errs | 24 | def resolve_app_errs |
| 25 | - app.errs.unresolved.in_env(environment).each {|err| err.resolve!} | 25 | + app.problems.unresolved.in_env(environment).each {|problem| problem.resolve!} |
| 26 | end | 26 | end |
| 27 | - | 27 | + |
| 28 | def short_revision | 28 | def short_revision |
| 29 | revision.to_s[0,7] | 29 | revision.to_s[0,7] |
| 30 | end | 30 | end |
| 31 | - | 31 | + |
| 32 | protected | 32 | protected |
| 33 | - | 33 | + |
| 34 | def should_notify? | 34 | def should_notify? |
| 35 | app.notify_on_deploys? && app.watchers.any? | 35 | app.notify_on_deploys? && app.watchers.any? |
| 36 | end | 36 | end |
| 37 | - | 37 | + |
| 38 | def should_resolve_app_errs? | 38 | def should_resolve_app_errs? |
| 39 | app.resolve_errs_on_deploy? | 39 | app.resolve_errs_on_deploy? |
| 40 | end | 40 | end |
| 41 | - | 41 | + |
| 42 | end | 42 | end |
| 43 | 43 |
app/models/err.rb
| 1 | +# An Err is a group of notices that can programatically | ||
| 2 | +# be determined to be equal. (Errbit groups notices into | ||
| 3 | +# errs by a notice's fingerprint.) | ||
| 4 | + | ||
| 1 | class Err | 5 | class Err |
| 2 | include Mongoid::Document | 6 | include Mongoid::Document |
| 3 | include Mongoid::Timestamps | 7 | include Mongoid::Timestamps |
| 4 | - | 8 | + |
| 5 | field :klass | 9 | field :klass |
| 6 | field :component | 10 | field :component |
| 7 | field :action | 11 | field :action |
| 8 | field :environment | 12 | field :environment |
| 9 | field :fingerprint | 13 | field :fingerprint |
| 10 | - field :last_notice_at, :type => DateTime | ||
| 11 | - field :resolved, :type => Boolean, :default => false | ||
| 12 | - field :issue_link, :type => String | ||
| 13 | - field :notices_count, :type => Integer, :default => 0 | ||
| 14 | - field :message | ||
| 15 | - | ||
| 16 | - index :last_notice_at | ||
| 17 | - index :app_id | ||
| 18 | - index :notices | ||
| 19 | - | ||
| 20 | - belongs_to :app | ||
| 21 | - has_many :notices | ||
| 22 | - has_many :comments, :inverse_of => :err, :dependent => :destroy | ||
| 23 | - | ||
| 24 | - validates_presence_of :klass, :environment | ||
| 25 | - | ||
| 26 | - scope :resolved, where(:resolved => true) | ||
| 27 | - scope :unresolved, where(:resolved => false) | ||
| 28 | - scope :ordered, order_by(:last_notice_at.desc) | ||
| 29 | - scope :for_apps, lambda {|apps| where(:app_id.in => apps.all.map(&:id))} | ||
| 30 | - | ||
| 31 | - def self.in_env(env) | ||
| 32 | - env.present? ? where(:environment => env) : scoped | ||
| 33 | - end | ||
| 34 | - | ||
| 35 | - def self.for(attrs) | ||
| 36 | - app = attrs.delete(:app) | ||
| 37 | - app.errs.where(attrs).first || app.errs.create!(attrs) | ||
| 38 | - end | ||
| 39 | - | ||
| 40 | - def resolve! | ||
| 41 | - self.update_attributes!(:resolved => true) | ||
| 42 | - end | ||
| 43 | - | ||
| 44 | - def unresolve! | ||
| 45 | - self.update_attributes!(:resolved => false) | ||
| 46 | - end | ||
| 47 | - | ||
| 48 | - def unresolved? | ||
| 49 | - !resolved? | ||
| 50 | - end | ||
| 51 | - | ||
| 52 | - def where | ||
| 53 | - where = component.dup | ||
| 54 | - where << "##{action}" if action.present? | ||
| 55 | - where | ||
| 56 | - end | ||
| 57 | - | ||
| 58 | - def message | ||
| 59 | - super || klass | ||
| 60 | - end | ||
| 61 | - | 14 | + |
| 15 | + belongs_to :problem | ||
| 16 | + has_many :notices, :inverse_of => :err, :dependent => :destroy | ||
| 17 | + | ||
| 18 | + validates_presence_of :klass, :environment, :problem | ||
| 19 | + | ||
| 20 | + delegate :app, | ||
| 21 | + :resolved?, | ||
| 22 | + :to => :problem | ||
| 23 | + | ||
| 24 | + | ||
| 62 | end | 25 | end |
app/models/issue_tracker.rb
| @@ -19,8 +19,8 @@ class IssueTracker | @@ -19,8 +19,8 @@ class IssueTracker | ||
| 19 | # Subclasses are responsible for overwriting this method. | 19 | # Subclasses are responsible for overwriting this method. |
| 20 | def check_params; true; end | 20 | def check_params; true; end |
| 21 | 21 | ||
| 22 | - def issue_title err | ||
| 23 | - "[#{ err.environment }][#{ err.where }] #{err.message.to_s.truncate(100)}" | 22 | + def issue_title(problem) |
| 23 | + "[#{ problem.environment }][#{ problem.where }] #{problem.message.to_s.truncate(100)}" | ||
| 24 | end | 24 | end |
| 25 | 25 | ||
| 26 | # Allows us to set the issue tracker class from a single form. | 26 | # Allows us to set the issue tracker class from a single form. |
app/models/issue_trackers/fogbugz_tracker.rb
| @@ -22,19 +22,19 @@ class IssueTrackers::FogbugzTracker < IssueTracker | @@ -22,19 +22,19 @@ class IssueTrackers::FogbugzTracker < IssueTracker | ||
| 22 | end | 22 | end |
| 23 | end | 23 | end |
| 24 | 24 | ||
| 25 | - def create_issue(err) | 25 | + def create_issue(problem) |
| 26 | fogbugz = Fogbugz::Interface.new(:email => username, :password => password, :uri => "https://#{account}.fogbugz.com") | 26 | fogbugz = Fogbugz::Interface.new(:email => username, :password => password, :uri => "https://#{account}.fogbugz.com") |
| 27 | fogbugz.authenticate | 27 | fogbugz.authenticate |
| 28 | 28 | ||
| 29 | issue = {} | 29 | issue = {} |
| 30 | - issue['sTitle'] = issue_title err | 30 | + issue['sTitle'] = issue_title problem |
| 31 | issue['sArea'] = project_id | 31 | issue['sArea'] = project_id |
| 32 | issue['sEvent'] = body_template.result(binding) | 32 | issue['sEvent'] = body_template.result(binding) |
| 33 | issue['sTags'] = ['errbit'].join(',') | 33 | issue['sTags'] = ['errbit'].join(',') |
| 34 | issue['cols'] = ['ixBug'].join(',') | 34 | issue['cols'] = ['ixBug'].join(',') |
| 35 | 35 | ||
| 36 | fb_resp = fogbugz.command(:new, issue) | 36 | fb_resp = fogbugz.command(:new, issue) |
| 37 | - err.update_attribute :issue_link, "https://#{account}.fogbugz.com/default.asp?#{fb_resp['case']['ixBug']}" | 37 | + problem.update_attribute :issue_link, "https://#{account}.fogbugz.com/default.asp?#{fb_resp['case']['ixBug']}" |
| 38 | end | 38 | end |
| 39 | 39 | ||
| 40 | def body_template | 40 | def body_template |
app/models/issue_trackers/lighthouse_tracker.rb
| @@ -18,20 +18,20 @@ class IssueTrackers::LighthouseTracker < IssueTracker | @@ -18,20 +18,20 @@ class IssueTrackers::LighthouseTracker < IssueTracker | ||
| 18 | end | 18 | end |
| 19 | end | 19 | end |
| 20 | 20 | ||
| 21 | - def create_issue(err) | 21 | + def create_issue(problem) |
| 22 | Lighthouse.account = account | 22 | Lighthouse.account = account |
| 23 | Lighthouse.token = api_token | 23 | Lighthouse.token = api_token |
| 24 | # updating lighthouse account | 24 | # updating lighthouse account |
| 25 | Lighthouse::Ticket.site | 25 | Lighthouse::Ticket.site |
| 26 | 26 | ||
| 27 | ticket = Lighthouse::Ticket.new(:project_id => project_id) | 27 | ticket = Lighthouse::Ticket.new(:project_id => project_id) |
| 28 | - ticket.title = issue_title err | 28 | + ticket.title = issue_title problem |
| 29 | 29 | ||
| 30 | ticket.body = body_template.result(binding) | 30 | ticket.body = body_template.result(binding) |
| 31 | 31 | ||
| 32 | ticket.tags << "errbit" | 32 | ticket.tags << "errbit" |
| 33 | ticket.save! | 33 | ticket.save! |
| 34 | - err.update_attribute :issue_link, "#{Lighthouse::Ticket.site.to_s.sub(/#{Lighthouse::Ticket.site.path}$/, '')}#{Lighthouse::Ticket.element_path(ticket.id, :project_id => project_id)}".sub(/\.xml$/, '') | 34 | + problem.update_attribute :issue_link, "#{Lighthouse::Ticket.site.to_s.sub(/#{Lighthouse::Ticket.site.path}$/, '')}#{Lighthouse::Ticket.element_path(ticket.id, :project_id => project_id)}".sub(/\.xml$/, '') |
| 35 | end | 35 | end |
| 36 | 36 | ||
| 37 | def body_template | 37 | def body_template |
app/models/issue_trackers/mingle_tracker.rb
| @@ -27,21 +27,21 @@ class IssueTrackers::MingleTracker < IssueTracker | @@ -27,21 +27,21 @@ class IssueTrackers::MingleTracker < IssueTracker | ||
| 27 | end | 27 | end |
| 28 | end | 28 | end |
| 29 | 29 | ||
| 30 | - def create_issue(err) | 30 | + def create_issue(problem) |
| 31 | properties = ticket_properties_hash | 31 | properties = ticket_properties_hash |
| 32 | basic_auth = account.gsub(/https?:\/\//, "https://#{username}:#{password}@") | 32 | basic_auth = account.gsub(/https?:\/\//, "https://#{username}:#{password}@") |
| 33 | Mingle.set_site "#{basic_auth}/api/v1/projects/#{project_id}/" | 33 | Mingle.set_site "#{basic_auth}/api/v1/projects/#{project_id}/" |
| 34 | 34 | ||
| 35 | card = Mingle::Card.new | 35 | card = Mingle::Card.new |
| 36 | card.card_type_name = properties.delete("card_type") | 36 | card.card_type_name = properties.delete("card_type") |
| 37 | - card.name = issue_title(err) | 37 | + card.name = issue_title(problem) |
| 38 | card.description = body_template.result(binding) | 38 | card.description = body_template.result(binding) |
| 39 | properties.each do |property, value| | 39 | properties.each do |property, value| |
| 40 | card.send("cp_#{property}=", value) | 40 | card.send("cp_#{property}=", value) |
| 41 | end | 41 | end |
| 42 | 42 | ||
| 43 | card.save! | 43 | card.save! |
| 44 | - err.update_attribute :issue_link, URI.parse("#{account}/projects/#{project_id}/cards/#{card.id}").to_s | 44 | + problem.update_attribute :issue_link, URI.parse("#{account}/projects/#{project_id}/cards/#{card.id}").to_s |
| 45 | end | 45 | end |
| 46 | 46 | ||
| 47 | def body_template | 47 | def body_template |
app/models/issue_trackers/pivotal_labs_tracker.rb
| @@ -13,12 +13,12 @@ class IssueTrackers::PivotalLabsTracker < IssueTracker | @@ -13,12 +13,12 @@ class IssueTrackers::PivotalLabsTracker < IssueTracker | ||
| 13 | end | 13 | end |
| 14 | end | 14 | end |
| 15 | 15 | ||
| 16 | - def create_issue(err) | 16 | + def create_issue(problem) |
| 17 | PivotalTracker::Client.token = api_token | 17 | PivotalTracker::Client.token = api_token |
| 18 | PivotalTracker::Client.use_ssl = true | 18 | PivotalTracker::Client.use_ssl = true |
| 19 | project = PivotalTracker::Project.find project_id.to_i | 19 | project = PivotalTracker::Project.find project_id.to_i |
| 20 | - story = project.stories.create :name => issue_title(err), :story_type => 'bug', :description => body_template.result(binding) | ||
| 21 | - err.update_attribute :issue_link, "https://www.pivotaltracker.com/story/show/#{story.id}" | 20 | + story = project.stories.create :name => issue_title(problem), :story_type => 'bug', :description => body_template.result(binding) |
| 21 | + problem.update_attribute :issue_link, "https://www.pivotaltracker.com/story/show/#{story.id}" | ||
| 22 | end | 22 | end |
| 23 | 23 | ||
| 24 | def body_template | 24 | def body_template |
app/models/issue_trackers/redmine_tracker.rb
| @@ -17,7 +17,7 @@ class IssueTrackers::RedmineTracker < IssueTracker | @@ -17,7 +17,7 @@ class IssueTrackers::RedmineTracker < IssueTracker | ||
| 17 | end | 17 | end |
| 18 | end | 18 | end |
| 19 | 19 | ||
| 20 | - def create_issue(err) | 20 | + def create_issue(problem) |
| 21 | token = api_token | 21 | token = api_token |
| 22 | acc = account | 22 | acc = account |
| 23 | RedmineClient::Base.configure do | 23 | RedmineClient::Base.configure do |
| @@ -25,10 +25,10 @@ class IssueTrackers::RedmineTracker < IssueTracker | @@ -25,10 +25,10 @@ class IssueTrackers::RedmineTracker < IssueTracker | ||
| 25 | self.site = acc | 25 | self.site = acc |
| 26 | end | 26 | end |
| 27 | issue = RedmineClient::Issue.new(:project_id => project_id) | 27 | issue = RedmineClient::Issue.new(:project_id => project_id) |
| 28 | - issue.subject = issue_title err | 28 | + issue.subject = issue_title problem |
| 29 | issue.description = body_template.result(binding) | 29 | issue.description = body_template.result(binding) |
| 30 | issue.save! | 30 | issue.save! |
| 31 | - err.update_attribute :issue_link, "#{RedmineClient::Issue.site.to_s.sub(/#{RedmineClient::Issue.site.path}$/, '')}#{RedmineClient::Issue.element_path(issue.id, :project_id => project_id)}".sub(/\.xml\?project_id=#{project_id}$/, "\?project_id=#{project_id}") | 31 | + problem.update_attribute :issue_link, "#{RedmineClient::Issue.site.to_s.sub(/#{RedmineClient::Issue.site.path}$/, '')}#{RedmineClient::Issue.element_path(issue.id, :project_id => project_id)}".sub(/\.xml\?project_id=#{project_id}$/, "\?project_id=#{project_id}") |
| 32 | end | 32 | end |
| 33 | 33 | ||
| 34 | def body_template | 34 | def body_template |
app/models/notice.rb
| @@ -4,46 +4,50 @@ require 'recurse' | @@ -4,46 +4,50 @@ require 'recurse' | ||
| 4 | class Notice | 4 | class Notice |
| 5 | include Mongoid::Document | 5 | include Mongoid::Document |
| 6 | include Mongoid::Timestamps | 6 | include Mongoid::Timestamps |
| 7 | - | 7 | + |
| 8 | field :message | 8 | field :message |
| 9 | field :backtrace, :type => Array | 9 | field :backtrace, :type => Array |
| 10 | field :server_environment, :type => Hash | 10 | field :server_environment, :type => Hash |
| 11 | field :request, :type => Hash | 11 | field :request, :type => Hash |
| 12 | field :notifier, :type => Hash | 12 | field :notifier, :type => Hash |
| 13 | - | 13 | + field :klass |
| 14 | + | ||
| 14 | belongs_to :err | 15 | belongs_to :err |
| 15 | index :err_id | 16 | index :err_id |
| 16 | - | ||
| 17 | - after_create :cache_last_notice_at | 17 | + index :created_at |
| 18 | + | ||
| 18 | after_create :deliver_notification, :if => :should_notify? | 19 | after_create :deliver_notification, :if => :should_notify? |
| 19 | - before_create :increase_counter_cache, :cache_message | 20 | + after_create :increase_counter_cache, :cache_attributes_on_problem |
| 20 | before_save :sanitize | 21 | before_save :sanitize |
| 21 | before_destroy :decrease_counter_cache | 22 | before_destroy :decrease_counter_cache |
| 22 | - | 23 | + |
| 23 | validates_presence_of :backtrace, :server_environment, :notifier | 24 | validates_presence_of :backtrace, :server_environment, :notifier |
| 24 | - | 25 | + |
| 25 | scope :ordered, order_by(:created_at.asc) | 26 | scope :ordered, order_by(:created_at.asc) |
| 26 | - index :created_at | ||
| 27 | - | 27 | + scope :for_errs, lambda {|errs| where(:err_id.in => errs.all.map(&:id))} |
| 28 | + | ||
| 29 | + delegate :app, :problem, :to => :err | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 28 | def self.from_xml(hoptoad_xml) | 33 | def self.from_xml(hoptoad_xml) |
| 29 | hoptoad_notice = Hoptoad::V2.parse_xml(hoptoad_xml) | 34 | hoptoad_notice = Hoptoad::V2.parse_xml(hoptoad_xml) |
| 30 | app = App.find_by_api_key!(hoptoad_notice['api-key']) | 35 | app = App.find_by_api_key!(hoptoad_notice['api-key']) |
| 31 | - | 36 | + |
| 32 | hoptoad_notice['request'] ||= {} | 37 | hoptoad_notice['request'] ||= {} |
| 33 | hoptoad_notice['request']['component'] = 'unknown' if hoptoad_notice['request']['component'].blank? | 38 | hoptoad_notice['request']['component'] = 'unknown' if hoptoad_notice['request']['component'].blank? |
| 34 | hoptoad_notice['request']['action'] = nil if hoptoad_notice['request']['action'].blank? | 39 | hoptoad_notice['request']['action'] = nil if hoptoad_notice['request']['action'].blank? |
| 35 | - | ||
| 36 | - err = Err.for({ | ||
| 37 | - :app => app, | ||
| 38 | - :klass => hoptoad_notice['error']['class'], | ||
| 39 | - :component => hoptoad_notice['request']['component'], | ||
| 40 | - :action => hoptoad_notice['request']['action'], | ||
| 41 | - :environment => hoptoad_notice['server-environment']['environment-name'], | ||
| 42 | - :fingerprint => hoptoad_notice['fingerprint'] | 40 | + |
| 41 | + err = app.find_or_create_err!({ | ||
| 42 | + :klass => hoptoad_notice['error']['class'], | ||
| 43 | + :component => hoptoad_notice['request']['component'], | ||
| 44 | + :action => hoptoad_notice['request']['action'], | ||
| 45 | + :environment => hoptoad_notice['server-environment']['environment-name'], | ||
| 46 | + :fingerprint => hoptoad_notice['fingerprint'] | ||
| 43 | }) | 47 | }) |
| 44 | - err.update_attributes(:resolved => false) if err.resolved? | ||
| 45 | - | 48 | + err.problem.update_attributes(:resolved => false) if err.problem.resolved? |
| 46 | err.notices.create!({ | 49 | err.notices.create!({ |
| 50 | + :klass => hoptoad_notice['error']['class'], | ||
| 47 | :message => hoptoad_notice['error']['message'], | 51 | :message => hoptoad_notice['error']['message'], |
| 48 | :backtrace => [hoptoad_notice['error']['backtrace']['line']].flatten, | 52 | :backtrace => [hoptoad_notice['error']['backtrace']['line']].flatten, |
| 49 | :server_environment => hoptoad_notice['server-environment'], | 53 | :server_environment => hoptoad_notice['server-environment'], |
| @@ -51,64 +55,97 @@ class Notice | @@ -51,64 +55,97 @@ class Notice | ||
| 51 | :notifier => hoptoad_notice['notifier'] | 55 | :notifier => hoptoad_notice['notifier'] |
| 52 | }) | 56 | }) |
| 53 | end | 57 | end |
| 54 | - | 58 | + |
| 59 | + | ||
| 60 | + | ||
| 55 | def user_agent | 61 | def user_agent |
| 56 | agent_string = env_vars['HTTP_USER_AGENT'] | 62 | agent_string = env_vars['HTTP_USER_AGENT'] |
| 57 | agent_string.blank? ? nil : UserAgent.parse(agent_string) | 63 | agent_string.blank? ? nil : UserAgent.parse(agent_string) |
| 58 | end | 64 | end |
| 59 | - | 65 | + |
| 66 | + def environment_name | ||
| 67 | + server_environment['server-environment'] || server_environment['environment-name'] | ||
| 68 | + end | ||
| 69 | + | ||
| 70 | + def component | ||
| 71 | + request['component'] | ||
| 72 | + end | ||
| 73 | + | ||
| 74 | + def action | ||
| 75 | + request['action'] | ||
| 76 | + end | ||
| 77 | + | ||
| 78 | + def where | ||
| 79 | + where = component.to_s.dup | ||
| 80 | + where << "##{action}" if action.present? | ||
| 81 | + where | ||
| 82 | + end | ||
| 83 | + | ||
| 84 | + | ||
| 85 | + | ||
| 60 | def self.in_app_backtrace_line? line | 86 | def self.in_app_backtrace_line? line |
| 61 | !!(line['file'] =~ %r{^\[PROJECT_ROOT\]/(?!(vendor))}) | 87 | !!(line['file'] =~ %r{^\[PROJECT_ROOT\]/(?!(vendor))}) |
| 62 | end | 88 | end |
| 63 | - | 89 | + |
| 90 | + | ||
| 91 | + | ||
| 64 | def request | 92 | def request |
| 65 | read_attribute(:request) || {} | 93 | read_attribute(:request) || {} |
| 66 | end | 94 | end |
| 67 | - | 95 | + |
| 68 | def env_vars | 96 | def env_vars |
| 69 | request['cgi-data'] || {} | 97 | request['cgi-data'] || {} |
| 70 | end | 98 | end |
| 71 | - | 99 | + |
| 72 | def params | 100 | def params |
| 73 | request['params'] || {} | 101 | request['params'] || {} |
| 74 | end | 102 | end |
| 75 | - | 103 | + |
| 76 | def session | 104 | def session |
| 77 | request['session'] || {} | 105 | request['session'] || {} |
| 78 | end | 106 | end |
| 79 | - | 107 | + |
| 108 | + | ||
| 109 | + | ||
| 80 | def deliver_notification | 110 | def deliver_notification |
| 81 | Mailer.err_notification(self).deliver | 111 | Mailer.err_notification(self).deliver |
| 82 | end | 112 | end |
| 83 | - | 113 | + |
| 84 | def cache_last_notice_at | 114 | def cache_last_notice_at |
| 85 | err.update_attributes(:last_notice_at => created_at) | 115 | err.update_attributes(:last_notice_at => created_at) |
| 86 | end | 116 | end |
| 87 | - | 117 | + |
| 88 | # Backtrace containing only files from the app itself (ignore gems) | 118 | # Backtrace containing only files from the app itself (ignore gems) |
| 89 | def app_backtrace | 119 | def app_backtrace |
| 90 | backtrace.select { |l| l && l['file'] && l['file'].include?("[PROJECT_ROOT]") } | 120 | backtrace.select { |l| l && l['file'] && l['file'].include?("[PROJECT_ROOT]") } |
| 91 | end | 121 | end |
| 92 | - | ||
| 93 | - protected | ||
| 94 | - | 122 | + |
| 123 | + | ||
| 124 | + | ||
| 125 | +protected | ||
| 126 | + | ||
| 127 | + | ||
| 128 | + | ||
| 95 | def should_notify? | 129 | def should_notify? |
| 96 | - err.app.notify_on_errs? && (Errbit::Config.per_app_email_at_notices && err.app.email_at_notices || Errbit::Config.email_at_notices).include?(err.notices.count) && err.app.watchers.any? | 130 | + app.notify_on_errs? && (Errbit::Config.per_app_email_at_notices && app.email_at_notices || Errbit::Config.email_at_notices).include?(err.notices.count) && app.watchers.any? |
| 97 | end | 131 | end |
| 98 | - | ||
| 99 | - | 132 | + |
| 133 | + | ||
| 100 | def increase_counter_cache | 134 | def increase_counter_cache |
| 101 | - err.inc(:notices_count,1) | 135 | + problem.inc(:notices_count, 1) |
| 102 | end | 136 | end |
| 103 | - | 137 | + |
| 138 | + | ||
| 104 | def decrease_counter_cache | 139 | def decrease_counter_cache |
| 105 | - err.inc(:notices_count,-1) | 140 | + problem.inc(:notices_count, -1) |
| 106 | end | 141 | end |
| 107 | - | ||
| 108 | - def cache_message | ||
| 109 | - err.update_attribute(:message, message) if err.notices_count == 1 | 142 | + |
| 143 | + | ||
| 144 | + def cache_attributes_on_problem | ||
| 145 | + problem.cache_notice_attributes(self) if problem.notices_count == 1 | ||
| 110 | end | 146 | end |
| 111 | - | 147 | + |
| 148 | + | ||
| 112 | def sanitize | 149 | def sanitize |
| 113 | [:server_environment, :request, :notifier].each do |h| | 150 | [:server_environment, :request, :notifier].each do |h| |
| 114 | send("#{h}=",sanitize_hash(send(h))) | 151 | send("#{h}=",sanitize_hash(send(h))) |
| @@ -116,7 +153,8 @@ class Notice | @@ -116,7 +153,8 @@ class Notice | ||
| 116 | # Set unknown backtrace files | 153 | # Set unknown backtrace files |
| 117 | backtrace.each{|line| line['file'] = "[unknown source]" if line['file'].blank? } | 154 | backtrace.each{|line| line['file'] = "[unknown source]" if line['file'].blank? } |
| 118 | end | 155 | end |
| 119 | - | 156 | + |
| 157 | + | ||
| 120 | def sanitize_hash(h) | 158 | def sanitize_hash(h) |
| 121 | h.recurse do | 159 | h.recurse do |
| 122 | |h| h.inject({}) do |h,(k,v)| | 160 | |h| h.inject({}) do |h,(k,v)| |
| @@ -129,5 +167,6 @@ class Notice | @@ -129,5 +167,6 @@ class Notice | ||
| 129 | end | 167 | end |
| 130 | end | 168 | end |
| 131 | end | 169 | end |
| 170 | + | ||
| 171 | + | ||
| 132 | end | 172 | end |
| 133 | - |
| @@ -0,0 +1,70 @@ | @@ -0,0 +1,70 @@ | ||
| 1 | +# An Problem is a group of errs that the user | ||
| 2 | +# has declared to be equal. | ||
| 3 | + | ||
| 4 | +class Problem | ||
| 5 | + include Mongoid::Document | ||
| 6 | + include Mongoid::Timestamps | ||
| 7 | + | ||
| 8 | + field :last_notice_at, :type => DateTime | ||
| 9 | + field :resolved, :type => Boolean, :default => false | ||
| 10 | + field :issue_link, :type => String | ||
| 11 | + | ||
| 12 | + # Cached fields | ||
| 13 | + field :notices_count, :type => Integer, :default => 0 | ||
| 14 | + field :message | ||
| 15 | + field :environment | ||
| 16 | + field :klass | ||
| 17 | + field :where | ||
| 18 | + | ||
| 19 | + index :last_notice_at | ||
| 20 | + index :app_id | ||
| 21 | + | ||
| 22 | + belongs_to :app | ||
| 23 | + has_many :errs, :inverse_of => :problem, :dependent => :destroy | ||
| 24 | + has_many :comments, :inverse_of => :err, :dependent => :destroy | ||
| 25 | + | ||
| 26 | + scope :resolved, where(:resolved => true) | ||
| 27 | + scope :unresolved, where(:resolved => false) | ||
| 28 | + scope :ordered, order_by(:last_notice_at.desc) | ||
| 29 | + scope :for_apps, lambda {|apps| where(:app_id.in => apps.all.map(&:id))} | ||
| 30 | + | ||
| 31 | + def self.in_env(env) | ||
| 32 | + env.present? ? where(:environment => env) : scoped | ||
| 33 | + end | ||
| 34 | + | ||
| 35 | + | ||
| 36 | + | ||
| 37 | + def notices | ||
| 38 | + Notice.for_errs(errs).ordered | ||
| 39 | + end | ||
| 40 | + | ||
| 41 | + | ||
| 42 | + def resolve! | ||
| 43 | + self.update_attributes!(:resolved => true) | ||
| 44 | + end | ||
| 45 | + | ||
| 46 | + | ||
| 47 | + def unresolve! | ||
| 48 | + self.update_attributes!(:resolved => false) | ||
| 49 | + end | ||
| 50 | + | ||
| 51 | + | ||
| 52 | + def unresolved? | ||
| 53 | + !resolved? | ||
| 54 | + end | ||
| 55 | + | ||
| 56 | + | ||
| 57 | + | ||
| 58 | + def cache_notice_attributes(notice=nil) | ||
| 59 | + notice ||= notices.first | ||
| 60 | + attrs = {:last_notice_at => notices.max(:created_at)} | ||
| 61 | + attrs.merge!( | ||
| 62 | + :message => notice.message, | ||
| 63 | + :environment => notice.environment_name, | ||
| 64 | + :klass => notice.klass, | ||
| 65 | + :where => notice.where) if notice | ||
| 66 | + update_attributes!(attrs) | ||
| 67 | + end | ||
| 68 | + | ||
| 69 | + | ||
| 70 | +end | ||
| 0 | \ No newline at end of file | 71 | \ No newline at end of file |
app/views/apps/index.html.haml
| @@ -14,7 +14,7 @@ | @@ -14,7 +14,7 @@ | ||
| 14 | %td.name= link_to app.name, app_path(app) | 14 | %td.name= link_to app.name, app_path(app) |
| 15 | %td.deploy= app.last_deploy_at ? link_to( app.last_deploy_at.to_s(:micro) << " (#{app.deploys.last.short_revision})", app_deploys_path(app)) : 'n/a' | 15 | %td.deploy= app.last_deploy_at ? link_to( app.last_deploy_at.to_s(:micro) << " (#{app.deploys.last.short_revision})", app_deploys_path(app)) : 'n/a' |
| 16 | %td.count | 16 | %td.count |
| 17 | - - if app.errs.count > 0 | 17 | + - if app.problems.count > 0 |
| 18 | - unresolved = @unresolved_counts[app.id] | 18 | - unresolved = @unresolved_counts[app.id] |
| 19 | = link_to unresolved, app_path(app), :class => (unresolved == 0 ? "resolved" : nil) | 19 | = link_to unresolved, app_path(app), :class => (unresolved == 0 ? "resolved" : nil) |
| 20 | - else | 20 | - else |
app/views/apps/show.html.haml
| @@ -4,7 +4,7 @@ | @@ -4,7 +4,7 @@ | ||
| 4 | = javascript_include_tag 'apps.show' | 4 | = javascript_include_tag 'apps.show' |
| 5 | - content_for :meta do | 5 | - content_for :meta do |
| 6 | %strong Errs Caught: | 6 | %strong Errs Caught: |
| 7 | - = @app.errs.count | 7 | + = @app.problems.count |
| 8 | %strong Deploy Count: | 8 | %strong Deploy Count: |
| 9 | = @app.deploys.count | 9 | = @app.deploys.count |
| 10 | %strong API Key: | 10 | %strong API Key: |
| @@ -82,9 +82,9 @@ | @@ -82,9 +82,9 @@ | ||
| 82 | - else | 82 | - else |
| 83 | %h3 No deploys | 83 | %h3 No deploys |
| 84 | 84 | ||
| 85 | -- if @app.errs.count > 0 | 85 | +- if @app.problems.any? |
| 86 | %h3.clear Errs | 86 | %h3.clear Errs |
| 87 | - = render 'errs/table', :errs => @errs | 87 | + = render 'errs/table', :errs => @problems |
| 88 | - else | 88 | - else |
| 89 | %h3.clear No errs have been caught yet, make sure you setup your app | 89 | %h3.clear No errs have been caught yet, make sure you setup your app |
| 90 | = render 'configuration_instructions', :app => @app | 90 | = render 'configuration_instructions', :app => @app |
app/views/errs/_list.atom.builder
| 1 | -feed.updated(@errs.first.created_at) | 1 | +feed.updated(@problems.first.created_at) |
| 2 | 2 | ||
| 3 | -for err in @errs | ||
| 4 | - notice = err.notices.first | 3 | +for problem in @problems |
| 4 | + notice = problem.notices.first | ||
| 5 | 5 | ||
| 6 | - feed.entry(err, :url => app_err_url(err.app, err)) do |entry| | ||
| 7 | - entry.title "[#{ err.where }] #{err.message.to_s.truncate(27)}" | 6 | + feed.entry(problem, :url => app_err_url(problem.app, problem)) do |entry| |
| 7 | + entry.title "[#{ problem.where }] #{problem.message.to_s.truncate(27)}" | ||
| 8 | entry.author do |author| | 8 | entry.author do |author| |
| 9 | - author.name "#{ err.app.name } [#{ err.environment }]" | 9 | + author.name "#{ problem.app.name } [#{ problem.environment }]" |
| 10 | end | 10 | end |
| 11 | if notice | 11 | if notice |
| 12 | entry.summary(notice_atom_summary(notice), :type => "html") | 12 | entry.summary(notice_atom_summary(notice), :type => "html") |
app/views/errs/_table.html.haml
| @@ -10,28 +10,28 @@ | @@ -10,28 +10,28 @@ | ||
| 10 | %th Count | 10 | %th Count |
| 11 | %th Resolve | 11 | %th Resolve |
| 12 | %tbody | 12 | %tbody |
| 13 | - - errs.each do |err| | ||
| 14 | - %tr{:class => err.resolved? ? 'resolved' : 'unresolved'} | 13 | + - errs.each do |problem| |
| 14 | + %tr{:class => problem.resolved? ? 'resolved' : 'unresolved'} | ||
| 15 | %td.select | 15 | %td.select |
| 16 | - = check_box_tag "errs[]", err.id, @selected_errs.member?(err.id.to_s) | 16 | + = check_box_tag "problems[]", problem.id, @selected_problems.member?(problem.id.to_s) |
| 17 | %td.app | 17 | %td.app |
| 18 | - = link_to err.app.name, app_path(err.app) | 18 | + = link_to problem.app.name, app_path(problem.app) |
| 19 | - if current_page?(:controller => 'errs') | 19 | - if current_page?(:controller => 'errs') |
| 20 | - %span.environment= link_to err.environment, errs_path(environment: err.environment) | 20 | + %span.environment= link_to problem.environment, errs_path(environment: problem.environment) |
| 21 | - else | 21 | - else |
| 22 | - %span.environment= link_to err.environment, app_path(err.app, environment: err.environment) | 22 | + %span.environment= link_to problem.environment, app_path(problem.app, environment: problem.environment) |
| 23 | %td.message | 23 | %td.message |
| 24 | - = link_to err.message, app_err_path(err.app, err) | ||
| 25 | - %em= err.where | ||
| 26 | - %td.latest #{time_ago_in_words(last_notice_at err)} ago | ||
| 27 | - %td.deploy= err.app.last_deploy_at ? err.app.last_deploy_at.to_s(:micro) : 'n/a' | ||
| 28 | - %td.count= link_to err.notices.count, app_err_path(err.app, err) | ||
| 29 | - %td.resolve= link_to image_tag("thumbs-up.png"), resolve_app_err_path(err.app, err), :title => "Resolve", :method => :put, :confirm => err_confirm, :class => 'resolve' if err.unresolved? | 24 | + = link_to problem.message, app_err_path(problem.app, problem) |
| 25 | + %em= problem.where | ||
| 26 | + %td.latest #{time_ago_in_words(last_notice_at problem)} ago | ||
| 27 | + %td.deploy= problem.app.last_deploy_at ? problem.app.last_deploy_at.to_s(:micro) : 'n/a' | ||
| 28 | + %td.count= link_to problem.notices.count, app_err_path(problem.app, problem) | ||
| 29 | + %td.resolve= link_to image_tag("thumbs-up.png"), resolve_app_err_path(problem.app, problem), :title => "Resolve", :method => :put, :confirm => err_confirm, :class => 'resolve' if problem.unresolved? | ||
| 30 | - if errs.none? | 30 | - if errs.none? |
| 31 | %tr | 31 | %tr |
| 32 | %td{:colspan => (@app ? 5 : 6)} | 32 | %td{:colspan => (@app ? 5 : 6)} |
| 33 | %em No errs here | 33 | %em No errs here |
| 34 | - = will_paginate @errs, :previous_label => '« Previous', :next_label => 'Next »' | 34 | + = will_paginate @problems, :previous_label => '« Previous', :next_label => 'Next »' |
| 35 | .tab-bar | 35 | .tab-bar |
| 36 | %ul | 36 | %ul |
| 37 | %li= submit_tag 'Resolve', :id => 'resolve_errs', :class => 'button', 'data-action' => resolve_several_errs_path | 37 | %li= submit_tag 'Resolve', :id => 'resolve_errs', :class => 'button', 'data-action' => resolve_several_errs_path |
app/views/errs/all.html.haml
| 1 | - content_for :title, 'All Errs' | 1 | - content_for :title, 'All Errs' |
| 2 | - content_for :action_bar do | 2 | - content_for :action_bar do |
| 3 | = link_to 'hide resolved', errs_path, :class => 'button' | 3 | = link_to 'hide resolved', errs_path, :class => 'button' |
| 4 | -= render 'table', :errs => @errs | ||
| 5 | \ No newline at end of file | 4 | \ No newline at end of file |
| 5 | += render 'table', :errs => @problems | ||
| 6 | \ No newline at end of file | 6 | \ No newline at end of file |
app/views/errs/index.html.haml
| @@ -3,4 +3,4 @@ | @@ -3,4 +3,4 @@ | ||
| 3 | = auto_discovery_link_tag :atom, errs_url(User.token_authentication_key => current_user.authentication_token, :format => "atom"), :title => "Errbit notices at #{root_url}" | 3 | = auto_discovery_link_tag :atom, errs_url(User.token_authentication_key => current_user.authentication_token, :format => "atom"), :title => "Errbit notices at #{root_url}" |
| 4 | - content_for :action_bar do | 4 | - content_for :action_bar do |
| 5 | = link_to 'show resolved', all_errs_path, :class => 'button' | 5 | = link_to 'show resolved', all_errs_path, :class => 'button' |
| 6 | -= render 'table', :errs => @errs | ||
| 7 | \ No newline at end of file | 6 | \ No newline at end of file |
| 7 | += render 'table', :errs => @problems | ||
| 8 | \ No newline at end of file | 8 | \ No newline at end of file |
app/views/errs/show.html.haml
| 1 | -- content_for :page_title, @err.message | ||
| 2 | -- content_for :title, @err.klass | 1 | +- content_for :page_title, @problem.message |
| 2 | +- content_for :title, @problem.klass | ||
| 3 | - content_for :meta do | 3 | - content_for :meta do |
| 4 | %strong App: | 4 | %strong App: |
| 5 | = link_to @app.name, app_path(@app) | 5 | = link_to @app.name, app_path(@app) |
| 6 | %strong Where: | 6 | %strong Where: |
| 7 | - = @err.where | 7 | + = @problem.where |
| 8 | %br | 8 | %br |
| 9 | %strong Environment: | 9 | %strong Environment: |
| 10 | - = @err.environment | 10 | + = @problem.environment |
| 11 | %strong Last Notice: | 11 | %strong Last Notice: |
| 12 | - = last_notice_at(@err).to_s(:micro) | 12 | + = last_notice_at(@problem).to_s(:micro) |
| 13 | - content_for :action_bar do | 13 | - content_for :action_bar do |
| 14 | - - if @err.app.issue_tracker_configured? | ||
| 15 | - - if @err.issue_link.blank? | ||
| 16 | - %span= link_to 'create issue', create_issue_app_err_path(@app, @err), :method => :post, :class => "#{@app.issue_tracker.class::Label}_create create-issue" | 14 | + - if @problem.app.issue_tracker_configured? |
| 15 | + - if @problem.issue_link.blank? | ||
| 16 | + %span= link_to 'create issue', create_issue_app_err_path(@app, @problem), :method => :post, :class => "#{@app.issue_tracker.class::Label}_create create-issue" | ||
| 17 | - else | 17 | - else |
| 18 | - %span= link_to 'go to issue', @err.issue_link, :target => "_blank", :class => "#{@app.issue_tracker.class::Label}_goto goto-issue" | ||
| 19 | - = link_to 'unlink issue', unlink_issue_app_err_path(@app, @err), :method => :delete, :confirm => "Unlink err issues?", :class => "unlink-issue" | ||
| 20 | - - if @err.unresolved? | ||
| 21 | - %span= link_to 'resolve', resolve_app_err_path(@app, @err), :method => :put, :confirm => err_confirm, :class => 'resolve' | 18 | + %span= link_to 'go to issue', @problem.issue_link, :class => "#{@app.issue_tracker.class::Label}_goto goto-issue" |
| 19 | + = link_to 'unlink issue', unlink_issue_app_err_path(@app, @problem), :method => :delete, :confirm => "Unlink err issues?", :class => "unlink-issue" | ||
| 20 | + - if @problem.unresolved? | ||
| 21 | + %span= link_to 'resolve', resolve_app_err_path(@app, @problem), :method => :put, :confirm => err_confirm, :class => 'resolve' | ||
| 22 | 22 | ||
| 23 | -- if !@app.issue_tracker_configured? || @err.comments.any? | 23 | +- if !@app.issue_tracker_configured? || @problem.comments.any? |
| 24 | - content_for :comments do | 24 | - content_for :comments do |
| 25 | %h3 Comments on this Err | 25 | %h3 Comments on this Err |
| 26 | - - @err.comments.each do |comment| | 26 | + - @problem.comments.each do |comment| |
| 27 | .window | 27 | .window |
| 28 | %table.comment | 28 | %table.comment |
| 29 | %tr | 29 | %tr |
| 30 | %th | 30 | %th |
| 31 | - %span= link_to '✘'.html_safe, destroy_comment_app_err_path(@app, @err) << "?comment_id=#{comment.id}", :method => :delete, :confirm => "Are sure you don't need this comment?", :class => "destroy-comment" | 31 | + %span= link_to '✘'.html_safe, destroy_comment_app_err_path(@app, @problem) << "?comment_id=#{comment.id}", :method => :delete, :confirm => "Are sure you don't need this comment?", :class => "destroy-comment" |
| 32 | = time_ago_in_words(comment.created_at, true) << " ago by " | 32 | = time_ago_in_words(comment.created_at, true) << " ago by " |
| 33 | = link_to comment.user.email, user_path(comment.user) | 33 | = link_to comment.user.email, user_path(comment.user) |
| 34 | %tr | 34 | %tr |
| 35 | %td= comment.body.gsub("\n", "<br>").html_safe | 35 | %td= comment.body.gsub("\n", "<br>").html_safe |
| 36 | - unless @app.issue_tracker_configured? | 36 | - unless @app.issue_tracker_configured? |
| 37 | - = form_for @comment, :url => create_comment_app_err_path(@app, @err) do |comment_form| | 37 | + = form_for @comment, :url => create_comment_app_err_path(@app, @problem) do |comment_form| |
| 38 | %p Add a comment | 38 | %p Add a comment |
| 39 | = comment_form.text_area :body, :style => "width: 420px; height: 80px;" | 39 | = comment_form.text_area :body, :style => "width: 420px; height: 80px;" |
| 40 | = comment_form.submit "Save Comment" | 40 | = comment_form.submit "Save Comment" |
app/views/issue_trackers/fogbugz_body.txt.erb
| 1 | -"See this exception on Errbit": <%= app_err_url(err.app, err) %> | ||
| 2 | -<% if notice = err.notices.first %> | 1 | +"See this exception on Errbit": <%= app_err_url(problem.app, problem) %> |
| 2 | +<% if notice = problem.notices.first %> | ||
| 3 | <%= notice.message %> | 3 | <%= notice.message %> |
| 4 | 4 | ||
| 5 | Summary | 5 | Summary |
| 6 | - Where | 6 | - Where |
| 7 | - <%= notice.err.where %> | 7 | + <%= notice.where %> |
| 8 | 8 | ||
| 9 | - Occured | 9 | - Occured |
| 10 | <%= notice.created_at.to_s(:micro) %> | 10 | <%= notice.created_at.to_s(:micro) %> |
| 11 | 11 | ||
| 12 | - Similar | 12 | - Similar |
| 13 | - <%= (notice.err.notices_count - 1).to_s %> | 13 | + <%= (notice.problem.notices_count - 1).to_s %> |
| 14 | 14 | ||
| 15 | Params | 15 | Params |
| 16 | <%= pretty_hash(notice.params) %> | 16 | <%= pretty_hash(notice.params) %> |
app/views/issue_trackers/github_issues_body.txt.erb
| @@ -7,13 +7,13 @@ | @@ -7,13 +7,13 @@ | ||
| 7 | [<%= notice.request['url'] %>](<%= notice.request['url'] %>)" | 7 | [<%= notice.request['url'] %>](<%= notice.request['url'] %>)" |
| 8 | <% end %> | 8 | <% end %> |
| 9 | ### Where ### | 9 | ### Where ### |
| 10 | -<%= notice.err.where %> | 10 | +<%= notice.where %> |
| 11 | 11 | ||
| 12 | ### Occured ### | 12 | ### Occured ### |
| 13 | <%= notice.created_at.to_s(:micro) %> | 13 | <%= notice.created_at.to_s(:micro) %> |
| 14 | 14 | ||
| 15 | ### Similar ### | 15 | ### Similar ### |
| 16 | -<%= (notice.err.notices_count - 1).to_s %> | 16 | +<%= (notice.problem.notices_count - 1).to_s %> |
| 17 | 17 | ||
| 18 | ## Params ## | 18 | ## Params ## |
| 19 | ``` | 19 | ``` |
app/views/issue_trackers/lighthouseapp_body.txt.erb
| 1 | -[See this exception on Errbit](<%= app_err_url err.app, err %> "See this exception on Errbit") | ||
| 2 | -<% if notice = err.notices.first %> | 1 | +[See this exception on Errbit](<%= app_err_url problem.app, problem %> "See this exception on Errbit") |
| 2 | +<% if notice = problem.notices.first %> | ||
| 3 | # <%= notice.message %> # | 3 | # <%= notice.message %> # |
| 4 | ## Summary ## | 4 | ## Summary ## |
| 5 | <% if notice.request['url'].present? %> | 5 | <% if notice.request['url'].present? %> |
| @@ -7,13 +7,13 @@ | @@ -7,13 +7,13 @@ | ||
| 7 | [<%= notice.request['url'] %>](<%= notice.request['url'] %>)" | 7 | [<%= notice.request['url'] %>](<%= notice.request['url'] %>)" |
| 8 | <% end %> | 8 | <% end %> |
| 9 | ### Where ### | 9 | ### Where ### |
| 10 | - <%= notice.err.where %> | 10 | + <%= notice.where %> |
| 11 | 11 | ||
| 12 | ### Occured ### | 12 | ### Occured ### |
| 13 | <%= notice.created_at.to_s(:micro) %> | 13 | <%= notice.created_at.to_s(:micro) %> |
| 14 | 14 | ||
| 15 | ### Similar ### | 15 | ### Similar ### |
| 16 | - <%= (notice.err.notices_count - 1).to_s %> | 16 | + <%= (notice.problem.notices_count - 1).to_s %> |
| 17 | 17 | ||
| 18 | ## Params ## | 18 | ## Params ## |
| 19 | <code><%= pretty_hash(notice.params) %></code> | 19 | <code><%= pretty_hash(notice.params) %></code> |
app/views/issue_trackers/pivotal_body.txt.erb
| 1 | -See this exception on Errbit: <%= app_err_url err.app, err %> | ||
| 2 | -<% if notice = err.notices.first %> | 1 | +See this exception on Errbit: <%= app_err_url problem.app, problem %> |
| 2 | +<% if notice = problem.notices.first %> | ||
| 3 | <% if notice.request['url'].present? %>URL: <%= notice.request['url'] %><% end %> | 3 | <% if notice.request['url'].present? %>URL: <%= notice.request['url'] %><% end %> |
| 4 | - Where: <%= notice.err.where %> | 4 | + Where: <%= notice.where %> |
| 5 | Occurred: <%= notice.created_at.to_s :micro %> | 5 | Occurred: <%= notice.created_at.to_s :micro %> |
| 6 | - Similar: <%= (notice.err.notices.count - 1).to_s %> | 6 | + Similar: <%= (notice.problem.notices.count - 1).to_s %> |
| 7 | 7 | ||
| 8 | Params: | 8 | Params: |
| 9 | <%= pretty_hash notice.params %> | 9 | <%= pretty_hash notice.params %> |
app/views/issue_trackers/textile_body.txt.erb
| 1 | -<% if notice = err.notices.first %> | 1 | +<% if notice = problem.notices.first %> |
| 2 | h1. <%= notice.message %> | 2 | h1. <%= notice.message %> |
| 3 | 3 | ||
| 4 | -h3. "See this exception on Errbit":<%= app_err_url err.app, err %> | 4 | +h3. "See this exception on Errbit":<%= app_err_url problem.app, problem %> |
| 5 | 5 | ||
| 6 | h2. Summary | 6 | h2. Summary |
| 7 | <% if notice.request['url'].present? %> | 7 | <% if notice.request['url'].present? %> |
| @@ -11,7 +11,7 @@ h3. URL | @@ -11,7 +11,7 @@ h3. URL | ||
| 11 | <% end %> | 11 | <% end %> |
| 12 | h3. Where | 12 | h3. Where |
| 13 | 13 | ||
| 14 | -<%= notice.err.where %> | 14 | +<%= notice.where %> |
| 15 | 15 | ||
| 16 | h3. Occurred | 16 | h3. Occurred |
| 17 | 17 | ||
| @@ -19,7 +19,7 @@ h3. Occurred | @@ -19,7 +19,7 @@ h3. Occurred | ||
| 19 | 19 | ||
| 20 | h3. Similar | 20 | h3. Similar |
| 21 | 21 | ||
| 22 | -<%= (notice.err.notices_count - 1).to_s %> | 22 | +<%= (notice.problem.notices_count - 1).to_s %> |
| 23 | 23 | ||
| 24 | h2. Params | 24 | h2. Params |
| 25 | 25 |
app/views/mailer/err_notification.html.haml
| @@ -9,12 +9,12 @@ | @@ -9,12 +9,12 @@ | ||
| 9 | An err has just occurred in | 9 | An err has just occurred in |
| 10 | = link_to(@app.name, app_url(@app), :class => "bold") << "," | 10 | = link_to(@app.name, app_url(@app), :class => "bold") << "," |
| 11 | on the | 11 | on the |
| 12 | - %span.bold= @notice.err.environment | 12 | + %span.bold= @notice.environment_name |
| 13 | environment. | 13 | environment. |
| 14 | %br | 14 | %br |
| 15 | - This err has occurred #{pluralize @notice.err.notices_count, 'time'}. | 15 | + This err has occurred #{pluralize @notice.problem.notices_count, 'time'}. |
| 16 | %p | 16 | %p |
| 17 | - = link_to("Click here to view the error on Errbit", app_err_url(@app, @notice.err), :class => "bold") << "." | 17 | + = link_to("Click here to view the error on Errbit", app_err_url(@app, @notice.problem), :class => "bold") << "." |
| 18 | %tr | 18 | %tr |
| 19 | %td.section | 19 | %td.section |
| 20 | %table(cellpadding="0" cellspacing="0" border="0" align="left") | 20 | %table(cellpadding="0" cellspacing="0" border="0" align="left") |
| @@ -23,10 +23,10 @@ | @@ -23,10 +23,10 @@ | ||
| 23 | %td.content(valign="top") | 23 | %td.content(valign="top") |
| 24 | %div | 24 | %div |
| 25 | %p.heading ERROR MESSAGE: | 25 | %p.heading ERROR MESSAGE: |
| 26 | - %p= @notice.err.message | 26 | + %p= @notice.message |
| 27 | %p.heading WHERE: | 27 | %p.heading WHERE: |
| 28 | %p.monospace | 28 | %p.monospace |
| 29 | - = @notice.err.where | 29 | + = @notice.where |
| 30 | - if (app_backtrace = @notice.app_backtrace).any? | 30 | - if (app_backtrace = @notice.app_backtrace).any? |
| 31 | - app_backtrace.map {|l| "#{l['file']}:#{l['number']}" }.each do |line| | 31 | - app_backtrace.map {|l| "#{l['file']}:#{l['number']}" }.each do |line| |
| 32 | %p.backtrace= line | 32 | %p.backtrace= line |
app/views/mailer/err_notification.text.erb
| 1 | -An err has just occurred in <%= @app.name %>, on the <%= @notice.err.environment %> environment. | 1 | +An err has just occurred in <%= @notice.environment_name %>: <%= raw(@notice.message) %> |
| 2 | 2 | ||
| 3 | -This err has occurred <%= pluralize @notice.err.notices_count, 'time' %>. | 3 | +This err has occurred <%= pluralize @notice.problem.notices_count, 'time' %>. You should really look into it here: |
| 4 | 4 | ||
| 5 | -You can view it on Errbit here: <%= app_err_url(@app, @notice.err) %> | 5 | + <%= app_err_url(@app, @notice.problem) %> |
| 6 | 6 | ||
| 7 | 7 | ||
| 8 | ERROR MESSAGE: | 8 | ERROR MESSAGE: |
| 9 | 9 | ||
| 10 | -<%= raw(@notice.err.message) %> | 10 | +<%= raw(@notice.message) %> |
| 11 | 11 | ||
| 12 | 12 | ||
| 13 | WHERE: | 13 | WHERE: |
| 14 | 14 | ||
| 15 | -<%= @notice.err.where %> | 15 | +<%= @notice.where %> |
| 16 | 16 | ||
| 17 | <% @notice.app_backtrace.map {|l| "#{l['file']}:#{l['number']}" }.each do |line| %> | 17 | <% @notice.app_backtrace.map {|l| "#{l['file']}:#{l['number']}" }.each do |line| %> |
| 18 | <%= line %> | 18 | <%= line %> |
app/views/notices/_atom_entry.html.haml
| @@ -6,13 +6,13 @@ | @@ -6,13 +6,13 @@ | ||
| 6 | = link_to(notice.request['url'], notice.request['url']) | 6 | = link_to(notice.request['url'], notice.request['url']) |
| 7 | %p | 7 | %p |
| 8 | %strong Where: | 8 | %strong Where: |
| 9 | - = notice.err.where | 9 | + = notice.where |
| 10 | %p | 10 | %p |
| 11 | %strong Occured: | 11 | %strong Occured: |
| 12 | = notice.created_at.to_s(:micro) | 12 | = notice.created_at.to_s(:micro) |
| 13 | %p | 13 | %p |
| 14 | %strong Similar: | 14 | %strong Similar: |
| 15 | - = notice.err.notices_count - 1 | 15 | + = notice.problem.notices_count - 1 |
| 16 | 16 | ||
| 17 | %h3 Params | 17 | %h3 Params |
| 18 | %p= pretty_hash(notice.params) | 18 | %p= pretty_hash(notice.params) |
app/views/notices/_summary.html.haml
| @@ -9,13 +9,13 @@ | @@ -9,13 +9,13 @@ | ||
| 9 | %td.nowrap= link_to notice.request['url'], notice.request['url'] | 9 | %td.nowrap= link_to notice.request['url'], notice.request['url'] |
| 10 | %tr | 10 | %tr |
| 11 | %th Where | 11 | %th Where |
| 12 | - %td= notice.err.where | 12 | + %td= notice.where |
| 13 | %tr | 13 | %tr |
| 14 | %th Occurred | 14 | %th Occurred |
| 15 | %td= notice.created_at.to_s(:micro) | 15 | %td= notice.created_at.to_s(:micro) |
| 16 | %tr | 16 | %tr |
| 17 | %th Similar | 17 | %th Similar |
| 18 | - %td= notice.err.notices.count - 1 | 18 | + %td= notice.problem.notices.count - 1 |
| 19 | %tr | 19 | %tr |
| 20 | %th Browser | 20 | %th Browser |
| 21 | - %td= user_agent_graph(notice.err) | 21 | + %td= user_agent_graph(notice.problem) |
db/migrate/20110422152027_move_notices_to_separate_collection.rb
| @@ -17,10 +17,9 @@ class MoveNoticesToSeparateCollection < Mongoid::Migration | @@ -17,10 +17,9 @@ class MoveNoticesToSeparateCollection < Mongoid::Migration | ||
| 17 | mongo_db.collection("errs").update({ "_id" => err['_id']}, { "$unset" => { "notices" => 1}}) | 17 | mongo_db.collection("errs").update({ "_id" => err['_id']}, { "$unset" => { "notices" => 1}}) |
| 18 | end | 18 | end |
| 19 | Rake::Task["errbit:db:update_notices_count"].invoke | 19 | Rake::Task["errbit:db:update_notices_count"].invoke |
| 20 | - Rake::Task["errbit:db:update_err_message"].invoke | 20 | + Rake::Task["errbit:db:update_problem_attrs"].invoke |
| 21 | end | 21 | end |
| 22 | - | 22 | + |
| 23 | def self.down | 23 | def self.down |
| 24 | end | 24 | end |
| 25 | - | ||
| 26 | end | 25 | end |
| @@ -0,0 +1,27 @@ | @@ -0,0 +1,27 @@ | ||
| 1 | +class LinkErrsToProblems < Mongoid::Migration | ||
| 2 | + def self.up | ||
| 3 | + | ||
| 4 | + # Copy err.klass to notice.klass | ||
| 5 | + Notice.all.each do |notice| | ||
| 6 | + if notice.err && (klass = notice.err['klass']) | ||
| 7 | + notice.update_attribute(:klass, klass) | ||
| 8 | + end | ||
| 9 | + end | ||
| 10 | + | ||
| 11 | + # Create a Problem for each Err | ||
| 12 | + Err.all.each do |err| | ||
| 13 | + app_id = err['app_id'] | ||
| 14 | + app = app_id && App.find(app_id) | ||
| 15 | + if app | ||
| 16 | + err.problem = app.problems.create | ||
| 17 | + err.save | ||
| 18 | + end | ||
| 19 | + end | ||
| 20 | + | ||
| 21 | + Rake::Task["errbit:db:update_notices_count"].invoke | ||
| 22 | + Rake::Task["errbit:db:update_problem_attrs"].invoke | ||
| 23 | + end | ||
| 24 | + | ||
| 25 | + def self.down | ||
| 26 | + end | ||
| 27 | +end | ||
| 0 | \ No newline at end of file | 28 | \ No newline at end of file |
lib/tasks/errbit/database.rake
| 1 | namespace :errbit do | 1 | namespace :errbit do |
| 2 | namespace :db do | 2 | namespace :db do |
| 3 | - desc "Updates Err#notices_count" | ||
| 4 | - task :update_err_message => :environment do | ||
| 5 | - puts "Updating err.message" | ||
| 6 | - Err.all.each do |e| | ||
| 7 | - e.update_attributes(:message => e.notices.first.message) if e.notices.first | ||
| 8 | - end | 3 | + |
| 4 | + desc "Updates cached attributes on Problem" | ||
| 5 | + task :update_problem_attrs => :environment do | ||
| 6 | + puts "Updating problems" | ||
| 7 | + Problem.all.each(&:cache_notice_attributes) | ||
| 9 | end | 8 | end |
| 10 | - | ||
| 11 | - desc "Updates Err#notices_count" | 9 | + |
| 10 | + desc "Updates Problem#notices_count" | ||
| 12 | task :update_notices_count => :environment do | 11 | task :update_notices_count => :environment do |
| 13 | - puts "Updating err.notices_count" | ||
| 14 | - Err.all.each do |e| | ||
| 15 | - e.update_attributes(:notices_count => e.notices.count) | 12 | + puts "Updating problem.notices_count" |
| 13 | + Problem.all.each do |p| | ||
| 14 | + p.update_attributes(:notices_count => p.notices.count) | ||
| 16 | end | 15 | end |
| 17 | end | 16 | end |
| 18 | - | 17 | + |
| 19 | desc "Delete resolved errors from the database. (Useful for limited heroku databases)" | 18 | desc "Delete resolved errors from the database. (Useful for limited heroku databases)" |
| 20 | task :clear_resolved => :environment do | 19 | task :clear_resolved => :environment do |
| 21 | - count = Err.resolved.count | ||
| 22 | - Err.resolved.each {|err| err.destroy } | 20 | + count = Problem.resolved.count |
| 21 | + Problem.resolved.each {|problem| problem.destroy } | ||
| 23 | puts "=== Cleared #{count} resolved errors from the database." if count > 0 | 22 | puts "=== Cleared #{count} resolved errors from the database." if count > 0 |
| 24 | end | 23 | end |
| 25 | end | 24 | end |
| 26 | end | 25 | end |
| 27 | - |
lib/tasks/errbit/demo.rake
| @@ -4,7 +4,7 @@ namespace :errbit do | @@ -4,7 +4,7 @@ namespace :errbit do | ||
| 4 | require 'factory_girl_rails' | 4 | require 'factory_girl_rails' |
| 5 | Dir.glob(File.join(Rails.root,'spec/factories/*.rb')).each {|f| require f } | 5 | Dir.glob(File.join(Rails.root,'spec/factories/*.rb')).each {|f| require f } |
| 6 | app = Factory(:app, :name => "Demo App #{Time.now.strftime("%N")}") | 6 | app = Factory(:app, :name => "Demo App #{Time.now.strftime("%N")}") |
| 7 | - Factory(:notice, :err => Factory(:err, :app => app)) | 7 | + Factory(:notice, :err => Factory(:err, :problem => Factory(:problem, :app => app))) |
| 8 | puts "=== Created demo app: '#{app.name}', with an example error." | 8 | puts "=== Created demo app: '#{app.name}', with an example error." |
| 9 | end | 9 | end |
| 10 | end | 10 | end |
public/javascripts/application.js
| @@ -62,7 +62,7 @@ $(function() { | @@ -62,7 +62,7 @@ $(function() { | ||
| 62 | function activateSelectableRows() { | 62 | function activateSelectableRows() { |
| 63 | $('.selectable tr').click(function(event) { | 63 | $('.selectable tr').click(function(event) { |
| 64 | if(!_.include(['A', 'INPUT', 'BUTTON', 'TEXTAREA'], event.target.nodeName)) { | 64 | if(!_.include(['A', 'INPUT', 'BUTTON', 'TEXTAREA'], event.target.nodeName)) { |
| 65 | - var checkbox = $(this).find('input[name="errs[]"]'); | 65 | + var checkbox = $(this).find('input[name="problems[]"]'); |
| 66 | checkbox.attr('checked', !checkbox.is(':checked')); | 66 | checkbox.attr('checked', !checkbox.is(':checked')); |
| 67 | } | 67 | } |
| 68 | }) | 68 | }) |
spec/controllers/apps_controller_spec.rb
| @@ -2,10 +2,11 @@ require 'spec_helper' | @@ -2,10 +2,11 @@ require 'spec_helper' | ||
| 2 | 2 | ||
| 3 | describe AppsController do | 3 | describe AppsController do |
| 4 | render_views | 4 | render_views |
| 5 | - | 5 | + |
| 6 | it_requires_authentication | 6 | it_requires_authentication |
| 7 | it_requires_admin_privileges :for => {:new => :get, :edit => :get, :create => :post, :update => :put, :destroy => :delete} | 7 | it_requires_admin_privileges :for => {:new => :get, :edit => :get, :create => :post, :update => :put, :destroy => :delete} |
| 8 | - | 8 | + |
| 9 | + | ||
| 9 | describe "GET /apps" do | 10 | describe "GET /apps" do |
| 10 | context 'when logged in as an admin' do | 11 | context 'when logged in as an admin' do |
| 11 | it 'finds all apps' do | 12 | it 'finds all apps' do |
| @@ -16,7 +17,7 @@ describe AppsController do | @@ -16,7 +17,7 @@ describe AppsController do | ||
| 16 | assigns(:apps).should == apps | 17 | assigns(:apps).should == apps |
| 17 | end | 18 | end |
| 18 | end | 19 | end |
| 19 | - | 20 | + |
| 20 | context 'when logged in as a regular user' do | 21 | context 'when logged in as a regular user' do |
| 21 | it 'finds apps the user is watching' do | 22 | it 'finds apps the user is watching' do |
| 22 | sign_in(user = Factory(:user)) | 23 | sign_in(user = Factory(:user)) |
| @@ -31,116 +32,116 @@ describe AppsController do | @@ -31,116 +32,116 @@ describe AppsController do | ||
| 31 | end | 32 | end |
| 32 | end | 33 | end |
| 33 | end | 34 | end |
| 34 | - | 35 | + |
| 35 | describe "GET /apps/:id" do | 36 | describe "GET /apps/:id" do |
| 36 | context 'logged in as an admin' do | 37 | context 'logged in as an admin' do |
| 37 | before(:each) do | 38 | before(:each) do |
| 38 | @user = Factory(:admin) | 39 | @user = Factory(:admin) |
| 39 | sign_in @user | 40 | sign_in @user |
| 40 | @app = Factory(:app) | 41 | @app = Factory(:app) |
| 41 | - @err = Factory :err, :app => @app | ||
| 42 | - @notice = Factory :notice, :err => @err | 42 | + @problem = Factory(:notice, :err => Factory(:err, :problem => Factory(:problem, :app => @app))).problem |
| 43 | end | 43 | end |
| 44 | - | 44 | + |
| 45 | it 'finds the app' do | 45 | it 'finds the app' do |
| 46 | get :show, :id => @app.id | 46 | get :show, :id => @app.id |
| 47 | assigns(:app).should == @app | 47 | assigns(:app).should == @app |
| 48 | end | 48 | end |
| 49 | - | 49 | + |
| 50 | it "should not raise errors for app with err without notices" do | 50 | it "should not raise errors for app with err without notices" do |
| 51 | - Factory :err, :app => @app | 51 | + Factory(:err, :problem => Factory(:problem, :app => @app)) |
| 52 | lambda { get :show, :id => @app.id }.should_not raise_error | 52 | lambda { get :show, :id => @app.id }.should_not raise_error |
| 53 | end | 53 | end |
| 54 | - | 54 | + |
| 55 | it "should list atom feed successfully" do | 55 | it "should list atom feed successfully" do |
| 56 | get :show, :id => @app.id, :format => "atom" | 56 | get :show, :id => @app.id, :format => "atom" |
| 57 | response.should be_success | 57 | response.should be_success |
| 58 | - response.body.should match(@err.message) | 58 | + response.body.should match(@problem.message) |
| 59 | end | 59 | end |
| 60 | - | 60 | + |
| 61 | context "pagination" do | 61 | context "pagination" do |
| 62 | before(:each) do | 62 | before(:each) do |
| 63 | - 35.times { Factory :err, :app => @app } | 63 | + 35.times { Factory(:err, :problem => Factory(:problem, :app => @app)) } |
| 64 | end | 64 | end |
| 65 | - | 65 | + |
| 66 | it "should have default per_page value for user" do | 66 | it "should have default per_page value for user" do |
| 67 | get :show, :id => @app.id | 67 | get :show, :id => @app.id |
| 68 | - assigns(:errs).size.should == User::PER_PAGE | 68 | + assigns(:problems).size.should == User::PER_PAGE |
| 69 | end | 69 | end |
| 70 | - | 70 | + |
| 71 | it "should be able to override default per_page value" do | 71 | it "should be able to override default per_page value" do |
| 72 | @user.update_attribute :per_page, 10 | 72 | @user.update_attribute :per_page, 10 |
| 73 | get :show, :id => @app.id | 73 | get :show, :id => @app.id |
| 74 | - assigns(:errs).size.should == 10 | 74 | + assigns(:problems).size.should == 10 |
| 75 | end | 75 | end |
| 76 | end | 76 | end |
| 77 | - | 77 | + |
| 78 | context 'with resolved errors' do | 78 | context 'with resolved errors' do |
| 79 | before(:each) do | 79 | before(:each) do |
| 80 | - resolved_err = Factory.create(:err, :app => @app, :resolved => true) | ||
| 81 | - Factory.create(:notice, :err => resolved_err) | 80 | + resolved_problem = Factory(:problem, :app => @app) |
| 81 | + Factory(:notice, :err => Factory(:err, :problem => resolved_problem)) | ||
| 82 | + resolved_problem.resolve! | ||
| 82 | end | 83 | end |
| 83 | - | 84 | + |
| 84 | context 'and no params' do | 85 | context 'and no params' do |
| 85 | - it 'shows only unresolved errs' do | 86 | + it 'shows only unresolved problems' do |
| 86 | get :show, :id => @app.id | 87 | get :show, :id => @app.id |
| 87 | - assigns(:errs).size.should == 1 | 88 | + assigns(:problems).size.should == 1 |
| 88 | end | 89 | end |
| 89 | end | 90 | end |
| 90 | - | ||
| 91 | - context 'and all_errs=true params' do | 91 | + |
| 92 | + context 'and all_problems=true params' do | ||
| 92 | it 'shows all errors' do | 93 | it 'shows all errors' do |
| 93 | get :show, :id => @app.id, :all_errs => true | 94 | get :show, :id => @app.id, :all_errs => true |
| 94 | - assigns(:errs).size.should == 2 | 95 | + assigns(:problems).size.should == 2 |
| 95 | end | 96 | end |
| 96 | end | 97 | end |
| 97 | end | 98 | end |
| 98 | - | 99 | + |
| 99 | context 'with environment filters' do | 100 | context 'with environment filters' do |
| 100 | before(:each) do | 101 | before(:each) do |
| 101 | environments = ['production', 'test', 'development', 'staging'] | 102 | environments = ['production', 'test', 'development', 'staging'] |
| 102 | 20.times do |i| | 103 | 20.times do |i| |
| 103 | - Factory.create(:err, :app => @app, :environment => environments[i % environments.length]) | 104 | + Factory.create(:problem, :app => @app, :environment => environments[i % environments.length]) |
| 104 | end | 105 | end |
| 105 | end | 106 | end |
| 106 | - | 107 | + |
| 107 | context 'no params' do | 108 | context 'no params' do |
| 108 | it 'shows errs for all environments' do | 109 | it 'shows errs for all environments' do |
| 109 | get :show, :id => @app.id | 110 | get :show, :id => @app.id |
| 110 | - assigns(:errs).size.should == 21 | 111 | + assigns(:problems).size.should == 21 |
| 111 | end | 112 | end |
| 112 | end | 113 | end |
| 113 | - | 114 | + |
| 114 | context 'environment production' do | 115 | context 'environment production' do |
| 115 | it 'shows errs for just production' do | 116 | it 'shows errs for just production' do |
| 116 | - get :show, :id => @app.id, :environment => :production | ||
| 117 | - assigns(:errs).size.should == 6 | 117 | + get :show, :id => @app.id, :environment => 'production' |
| 118 | + assigns(:problems).size.should == 6 | ||
| 118 | end | 119 | end |
| 119 | end | 120 | end |
| 120 | - | 121 | + |
| 121 | context 'environment staging' do | 122 | context 'environment staging' do |
| 122 | it 'shows errs for just staging' do | 123 | it 'shows errs for just staging' do |
| 123 | - get :show, :id => @app.id, :environment => :staging | ||
| 124 | - assigns(:errs).size.should == 5 | 124 | + get :show, :id => @app.id, :environment => 'staging' |
| 125 | + assigns(:problems).size.should == 5 | ||
| 125 | end | 126 | end |
| 126 | end | 127 | end |
| 127 | - | 128 | + |
| 128 | context 'environment development' do | 129 | context 'environment development' do |
| 129 | it 'shows errs for just development' do | 130 | it 'shows errs for just development' do |
| 130 | - get :show, :id => @app.id, :environment => :development | ||
| 131 | - assigns(:errs).size.should == 5 | 131 | + get :show, :id => @app.id, :environment => 'development' |
| 132 | + assigns(:problems).size.should == 5 | ||
| 132 | end | 133 | end |
| 133 | end | 134 | end |
| 134 | - | 135 | + |
| 135 | context 'environment test' do | 136 | context 'environment test' do |
| 136 | it 'shows errs for just test' do | 137 | it 'shows errs for just test' do |
| 137 | - get :show, :id => @app.id, :environment => :test | ||
| 138 | - assigns(:errs).size.should == 5 | 138 | + get :show, :id => @app.id, :environment => 'test' |
| 139 | + assigns(:problems).size.should == 5 | ||
| 139 | end | 140 | end |
| 140 | end | 141 | end |
| 141 | end | 142 | end |
| 142 | end | 143 | end |
| 143 | - | 144 | + |
| 144 | context 'logged in as a user' do | 145 | context 'logged in as a user' do |
| 145 | it 'finds the app if the user is watching it' do | 146 | it 'finds the app if the user is watching it' do |
| 146 | user = Factory(:user) | 147 | user = Factory(:user) |
| @@ -150,7 +151,7 @@ describe AppsController do | @@ -150,7 +151,7 @@ describe AppsController do | ||
| 150 | get :show, :id => app.id | 151 | get :show, :id => app.id |
| 151 | assigns(:app).should == app | 152 | assigns(:app).should == app |
| 152 | end | 153 | end |
| 153 | - | 154 | + |
| 154 | it 'does not find the app if the user is not watching it' do | 155 | it 'does not find the app if the user is not watching it' do |
| 155 | sign_in Factory(:user) | 156 | sign_in Factory(:user) |
| 156 | app = Factory(:app) | 157 | app = Factory(:app) |
| @@ -160,12 +161,12 @@ describe AppsController do | @@ -160,12 +161,12 @@ describe AppsController do | ||
| 160 | end | 161 | end |
| 161 | end | 162 | end |
| 162 | end | 163 | end |
| 163 | - | 164 | + |
| 164 | context 'logged in as an admin' do | 165 | context 'logged in as an admin' do |
| 165 | before do | 166 | before do |
| 166 | sign_in Factory(:admin) | 167 | sign_in Factory(:admin) |
| 167 | end | 168 | end |
| 168 | - | 169 | + |
| 169 | describe "GET /apps/new" do | 170 | describe "GET /apps/new" do |
| 170 | it 'instantiates a new app with a prebuilt watcher' do | 171 | it 'instantiates a new app with a prebuilt watcher' do |
| 171 | get :new | 172 | get :new |
| @@ -184,7 +185,7 @@ describe AppsController do | @@ -184,7 +185,7 @@ describe AppsController do | ||
| 184 | assigns(:app).github_url.should == "github.com/test/example" | 185 | assigns(:app).github_url.should == "github.com/test/example" |
| 185 | end | 186 | end |
| 186 | end | 187 | end |
| 187 | - | 188 | + |
| 188 | describe "GET /apps/:id/edit" do | 189 | describe "GET /apps/:id/edit" do |
| 189 | it 'finds the correct app' do | 190 | it 'finds the correct app' do |
| 190 | app = Factory(:app) | 191 | app = Factory(:app) |
| @@ -192,47 +193,47 @@ describe AppsController do | @@ -192,47 +193,47 @@ describe AppsController do | ||
| 192 | assigns(:app).should == app | 193 | assigns(:app).should == app |
| 193 | end | 194 | end |
| 194 | end | 195 | end |
| 195 | - | 196 | + |
| 196 | describe "POST /apps" do | 197 | describe "POST /apps" do |
| 197 | before do | 198 | before do |
| 198 | @app = Factory(:app) | 199 | @app = Factory(:app) |
| 199 | App.stub(:new).and_return(@app) | 200 | App.stub(:new).and_return(@app) |
| 200 | end | 201 | end |
| 201 | - | 202 | + |
| 202 | context "when the create is successful" do | 203 | context "when the create is successful" do |
| 203 | before do | 204 | before do |
| 204 | @app.should_receive(:save).and_return(true) | 205 | @app.should_receive(:save).and_return(true) |
| 205 | end | 206 | end |
| 206 | - | 207 | + |
| 207 | it "should redirect to the app page" do | 208 | it "should redirect to the app page" do |
| 208 | post :create, :app => {} | 209 | post :create, :app => {} |
| 209 | response.should redirect_to(app_path(@app)) | 210 | response.should redirect_to(app_path(@app)) |
| 210 | end | 211 | end |
| 211 | - | 212 | + |
| 212 | it "should display a message" do | 213 | it "should display a message" do |
| 213 | post :create, :app => {} | 214 | post :create, :app => {} |
| 214 | request.flash[:success].should match(/success/) | 215 | request.flash[:success].should match(/success/) |
| 215 | end | 216 | end |
| 216 | end | 217 | end |
| 217 | end | 218 | end |
| 218 | - | 219 | + |
| 219 | describe "PUT /apps/:id" do | 220 | describe "PUT /apps/:id" do |
| 220 | before do | 221 | before do |
| 221 | @app = Factory(:app) | 222 | @app = Factory(:app) |
| 222 | end | 223 | end |
| 223 | - | 224 | + |
| 224 | context "when the update is successful" do | 225 | context "when the update is successful" do |
| 225 | it "should redirect to the app page" do | 226 | it "should redirect to the app page" do |
| 226 | put :update, :id => @app.id, :app => {} | 227 | put :update, :id => @app.id, :app => {} |
| 227 | response.should redirect_to(app_path(@app)) | 228 | response.should redirect_to(app_path(@app)) |
| 228 | end | 229 | end |
| 229 | - | 230 | + |
| 230 | it "should display a message" do | 231 | it "should display a message" do |
| 231 | put :update, :id => @app.id, :app => {} | 232 | put :update, :id => @app.id, :app => {} |
| 232 | request.flash[:success].should match(/success/) | 233 | request.flash[:success].should match(/success/) |
| 233 | end | 234 | end |
| 234 | end | 235 | end |
| 235 | - | 236 | + |
| 236 | context "changing name" do | 237 | context "changing name" do |
| 237 | it "should redirect to app page" do | 238 | it "should redirect to app page" do |
| 238 | id = @app.id | 239 | id = @app.id |
| @@ -240,14 +241,14 @@ describe AppsController do | @@ -240,14 +241,14 @@ describe AppsController do | ||
| 240 | response.should redirect_to(app_path(id)) | 241 | response.should redirect_to(app_path(id)) |
| 241 | end | 242 | end |
| 242 | end | 243 | end |
| 243 | - | 244 | + |
| 244 | context "when the update is unsuccessful" do | 245 | context "when the update is unsuccessful" do |
| 245 | it "should render the edit page" do | 246 | it "should render the edit page" do |
| 246 | put :update, :id => @app.id, :app => { :name => '' } | 247 | put :update, :id => @app.id, :app => { :name => '' } |
| 247 | response.should render_template(:edit) | 248 | response.should render_template(:edit) |
| 248 | end | 249 | end |
| 249 | end | 250 | end |
| 250 | - | 251 | + |
| 251 | context "changing email_at_notices" do | 252 | context "changing email_at_notices" do |
| 252 | it "should parse legal csv values" do | 253 | it "should parse legal csv values" do |
| 253 | put :update, :id => @app.id, :app => { :email_at_notices => '1, 4, 7,8, 10' } | 254 | put :update, :id => @app.id, :app => { :email_at_notices => '1, 4, 7,8, 10' } |
| @@ -261,14 +262,14 @@ describe AppsController do | @@ -261,14 +262,14 @@ describe AppsController do | ||
| 261 | @app.reload | 262 | @app.reload |
| 262 | @app.email_at_notices.should == Errbit::Config.email_at_notices | 263 | @app.email_at_notices.should == Errbit::Config.email_at_notices |
| 263 | end | 264 | end |
| 264 | - | 265 | + |
| 265 | it "should display a message" do | 266 | it "should display a message" do |
| 266 | put :update, :id => @app.id, :app => { :email_at_notices => 'qwertyuiop' } | 267 | put :update, :id => @app.id, :app => { :email_at_notices => 'qwertyuiop' } |
| 267 | request.flash[:error].should match(/Couldn't parse/) | 268 | request.flash[:error].should match(/Couldn't parse/) |
| 268 | end | 269 | end |
| 269 | end | 270 | end |
| 270 | end | 271 | end |
| 271 | - | 272 | + |
| 272 | context "setting up issue tracker", :cur => true do | 273 | context "setting up issue tracker", :cur => true do |
| 273 | context "unknown tracker type" do | 274 | context "unknown tracker type" do |
| 274 | before(:each) do | 275 | before(:each) do |
| @@ -277,12 +278,12 @@ describe AppsController do | @@ -277,12 +278,12 @@ describe AppsController do | ||
| 277 | } } | 278 | } } |
| 278 | @app.reload | 279 | @app.reload |
| 279 | end | 280 | end |
| 280 | - | 281 | + |
| 281 | it "should not create issue tracker" do | 282 | it "should not create issue tracker" do |
| 282 | @app.issue_tracker_configured?.should == false | 283 | @app.issue_tracker_configured?.should == false |
| 283 | end | 284 | end |
| 284 | end | 285 | end |
| 285 | - | 286 | + |
| 286 | IssueTracker.subclasses.each do |tracker_klass| | 287 | IssueTracker.subclasses.each do |tracker_klass| |
| 287 | context tracker_klass do | 288 | context tracker_klass do |
| 288 | it "should save tracker params" do | 289 | it "should save tracker params" do |
| @@ -290,7 +291,7 @@ describe AppsController do | @@ -290,7 +291,7 @@ describe AppsController do | ||
| 290 | params['ticket_properties'] = "card_type = defect" if tracker_klass == MingleTracker | 291 | params['ticket_properties'] = "card_type = defect" if tracker_klass == MingleTracker |
| 291 | params['type'] = tracker_klass.to_s | 292 | params['type'] = tracker_klass.to_s |
| 292 | put :update, :id => @app.id, :app => {:issue_tracker_attributes => params} | 293 | put :update, :id => @app.id, :app => {:issue_tracker_attributes => params} |
| 293 | - | 294 | + |
| 294 | @app.reload | 295 | @app.reload |
| 295 | tracker = @app.issue_tracker | 296 | tracker = @app.issue_tracker |
| 296 | tracker.should be_a(tracker_klass) | 297 | tracker.should be_a(tracker_klass) |
| @@ -301,13 +302,13 @@ describe AppsController do | @@ -301,13 +302,13 @@ describe AppsController do | ||
| 301 | end | 302 | end |
| 302 | end | 303 | end |
| 303 | end | 304 | end |
| 304 | - | 305 | + |
| 305 | it "should show validation notice when sufficient params are not present" do | 306 | it "should show validation notice when sufficient params are not present" do |
| 306 | # Leave out one required param | 307 | # Leave out one required param |
| 307 | params = tracker_klass::Fields[1..-1].inject({}){|hash,f| hash[f[0]] = "test_value"; hash } | 308 | params = tracker_klass::Fields[1..-1].inject({}){|hash,f| hash[f[0]] = "test_value"; hash } |
| 308 | params['type'] = tracker_klass.to_s | 309 | params['type'] = tracker_klass.to_s |
| 309 | put :update, :id => @app.id, :app => {:issue_tracker_attributes => params} | 310 | put :update, :id => @app.id, :app => {:issue_tracker_attributes => params} |
| 310 | - | 311 | + |
| 311 | @app.reload | 312 | @app.reload |
| 312 | @app.issue_tracker_configured?.should == false | 313 | @app.issue_tracker_configured?.should == false |
| 313 | response.body.should match(/You must specify your/) | 314 | response.body.should match(/You must specify your/) |
| @@ -316,34 +317,34 @@ describe AppsController do | @@ -316,34 +317,34 @@ describe AppsController do | ||
| 316 | end | 317 | end |
| 317 | end | 318 | end |
| 318 | end | 319 | end |
| 319 | - | 320 | + |
| 320 | describe "DELETE /apps/:id" do | 321 | describe "DELETE /apps/:id" do |
| 321 | before do | 322 | before do |
| 322 | @app = Factory(:app) | 323 | @app = Factory(:app) |
| 323 | App.stub(:find).with(@app.id).and_return(@app) | 324 | App.stub(:find).with(@app.id).and_return(@app) |
| 324 | end | 325 | end |
| 325 | - | 326 | + |
| 326 | it "should find the app" do | 327 | it "should find the app" do |
| 327 | delete :destroy, :id => @app.id | 328 | delete :destroy, :id => @app.id |
| 328 | assigns(:app).should == @app | 329 | assigns(:app).should == @app |
| 329 | end | 330 | end |
| 330 | - | 331 | + |
| 331 | it "should destroy the app" do | 332 | it "should destroy the app" do |
| 332 | @app.should_receive(:destroy) | 333 | @app.should_receive(:destroy) |
| 333 | delete :destroy, :id => @app.id | 334 | delete :destroy, :id => @app.id |
| 334 | end | 335 | end |
| 335 | - | 336 | + |
| 336 | it "should display a message" do | 337 | it "should display a message" do |
| 337 | delete :destroy, :id => @app.id | 338 | delete :destroy, :id => @app.id |
| 338 | request.flash[:success].should match(/success/) | 339 | request.flash[:success].should match(/success/) |
| 339 | end | 340 | end |
| 340 | - | 341 | + |
| 341 | it "should redirect to the apps page" do | 342 | it "should redirect to the apps page" do |
| 342 | delete :destroy, :id => @app.id | 343 | delete :destroy, :id => @app.id |
| 343 | response.should redirect_to(apps_path) | 344 | response.should redirect_to(apps_path) |
| 344 | end | 345 | end |
| 345 | end | 346 | end |
| 346 | end | 347 | end |
| 347 | - | 348 | + |
| 349 | + | ||
| 348 | end | 350 | end |
| 349 | - |
spec/controllers/errs_controller_spec.rb
| @@ -6,443 +6,443 @@ describe ErrsController do | @@ -6,443 +6,443 @@ describe ErrsController do | ||
| 6 | :index => :get, :all => :get, :show => :get, :resolve => :put | 6 | :index => :get, :all => :get, :show => :get, :resolve => :put |
| 7 | }, | 7 | }, |
| 8 | :params => {:app_id => 'dummyid', :id => 'dummyid'} | 8 | :params => {:app_id => 'dummyid', :id => 'dummyid'} |
| 9 | - | 9 | + |
| 10 | let(:app) { Factory(:app) } | 10 | let(:app) { Factory(:app) } |
| 11 | - let(:err) { Factory(:err, :app => app) } | ||
| 12 | - | 11 | + let(:err) { Factory(:err, :problem => Factory(:problem, :app => app, :environment => "production")) } |
| 12 | + | ||
| 13 | + | ||
| 13 | describe "GET /errs" do | 14 | describe "GET /errs" do |
| 14 | render_views | 15 | render_views |
| 15 | context 'when logged in as an admin' do | 16 | context 'when logged in as an admin' do |
| 16 | before(:each) do | 17 | before(:each) do |
| 17 | @user = Factory(:admin) | 18 | @user = Factory(:admin) |
| 18 | sign_in @user | 19 | sign_in @user |
| 19 | - @notice = Factory :notice | ||
| 20 | - @err = @notice.err | 20 | + @problem = Factory(:notice, :err => Factory(:err, :problem => Factory(:problem, :app => app, :environment => "production"))).problem |
| 21 | end | 21 | end |
| 22 | - | 22 | + |
| 23 | it "should successfully list errs" do | 23 | it "should successfully list errs" do |
| 24 | get :index | 24 | get :index |
| 25 | response.should be_success | 25 | response.should be_success |
| 26 | - response.body.should match(@err.message) | 26 | + response.body.should match(@problem.message) |
| 27 | end | 27 | end |
| 28 | - | 28 | + |
| 29 | it "should list atom feed successfully" do | 29 | it "should list atom feed successfully" do |
| 30 | get :index, :format => "atom" | 30 | get :index, :format => "atom" |
| 31 | response.should be_success | 31 | response.should be_success |
| 32 | - response.body.should match(@err.message) | 32 | + response.body.should match(@problem.message) |
| 33 | end | 33 | end |
| 34 | - | 34 | + |
| 35 | context "pagination" do | 35 | context "pagination" do |
| 36 | before(:each) do | 36 | before(:each) do |
| 37 | 35.times { Factory :err } | 37 | 35.times { Factory :err } |
| 38 | end | 38 | end |
| 39 | - | 39 | + |
| 40 | it "should have default per_page value for user" do | 40 | it "should have default per_page value for user" do |
| 41 | get :index | 41 | get :index |
| 42 | - assigns(:errs).size.should == User::PER_PAGE | 42 | + assigns(:problems).size.should == User::PER_PAGE |
| 43 | end | 43 | end |
| 44 | - | 44 | + |
| 45 | it "should be able to override default per_page value" do | 45 | it "should be able to override default per_page value" do |
| 46 | @user.update_attribute :per_page, 10 | 46 | @user.update_attribute :per_page, 10 |
| 47 | get :index | 47 | get :index |
| 48 | - assigns(:errs).size.should == 10 | 48 | + assigns(:problems).size.should == 10 |
| 49 | end | 49 | end |
| 50 | end | 50 | end |
| 51 | - | 51 | + |
| 52 | context 'with environment filters' do | 52 | context 'with environment filters' do |
| 53 | before(:each) do | 53 | before(:each) do |
| 54 | environments = ['production', 'test', 'development', 'staging'] | 54 | environments = ['production', 'test', 'development', 'staging'] |
| 55 | 20.times do |i| | 55 | 20.times do |i| |
| 56 | - Factory.create(:err, :environment => environments[i % environments.length]) | 56 | + Factory(:problem, :environment => environments[i % environments.length]) |
| 57 | end | 57 | end |
| 58 | end | 58 | end |
| 59 | - | 59 | + |
| 60 | context 'no params' do | 60 | context 'no params' do |
| 61 | it 'shows errs for all environments' do | 61 | it 'shows errs for all environments' do |
| 62 | get :index | 62 | get :index |
| 63 | - assigns(:errs).size.should == 21 | 63 | + assigns(:problems).size.should == 21 |
| 64 | end | 64 | end |
| 65 | end | 65 | end |
| 66 | - | 66 | + |
| 67 | context 'environment production' do | 67 | context 'environment production' do |
| 68 | it 'shows errs for just production' do | 68 | it 'shows errs for just production' do |
| 69 | - get :index, :environment => :production | ||
| 70 | - assigns(:errs).size.should == 6 | 69 | + get :index, :environment => 'production' |
| 70 | + assigns(:problems).size.should == 6 | ||
| 71 | end | 71 | end |
| 72 | end | 72 | end |
| 73 | - | 73 | + |
| 74 | context 'environment staging' do | 74 | context 'environment staging' do |
| 75 | it 'shows errs for just staging' do | 75 | it 'shows errs for just staging' do |
| 76 | - get :index, :environment => :staging | ||
| 77 | - assigns(:errs).size.should == 5 | 76 | + get :index, :environment => 'staging' |
| 77 | + assigns(:problems).size.should == 5 | ||
| 78 | end | 78 | end |
| 79 | end | 79 | end |
| 80 | - | 80 | + |
| 81 | context 'environment development' do | 81 | context 'environment development' do |
| 82 | it 'shows errs for just development' do | 82 | it 'shows errs for just development' do |
| 83 | - get :index, :environment => :development | ||
| 84 | - assigns(:errs).size.should == 5 | 83 | + get :index, :environment => 'development' |
| 84 | + assigns(:problems).size.should == 5 | ||
| 85 | end | 85 | end |
| 86 | end | 86 | end |
| 87 | - | 87 | + |
| 88 | context 'environment test' do | 88 | context 'environment test' do |
| 89 | it 'shows errs for just test' do | 89 | it 'shows errs for just test' do |
| 90 | - get :index, :environment => :test | ||
| 91 | - assigns(:errs).size.should == 5 | 90 | + get :index, :environment => 'test' |
| 91 | + assigns(:problems).size.should == 5 | ||
| 92 | end | 92 | end |
| 93 | end | 93 | end |
| 94 | end | 94 | end |
| 95 | end | 95 | end |
| 96 | - | 96 | + |
| 97 | context 'when logged in as a user' do | 97 | context 'when logged in as a user' do |
| 98 | it 'gets a paginated list of unresolved errs for the users apps' do | 98 | it 'gets a paginated list of unresolved errs for the users apps' do |
| 99 | sign_in(user = Factory(:user)) | 99 | sign_in(user = Factory(:user)) |
| 100 | unwatched_err = Factory(:err) | 100 | unwatched_err = Factory(:err) |
| 101 | - watched_unresolved_err = Factory(:err, :app => Factory(:user_watcher, :user => user).app, :resolved => false) | ||
| 102 | - watched_resolved_err = Factory(:err, :app => Factory(:user_watcher, :user => user).app, :resolved => true) | 101 | + watched_unresolved_err = Factory(:err, :problem => Factory(:problem, :app => Factory(:user_watcher, :user => user).app, :resolved => false)) |
| 102 | + watched_resolved_err = Factory(:err, :problem => Factory(:problem, :app => Factory(:user_watcher, :user => user).app, :resolved => true)) | ||
| 103 | get :index | 103 | get :index |
| 104 | - assigns(:errs).should include(watched_unresolved_err) | ||
| 105 | - assigns(:errs).should_not include(unwatched_err, watched_resolved_err) | 104 | + assigns(:problems).should include(watched_unresolved_err.problem) |
| 105 | + assigns(:problems).should_not include(unwatched_err.problem, watched_resolved_err.problem) | ||
| 106 | end | 106 | end |
| 107 | end | 107 | end |
| 108 | end | 108 | end |
| 109 | - | 109 | + |
| 110 | describe "GET /errs/all" do | 110 | describe "GET /errs/all" do |
| 111 | context 'when logged in as an admin' do | 111 | context 'when logged in as an admin' do |
| 112 | it "gets a paginated list of all errs" do | 112 | it "gets a paginated list of all errs" do |
| 113 | sign_in Factory(:admin) | 113 | sign_in Factory(:admin) |
| 114 | errs = WillPaginate::Collection.new(1,30) | 114 | errs = WillPaginate::Collection.new(1,30) |
| 115 | - 3.times { errs << Factory(:err) } | ||
| 116 | - 3.times { errs << Factory(:err, :resolved => true)} | ||
| 117 | - Err.should_receive(:ordered).and_return( | 115 | + 3.times { errs << Factory(:err).problem } |
| 116 | + 3.times { errs << Factory(:err, :problem => Factory(:problem, :resolved => true)).problem } | ||
| 117 | + Problem.should_receive(:ordered).and_return( | ||
| 118 | mock('proxy', :paginate => errs) | 118 | mock('proxy', :paginate => errs) |
| 119 | ) | 119 | ) |
| 120 | get :all | 120 | get :all |
| 121 | - assigns(:errs).should == errs | 121 | + assigns(:problems).should == errs |
| 122 | end | 122 | end |
| 123 | end | 123 | end |
| 124 | - | 124 | + |
| 125 | context 'when logged in as a user' do | 125 | context 'when logged in as a user' do |
| 126 | it 'gets a paginated list of all errs for the users apps' do | 126 | it 'gets a paginated list of all errs for the users apps' do |
| 127 | sign_in(user = Factory(:user)) | 127 | sign_in(user = Factory(:user)) |
| 128 | - unwatched_err = Factory(:err) | ||
| 129 | - watched_unresolved_err = Factory(:err, :app => Factory(:user_watcher, :user => user).app, :resolved => false) | ||
| 130 | - watched_resolved_err = Factory(:err, :app => Factory(:user_watcher, :user => user).app, :resolved => true) | 128 | + unwatched_err = Factory(:problem) |
| 129 | + watched_unresolved_err = Factory(:problem, :app => Factory(:user_watcher, :user => user).app, :resolved => false) | ||
| 130 | + watched_resolved_err = Factory(:problem, :app => Factory(:user_watcher, :user => user).app, :resolved => true) | ||
| 131 | get :all | 131 | get :all |
| 132 | - assigns(:errs).should include(watched_resolved_err, watched_unresolved_err) | ||
| 133 | - assigns(:errs).should_not include(unwatched_err) | 132 | + assigns(:problems).should include(watched_resolved_err, watched_unresolved_err) |
| 133 | + assigns(:problems).should_not include(unwatched_err) | ||
| 134 | end | 134 | end |
| 135 | end | 135 | end |
| 136 | end | 136 | end |
| 137 | - | 137 | + |
| 138 | describe "GET /apps/:app_id/errs/:id" do | 138 | describe "GET /apps/:app_id/errs/:id" do |
| 139 | render_views | 139 | render_views |
| 140 | - | 140 | + |
| 141 | before do | 141 | before do |
| 142 | 3.times { Factory(:notice, :err => err)} | 142 | 3.times { Factory(:notice, :err => err)} |
| 143 | end | 143 | end |
| 144 | - | 144 | + |
| 145 | context 'when logged in as an admin' do | 145 | context 'when logged in as an admin' do |
| 146 | before do | 146 | before do |
| 147 | sign_in Factory(:admin) | 147 | sign_in Factory(:admin) |
| 148 | end | 148 | end |
| 149 | - | 149 | + |
| 150 | it "finds the app" do | 150 | it "finds the app" do |
| 151 | - get :show, :app_id => app.id, :id => err.id | 151 | + get :show, :app_id => app.id, :id => err.problem.id |
| 152 | assigns(:app).should == app | 152 | assigns(:app).should == app |
| 153 | end | 153 | end |
| 154 | - | 154 | + |
| 155 | it "finds the err" do | 155 | it "finds the err" do |
| 156 | - get :show, :app_id => app.id, :id => err.id | ||
| 157 | - assigns(:err).should == err | 156 | + get :show, :app_id => app.id, :id => err.problem.id |
| 157 | + assigns(:problem).should == err.problem | ||
| 158 | end | 158 | end |
| 159 | - | 159 | + |
| 160 | it "successfully render page" do | 160 | it "successfully render page" do |
| 161 | - get :show, :app_id => app.id, :id => err.id | 161 | + get :show, :app_id => app.id, :id => err.problem.id |
| 162 | response.should be_success | 162 | response.should be_success |
| 163 | end | 163 | end |
| 164 | - | 164 | + |
| 165 | context "create issue button" do | 165 | context "create issue button" do |
| 166 | let(:button_matcher) { match(/create issue/) } | 166 | let(:button_matcher) { match(/create issue/) } |
| 167 | - | 167 | + |
| 168 | it "should not exist for err's app without issue tracker" do | 168 | it "should not exist for err's app without issue tracker" do |
| 169 | err = Factory :err | 169 | err = Factory :err |
| 170 | - get :show, :app_id => err.app.id, :id => err.id | ||
| 171 | - | 170 | + get :show, :app_id => err.app.id, :id => err.problem.id |
| 171 | + | ||
| 172 | response.body.should_not button_matcher | 172 | response.body.should_not button_matcher |
| 173 | end | 173 | end |
| 174 | - | 174 | + |
| 175 | it "should exist for err's app with issue tracker" do | 175 | it "should exist for err's app with issue tracker" do |
| 176 | tracker = Factory(:lighthouse_tracker) | 176 | tracker = Factory(:lighthouse_tracker) |
| 177 | - err = Factory(:err, :app => tracker.app) | ||
| 178 | - get :show, :app_id => err.app.id, :id => err.id | ||
| 179 | - | 177 | + err = Factory(:err, :problem => Factory(:problem, :app => tracker.app)) |
| 178 | + get :show, :app_id => err.app.id, :id => err.problem.id | ||
| 179 | + | ||
| 180 | response.body.should button_matcher | 180 | response.body.should button_matcher |
| 181 | end | 181 | end |
| 182 | - | 182 | + |
| 183 | it "should not exist for err with issue_link" do | 183 | it "should not exist for err with issue_link" do |
| 184 | tracker = Factory(:lighthouse_tracker) | 184 | tracker = Factory(:lighthouse_tracker) |
| 185 | - err = Factory(:err, :app => tracker.app, :issue_link => "http://some.host") | ||
| 186 | - get :show, :app_id => err.app.id, :id => err.id | ||
| 187 | - | 185 | + err = Factory(:err, :problem => Factory(:problem, :app => tracker.app, :issue_link => "http://some.host")) |
| 186 | + get :show, :app_id => err.app.id, :id => err.problem.id | ||
| 187 | + | ||
| 188 | response.body.should_not button_matcher | 188 | response.body.should_not button_matcher |
| 189 | end | 189 | end |
| 190 | end | 190 | end |
| 191 | end | 191 | end |
| 192 | - | 192 | + |
| 193 | context 'when logged in as a user' do | 193 | context 'when logged in as a user' do |
| 194 | before do | 194 | before do |
| 195 | sign_in(@user = Factory(:user)) | 195 | sign_in(@user = Factory(:user)) |
| 196 | @unwatched_err = Factory(:err) | 196 | @unwatched_err = Factory(:err) |
| 197 | @watched_app = Factory(:app) | 197 | @watched_app = Factory(:app) |
| 198 | @watcher = Factory(:user_watcher, :user => @user, :app => @watched_app) | 198 | @watcher = Factory(:user_watcher, :user => @user, :app => @watched_app) |
| 199 | - @watched_err = Factory(:err, :app => @watched_app) | 199 | + @watched_err = Factory(:err, :problem => Factory(:problem, :app => @watched_app)) |
| 200 | end | 200 | end |
| 201 | - | 201 | + |
| 202 | it 'finds the err if the user is watching the app' do | 202 | it 'finds the err if the user is watching the app' do |
| 203 | - get :show, :app_id => @watched_app.to_param, :id => @watched_err.id | ||
| 204 | - assigns(:err).should == @watched_err | 203 | + get :show, :app_id => @watched_app.to_param, :id => @watched_err.problem.id |
| 204 | + assigns(:problem).should == @watched_err.problem | ||
| 205 | end | 205 | end |
| 206 | - | 206 | + |
| 207 | it 'raises a DocumentNotFound error if the user is not watching the app' do | 207 | it 'raises a DocumentNotFound error if the user is not watching the app' do |
| 208 | lambda { | 208 | lambda { |
| 209 | - get :show, :app_id => @unwatched_err.app_id, :id => @unwatched_err.id | 209 | + get :show, :app_id => @unwatched_err.problem.app_id, :id => @unwatched_err.problem.id |
| 210 | }.should raise_error(Mongoid::Errors::DocumentNotFound) | 210 | }.should raise_error(Mongoid::Errors::DocumentNotFound) |
| 211 | end | 211 | end |
| 212 | end | 212 | end |
| 213 | end | 213 | end |
| 214 | - | 214 | + |
| 215 | describe "PUT /apps/:app_id/errs/:id/resolve" do | 215 | describe "PUT /apps/:app_id/errs/:id/resolve" do |
| 216 | before do | 216 | before do |
| 217 | sign_in Factory(:admin) | 217 | sign_in Factory(:admin) |
| 218 | - | ||
| 219 | - @err = Factory(:err) | ||
| 220 | - App.stub(:find).with(@err.app.id).and_return(@err.app) | ||
| 221 | - @err.app.errs.stub(:find).and_return(@err) | ||
| 222 | - @err.stub(:resolve!) | 218 | + |
| 219 | + @problem = Factory(:err) | ||
| 220 | + App.stub(:find).with(@problem.app.id).and_return(@problem.app) | ||
| 221 | + @problem.app.problems.stub(:find).and_return(@problem.problem) | ||
| 222 | + @problem.problem.stub(:resolve!) | ||
| 223 | end | 223 | end |
| 224 | - | 224 | + |
| 225 | it 'finds the app and the err' do | 225 | it 'finds the app and the err' do |
| 226 | - App.should_receive(:find).with(@err.app.id).and_return(@err.app) | ||
| 227 | - @err.app.errs.should_receive(:find).and_return(@err) | ||
| 228 | - put :resolve, :app_id => @err.app.id, :id => @err.id | ||
| 229 | - assigns(:app).should == @err.app | ||
| 230 | - assigns(:err).should == @err | 226 | + App.should_receive(:find).with(@problem.app.id).and_return(@problem.app) |
| 227 | + @problem.app.problems.should_receive(:find).and_return(@problem.problem) | ||
| 228 | + put :resolve, :app_id => @problem.app.id, :id => @problem.problem.id | ||
| 229 | + assigns(:app).should == @problem.app | ||
| 230 | + assigns(:problem).should == @problem.problem | ||
| 231 | end | 231 | end |
| 232 | - | 232 | + |
| 233 | it "should resolve the issue" do | 233 | it "should resolve the issue" do |
| 234 | - @err.should_receive(:resolve!).and_return(true) | ||
| 235 | - put :resolve, :app_id => @err.app.id, :id => @err.id | 234 | + @problem.problem.should_receive(:resolve!).and_return(true) |
| 235 | + put :resolve, :app_id => @problem.app.id, :id => @problem.problem.id | ||
| 236 | end | 236 | end |
| 237 | - | 237 | + |
| 238 | it "should display a message" do | 238 | it "should display a message" do |
| 239 | - put :resolve, :app_id => @err.app.id, :id => @err.id | 239 | + put :resolve, :app_id => @problem.app.id, :id => @problem.problem.id |
| 240 | request.flash[:success].should match(/Great news/) | 240 | request.flash[:success].should match(/Great news/) |
| 241 | end | 241 | end |
| 242 | - | 242 | + |
| 243 | it "should redirect to the app page" do | 243 | it "should redirect to the app page" do |
| 244 | - put :resolve, :app_id => @err.app.id, :id => @err.id | ||
| 245 | - response.should redirect_to(app_path(@err.app)) | 244 | + put :resolve, :app_id => @problem.app.id, :id => @problem.problem.id |
| 245 | + response.should redirect_to(app_path(@problem.app)) | ||
| 246 | end | 246 | end |
| 247 | - | 247 | + |
| 248 | it "should redirect back to errs page" do | 248 | it "should redirect back to errs page" do |
| 249 | request.env["Referer"] = errs_path | 249 | request.env["Referer"] = errs_path |
| 250 | - put :resolve, :app_id => @err.app.id, :id => @err.id | 250 | + put :resolve, :app_id => @problem.app.id, :id => @problem.problem.id |
| 251 | response.should redirect_to(errs_path) | 251 | response.should redirect_to(errs_path) |
| 252 | end | 252 | end |
| 253 | end | 253 | end |
| 254 | - | 254 | + |
| 255 | describe "POST /apps/:app_id/errs/:id/create_issue" do | 255 | describe "POST /apps/:app_id/errs/:id/create_issue" do |
| 256 | render_views | 256 | render_views |
| 257 | - | 257 | + |
| 258 | before(:each) do | 258 | before(:each) do |
| 259 | sign_in Factory(:admin) | 259 | sign_in Factory(:admin) |
| 260 | end | 260 | end |
| 261 | - | 261 | + |
| 262 | context "successful issue creation" do | 262 | context "successful issue creation" do |
| 263 | context "lighthouseapp tracker" do | 263 | context "lighthouseapp tracker" do |
| 264 | let(:notice) { Factory :notice } | 264 | let(:notice) { Factory :notice } |
| 265 | - let(:tracker) { Factory :lighthouse_tracker, :app => notice.err.app } | ||
| 266 | - let(:err) { notice.err } | ||
| 267 | - | 265 | + let(:tracker) { Factory :lighthouse_tracker, :app => notice.app } |
| 266 | + let(:problem) { notice.problem } | ||
| 267 | + | ||
| 268 | before(:each) do | 268 | before(:each) do |
| 269 | number = 5 | 269 | number = 5 |
| 270 | @issue_link = "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets/#{number}.xml" | 270 | @issue_link = "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets/#{number}.xml" |
| 271 | body = "<ticket><number type=\"integer\">#{number}</number></ticket>" | 271 | body = "<ticket><number type=\"integer\">#{number}</number></ticket>" |
| 272 | stub_request(:post, "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets.xml"). | 272 | stub_request(:post, "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets.xml"). |
| 273 | to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body ) | 273 | to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body ) |
| 274 | - | ||
| 275 | - post :create_issue, :app_id => err.app.id, :id => err.id | ||
| 276 | - err.reload | 274 | + |
| 275 | + post :create_issue, :app_id => problem.app.id, :id => problem.id | ||
| 276 | + problem.reload | ||
| 277 | end | 277 | end |
| 278 | - | ||
| 279 | - it "should redirect to err page" do | ||
| 280 | - response.should redirect_to( app_err_path(err.app, err) ) | 278 | + |
| 279 | + it "should redirect to problem page" do | ||
| 280 | + response.should redirect_to( app_err_path(problem.app, problem) ) | ||
| 281 | end | 281 | end |
| 282 | end | 282 | end |
| 283 | end | 283 | end |
| 284 | - | 284 | + |
| 285 | context "absent issue tracker" do | 285 | context "absent issue tracker" do |
| 286 | - let(:err) { Factory :err } | ||
| 287 | - | 286 | + let(:problem) { Factory :problem } |
| 287 | + | ||
| 288 | before(:each) do | 288 | before(:each) do |
| 289 | - post :create_issue, :app_id => err.app.id, :id => err.id | 289 | + post :create_issue, :app_id => problem.app.id, :id => problem.id |
| 290 | end | 290 | end |
| 291 | - | ||
| 292 | - it "should redirect to err page" do | ||
| 293 | - response.should redirect_to( app_err_path(err.app, err) ) | 291 | + |
| 292 | + it "should redirect to problem page" do | ||
| 293 | + response.should redirect_to( app_err_path(problem.app, problem) ) | ||
| 294 | end | 294 | end |
| 295 | - | 295 | + |
| 296 | it "should set flash error message telling issue tracker of the app doesn't exist" do | 296 | it "should set flash error message telling issue tracker of the app doesn't exist" do |
| 297 | flash[:error].should == "This app has no issue tracker setup." | 297 | flash[:error].should == "This app has no issue tracker setup." |
| 298 | end | 298 | end |
| 299 | end | 299 | end |
| 300 | - | 300 | + |
| 301 | context "error during request to a tracker" do | 301 | context "error during request to a tracker" do |
| 302 | context "lighthouseapp tracker" do | 302 | context "lighthouseapp tracker" do |
| 303 | let(:tracker) { Factory :lighthouse_tracker } | 303 | let(:tracker) { Factory :lighthouse_tracker } |
| 304 | - let(:err) { Factory :err, :app => tracker.app } | ||
| 305 | - | 304 | + let(:err) { Factory(:err, :problem => Factory(:problem, :app => tracker.app)) } |
| 305 | + | ||
| 306 | before(:each) do | 306 | before(:each) do |
| 307 | stub_request(:post, "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets.xml").to_return(:status => 500) | 307 | stub_request(:post, "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets.xml").to_return(:status => 500) |
| 308 | - | ||
| 309 | - post :create_issue, :app_id => err.app.id, :id => err.id | 308 | + |
| 309 | + post :create_issue, :app_id => err.app.id, :id => err.problem.id | ||
| 310 | end | 310 | end |
| 311 | - | 311 | + |
| 312 | it "should redirect to err page" do | 312 | it "should redirect to err page" do |
| 313 | - response.should redirect_to( app_err_path(err.app, err) ) | 313 | + response.should redirect_to( app_err_path(err.app, err.problem) ) |
| 314 | end | 314 | end |
| 315 | - | 315 | + |
| 316 | it "should notify of connection error" do | 316 | it "should notify of connection error" do |
| 317 | flash[:error].should == "There was an error during issue creation. Check your tracker settings or try again later." | 317 | flash[:error].should == "There was an error during issue creation. Check your tracker settings or try again later." |
| 318 | end | 318 | end |
| 319 | end | 319 | end |
| 320 | end | 320 | end |
| 321 | end | 321 | end |
| 322 | - | 322 | + |
| 323 | describe "DELETE /apps/:app_id/errs/:id/unlink_issue" do | 323 | describe "DELETE /apps/:app_id/errs/:id/unlink_issue" do |
| 324 | before(:each) do | 324 | before(:each) do |
| 325 | sign_in Factory(:admin) | 325 | sign_in Factory(:admin) |
| 326 | end | 326 | end |
| 327 | - | 327 | + |
| 328 | context "err with issue" do | 328 | context "err with issue" do |
| 329 | - let(:err) { Factory :err, :issue_link => "http://some.host" } | ||
| 330 | - | 329 | + let(:err) { Factory(:err, :problem => Factory(:problem, :issue_link => "http://some.host")) } |
| 330 | + | ||
| 331 | before(:each) do | 331 | before(:each) do |
| 332 | - delete :unlink_issue, :app_id => err.app.id, :id => err.id | ||
| 333 | - err.reload | 332 | + delete :unlink_issue, :app_id => err.app.id, :id => err.problem.id |
| 333 | + err.problem.reload | ||
| 334 | end | 334 | end |
| 335 | - | 335 | + |
| 336 | it "should redirect to err page" do | 336 | it "should redirect to err page" do |
| 337 | - response.should redirect_to( app_err_path(err.app, err) ) | 337 | + response.should redirect_to( app_err_path(err.app, err.problem) ) |
| 338 | end | 338 | end |
| 339 | - | 339 | + |
| 340 | it "should clear issue link" do | 340 | it "should clear issue link" do |
| 341 | - err.issue_link.should be_nil | 341 | + err.problem.issue_link.should be_nil |
| 342 | end | 342 | end |
| 343 | end | 343 | end |
| 344 | - | 344 | + |
| 345 | context "err without issue" do | 345 | context "err without issue" do |
| 346 | let(:err) { Factory :err } | 346 | let(:err) { Factory :err } |
| 347 | - | 347 | + |
| 348 | before(:each) do | 348 | before(:each) do |
| 349 | - delete :unlink_issue, :app_id => err.app.id, :id => err.id | ||
| 350 | - err.reload | 349 | + delete :unlink_issue, :app_id => err.app.id, :id => err.problem.id |
| 350 | + err.problem.reload | ||
| 351 | end | 351 | end |
| 352 | - | 352 | + |
| 353 | it "should redirect to err page" do | 353 | it "should redirect to err page" do |
| 354 | - response.should redirect_to( app_err_path(err.app, err) ) | 354 | + response.should redirect_to( app_err_path(err.app, err.problem) ) |
| 355 | end | 355 | end |
| 356 | end | 356 | end |
| 357 | end | 357 | end |
| 358 | - | ||
| 359 | - | 358 | + |
| 359 | + | ||
| 360 | describe "POST /apps/:app_id/errs/:id/create_comment" do | 360 | describe "POST /apps/:app_id/errs/:id/create_comment" do |
| 361 | render_views | 361 | render_views |
| 362 | - | 362 | + |
| 363 | before(:each) do | 363 | before(:each) do |
| 364 | sign_in Factory(:admin) | 364 | sign_in Factory(:admin) |
| 365 | end | 365 | end |
| 366 | - | 366 | + |
| 367 | context "successful comment creation" do | 367 | context "successful comment creation" do |
| 368 | - let(:err) { Factory(:err) } | 368 | + let(:problem) { Factory(:problem) } |
| 369 | let(:user) { Factory(:user) } | 369 | let(:user) { Factory(:user) } |
| 370 | - | 370 | + |
| 371 | before(:each) do | 371 | before(:each) do |
| 372 | - post :create_comment, :app_id => err.app.id, :id => err.id, | 372 | + post :create_comment, :app_id => problem.app.id, :id => problem.id, |
| 373 | :comment => { :body => "One test comment", :user_id => user.id } | 373 | :comment => { :body => "One test comment", :user_id => user.id } |
| 374 | - err.reload | 374 | + problem.reload |
| 375 | end | 375 | end |
| 376 | - | 376 | + |
| 377 | it "should create the comment" do | 377 | it "should create the comment" do |
| 378 | - err.comments.size.should == 1 | 378 | + problem.comments.size.should == 1 |
| 379 | end | 379 | end |
| 380 | - | ||
| 381 | - it "should redirect to err page" do | ||
| 382 | - response.should redirect_to( app_err_path(err.app, err) ) | 380 | + |
| 381 | + it "should redirect to problem page" do | ||
| 382 | + response.should redirect_to( app_err_path(problem.app, problem) ) | ||
| 383 | end | 383 | end |
| 384 | end | 384 | end |
| 385 | end | 385 | end |
| 386 | - | 386 | + |
| 387 | describe "DELETE /apps/:app_id/errs/:id/destroy_comment" do | 387 | describe "DELETE /apps/:app_id/errs/:id/destroy_comment" do |
| 388 | render_views | 388 | render_views |
| 389 | - | 389 | + |
| 390 | before(:each) do | 390 | before(:each) do |
| 391 | sign_in Factory(:admin) | 391 | sign_in Factory(:admin) |
| 392 | end | 392 | end |
| 393 | - | 393 | + |
| 394 | context "successful comment deletion" do | 394 | context "successful comment deletion" do |
| 395 | - let(:err) { Factory :err_with_comments } | ||
| 396 | - let(:comment) { err.comments.first } | ||
| 397 | - | 395 | + let(:problem) { Factory(:problem_with_comments) } |
| 396 | + let(:comment) { problem.comments.first } | ||
| 397 | + | ||
| 398 | before(:each) do | 398 | before(:each) do |
| 399 | - delete :destroy_comment, :app_id => err.app.id, :id => err.id, :comment_id => comment.id | ||
| 400 | - err.reload | 399 | + delete :destroy_comment, :app_id => problem.app.id, :id => problem.id, :comment_id => comment.id |
| 400 | + problem.reload | ||
| 401 | end | 401 | end |
| 402 | - | 402 | + |
| 403 | it "should delete the comment" do | 403 | it "should delete the comment" do |
| 404 | - err.comments.detect{|c| c.id.to_s == comment.id }.should == nil | 404 | + problem.comments.detect{|c| c.id.to_s == comment.id }.should == nil |
| 405 | end | 405 | end |
| 406 | - | ||
| 407 | - it "should redirect to err page" do | ||
| 408 | - response.should redirect_to( app_err_path(err.app, err) ) | 406 | + |
| 407 | + it "should redirect to problem page" do | ||
| 408 | + response.should redirect_to( app_err_path(problem.app, problem) ) | ||
| 409 | end | 409 | end |
| 410 | end | 410 | end |
| 411 | end | 411 | end |
| 412 | - | 412 | + |
| 413 | describe "Bulk Actions" do | 413 | describe "Bulk Actions" do |
| 414 | before(:each) do | 414 | before(:each) do |
| 415 | sign_in Factory(:admin) | 415 | sign_in Factory(:admin) |
| 416 | - @err1 = Factory(:err, :resolved => true) | ||
| 417 | - @err2 = Factory(:err, :resolved => false) | 416 | + @problem1 = Factory(:problem, :resolved => true) |
| 417 | + @problem2 = Factory(:problem, :resolved => false) | ||
| 418 | end | 418 | end |
| 419 | 419 | ||
| 420 | - it "should apply to multiple errs" do | ||
| 421 | - post :resolve_several, :errs => [@err1.id.to_s, @err2.id.to_s] | ||
| 422 | - assigns(:selected_errs).should == [@err1, @err2] | 420 | + it "should apply to multiple problems" do |
| 421 | + post :resolve_several, :problems => [@problem1.id.to_s, @problem2.id.to_s] | ||
| 422 | + assigns(:selected_problems).should == [@problem1, @problem2] | ||
| 423 | end | 423 | end |
| 424 | 424 | ||
| 425 | context "POST /errs/resolve_several" do | 425 | context "POST /errs/resolve_several" do |
| 426 | it "should resolve the issue" do | 426 | it "should resolve the issue" do |
| 427 | - post :resolve_several, :errs => [@err2.id.to_s] | ||
| 428 | - @err2.reload.resolved?.should == true | 427 | + post :resolve_several, :problems => [@problem2.id.to_s] |
| 428 | + @problem2.reload.resolved?.should == true | ||
| 429 | end | 429 | end |
| 430 | end | 430 | end |
| 431 | 431 | ||
| 432 | context "POST /errs/unresolve_several" do | 432 | context "POST /errs/unresolve_several" do |
| 433 | it "should unresolve the issue" do | 433 | it "should unresolve the issue" do |
| 434 | - post :unresolve_several, :errs => [@err1.id.to_s] | ||
| 435 | - @err1.reload.resolved?.should == false | 434 | + post :unresolve_several, :problems => [@problem1.id.to_s] |
| 435 | + @problem1.reload.resolved?.should == false | ||
| 436 | end | 436 | end |
| 437 | end | 437 | end |
| 438 | 438 | ||
| 439 | context "POST /errs/destroy_several" do | 439 | context "POST /errs/destroy_several" do |
| 440 | it "should delete the errs" do | 440 | it "should delete the errs" do |
| 441 | lambda { | 441 | lambda { |
| 442 | - post :destroy_several, :errs => [@err1.id.to_s] | ||
| 443 | - }.should change(Err, :count).by(-1) | 442 | + post :destroy_several, :problems => [@problem1.id.to_s] |
| 443 | + }.should change(Problem, :count).by(-1) | ||
| 444 | end | 444 | end |
| 445 | end | 445 | end |
| 446 | end | 446 | end |
| 447 | + | ||
| 447 | end | 448 | end |
| 448 | - |
spec/controllers/notices_controller_spec.rb
| @@ -29,9 +29,9 @@ describe NoticesController do | @@ -29,9 +29,9 @@ describe NoticesController do | ||
| 29 | post :create | 29 | post :create |
| 30 | email = ActionMailer::Base.deliveries.last | 30 | email = ActionMailer::Base.deliveries.last |
| 31 | email.to.should include(@app.watchers.first.email) | 31 | email.to.should include(@app.watchers.first.email) |
| 32 | - email.subject.should include(@notice.err.message) | 32 | + email.subject.should include(@notice.message) |
| 33 | email.subject.should include("[#{@app.name}]") | 33 | email.subject.should include("[#{@app.name}]") |
| 34 | - email.subject.should include("[#{@notice.err.environment}]") | 34 | + email.subject.should include("[#{@notice.environment_name}]") |
| 35 | end | 35 | end |
| 36 | end | 36 | end |
| 37 | 37 |
spec/factories/app_factories.rb
| @@ -20,7 +20,7 @@ Factory.define(:user_watcher, :parent => :watcher) do |w| | @@ -20,7 +20,7 @@ Factory.define(:user_watcher, :parent => :watcher) do |w| | ||
| 20 | end | 20 | end |
| 21 | 21 | ||
| 22 | Factory.define(:deploy) do |d| | 22 | Factory.define(:deploy) do |d| |
| 23 | - d.app {|p| p.association :app} | 23 | + d.app {|p| p.association :app} |
| 24 | d.username 'clyde.frog' | 24 | d.username 'clyde.frog' |
| 25 | d.repository 'git@github.com/errbit/errbit.git' | 25 | d.repository 'git@github.com/errbit/errbit.git' |
| 26 | d.environment 'production' | 26 | d.environment 'production' |
spec/factories/err_factories.rb
| 1 | +Factory.define :problem do |p| | ||
| 2 | + p.app {|a| a.association :app} | ||
| 3 | + p.comments [] | ||
| 4 | +end | ||
| 5 | + | ||
| 6 | +Factory.define(:problem_with_comments, :parent => :problem) do |ec| | ||
| 7 | + ec.comments { (1..3).map { Factory(:comment) } } | ||
| 8 | +end | ||
| 9 | + | ||
| 10 | + | ||
| 11 | + | ||
| 1 | Factory.define :err do |e| | 12 | Factory.define :err do |e| |
| 2 | - e.app {|p| p.association :app } | 13 | + e.problem {|p| p.association :problem} |
| 3 | e.klass 'FooError' | 14 | e.klass 'FooError' |
| 4 | e.component 'foo' | 15 | e.component 'foo' |
| 5 | e.action 'bar' | 16 | e.action 'bar' |
| 6 | e.environment 'production' | 17 | e.environment 'production' |
| 7 | - e.comments [] | ||
| 8 | end | 18 | end |
| 9 | 19 | ||
| 10 | -Factory.define(:err_with_comments, :parent => :err) do |ec| | ||
| 11 | - ec.comments { (1..3).map{Factory(:comment)} } | ||
| 12 | -end | 20 | + |
| 13 | 21 | ||
| 14 | Factory.define :notice do |n| | 22 | Factory.define :notice do |n| |
| 15 | n.err {|e| e.association :err} | 23 | n.err {|e| e.association :err} |
| 16 | n.message 'FooError: Too Much Bar' | 24 | n.message 'FooError: Too Much Bar' |
| 17 | n.backtrace { random_backtrace } | 25 | n.backtrace { random_backtrace } |
| 18 | - n.server_environment 'server-environment' => 'production' | 26 | + n.server_environment 'environment-name' => 'production' |
| 19 | n.notifier 'name' => 'Notifier', 'version' => '1', 'url' => 'http://toad.com' | 27 | n.notifier 'name' => 'Notifier', 'version' => '1', 'url' => 'http://toad.com' |
| 20 | end | 28 | end |
| 21 | 29 | ||
| @@ -28,4 +36,3 @@ def random_backtrace | @@ -28,4 +36,3 @@ def random_backtrace | ||
| 28 | }} | 36 | }} |
| 29 | backtrace | 37 | backtrace |
| 30 | end | 38 | end |
| 31 | - |
spec/models/app_spec.rb
| 1 | require 'spec_helper' | 1 | require 'spec_helper' |
| 2 | 2 | ||
| 3 | describe App do | 3 | describe App do |
| 4 | - | 4 | + |
| 5 | + | ||
| 5 | context 'validations' do | 6 | context 'validations' do |
| 6 | it 'requires a name' do | 7 | it 'requires a name' do |
| 7 | app = Factory.build(:app, :name => nil) | 8 | app = Factory.build(:app, :name => nil) |
| 8 | app.should_not be_valid | 9 | app.should_not be_valid |
| 9 | app.errors[:name].should include("can't be blank") | 10 | app.errors[:name].should include("can't be blank") |
| 10 | end | 11 | end |
| 11 | - | 12 | + |
| 12 | it 'requires unique names' do | 13 | it 'requires unique names' do |
| 13 | Factory(:app, :name => 'Errbit') | 14 | Factory(:app, :name => 'Errbit') |
| 14 | app = Factory.build(:app, :name => 'Errbit') | 15 | app = Factory.build(:app, :name => 'Errbit') |
| 15 | app.should_not be_valid | 16 | app.should_not be_valid |
| 16 | app.errors[:name].should include('is already taken') | 17 | app.errors[:name].should include('is already taken') |
| 17 | end | 18 | end |
| 18 | - | 19 | + |
| 19 | it 'requires unique api_keys' do | 20 | it 'requires unique api_keys' do |
| 20 | Factory(:app, :api_key => 'APIKEY') | 21 | Factory(:app, :api_key => 'APIKEY') |
| 21 | app = Factory.build(:app, :api_key => 'APIKEY') | 22 | app = Factory.build(:app, :api_key => 'APIKEY') |
| @@ -23,7 +24,8 @@ describe App do | @@ -23,7 +24,8 @@ describe App do | ||
| 23 | app.errors[:api_key].should include('is already taken') | 24 | app.errors[:api_key].should include('is already taken') |
| 24 | end | 25 | end |
| 25 | end | 26 | end |
| 26 | - | 27 | + |
| 28 | + | ||
| 27 | context 'being created' do | 29 | context 'being created' do |
| 28 | it 'generates a new api-key' do | 30 | it 'generates a new api-key' do |
| 29 | app = Factory.build(:app) | 31 | app = Factory.build(:app) |
| @@ -31,62 +33,62 @@ describe App do | @@ -31,62 +33,62 @@ describe App do | ||
| 31 | app.save | 33 | app.save |
| 32 | app.api_key.should_not be_nil | 34 | app.api_key.should_not be_nil |
| 33 | end | 35 | end |
| 34 | - | 36 | + |
| 35 | it 'generates a correct api-key' do | 37 | it 'generates a correct api-key' do |
| 36 | app = Factory(:app) | 38 | app = Factory(:app) |
| 37 | app.api_key.should match(/^[a-f0-9]{32}$/) | 39 | app.api_key.should match(/^[a-f0-9]{32}$/) |
| 38 | end | 40 | end |
| 39 | - | 41 | + |
| 40 | it 'is fine with blank github urls' do | 42 | it 'is fine with blank github urls' do |
| 41 | app = Factory.build(:app, :github_url => "") | 43 | app = Factory.build(:app, :github_url => "") |
| 42 | app.save | 44 | app.save |
| 43 | app.github_url.should == "" | 45 | app.github_url.should == "" |
| 44 | end | 46 | end |
| 45 | - | 47 | + |
| 46 | it 'does not touch https github urls' do | 48 | it 'does not touch https github urls' do |
| 47 | app = Factory.build(:app, :github_url => "https://github.com/errbit/errbit") | 49 | app = Factory.build(:app, :github_url => "https://github.com/errbit/errbit") |
| 48 | app.save | 50 | app.save |
| 49 | app.github_url.should == "https://github.com/errbit/errbit" | 51 | app.github_url.should == "https://github.com/errbit/errbit" |
| 50 | end | 52 | end |
| 51 | - | 53 | + |
| 52 | it 'normalizes http github urls' do | 54 | it 'normalizes http github urls' do |
| 53 | app = Factory.build(:app, :github_url => "http://github.com/errbit/errbit") | 55 | app = Factory.build(:app, :github_url => "http://github.com/errbit/errbit") |
| 54 | app.save | 56 | app.save |
| 55 | app.github_url.should == "https://github.com/errbit/errbit" | 57 | app.github_url.should == "https://github.com/errbit/errbit" |
| 56 | end | 58 | end |
| 57 | - | 59 | + |
| 58 | it 'normalizes public git repo as a github url' do | 60 | it 'normalizes public git repo as a github url' do |
| 59 | app = Factory.build(:app, :github_url => "https://github.com/errbit/errbit.git") | 61 | app = Factory.build(:app, :github_url => "https://github.com/errbit/errbit.git") |
| 60 | app.save | 62 | app.save |
| 61 | app.github_url.should == "https://github.com/errbit/errbit" | 63 | app.github_url.should == "https://github.com/errbit/errbit" |
| 62 | end | 64 | end |
| 63 | - | 65 | + |
| 64 | it 'normalizes private git repo as a github url' do | 66 | it 'normalizes private git repo as a github url' do |
| 65 | app = Factory.build(:app, :github_url => "git@github.com:errbit/errbit.git") | 67 | app = Factory.build(:app, :github_url => "git@github.com:errbit/errbit.git") |
| 66 | app.save | 68 | app.save |
| 67 | app.github_url.should == "https://github.com/errbit/errbit" | 69 | app.github_url.should == "https://github.com/errbit/errbit" |
| 68 | end | 70 | end |
| 69 | end | 71 | end |
| 70 | - | 72 | + |
| 71 | context '#github_url_to_file' do | 73 | context '#github_url_to_file' do |
| 72 | it 'resolves to full path to file' do | 74 | it 'resolves to full path to file' do |
| 73 | app = Factory(:app, :github_url => "https://github.com/errbit/errbit") | 75 | app = Factory(:app, :github_url => "https://github.com/errbit/errbit") |
| 74 | app.github_url_to_file('/path/to/file').should == "https://github.com/errbit/errbit/blob/master/path/to/file" | 76 | app.github_url_to_file('/path/to/file').should == "https://github.com/errbit/errbit/blob/master/path/to/file" |
| 75 | end | 77 | end |
| 76 | end | 78 | end |
| 77 | - | 79 | + |
| 78 | context '#github_url?' do | 80 | context '#github_url?' do |
| 79 | it 'is true when there is a github_url' do | 81 | it 'is true when there is a github_url' do |
| 80 | app = Factory(:app, :github_url => "https://github.com/errbit/errbit") | 82 | app = Factory(:app, :github_url => "https://github.com/errbit/errbit") |
| 81 | app.github_url?.should be_true | 83 | app.github_url?.should be_true |
| 82 | end | 84 | end |
| 83 | - | 85 | + |
| 84 | it 'is false when no github_url' do | 86 | it 'is false when no github_url' do |
| 85 | app = Factory(:app) | 87 | app = Factory(:app) |
| 86 | app.github_url?.should be_false | 88 | app.github_url?.should be_false |
| 87 | end | 89 | end |
| 88 | end | 90 | end |
| 89 | - | 91 | + |
| 90 | context "notification recipients" do | 92 | context "notification recipients" do |
| 91 | it "should send notices to either all users, or the configured watchers" do | 93 | it "should send notices to either all users, or the configured watchers" do |
| 92 | @app = Factory(:app) | 94 | @app = Factory(:app) |
| @@ -98,7 +100,8 @@ describe App do | @@ -98,7 +100,8 @@ describe App do | ||
| 98 | @app.notification_recipients.size.should == 5 | 100 | @app.notification_recipients.size.should == 5 |
| 99 | end | 101 | end |
| 100 | end | 102 | end |
| 101 | - | 103 | + |
| 104 | + | ||
| 102 | context "copying attributes from existing app" do | 105 | context "copying attributes from existing app" do |
| 103 | it "should only copy the necessary fields" do | 106 | it "should only copy the necessary fields" do |
| 104 | @app, @copy_app = Factory(:app, :name => "app", :github_url => "url"), | 107 | @app, @copy_app = Factory(:app, :name => "app", :github_url => "url"), |
| @@ -110,5 +113,36 @@ describe App do | @@ -110,5 +113,36 @@ describe App do | ||
| 110 | @app.watchers.first.email.should == "copywatcher@example.com" | 113 | @app.watchers.first.email.should == "copywatcher@example.com" |
| 111 | end | 114 | end |
| 112 | end | 115 | end |
| 116 | + | ||
| 117 | + | ||
| 118 | + context '#find_or_create_err!' do | ||
| 119 | + before do | ||
| 120 | + @app = Factory(:app) | ||
| 121 | + @conditions = { | ||
| 122 | + :klass => 'Whoops', | ||
| 123 | + :component => 'Foo', | ||
| 124 | + :action => 'bar', | ||
| 125 | + :environment => 'production' | ||
| 126 | + } | ||
| 127 | + end | ||
| 128 | + | ||
| 129 | + it 'returns the correct err if one already exists' do | ||
| 130 | + existing = Factory(:err, @conditions.merge(:problem => Factory(:problem, :app => @app))) | ||
| 131 | + Err.where(@conditions).first.should == existing | ||
| 132 | + @app.find_or_create_err!(@conditions).should == existing | ||
| 133 | + end | ||
| 134 | + | ||
| 135 | + it 'assigns the returned err to the given app' do | ||
| 136 | + @app.find_or_create_err!(@conditions).app.should == @app | ||
| 137 | + end | ||
| 138 | + | ||
| 139 | + it 'creates a new problem if a matching one does not already exist' do | ||
| 140 | + Err.where(@conditions).first.should be_nil | ||
| 141 | + lambda { | ||
| 142 | + @app.find_or_create_err!(@conditions) | ||
| 143 | + }.should change(Problem,:count).by(1) | ||
| 144 | + end | ||
| 145 | + end | ||
| 146 | + | ||
| 147 | + | ||
| 113 | end | 148 | end |
| 114 | - |
spec/models/deploy_spec.rb
| @@ -26,20 +26,20 @@ describe Deploy do | @@ -26,20 +26,20 @@ describe Deploy do | ||
| 26 | context 'when the app has resolve_errs_on_deploy set to false' do | 26 | context 'when the app has resolve_errs_on_deploy set to false' do |
| 27 | it 'should not resolve the apps errs' do | 27 | it 'should not resolve the apps errs' do |
| 28 | app = Factory(:app, :resolve_errs_on_deploy => false) | 28 | app = Factory(:app, :resolve_errs_on_deploy => false) |
| 29 | - @errs = 3.times.inject([]) {|errs,_| errs << Factory(:err, :resolved => false, :app => app)} | 29 | + @problems = 3.times.map{Factory(:err, :problem => Factory(:problem, :resolved => false, :app => app))} |
| 30 | Factory(:deploy, :app => app) | 30 | Factory(:deploy, :app => app) |
| 31 | - app.reload.errs.none?{|err| err.resolved?}.should == true | 31 | + app.reload.problems.none?{|problem| problem.resolved?}.should == true |
| 32 | end | 32 | end |
| 33 | end | 33 | end |
| 34 | 34 | ||
| 35 | context 'when the app has resolve_errs_on_deploy set to true' do | 35 | context 'when the app has resolve_errs_on_deploy set to true' do |
| 36 | it 'should resolve the apps errs that were in the same environment' do | 36 | it 'should resolve the apps errs that were in the same environment' do |
| 37 | app = Factory(:app, :resolve_errs_on_deploy => true) | 37 | app = Factory(:app, :resolve_errs_on_deploy => true) |
| 38 | - @prod_errs = 3.times.inject([]) {|errs,_| errs << Factory(:err, :resolved => false, :app => app, :environment => 'production')} | ||
| 39 | - @staging_errs = 3.times.inject([]) {|errs,_| errs << Factory(:err, :resolved => false, :app => app, :environment => 'staging')} | 38 | + @prod_errs = 3.times.map{Factory(:problem, :resolved => false, :app => app, :environment => 'production')} |
| 39 | + @staging_errs = 3.times.map{Factory(:problem, :resolved => false, :app => app, :environment => 'staging')} | ||
| 40 | Factory(:deploy, :app => app, :environment => 'production') | 40 | Factory(:deploy, :app => app, :environment => 'production') |
| 41 | - @prod_errs.all?{|err| err.reload.resolved?}.should == true | ||
| 42 | - @staging_errs.all?{|err| err.reload.resolved?}.should == false | 41 | + @prod_errs.all?{|problem| problem.reload.resolved?}.should == true |
| 42 | + @staging_errs.all?{|problem| problem.reload.resolved?}.should == false | ||
| 43 | end | 43 | end |
| 44 | end | 44 | end |
| 45 | 45 |
spec/models/err_spec.rb
| 1 | require 'spec_helper' | 1 | require 'spec_helper' |
| 2 | 2 | ||
| 3 | describe Err do | 3 | describe Err do |
| 4 | - | 4 | + |
| 5 | context 'validations' do | 5 | context 'validations' do |
| 6 | it 'requires a klass' do | 6 | it 'requires a klass' do |
| 7 | err = Factory.build(:err, :klass => nil) | 7 | err = Factory.build(:err, :klass => nil) |
| 8 | err.should_not be_valid | 8 | err.should_not be_valid |
| 9 | err.errors[:klass].should include("can't be blank") | 9 | err.errors[:klass].should include("can't be blank") |
| 10 | end | 10 | end |
| 11 | - | 11 | + |
| 12 | it 'requires an environment' do | 12 | it 'requires an environment' do |
| 13 | err = Factory.build(:err, :environment => nil) | 13 | err = Factory.build(:err, :environment => nil) |
| 14 | err.should_not be_valid | 14 | err.should_not be_valid |
| 15 | err.errors[:environment].should include("can't be blank") | 15 | err.errors[:environment].should include("can't be blank") |
| 16 | end | 16 | end |
| 17 | end | 17 | end |
| 18 | - | ||
| 19 | - context '#for' do | ||
| 20 | - before do | ||
| 21 | - @app = Factory(:app) | ||
| 22 | - @conditions = { | ||
| 23 | - :app => @app, | ||
| 24 | - :klass => 'Whoops', | ||
| 25 | - :component => 'Foo', | ||
| 26 | - :action => 'bar', | ||
| 27 | - :environment => 'production' | ||
| 28 | - } | ||
| 29 | - end | ||
| 30 | - | ||
| 31 | - it 'returns the correct err if one already exists' do | ||
| 32 | - existing = Err.create(@conditions) | ||
| 33 | - Err.for(@conditions).should == existing | ||
| 34 | - end | ||
| 35 | - | ||
| 36 | - it 'assigns the returned err to the given app' do | ||
| 37 | - Err.for(@conditions).app.should == @app | ||
| 38 | - end | ||
| 39 | - | ||
| 40 | - it 'creates a new err if a matching one does not already exist' do | ||
| 41 | - Err.where(@conditions.except(:app)).exists?.should == false | ||
| 42 | - lambda { | ||
| 43 | - Err.for(@conditions) | ||
| 44 | - }.should change(Err,:count).by(1) | ||
| 45 | - end | ||
| 46 | - end | ||
| 47 | - | ||
| 48 | - context '#last_notice_at' do | ||
| 49 | - it "returns the created_at timestamp of the latest notice" do | ||
| 50 | - err = Factory(:err) | ||
| 51 | - err.last_notice_at.should be_nil | ||
| 52 | - | ||
| 53 | - notice1 = Factory(:notice, :err => err) | ||
| 54 | - err.last_notice_at.should == notice1.created_at | ||
| 55 | - | ||
| 56 | - notice2 = Factory(:notice, :err => err) | ||
| 57 | - err.last_notice_at.should == notice2.created_at | ||
| 58 | - end | ||
| 59 | - end | ||
| 60 | - | ||
| 61 | - context '#message' do | ||
| 62 | - it "returns klass by default" do | ||
| 63 | - err = Factory(:err) | ||
| 64 | - err.message.should == err.klass | ||
| 65 | - end | ||
| 66 | - | ||
| 67 | - it 'returns the message from the first notice' do | ||
| 68 | - err = Factory(:err) | ||
| 69 | - notice1 = Factory(:notice, :err => err, :message => 'ERR 1') | ||
| 70 | - notice2 = Factory(:notice, :err => err, :message => 'ERR 2') | ||
| 71 | - err.message.should == notice1.message | ||
| 72 | - end | ||
| 73 | - | ||
| 74 | - it "adding a notice caches its message" do | ||
| 75 | - err = Factory(:err) | ||
| 76 | - lambda { | ||
| 77 | - notice1 = Factory(:notice, :err => err, :message => 'ERR 1')}.should change(err, :message).from(err.klass).to('ERR 1') | ||
| 78 | - end | ||
| 79 | - end | ||
| 80 | - | ||
| 81 | - context "#resolved?" do | ||
| 82 | - it "should start out as unresolved" do | ||
| 83 | - err = Err.new | ||
| 84 | - err.should_not be_resolved | ||
| 85 | - err.should be_unresolved | ||
| 86 | - end | ||
| 87 | - | ||
| 88 | - it "should be able to be resolved" do | ||
| 89 | - err = Factory(:err) | ||
| 90 | - err.should_not be_resolved | ||
| 91 | - err.resolve! | ||
| 92 | - err.reload.should be_resolved | ||
| 93 | - end | ||
| 94 | - end | ||
| 95 | - | ||
| 96 | - context "resolve!" do | ||
| 97 | - it "marks the err as resolved" do | ||
| 98 | - err = Factory(:err) | ||
| 99 | - err.should_not be_resolved | ||
| 100 | - err.resolve! | ||
| 101 | - err.should be_resolved | ||
| 102 | - end | ||
| 103 | - | ||
| 104 | - it "should throw an err if it's not successful" do | ||
| 105 | - err = Factory(:err) | ||
| 106 | - err.should_not be_resolved | ||
| 107 | - err.klass = nil | ||
| 108 | - err.should_not be_valid | ||
| 109 | - lambda { | ||
| 110 | - err.resolve! | ||
| 111 | - }.should raise_error(Mongoid::Errors::Validations) | ||
| 112 | - end | ||
| 113 | - end | ||
| 114 | - | ||
| 115 | - context "Scopes" do | ||
| 116 | - context "resolved" do | ||
| 117 | - it 'only finds resolved Errs' do | ||
| 118 | - resolved = Factory(:err, :resolved => true) | ||
| 119 | - unresolved = Factory(:err, :resolved => false) | ||
| 120 | - Err.resolved.all.should include(resolved) | ||
| 121 | - Err.resolved.all.should_not include(unresolved) | ||
| 122 | - end | ||
| 123 | - end | ||
| 124 | - | ||
| 125 | - context "unresolved" do | ||
| 126 | - it 'only finds unresolved Errs' do | ||
| 127 | - resolved = Factory(:err, :resolved => true) | ||
| 128 | - unresolved = Factory(:err, :resolved => false) | ||
| 129 | - Err.unresolved.all.should_not include(resolved) | ||
| 130 | - Err.unresolved.all.should include(unresolved) | ||
| 131 | - end | ||
| 132 | - end | ||
| 133 | - end | ||
| 134 | - | ||
| 135 | - context 'being created' do | ||
| 136 | - context 'when the app has err notifications set to false' do | ||
| 137 | - it 'should not send an email notification' do | ||
| 138 | - app = Factory(:app_with_watcher, :notify_on_errs => false) | ||
| 139 | - Mailer.should_not_receive(:err_notification) | ||
| 140 | - Factory(:err, :app => app) | ||
| 141 | - end | ||
| 142 | - end | ||
| 143 | - end | ||
| 144 | - | ||
| 145 | - context "notice counter cache" do | ||
| 146 | - | ||
| 147 | - before do | ||
| 148 | - @app = Factory(:app) | ||
| 149 | - @err = Factory(:err, :app => @app) | ||
| 150 | - end | ||
| 151 | - | ||
| 152 | - it "#notices_count returns 0 by default" do | ||
| 153 | - @err.notices_count.should == 0 | ||
| 154 | - end | ||
| 155 | - | ||
| 156 | - it "adding a notice increases #notices_count by 1" do | ||
| 157 | - lambda { | ||
| 158 | - notice1 = Factory(:notice, :err => @err, :message => 'ERR 1')}.should change(@err, :notices_count).from(0).to(1) | ||
| 159 | - end | ||
| 160 | - | ||
| 161 | - it "removing a notice decreases #notices_count by 1" do | ||
| 162 | - notice1 = Factory(:notice, :err => @err, :message => 'ERR 1') | ||
| 163 | - lambda { | ||
| 164 | - @err.notices.first.destroy | ||
| 165 | - @err.reload | ||
| 166 | - }.should change(@err, :notices_count).from(1).to(0) | ||
| 167 | - end | ||
| 168 | - end | ||
| 169 | - | ||
| 170 | - | 18 | + |
| 171 | end | 19 | end |
| 172 | - |
spec/models/issue_trackers/fogbugz_tracker_spec.rb
| 1 | require 'spec_helper' | 1 | require 'spec_helper' |
| 2 | 2 | ||
| 3 | describe FogbugzTracker do | 3 | describe FogbugzTracker do |
| 4 | - it "should create an issue on Fogbugz with err params, and set issue link for err" do | 4 | + it "should create an issue on Fogbugz with problem params, and set issue link for problem" do |
| 5 | notice = Factory :notice | 5 | notice = Factory :notice |
| 6 | - tracker = Factory :fogbugz_tracker, :app => notice.err.app | ||
| 7 | - err = notice.err | ||
| 8 | - | 6 | + tracker = Factory :fogbugz_tracker, :app => notice.app |
| 7 | + problem = notice.problem | ||
| 8 | + | ||
| 9 | number = 123 | 9 | number = 123 |
| 10 | @issue_link = "https://#{tracker.account}.fogbugz.com/default.asp?#{number}" | 10 | @issue_link = "https://#{tracker.account}.fogbugz.com/default.asp?#{number}" |
| 11 | response = "<response><token>12345</token><case><ixBug>123</ixBug></case></response>" | 11 | response = "<response><token>12345</token><case><ixBug>123</ixBug></case></response>" |
| @@ -13,11 +13,10 @@ describe FogbugzTracker do | @@ -13,11 +13,10 @@ describe FogbugzTracker do | ||
| 13 | http_mock.should_receive(:new).and_return(http_mock) | 13 | http_mock.should_receive(:new).and_return(http_mock) |
| 14 | http_mock.should_receive(:request).twice.and_return(response) | 14 | http_mock.should_receive(:request).twice.and_return(response) |
| 15 | Fogbugz.adapter[:http] = http_mock | 15 | Fogbugz.adapter[:http] = http_mock |
| 16 | - | ||
| 17 | - err.app.issue_tracker.create_issue(err) | ||
| 18 | - err.reload | ||
| 19 | - | ||
| 20 | - err.issue_link.should == @issue_link | 16 | + |
| 17 | + problem.app.issue_tracker.create_issue(problem) | ||
| 18 | + problem.reload | ||
| 19 | + | ||
| 20 | + problem.issue_link.should == @issue_link | ||
| 21 | end | 21 | end |
| 22 | end | 22 | end |
| 23 | - |
spec/models/issue_trackers/github_issues_tracker_spec.rb
| 1 | require 'spec_helper' | 1 | require 'spec_helper' |
| 2 | 2 | ||
| 3 | describe GithubIssuesTracker do | 3 | describe GithubIssuesTracker do |
| 4 | - it "should create an issue on Github Issues with err params, and set issue link for err" do | 4 | + it "should create an issue on Github Issues with problem params, and set issue link for problem" do |
| 5 | notice = Factory :notice | 5 | notice = Factory :notice |
| 6 | - tracker = Factory :github_issues_tracker, :app => notice.err.app | ||
| 7 | - err = notice.err | ||
| 8 | - | 6 | + tracker = Factory :github_issues_tracker, :app => notice.app |
| 7 | + problem = notice.problem | ||
| 8 | + | ||
| 9 | number = 5 | 9 | number = 5 |
| 10 | @issue_link = "https://github.com/#{tracker.project_id}/issues/#{number}" | 10 | @issue_link = "https://github.com/#{tracker.project_id}/issues/#{number}" |
| 11 | body = <<EOF | 11 | body = <<EOF |
| @@ -26,16 +26,15 @@ describe GithubIssuesTracker do | @@ -26,16 +26,15 @@ describe GithubIssuesTracker do | ||
| 26 | EOF | 26 | EOF |
| 27 | stub_request(:post, "https://#{tracker.username}%2Ftoken:#{tracker.api_token}@github.com/api/v2/json/issues/open/#{tracker.project_id}"). | 27 | stub_request(:post, "https://#{tracker.username}%2Ftoken:#{tracker.api_token}@github.com/api/v2/json/issues/open/#{tracker.project_id}"). |
| 28 | to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body ) | 28 | to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body ) |
| 29 | - | ||
| 30 | - err.app.issue_tracker.create_issue(err) | ||
| 31 | - err.reload | ||
| 32 | - | 29 | + |
| 30 | + problem.app.issue_tracker.create_issue(problem) | ||
| 31 | + problem.reload | ||
| 32 | + | ||
| 33 | requested = have_requested(:post, "https://#{tracker.username}%2Ftoken:#{tracker.api_token}@github.com/api/v2/json/issues/open/#{tracker.project_id}") | 33 | requested = have_requested(:post, "https://#{tracker.username}%2Ftoken:#{tracker.api_token}@github.com/api/v2/json/issues/open/#{tracker.project_id}") |
| 34 | WebMock.should requested.with(:headers => {'Content-Type' => 'application/x-www-form-urlencoded'}) | 34 | WebMock.should requested.with(:headers => {'Content-Type' => 'application/x-www-form-urlencoded'}) |
| 35 | WebMock.should requested.with(:body => /title=%5Bproduction%5D%5Bfoo%23bar%5D%20FooError%3A%20Too%20Much%20Bar/) | 35 | WebMock.should requested.with(:body => /title=%5Bproduction%5D%5Bfoo%23bar%5D%20FooError%3A%20Too%20Much%20Bar/) |
| 36 | WebMock.should requested.with(:body => /See%20this%20exception%20on%20Errbit/) | 36 | WebMock.should requested.with(:body => /See%20this%20exception%20on%20Errbit/) |
| 37 | - | ||
| 38 | - err.issue_link.should == @issue_link | 37 | + |
| 38 | + problem.issue_link.should == @issue_link | ||
| 39 | end | 39 | end |
| 40 | end | 40 | end |
| 41 | - |
spec/models/issue_trackers/lighthouse_tracker_spec.rb
| 1 | require 'spec_helper' | 1 | require 'spec_helper' |
| 2 | 2 | ||
| 3 | describe LighthouseTracker do | 3 | describe LighthouseTracker do |
| 4 | - it "should create an issue on Lighthouse with err params, and set issue link for err" do | 4 | + it "should create an issue on Lighthouse with problem params, and set issue link for problem" do |
| 5 | notice = Factory :notice | 5 | notice = Factory :notice |
| 6 | - tracker = Factory :lighthouse_tracker, :app => notice.err.app | ||
| 7 | - err = notice.err | ||
| 8 | - | 6 | + tracker = Factory :lighthouse_tracker, :app => notice.app |
| 7 | + problem = notice.problem | ||
| 8 | + | ||
| 9 | number = 5 | 9 | number = 5 |
| 10 | @issue_link = "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets/#{number}.xml" | 10 | @issue_link = "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets/#{number}.xml" |
| 11 | body = "<ticket><number type=\"integer\">#{number}</number></ticket>" | 11 | body = "<ticket><number type=\"integer\">#{number}</number></ticket>" |
| 12 | stub_request(:post, "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets.xml"). | 12 | stub_request(:post, "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets.xml"). |
| 13 | to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body ) | 13 | to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body ) |
| 14 | - | ||
| 15 | - err.app.issue_tracker.create_issue(err) | ||
| 16 | - err.reload | ||
| 17 | - | 14 | + |
| 15 | + problem.app.issue_tracker.create_issue(problem) | ||
| 16 | + problem.reload | ||
| 17 | + | ||
| 18 | requested = have_requested(:post, "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets.xml") | 18 | requested = have_requested(:post, "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets.xml") |
| 19 | WebMock.should requested.with(:headers => {'X-Lighthousetoken' => tracker.api_token}) | 19 | WebMock.should requested.with(:headers => {'X-Lighthousetoken' => tracker.api_token}) |
| 20 | WebMock.should requested.with(:body => /<tag>errbit<\/tag>/) | 20 | WebMock.should requested.with(:body => /<tag>errbit<\/tag>/) |
| 21 | - WebMock.should requested.with(:body => /<title>\[#{ err.environment }\]\[#{err.where}\] #{err.message.to_s.truncate(100)}<\/title>/) | 21 | + WebMock.should requested.with(:body => /<title>\[#{ problem.environment }\]\[#{problem.where}\] #{problem.message.to_s.truncate(100)}<\/title>/) |
| 22 | WebMock.should requested.with(:body => /<body>.+<\/body>/m) | 22 | WebMock.should requested.with(:body => /<body>.+<\/body>/m) |
| 23 | - | ||
| 24 | - err.issue_link.should == @issue_link.sub(/\.xml$/, '') | 23 | + |
| 24 | + problem.issue_link.should == @issue_link.sub(/\.xml$/, '') | ||
| 25 | end | 25 | end |
| 26 | end | 26 | end |
| 27 | - |
spec/models/issue_trackers/mingle_tracker_spec.rb
| 1 | require 'spec_helper' | 1 | require 'spec_helper' |
| 2 | 2 | ||
| 3 | describe MingleTracker do | 3 | describe MingleTracker do |
| 4 | - it "should create an issue on Mingle with err params, and set issue link for err" do | 4 | + it "should create an issue on Mingle with problem params, and set issue link for problem" do |
| 5 | notice = Factory :notice | 5 | notice = Factory :notice |
| 6 | - tracker = Factory :mingle_tracker, :app => notice.err.app | ||
| 7 | - err = notice.err | ||
| 8 | - | 6 | + tracker = Factory :mingle_tracker, :app => notice.app |
| 7 | + problem = notice.problem | ||
| 8 | + | ||
| 9 | number = 5 | 9 | number = 5 |
| 10 | @issue_link = "#{tracker.account}/projects/#{tracker.project_id}/cards/#{number}.xml" | 10 | @issue_link = "#{tracker.account}/projects/#{tracker.project_id}/cards/#{number}.xml" |
| 11 | @basic_auth = tracker.account.gsub("://", "://#{tracker.username}:#{tracker.password}@") | 11 | @basic_auth = tracker.account.gsub("://", "://#{tracker.username}:#{tracker.password}@") |
| 12 | body = "<card><id type=\"integer\">#{number}</id></card>" | 12 | body = "<card><id type=\"integer\">#{number}</id></card>" |
| 13 | stub_request(:post, "#{@basic_auth}/api/v1/projects/#{tracker.project_id}/cards.xml"). | 13 | stub_request(:post, "#{@basic_auth}/api/v1/projects/#{tracker.project_id}/cards.xml"). |
| 14 | to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body ) | 14 | to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body ) |
| 15 | - | ||
| 16 | - err.app.issue_tracker.create_issue(err) | ||
| 17 | - err.reload | ||
| 18 | - | 15 | + |
| 16 | + problem.app.issue_tracker.create_issue(problem) | ||
| 17 | + problem.reload | ||
| 18 | + | ||
| 19 | requested = have_requested(:post, "#{@basic_auth}/api/v1/projects/#{tracker.project_id}/cards.xml") | 19 | requested = have_requested(:post, "#{@basic_auth}/api/v1/projects/#{tracker.project_id}/cards.xml") |
| 20 | WebMock.should requested.with(:headers => {'Content-Type' => 'application/xml'}) | 20 | WebMock.should requested.with(:headers => {'Content-Type' => 'application/xml'}) |
| 21 | WebMock.should requested.with(:body => /FooError: Too Much Bar/) | 21 | WebMock.should requested.with(:body => /FooError: Too Much Bar/) |
| 22 | WebMock.should requested.with(:body => /See this exception on Errbit/) | 22 | WebMock.should requested.with(:body => /See this exception on Errbit/) |
| 23 | WebMock.should requested.with(:body => /<card-type-name>Defect<\/card-type-name>/) | 23 | WebMock.should requested.with(:body => /<card-type-name>Defect<\/card-type-name>/) |
| 24 | - | ||
| 25 | - err.issue_link.should == @issue_link.sub(/\.xml$/, '') | 24 | + |
| 25 | + problem.issue_link.should == @issue_link.sub(/\.xml$/, '') | ||
| 26 | end | 26 | end |
| 27 | end | 27 | end |
| 28 | - |
spec/models/issue_trackers/pivotal_labs_tracker_spec.rb
| 1 | require 'spec_helper' | 1 | require 'spec_helper' |
| 2 | 2 | ||
| 3 | describe PivotalLabsTracker do | 3 | describe PivotalLabsTracker do |
| 4 | - it "should create an issue on Pivotal Tracker with err params, and set issue link for err" do | 4 | + it "should create an issue on Pivotal Tracker with problem params, and set issue link for problem" do |
| 5 | notice = Factory :notice | 5 | notice = Factory :notice |
| 6 | - tracker = Factory :pivotal_labs_tracker, :app => notice.err.app, :project_id => 10 | ||
| 7 | - err = notice.err | ||
| 8 | - | 6 | + tracker = Factory :pivotal_labs_tracker, :app => notice.app, :project_id => 10 |
| 7 | + problem = notice.problem | ||
| 8 | + | ||
| 9 | story_id = 5 | 9 | story_id = 5 |
| 10 | @issue_link = "https://www.pivotaltracker.com/story/show/#{story_id}" | 10 | @issue_link = "https://www.pivotaltracker.com/story/show/#{story_id}" |
| 11 | project_body = "<project><id>#{tracker.project_id}</id><name>TestProject</name></project>" | 11 | project_body = "<project><id>#{tracker.project_id}</id><name>TestProject</name></project>" |
| @@ -14,17 +14,16 @@ describe PivotalLabsTracker do | @@ -14,17 +14,16 @@ describe PivotalLabsTracker do | ||
| 14 | story_body = "<story><name>Test Story</name><id>#{story_id}</id></story>" | 14 | story_body = "<story><name>Test Story</name><id>#{story_id}</id></story>" |
| 15 | stub_request(:post, "https://www.pivotaltracker.com/services/v3/projects/#{tracker.project_id}/stories"). | 15 | stub_request(:post, "https://www.pivotaltracker.com/services/v3/projects/#{tracker.project_id}/stories"). |
| 16 | to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => story_body ) | 16 | to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => story_body ) |
| 17 | - | ||
| 18 | - err.app.issue_tracker.create_issue(err) | ||
| 19 | - err.reload | ||
| 20 | - | 17 | + |
| 18 | + problem.app.issue_tracker.create_issue(problem) | ||
| 19 | + problem.reload | ||
| 20 | + | ||
| 21 | requested = have_requested(:post, "https://www.pivotaltracker.com/services/v3/projects/#{tracker.project_id}/stories") | 21 | requested = have_requested(:post, "https://www.pivotaltracker.com/services/v3/projects/#{tracker.project_id}/stories") |
| 22 | WebMock.should requested.with(:headers => {'X-Trackertoken' => tracker.api_token}) | 22 | WebMock.should requested.with(:headers => {'X-Trackertoken' => tracker.api_token}) |
| 23 | WebMock.should requested.with(:body => /See this exception on Errbit/) | 23 | WebMock.should requested.with(:body => /See this exception on Errbit/) |
| 24 | - WebMock.should requested.with(:body => /<name>\[#{ err.environment }\]\[#{err.where}\] #{err.message.to_s.truncate(100)}<\/name>/) | 24 | + WebMock.should requested.with(:body => /<name>\[#{ problem.environment }\]\[#{problem.where}\] #{problem.message.to_s.truncate(100)}<\/name>/) |
| 25 | WebMock.should requested.with(:body => /<description>.+<\/description>/m) | 25 | WebMock.should requested.with(:body => /<description>.+<\/description>/m) |
| 26 | - | ||
| 27 | - err.issue_link.should == @issue_link | 26 | + |
| 27 | + problem.issue_link.should == @issue_link | ||
| 28 | end | 28 | end |
| 29 | end | 29 | end |
| 30 | - |
spec/models/issue_trackers/redmine_tracker_spec.rb
| 1 | require 'spec_helper' | 1 | require 'spec_helper' |
| 2 | 2 | ||
| 3 | describe RedmineTracker do | 3 | describe RedmineTracker do |
| 4 | - it "should create an issue on Redmine with err params, and set issue link for err" do | 4 | + it "should create an issue on Redmine with problem params, and set issue link for problem" do |
| 5 | notice = Factory :notice | 5 | notice = Factory :notice |
| 6 | - tracker = Factory :redmine_tracker, :app => notice.err.app, :project_id => 10 | ||
| 7 | - err = notice.err | 6 | + tracker = Factory :redmine_tracker, :app => notice.app, :project_id => 10 |
| 7 | + problem = notice.problem | ||
| 8 | number = 5 | 8 | number = 5 |
| 9 | @issue_link = "#{tracker.account}/issues/#{number}.xml?project_id=#{tracker.project_id}" | 9 | @issue_link = "#{tracker.account}/issues/#{number}.xml?project_id=#{tracker.project_id}" |
| 10 | body = "<issue><subject>my subject</subject><id>#{number}</id></issue>" | 10 | body = "<issue><subject>my subject</subject><id>#{number}</id></issue>" |
| 11 | stub_request(:post, "#{tracker.account}/issues.xml"). | 11 | stub_request(:post, "#{tracker.account}/issues.xml"). |
| 12 | to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body ) | 12 | to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body ) |
| 13 | - | ||
| 14 | - err.app.issue_tracker.create_issue(err) | ||
| 15 | - err.reload | ||
| 16 | - | 13 | + |
| 14 | + problem.app.issue_tracker.create_issue(problem) | ||
| 15 | + problem.reload | ||
| 16 | + | ||
| 17 | requested = have_requested(:post, "#{tracker.account}/issues.xml") | 17 | requested = have_requested(:post, "#{tracker.account}/issues.xml") |
| 18 | WebMock.should requested.with(:headers => {'X-Redmine-API-Key' => tracker.api_token}) | 18 | WebMock.should requested.with(:headers => {'X-Redmine-API-Key' => tracker.api_token}) |
| 19 | WebMock.should requested.with(:body => /<project-id>#{tracker.project_id}<\/project-id>/) | 19 | WebMock.should requested.with(:body => /<project-id>#{tracker.project_id}<\/project-id>/) |
| 20 | - WebMock.should requested.with(:body => /<subject>\[#{ err.environment }\]\[#{err.where}\] #{err.message.to_s.truncate(100)}<\/subject>/) | 20 | + WebMock.should requested.with(:body => /<subject>\[#{ problem.environment }\]\[#{problem.where}\] #{problem.message.to_s.truncate(100)}<\/subject>/) |
| 21 | WebMock.should requested.with(:body => /<description>.+<\/description>/m) | 21 | WebMock.should requested.with(:body => /<description>.+<\/description>/m) |
| 22 | - | ||
| 23 | - err.issue_link.should == @issue_link.sub(/\.xml/, '') | 22 | + |
| 23 | + problem.issue_link.should == @issue_link.sub(/\.xml/, '') | ||
| 24 | end | 24 | end |
| 25 | end | 25 | end |
| 26 | - |
spec/models/notice_spec.rb
| 1 | require 'spec_helper' | 1 | require 'spec_helper' |
| 2 | 2 | ||
| 3 | describe Notice do | 3 | describe Notice do |
| 4 | - | 4 | + |
| 5 | + | ||
| 5 | context 'validations' do | 6 | context 'validations' do |
| 6 | it 'requires a backtrace' do | 7 | it 'requires a backtrace' do |
| 7 | notice = Factory.build(:notice, :backtrace => nil) | 8 | notice = Factory.build(:notice, :backtrace => nil) |
| 8 | notice.should_not be_valid | 9 | notice.should_not be_valid |
| 9 | notice.errors[:backtrace].should include("can't be blank") | 10 | notice.errors[:backtrace].should include("can't be blank") |
| 10 | end | 11 | end |
| 11 | - | 12 | + |
| 12 | it 'requires the server_environment' do | 13 | it 'requires the server_environment' do |
| 13 | notice = Factory.build(:notice, :server_environment => nil) | 14 | notice = Factory.build(:notice, :server_environment => nil) |
| 14 | notice.should_not be_valid | 15 | notice.should_not be_valid |
| 15 | notice.errors[:server_environment].should include("can't be blank") | 16 | notice.errors[:server_environment].should include("can't be blank") |
| 16 | end | 17 | end |
| 17 | - | 18 | + |
| 18 | it 'requires the notifier' do | 19 | it 'requires the notifier' do |
| 19 | notice = Factory.build(:notice, :notifier => nil) | 20 | notice = Factory.build(:notice, :notifier => nil) |
| 20 | notice.should_not be_valid | 21 | notice.should_not be_valid |
| 21 | notice.errors[:notifier].should include("can't be blank") | 22 | notice.errors[:notifier].should include("can't be blank") |
| 22 | end | 23 | end |
| 23 | end | 24 | end |
| 24 | - | 25 | + |
| 26 | + | ||
| 25 | context '.in_app_backtrace_line?' do | 27 | context '.in_app_backtrace_line?' do |
| 26 | let(:backtrace) do [{ | 28 | let(:backtrace) do [{ |
| 27 | 'number' => rand(999), | 29 | 'number' => rand(999), |
| @@ -50,22 +52,23 @@ describe Notice do | @@ -50,22 +52,23 @@ describe Notice do | ||
| 50 | Notice.in_app_backtrace_line?(backtrace[2]).should == true | 52 | Notice.in_app_backtrace_line?(backtrace[2]).should == true |
| 51 | end | 53 | end |
| 52 | end | 54 | end |
| 53 | - | 55 | + |
| 56 | + | ||
| 54 | context '#from_xml' do | 57 | context '#from_xml' do |
| 55 | before do | 58 | before do |
| 56 | @xml = Rails.root.join('spec','fixtures','hoptoad_test_notice.xml').read | 59 | @xml = Rails.root.join('spec','fixtures','hoptoad_test_notice.xml').read |
| 57 | @app = Factory(:app, :api_key => 'APIKEY') | 60 | @app = Factory(:app, :api_key => 'APIKEY') |
| 58 | Digest::MD5.stub(:hexdigest).and_return('fingerprintdigest') | 61 | Digest::MD5.stub(:hexdigest).and_return('fingerprintdigest') |
| 59 | end | 62 | end |
| 60 | - | 63 | + |
| 61 | it 'finds the correct app' do | 64 | it 'finds the correct app' do |
| 62 | @notice = Notice.from_xml(@xml) | 65 | @notice = Notice.from_xml(@xml) |
| 63 | @notice.err.app.should == @app | 66 | @notice.err.app.should == @app |
| 64 | end | 67 | end |
| 65 | - | 68 | + |
| 66 | it 'finds the correct err for the notice' do | 69 | it 'finds the correct err for the notice' do |
| 67 | - Err.should_receive(:for).with({ | ||
| 68 | - :app => @app, | 70 | + App.should_receive(:find_by_api_key!).and_return(@app) |
| 71 | + @app.should_receive(:find_or_create_err!).with({ | ||
| 69 | :klass => 'HoptoadTestingException', | 72 | :klass => 'HoptoadTestingException', |
| 70 | :component => 'application', | 73 | :component => 'application', |
| 71 | :action => 'verify', | 74 | :action => 'verify', |
| @@ -75,70 +78,72 @@ describe Notice do | @@ -75,70 +78,72 @@ describe Notice do | ||
| 75 | err.notices.stub(:create!) | 78 | err.notices.stub(:create!) |
| 76 | @notice = Notice.from_xml(@xml) | 79 | @notice = Notice.from_xml(@xml) |
| 77 | end | 80 | end |
| 78 | - | ||
| 79 | - it 'marks the err as unresolve if it was previously resolved' do | ||
| 80 | - Err.should_receive(:for).with({ | ||
| 81 | - :app => @app, | 81 | + |
| 82 | + | ||
| 83 | + it 'marks the err as unresolved if it was previously resolved' do | ||
| 84 | + App.should_receive(:find_by_api_key!).and_return(@app) | ||
| 85 | + @app.should_receive(:find_or_create_err!).with({ | ||
| 82 | :klass => 'HoptoadTestingException', | 86 | :klass => 'HoptoadTestingException', |
| 83 | :component => 'application', | 87 | :component => 'application', |
| 84 | :action => 'verify', | 88 | :action => 'verify', |
| 85 | :environment => 'development', | 89 | :environment => 'development', |
| 86 | :fingerprint => 'fingerprintdigest' | 90 | :fingerprint => 'fingerprintdigest' |
| 87 | - }).and_return(err = Factory(:err, :resolved => true)) | 91 | + }).and_return(err = Factory(:err, :problem => Factory(:problem, :resolved => true))) |
| 88 | err.should be_resolved | 92 | err.should be_resolved |
| 89 | @notice = Notice.from_xml(@xml) | 93 | @notice = Notice.from_xml(@xml) |
| 90 | @notice.err.should == err | 94 | @notice.err.should == err |
| 91 | @notice.err.should_not be_resolved | 95 | @notice.err.should_not be_resolved |
| 92 | end | 96 | end |
| 93 | - | 97 | + |
| 94 | it 'should create a new notice' do | 98 | it 'should create a new notice' do |
| 95 | @notice = Notice.from_xml(@xml) | 99 | @notice = Notice.from_xml(@xml) |
| 96 | @notice.should be_persisted | 100 | @notice.should be_persisted |
| 97 | end | 101 | end |
| 98 | - | 102 | + |
| 99 | it 'assigns an err to the notice' do | 103 | it 'assigns an err to the notice' do |
| 100 | @notice = Notice.from_xml(@xml) | 104 | @notice = Notice.from_xml(@xml) |
| 101 | @notice.err.should be_a(Err) | 105 | @notice.err.should be_a(Err) |
| 102 | end | 106 | end |
| 103 | - | 107 | + |
| 104 | it 'captures the err message' do | 108 | it 'captures the err message' do |
| 105 | @notice = Notice.from_xml(@xml) | 109 | @notice = Notice.from_xml(@xml) |
| 106 | @notice.message.should == 'HoptoadTestingException: Testing hoptoad via "rake hoptoad:test". If you can see this, it works.' | 110 | @notice.message.should == 'HoptoadTestingException: Testing hoptoad via "rake hoptoad:test". If you can see this, it works.' |
| 107 | end | 111 | end |
| 108 | - | 112 | + |
| 109 | it 'captures the backtrace' do | 113 | it 'captures the backtrace' do |
| 110 | @notice = Notice.from_xml(@xml) | 114 | @notice = Notice.from_xml(@xml) |
| 111 | @notice.backtrace.size.should == 73 | 115 | @notice.backtrace.size.should == 73 |
| 112 | @notice.backtrace.last['file'].should == '[GEM_ROOT]/bin/rake' | 116 | @notice.backtrace.last['file'].should == '[GEM_ROOT]/bin/rake' |
| 113 | end | 117 | end |
| 114 | - | 118 | + |
| 115 | it 'captures the server_environment' do | 119 | it 'captures the server_environment' do |
| 116 | @notice = Notice.from_xml(@xml) | 120 | @notice = Notice.from_xml(@xml) |
| 117 | @notice.server_environment['environment-name'].should == 'development' | 121 | @notice.server_environment['environment-name'].should == 'development' |
| 118 | end | 122 | end |
| 119 | - | 123 | + |
| 120 | it 'captures the request' do | 124 | it 'captures the request' do |
| 121 | @notice = Notice.from_xml(@xml) | 125 | @notice = Notice.from_xml(@xml) |
| 122 | @notice.request['url'].should == 'http://example.org/verify' | 126 | @notice.request['url'].should == 'http://example.org/verify' |
| 123 | @notice.request['params']['controller'].should == 'application' | 127 | @notice.request['params']['controller'].should == 'application' |
| 124 | end | 128 | end |
| 125 | - | 129 | + |
| 126 | it 'captures the notifier' do | 130 | it 'captures the notifier' do |
| 127 | @notice = Notice.from_xml(@xml) | 131 | @notice = Notice.from_xml(@xml) |
| 128 | @notice.notifier['name'].should == 'Hoptoad Notifier' | 132 | @notice.notifier['name'].should == 'Hoptoad Notifier' |
| 129 | end | 133 | end |
| 130 | - | ||
| 131 | - it "should handle params without 'request' section" do | 134 | + |
| 135 | + it "should handle params withour 'request' section" do | ||
| 132 | @xml = Rails.root.join('spec','fixtures','hoptoad_test_notice_without_request_section.xml').read | 136 | @xml = Rails.root.join('spec','fixtures','hoptoad_test_notice_without_request_section.xml').read |
| 133 | lambda { Notice.from_xml(@xml) }.should_not raise_error | 137 | lambda { Notice.from_xml(@xml) }.should_not raise_error |
| 134 | end | 138 | end |
| 135 | - | 139 | + |
| 136 | it "should raise ApiVersionError" do | 140 | it "should raise ApiVersionError" do |
| 137 | @xml = Rails.root.join('spec', 'fixtures', 'hoptoad_test_notice_with_wrong_version.xml').read | 141 | @xml = Rails.root.join('spec', 'fixtures', 'hoptoad_test_notice_with_wrong_version.xml').read |
| 138 | expect { Notice.from_xml(@xml) }.to raise_error(Hoptoad::V2::ApiVersionError) | 142 | expect { Notice.from_xml(@xml) }.to raise_error(Hoptoad::V2::ApiVersionError) |
| 139 | end | 143 | end |
| 140 | end | 144 | end |
| 141 | - | 145 | + |
| 146 | + | ||
| 142 | describe "key sanitization" do | 147 | describe "key sanitization" do |
| 143 | before do | 148 | before do |
| 144 | @hash = { "some.key" => { "$nested.key" => {"$Path" => "/", "some$key" => "key"}}} | 149 | @hash = { "some.key" => { "$nested.key" => {"$Path" => "/", "some$key" => "key"}}} |
| @@ -152,42 +157,44 @@ describe Notice do | @@ -152,42 +157,44 @@ describe Notice do | ||
| 152 | end | 157 | end |
| 153 | end | 158 | end |
| 154 | end | 159 | end |
| 155 | - | 160 | + |
| 161 | + | ||
| 156 | describe "user agent" do | 162 | describe "user agent" do |
| 157 | it "should be parsed and human-readable" do | 163 | it "should be parsed and human-readable" do |
| 158 | notice = Factory.build(:notice, :request => {'cgi-data' => {'HTTP_USER_AGENT' => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16'}}) | 164 | notice = Factory.build(:notice, :request => {'cgi-data' => {'HTTP_USER_AGENT' => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16'}}) |
| 159 | notice.user_agent.browser.should == 'Chrome' | 165 | notice.user_agent.browser.should == 'Chrome' |
| 160 | notice.user_agent.version.to_s.should =~ /^10\.0/ | 166 | notice.user_agent.version.to_s.should =~ /^10\.0/ |
| 161 | end | 167 | end |
| 162 | - | 168 | + |
| 163 | it "should be nil if HTTP_USER_AGENT is blank" do | 169 | it "should be nil if HTTP_USER_AGENT is blank" do |
| 164 | notice = Factory.build(:notice) | 170 | notice = Factory.build(:notice) |
| 165 | notice.user_agent.should == nil | 171 | notice.user_agent.should == nil |
| 166 | end | 172 | end |
| 167 | end | 173 | end |
| 168 | - | 174 | + |
| 175 | + | ||
| 169 | describe "email notifications (configured individually for each app)" do | 176 | describe "email notifications (configured individually for each app)" do |
| 170 | custom_thresholds = [2, 4, 8, 16, 32, 64] | 177 | custom_thresholds = [2, 4, 8, 16, 32, 64] |
| 171 | - | 178 | + |
| 172 | before do | 179 | before do |
| 173 | Errbit::Config.per_app_email_at_notices = true | 180 | Errbit::Config.per_app_email_at_notices = true |
| 174 | @app = Factory(:app_with_watcher, :email_at_notices => custom_thresholds) | 181 | @app = Factory(:app_with_watcher, :email_at_notices => custom_thresholds) |
| 175 | - @err = Factory(:err, :app => @app) | 182 | + @problem = Factory(:err, :problem => @app.problems.create!) |
| 176 | end | 183 | end |
| 177 | - | 184 | + |
| 178 | after do | 185 | after do |
| 179 | Errbit::Config.per_app_email_at_notices = false | 186 | Errbit::Config.per_app_email_at_notices = false |
| 180 | end | 187 | end |
| 181 | - | 188 | + |
| 182 | custom_thresholds.each do |threshold| | 189 | custom_thresholds.each do |threshold| |
| 183 | it "sends an email notification after #{threshold} notice(s)" do | 190 | it "sends an email notification after #{threshold} notice(s)" do |
| 184 | - @err.notices.stub(:count).and_return(threshold) | 191 | + @problem.notices.stub(:count).and_return(threshold) |
| 185 | Mailer.should_receive(:err_notification). | 192 | Mailer.should_receive(:err_notification). |
| 186 | and_return(mock('email', :deliver => true)) | 193 | and_return(mock('email', :deliver => true)) |
| 187 | - Factory(:notice, :err => @err) | 194 | + Factory(:notice, :err => @problem) |
| 188 | end | 195 | end |
| 189 | end | 196 | end |
| 190 | end | 197 | end |
| 191 | - | 198 | + |
| 199 | + | ||
| 192 | end | 200 | end |
| 193 | - |
| @@ -0,0 +1,130 @@ | @@ -0,0 +1,130 @@ | ||
| 1 | +require 'spec_helper' | ||
| 2 | + | ||
| 3 | +describe Problem do | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + context '#last_notice_at' do | ||
| 7 | + it "returns the created_at timestamp of the latest notice" do | ||
| 8 | + err = Factory(:err) | ||
| 9 | + problem = err.problem | ||
| 10 | + problem.should_not be_nil | ||
| 11 | + | ||
| 12 | + problem.last_notice_at.should be_nil | ||
| 13 | + | ||
| 14 | + notice1 = Factory(:notice, :err => err) | ||
| 15 | + problem.last_notice_at.should == notice1.created_at | ||
| 16 | + | ||
| 17 | + notice2 = Factory(:notice, :err => err) | ||
| 18 | + problem.last_notice_at.should == notice2.created_at | ||
| 19 | + end | ||
| 20 | + end | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + context '#message' do | ||
| 24 | + it "adding a notice caches its message" do | ||
| 25 | + err = Factory(:err) | ||
| 26 | + problem = err.problem | ||
| 27 | + lambda { | ||
| 28 | + Factory(:notice, :err => err, :message => 'ERR 1') | ||
| 29 | + }.should change(problem, :message).from(nil).to('ERR 1') | ||
| 30 | + end | ||
| 31 | + end | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + context 'being created' do | ||
| 35 | + context 'when the app has err notifications set to false' do | ||
| 36 | + it 'should not send an email notification' do | ||
| 37 | + app = Factory(:app_with_watcher, :notify_on_errs => false) | ||
| 38 | + Mailer.should_not_receive(:err_notification) | ||
| 39 | + Factory(:problem, :app => app) | ||
| 40 | + end | ||
| 41 | + end | ||
| 42 | + end | ||
| 43 | + | ||
| 44 | + | ||
| 45 | + context "#resolved?" do | ||
| 46 | + it "should start out as unresolved" do | ||
| 47 | + problem = Problem.new | ||
| 48 | + problem.should_not be_resolved | ||
| 49 | + problem.should be_unresolved | ||
| 50 | + end | ||
| 51 | + | ||
| 52 | + it "should be able to be resolved" do | ||
| 53 | + problem = Factory(:problem) | ||
| 54 | + problem.should_not be_resolved | ||
| 55 | + problem.resolve! | ||
| 56 | + problem.reload.should be_resolved | ||
| 57 | + end | ||
| 58 | + end | ||
| 59 | + | ||
| 60 | + | ||
| 61 | + context "resolve!" do | ||
| 62 | + it "marks the problem as resolved" do | ||
| 63 | + problem = Factory(:problem) | ||
| 64 | + problem.should_not be_resolved | ||
| 65 | + problem.resolve! | ||
| 66 | + problem.should be_resolved | ||
| 67 | + end | ||
| 68 | + | ||
| 69 | + it "should throw an err if it's not successful" do | ||
| 70 | + problem = Factory(:problem) | ||
| 71 | + problem.should_not be_resolved | ||
| 72 | + problem.stub!(:valid?).and_return(false) | ||
| 73 | + problem.should_not be_valid | ||
| 74 | + lambda { | ||
| 75 | + problem.resolve! | ||
| 76 | + }.should raise_error(Mongoid::Errors::Validations) | ||
| 77 | + end | ||
| 78 | + end | ||
| 79 | + | ||
| 80 | + | ||
| 81 | + context "Scopes" do | ||
| 82 | + context "resolved" do | ||
| 83 | + it 'only finds resolved Problems' do | ||
| 84 | + resolved = Factory(:problem, :resolved => true) | ||
| 85 | + unresolved = Factory(:problem, :resolved => false) | ||
| 86 | + Problem.resolved.all.should include(resolved) | ||
| 87 | + Problem.resolved.all.should_not include(unresolved) | ||
| 88 | + end | ||
| 89 | + end | ||
| 90 | + | ||
| 91 | + context "unresolved" do | ||
| 92 | + it 'only finds unresolved Problems' do | ||
| 93 | + resolved = Factory(:problem, :resolved => true) | ||
| 94 | + unresolved = Factory(:problem, :resolved => false) | ||
| 95 | + Problem.unresolved.all.should_not include(resolved) | ||
| 96 | + Problem.unresolved.all.should include(unresolved) | ||
| 97 | + end | ||
| 98 | + end | ||
| 99 | + end | ||
| 100 | + | ||
| 101 | + | ||
| 102 | + context "notice counter cache" do | ||
| 103 | + | ||
| 104 | + before do | ||
| 105 | + @app = Factory(:app) | ||
| 106 | + @problem = Factory(:problem, :app => @app) | ||
| 107 | + @err = Factory(:err, :problem => @problem) | ||
| 108 | + end | ||
| 109 | + | ||
| 110 | + it "#notices_count returns 0 by default" do | ||
| 111 | + @problem.notices_count.should == 0 | ||
| 112 | + end | ||
| 113 | + | ||
| 114 | + it "adding a notice increases #notices_count by 1" do | ||
| 115 | + lambda { | ||
| 116 | + Factory(:notice, :err => @err, :message => 'ERR 1') | ||
| 117 | + }.should change(@problem, :notices_count).from(0).to(1) | ||
| 118 | + end | ||
| 119 | + | ||
| 120 | + it "removing a notice decreases #notices_count by 1" do | ||
| 121 | + notice1 = Factory(:notice, :err => @err, :message => 'ERR 1') | ||
| 122 | + lambda { | ||
| 123 | + @err.notices.first.destroy | ||
| 124 | + @problem.reload | ||
| 125 | + }.should change(@problem, :notices_count).from(1).to(0) | ||
| 126 | + end | ||
| 127 | + end | ||
| 128 | + | ||
| 129 | + | ||
| 130 | +end |
spec/views/errs/show.html.haml_spec.rb
| @@ -3,23 +3,24 @@ require 'spec_helper' | @@ -3,23 +3,24 @@ require 'spec_helper' | ||
| 3 | describe "errs/show.html.haml" do | 3 | describe "errs/show.html.haml" do |
| 4 | before do | 4 | before do |
| 5 | err = Factory(:err) | 5 | err = Factory(:err) |
| 6 | + problem = err.problem | ||
| 6 | comment = Factory(:comment) | 7 | comment = Factory(:comment) |
| 7 | - assign :err, err | 8 | + assign :problem, problem |
| 8 | assign :comment, comment | 9 | assign :comment, comment |
| 9 | - assign :app, err.app | ||
| 10 | - assign :notices, err.notices.ordered.paginate(:page => 1, :per_page => 1) | 10 | + assign :app, problem.app |
| 11 | + assign :notices, err.notices.paginate(:page => 1, :per_page => 1) | ||
| 11 | assign :notice, err.notices.first | 12 | assign :notice, err.notices.first |
| 12 | end | 13 | end |
| 13 | - | 14 | + |
| 14 | describe "content_for :action_bar" do | 15 | describe "content_for :action_bar" do |
| 15 | - | 16 | + |
| 16 | it "should confirm the 'resolve' link by default" do | 17 | it "should confirm the 'resolve' link by default" do |
| 17 | render | 18 | render |
| 18 | action_bar = String.new(view.instance_variable_get(:@_content_for)[:action_bar]) | 19 | action_bar = String.new(view.instance_variable_get(:@_content_for)[:action_bar]) |
| 19 | resolve_link = action_bar.match(/(<a href.*?(class="resolve").*?>)/)[0] | 20 | resolve_link = action_bar.match(/(<a href.*?(class="resolve").*?>)/)[0] |
| 20 | resolve_link.should =~ /data-confirm="Seriously\?"/ | 21 | resolve_link.should =~ /data-confirm="Seriously\?"/ |
| 21 | end | 22 | end |
| 22 | - | 23 | + |
| 23 | it "should confirm the 'resolve' link if configuration is unset" do | 24 | it "should confirm the 'resolve' link if configuration is unset" do |
| 24 | Errbit::Config.stub(:confirm_resolve_err).and_return(nil) | 25 | Errbit::Config.stub(:confirm_resolve_err).and_return(nil) |
| 25 | render | 26 | render |
| @@ -27,7 +28,7 @@ describe "errs/show.html.haml" do | @@ -27,7 +28,7 @@ describe "errs/show.html.haml" do | ||
| 27 | resolve_link = action_bar.match(/(<a href.*?(class="resolve").*?>)/)[0] | 28 | resolve_link = action_bar.match(/(<a href.*?(class="resolve").*?>)/)[0] |
| 28 | resolve_link.should =~ /data-confirm="Seriously\?"/ | 29 | resolve_link.should =~ /data-confirm="Seriously\?"/ |
| 29 | end | 30 | end |
| 30 | - | 31 | + |
| 31 | it "should not confirm the 'resolve' link if configured not to" do | 32 | it "should not confirm the 'resolve' link if configured not to" do |
| 32 | Errbit::Config.stub(:confirm_resolve_err).and_return(false) | 33 | Errbit::Config.stub(:confirm_resolve_err).and_return(false) |
| 33 | render | 34 | render |
| @@ -35,35 +36,37 @@ describe "errs/show.html.haml" do | @@ -35,35 +36,37 @@ describe "errs/show.html.haml" do | ||
| 35 | resolve_link = action_bar.match(/(<a href.*?(class="resolve").*?>)/)[0] | 36 | resolve_link = action_bar.match(/(<a href.*?(class="resolve").*?>)/)[0] |
| 36 | resolve_link.should_not =~ /data-confirm=/ | 37 | resolve_link.should_not =~ /data-confirm=/ |
| 37 | end | 38 | end |
| 38 | - | 39 | + |
| 39 | end | 40 | end |
| 40 | - | 41 | + |
| 41 | describe "content_for :comments" do | 42 | describe "content_for :comments" do |
| 42 | it 'should display comments and new comment form when no issue tracker' do | 43 | it 'should display comments and new comment form when no issue tracker' do |
| 43 | - err = Factory(:err_with_comments) | ||
| 44 | - assign :err, err | ||
| 45 | - assign :app, err.app | 44 | + problem = Factory(:problem_with_comments) |
| 45 | + assign :problem, problem | ||
| 46 | + assign :app, problem.app | ||
| 46 | render | 47 | render |
| 47 | comments_section = String.new(view.instance_variable_get(:@_content_for)[:comments]) | 48 | comments_section = String.new(view.instance_variable_get(:@_content_for)[:comments]) |
| 48 | comments_section.should =~ /Test comment/ | 49 | comments_section.should =~ /Test comment/ |
| 49 | comments_section.should =~ /Add a comment/ | 50 | comments_section.should =~ /Add a comment/ |
| 50 | end | 51 | end |
| 51 | - | 52 | + |
| 52 | context "with issue tracker" do | 53 | context "with issue tracker" do |
| 53 | - def with_issue_tracker(err) | ||
| 54 | - err.app.issue_tracker = PivotalLabsTracker.new :api_token => "token token token", :project_id => "1234" | ||
| 55 | - assign :err, err | ||
| 56 | - assign :app, err.app | 54 | + def with_issue_tracker(problem) |
| 55 | + problem.app.issue_tracker = PivotalLabsTracker.new :api_token => "token token token", :project_id => "1234" | ||
| 56 | + assign :problem, problem | ||
| 57 | + assign :app, problem.app | ||
| 57 | end | 58 | end |
| 58 | - | 59 | + |
| 59 | it 'should not display the comments section' do | 60 | it 'should not display the comments section' do |
| 60 | - with_issue_tracker(Factory(:err)) | 61 | + problem = Factory(:problem) |
| 62 | + with_issue_tracker(problem) | ||
| 61 | render | 63 | render |
| 62 | view.instance_variable_get(:@_content_for)[:comments].should be_blank | 64 | view.instance_variable_get(:@_content_for)[:comments].should be_blank |
| 63 | end | 65 | end |
| 64 | - | 66 | + |
| 65 | it 'should display existing comments' do | 67 | it 'should display existing comments' do |
| 66 | - with_issue_tracker(Factory(:err_with_comments)) | 68 | + problem = Factory(:problem_with_comments) |
| 69 | + with_issue_tracker(problem) | ||
| 67 | render | 70 | render |
| 68 | comments_section = String.new(view.instance_variable_get(:@_content_for)[:comments]) | 71 | comments_section = String.new(view.instance_variable_get(:@_content_for)[:comments]) |
| 69 | comments_section.should =~ /Test comment/ | 72 | comments_section.should =~ /Test comment/ |
| @@ -72,4 +75,3 @@ describe "errs/show.html.haml" do | @@ -72,4 +75,3 @@ describe "errs/show.html.haml" do | ||
| 72 | end | 75 | end |
| 73 | end | 76 | end |
| 74 | end | 77 | end |
| 75 | - |