Commit 8c60eb54e744ccc0170231436f56d1f0fef7a99e
1 parent
8ef1dc2b
Exists in
master
and in
1 other branch
fix specs related to mongoid5 upgrade
Showing
32 changed files
with
236 additions
and
260 deletions
Show diff stats
app/controllers/problems_controller.rb
... | ... | @@ -22,7 +22,7 @@ class ProblemsController < ApplicationController |
22 | 22 | } |
23 | 23 | |
24 | 24 | expose(:problem) { |
25 | - app.problems.find(params[:id]) | |
25 | + ProblemDecorator.new app.problems.find(params[:id]) | |
26 | 26 | } |
27 | 27 | |
28 | 28 | expose(:all_errs) { |
... | ... | @@ -50,7 +50,8 @@ class ProblemsController < ApplicationController |
50 | 50 | def index; end |
51 | 51 | |
52 | 52 | def show |
53 | - @notices = problem.notices.reverse_ordered.page(params[:notice]).per(1) | |
53 | + @notices = problem.object.notices.reverse_ordered | |
54 | + .page(params[:notice]).per(1) | |
54 | 55 | @notice = @notices.first |
55 | 56 | @comment = Comment.new |
56 | 57 | end | ... | ... |
... | ... | @@ -0,0 +1,76 @@ |
1 | +class BacktraceLineDecorator < Draper::Decorator | |
2 | + EMPTY_STRING = ''.freeze | |
3 | + | |
4 | + def in_app? | |
5 | + object[:file].match Backtrace::IN_APP_PATH | |
6 | + end | |
7 | + | |
8 | + def number | |
9 | + object[:number] | |
10 | + end | |
11 | + | |
12 | + def file | |
13 | + object[:file] | |
14 | + end | |
15 | + | |
16 | + def method | |
17 | + object[:method] | |
18 | + end | |
19 | + | |
20 | + def file_relative | |
21 | + file.to_s.sub(Backtrace::IN_APP_PATH, EMPTY_STRING) | |
22 | + end | |
23 | + | |
24 | + def file_name | |
25 | + File.basename file | |
26 | + end | |
27 | + | |
28 | + def to_s | |
29 | + column = object.try(:[], :column) | |
30 | + "#{file_relative}:#{number}#{column.present? ? ":#{column}" : ''}" | |
31 | + end | |
32 | + | |
33 | + def link_to_source_file(app, &block) | |
34 | + text = h.capture_haml(&block) | |
35 | + link_to_in_app_source_file(app, text) || text | |
36 | + end | |
37 | + | |
38 | + private | |
39 | + def link_to_in_app_source_file(app, text) | |
40 | + return unless in_app? | |
41 | + if file_name =~ /\.js$/ | |
42 | + link_to_hosted_javascript(app, text) | |
43 | + else | |
44 | + link_to_repo_source_file(app, text) || | |
45 | + link_to_issue_tracker_file(app, text) | |
46 | + end | |
47 | + end | |
48 | + | |
49 | + def link_to_repo_source_file(app, text) | |
50 | + link_to_github(app, text) || link_to_bitbucket(app, text) | |
51 | + end | |
52 | + | |
53 | + def link_to_hosted_javascript(app, text) | |
54 | + if app.asset_host? | |
55 | + h.link_to(text, "#{app.asset_host}/#{file_relative}", :target => '_blank') | |
56 | + end | |
57 | + end | |
58 | + | |
59 | + def link_to_github(app, text = nil) | |
60 | + return unless app.github_repo? | |
61 | + href = "%s#L%s" % [app.github_url_to_file(decorated_path + file_name), number] | |
62 | + h.link_to(text || file_name, href, :target => '_blank') | |
63 | + end | |
64 | + | |
65 | + def link_to_bitbucket(app, text = nil) | |
66 | + return unless app.bitbucket_repo? | |
67 | + href = "%s#cl-%s" % [app.bitbucket_url_to_file(decorated_path + file_name), number] | |
68 | + h.link_to(text || file_name, href, :target => '_blank') | |
69 | + end | |
70 | + | |
71 | + def link_to_issue_tracker_file(app, text = nil) | |
72 | + return unless app.issue_tracker && app.issue_tracker.respond_to?(:url_to_file) | |
73 | + href = app.issue_tracker.url_to_file(file_relative, number) | |
74 | + h.link_to(text || file_name, href, :target => '_blank') | |
75 | + end | |
76 | +end | ... | ... |
app/helpers/backtrace_line_helper.rb
1 | 1 | module BacktraceLineHelper |
2 | - def link_to_source_file(line, &block) | |
2 | + def link_to_source_file(line, app, &block) | |
3 | 3 | text = capture_haml(&block) |
4 | - link_to_in_app_source_file(line, text) || link_to_external_source_file(text) | |
4 | + link_to_in_app_source_file(line, app, text) || text | |
5 | 5 | end |
6 | 6 | |
7 | 7 | private |
8 | - def link_to_in_app_source_file(line, text) | |
8 | + def link_to_in_app_source_file(line, app, text) | |
9 | 9 | return unless line.in_app? |
10 | 10 | if line.file_name =~ /\.js$/ |
11 | - link_to_hosted_javascript(line, text) | |
11 | + link_to_hosted_javascript(line, app, text) | |
12 | 12 | else |
13 | - link_to_repo_source_file(line, text) || | |
14 | - link_to_issue_tracker_file(line, text) | |
13 | + link_to_repo_source_file(line, app, text) || | |
14 | + link_to_issue_tracker_file(line, app, text) | |
15 | 15 | end |
16 | 16 | end |
17 | 17 | |
18 | - def link_to_repo_source_file(line, text) | |
19 | - link_to_github(line, text) || link_to_bitbucket(line, text) | |
18 | + def link_to_repo_source_file(line, app, text) | |
19 | + link_to_github(line, app, text) || link_to_bitbucket(line, app, text) | |
20 | 20 | end |
21 | 21 | |
22 | - def link_to_hosted_javascript(line, text) | |
23 | - if line.app.asset_host? | |
24 | - link_to(text, "#{line.app.asset_host}/#{line.file_relative}", :target => '_blank') | |
22 | + def link_to_hosted_javascript(line, app, text) | |
23 | + if app.asset_host? | |
24 | + link_to(text, "#{app.asset_host}/#{line.file_relative}", :target => '_blank') | |
25 | 25 | end |
26 | 26 | end |
27 | 27 | |
28 | - def link_to_external_source_file(text) | |
29 | - text | |
30 | - end | |
31 | - | |
32 | - def link_to_github(line, text = nil) | |
33 | - return unless line.app.github_repo? | |
34 | - href = "%s#L%s" % [line.app.github_url_to_file(line.decorated_path + line.file_name), line.number] | |
28 | + def link_to_github(line, app, text = nil) | |
29 | + return unless app.github_repo? | |
30 | + href = "%s#L%s" % [app.github_url_to_file(line.decorated_path + line.file_name), line.number] | |
35 | 31 | link_to(text || line.file_name, href, :target => '_blank') |
36 | 32 | end |
37 | 33 | |
38 | - def link_to_bitbucket(line, text = nil) | |
39 | - return unless line.app.bitbucket_repo? | |
40 | - href = "%s#cl-%s" % [line.app.bitbucket_url_to_file(line.decorated_path + line.file_name), line.number] | |
34 | + def link_to_bitbucket(line, app, text = nil) | |
35 | + return unless app.bitbucket_repo? | |
36 | + href = "%s#cl-%s" % [app.bitbucket_url_to_file(line.decorated_path + line.file_name), line.number] | |
41 | 37 | link_to(text || line.file_name, href, :target => '_blank') |
42 | 38 | end |
43 | 39 | |
44 | - def link_to_issue_tracker_file(line, text = nil) | |
45 | - return unless line.app.issue_tracker && line.app.issue_tracker.respond_to?(:url_to_file) | |
46 | - href = line.app.issue_tracker.url_to_file(line.file_relative, line.number) | |
40 | + def link_to_issue_tracker_file(line, app, text = nil) | |
41 | + return unless app.issue_tracker && app.issue_tracker.respond_to?(:url_to_file) | |
42 | + href = app.issue_tracker.url_to_file(line.file_relative, line.number) | |
47 | 43 | link_to(text || line.file_name, href, :target => '_blank') |
48 | 44 | end |
49 | 45 | ... | ... |
app/mailers/mailer.rb
... | ... | @@ -13,11 +13,11 @@ class Mailer < ActionMailer::Base |
13 | 13 | 'Precedence' => 'bulk', |
14 | 14 | 'Auto-Submitted' => 'auto-generated' |
15 | 15 | |
16 | - def err_notification(notice) | |
17 | - @notice = notice | |
18 | - @app = notice.app | |
16 | + def err_notification(error_report) | |
17 | + @notice = NoticeDecorator.new error_report.notice | |
18 | + @app = AppDecorator.new error_report.app | |
19 | 19 | |
20 | - count = @notice.similar_count | |
20 | + count = error_report.problem.notices_count | |
21 | 21 | count = count > 1 ? "(#{count}) " : "" |
22 | 22 | |
23 | 23 | errbit_headers 'App' => @app.name, |
... | ... | @@ -30,7 +30,7 @@ class Mailer < ActionMailer::Base |
30 | 30 | |
31 | 31 | def deploy_notification(deploy) |
32 | 32 | @deploy = deploy |
33 | - @app = deploy.app | |
33 | + @app = AppDecorator.new deploy.app | |
34 | 34 | |
35 | 35 | errbit_headers 'App' => @app.name, |
36 | 36 | 'Environment' => @deploy.environment, |
... | ... | @@ -44,7 +44,7 @@ class Mailer < ActionMailer::Base |
44 | 44 | def comment_notification(comment) |
45 | 45 | @comment = comment |
46 | 46 | @user = comment.user |
47 | - @problem = comment.err | |
47 | + @problem = ProblemDecorator.new comment.err | |
48 | 48 | @notice = @problem.notices.first |
49 | 49 | @app = @problem.app |
50 | 50 | ... | ... |
app/models/backtrace.rb
... | ... | @@ -2,31 +2,23 @@ class Backtrace |
2 | 2 | include Mongoid::Document |
3 | 3 | include Mongoid::Timestamps |
4 | 4 | |
5 | - field :fingerprint | |
6 | - index :fingerprint => 1 | |
7 | - | |
8 | - has_many :notices | |
9 | - has_one :notice | |
5 | + IN_APP_PATH = %r{^\[PROJECT_ROOT\](?!(\/vendor))/?} | |
10 | 6 | |
11 | - embeds_many :lines, :class_name => "BacktraceLine" | |
12 | - | |
13 | - after_initialize :generate_fingerprint | |
7 | + field :fingerprint | |
8 | + field :lines | |
14 | 9 | |
15 | - delegate :app, :to => :notice | |
10 | + index :fingerprint => 1 | |
16 | 11 | |
17 | 12 | def self.find_or_create(lines) |
18 | 13 | fingerprint = generate_fingerprint(lines) |
19 | 14 | |
20 | - where(fingerprint: generate_fingerprint(lines)). | |
21 | - find_one_and_update( | |
22 | - { '$setOnInsert' => { fingerprint: fingerprint, lines: lines } }, | |
23 | - { return_document: :after, upsert: true }) | |
15 | + where(fingerprint: fingerprint).find_one_and_update( | |
16 | + { '$setOnInsert' => { fingerprint: fingerprint, lines: lines } }, | |
17 | + { return_document: :after, upsert: true }) | |
24 | 18 | end |
25 | 19 | |
26 | - def raw=(raw) | |
27 | - raw.compact.each do |raw_line| | |
28 | - lines << BacktraceLine.new(BacktraceLineNormalizer.new(raw_line).call) | |
29 | - end | |
20 | + def self.generate_fingerprint(lines) | |
21 | + Digest::SHA1.hexdigest(lines.map(&:to_s).join) | |
30 | 22 | end |
31 | 23 | |
32 | 24 | private | ... | ... |
app/models/backtrace_line.rb
... | ... | @@ -1,42 +0,0 @@ |
1 | -class BacktraceLine | |
2 | - include Mongoid::Document | |
3 | - IN_APP_PATH = %r{^\[PROJECT_ROOT\](?!(\/vendor))/?} | |
4 | - GEMS_PATH = %r{\[GEM_ROOT\]\/gems\/([^\/]+)} | |
5 | - | |
6 | - field :number, :type => Integer | |
7 | - field :column, :type => Integer | |
8 | - field :file | |
9 | - field :method | |
10 | - | |
11 | - embedded_in :backtrace | |
12 | - | |
13 | - scope :in_app, ->{ where(:file => IN_APP_PATH) } | |
14 | - | |
15 | - delegate :app, :to => :backtrace | |
16 | - | |
17 | - def to_s | |
18 | - "#{file_relative}:#{number}" << (column.present? ? ":#{column}" : "") | |
19 | - end | |
20 | - | |
21 | - def in_app? | |
22 | - !!(file =~ IN_APP_PATH) | |
23 | - end | |
24 | - | |
25 | - def path | |
26 | - File.dirname(file).gsub(/^\.$/, '') + "/" | |
27 | - end | |
28 | - | |
29 | - def file_relative | |
30 | - file.to_s.sub(IN_APP_PATH, '') | |
31 | - end | |
32 | - | |
33 | - def file_name | |
34 | - File.basename file | |
35 | - end | |
36 | - | |
37 | - def decorated_path | |
38 | - path.sub(BacktraceLine::IN_APP_PATH, ''). | |
39 | - sub(BacktraceLine::GEMS_PATH, "<strong>\\1</strong>") | |
40 | - end | |
41 | - | |
42 | -end |
app/models/backtrace_line_normalizer.rb
... | ... | @@ -1,32 +0,0 @@ |
1 | -class BacktraceLineNormalizer | |
2 | - def initialize(raw_line) | |
3 | - @raw_line = raw_line || {} | |
4 | - end | |
5 | - | |
6 | - def call | |
7 | - @raw_line.merge 'file' => normalized_file, 'method' => normalized_method | |
8 | - end | |
9 | - | |
10 | - private | |
11 | - def normalized_file | |
12 | - if @raw_line['file'].blank? | |
13 | - "[unknown source]" | |
14 | - else | |
15 | - file = @raw_line['file'].to_s | |
16 | - # Detect lines from gem | |
17 | - file.gsub!(/\[PROJECT_ROOT\]\/.*\/ruby\/[0-9.]+\/gems/, '[GEM_ROOT]/gems') | |
18 | - # Strip any query strings | |
19 | - file.gsub!(/\?[^\?]*$/, '') | |
20 | - @raw_line['file'] = file | |
21 | - end | |
22 | - end | |
23 | - | |
24 | - def normalized_method | |
25 | - if @raw_line['method'].blank? | |
26 | - "[unknown method]" | |
27 | - else | |
28 | - @raw_line['method'].to_s.gsub(/[0-9_]{10,}+/, "__FRAGMENT__") | |
29 | - end | |
30 | - end | |
31 | - | |
32 | -end |
app/models/error_report.rb
... | ... | @@ -79,23 +79,20 @@ class ErrorReport |
79 | 79 | # Update problem cache with information about this notice |
80 | 80 | def cache_attributes_on_problem |
81 | 81 | @problem = Problem.cache_notice(@error.problem_id, @notice) |
82 | - | |
83 | - # cache_notice returns the old problem, so the count is one higher | |
84 | - @similar_count = @problem.notices_count + 1 | |
85 | 82 | end |
86 | 83 | |
87 | 84 | # Send email notification if needed |
88 | 85 | def email_notification |
89 | 86 | return false unless app.emailable? |
90 | - return false unless app.email_at_notices.include?(@similar_count) | |
91 | - Mailer.err_notification(@notice).deliver | |
87 | + return false unless app.email_at_notices.include?(@problem.notices_count) | |
88 | + Mailer.err_notification(self).deliver | |
92 | 89 | rescue => e |
93 | 90 | HoptoadNotifier.notify(e) |
94 | 91 | end |
95 | 92 | |
96 | 93 | def should_notify? |
97 | 94 | app.notification_service.notify_at_notices.include?(0) || |
98 | - app.notification_service.notify_at_notices.include?(@similar_count) | |
95 | + app.notification_service.notify_at_notices.include?(@problem.notices_count) | |
99 | 96 | end |
100 | 97 | |
101 | 98 | # Launch all notification define on the app associate to this notice | ... | ... |
app/models/notice.rb
app/models/problem.rb
... | ... | @@ -76,7 +76,7 @@ class Problem |
76 | 76 | host_digest = Digest::MD5.hexdigest(notice.host) |
77 | 77 | user_agent_digest = Digest::MD5.hexdigest(notice.user_agent_string) |
78 | 78 | |
79 | - Problem.where('_id' => id).find_one_and_update( | |
79 | + Problem.where('_id' => id).find_one_and_update({ | |
80 | 80 | '$set' => { |
81 | 81 | 'environment' => notice.environment_name, |
82 | 82 | 'error_class' => notice.error_class, |
... | ... | @@ -95,7 +95,7 @@ class Problem |
95 | 95 | "hosts.#{host_digest}.count" => 1, |
96 | 96 | "user_agents.#{user_agent_digest}.count" => 1, |
97 | 97 | } |
98 | - ) | |
98 | + }, return_document: :after) | |
99 | 99 | end |
100 | 100 | |
101 | 101 | def uncache_notice(notice) | ... | ... |
app/views/issue_trackers/issue.md.erb
... | ... | @@ -27,7 +27,7 @@ |
27 | 27 | |
28 | 28 | ## Backtrace ## |
29 | 29 | ~~~ |
30 | -<% notice.backtrace_lines.each do |line| %><%= line.number %>: <%= line.file_relative %> -> **<%= line.method %>** | |
30 | +<% notice.backtrace.lines.each do |line| %><%= line.number %>: <%= line.file_relative %> -> **<%= line.method %>** | |
31 | 31 | <% end %> |
32 | 32 | ~~~ |
33 | 33 | ... | ... |
app/views/issue_trackers/issue.txt.erb
... | ... | @@ -16,6 +16,6 @@ Env: <%= pretty_hash notice.env_vars %> |
16 | 16 | |
17 | 17 | Backtrace |
18 | 18 | --------- |
19 | -<% notice.backtrace_lines.each do |line| %><%= sprintf('%5d: %s **%s', line.number, line.file_relative, line.method) %> | |
19 | +<% notice.backtrace.lines.each do |line| %><%= sprintf('%5d: %s **%s', line.number, line.file_relative, line.method) %> | |
20 | 20 | <% end %> |
21 | 21 | <% end %> | ... | ... |
app/views/mailer/comment_notification.text.erb
app/views/mailer/err_notification.html.haml
... | ... | @@ -27,9 +27,10 @@ |
27 | 27 | %p.heading WHERE: |
28 | 28 | %p.monospace |
29 | 29 | = @notice.where |
30 | - - @notice.in_app_backtrace_lines.each do |line| | |
30 | + - @notice.backtrace.lines.each do |line| | |
31 | + - next unless line.in_app? | |
31 | 32 | %p.backtrace |
32 | - = link_to_source_file(line) do | |
33 | + = line.link_to_source_file(@app) do | |
33 | 34 | = line.to_s |
34 | 35 | %br |
35 | 36 | - if @notice.app_version.present? |
... | ... | @@ -59,11 +60,11 @@ |
59 | 60 | %td(style="text-align: right; padding-right: 10px; color: #6a6a6a;")= key.to_s.titleize + ":" |
60 | 61 | %td= auto_link(value.to_s) |
61 | 62 | %br |
62 | - - if @notice.backtrace_lines.any? | |
63 | + - if @notice.backtrace.lines.any? | |
63 | 64 | %br |
64 | 65 | %p.heading FULL BACKTRACE: |
65 | - - @notice.backtrace_lines.each do |line| | |
66 | + - @notice.backtrace.lines.each do |line| | |
66 | 67 | %p.backtrace |
67 | - = link_to_source_file(line) do | |
68 | + = link_to_source_file(line, @app) do | |
68 | 69 | = line.to_s |
69 | 70 | %br | ... | ... |
app/views/mailer/err_notification.text.erb
... | ... | @@ -14,7 +14,8 @@ WHERE: |
14 | 14 | |
15 | 15 | <%= @notice.where %> |
16 | 16 | |
17 | -<% @notice.in_app_backtrace_lines.each do |line| %> | |
17 | +<% @notice.backtrace.lines.each do |line| %> | |
18 | + <% next unless line.in_app? %> | |
18 | 19 | <%= line %> |
19 | 20 | <% end %> |
20 | 21 | |
... | ... | @@ -51,7 +52,7 @@ USER: |
51 | 52 | |
52 | 53 | BACKTRACE: |
53 | 54 | |
54 | -<% @notice.backtrace_lines.each do |line| %> | |
55 | +<% @notice.backtrace.lines.each do |line| %> | |
55 | 56 | <%= line %> |
56 | 57 | <% end %> |
57 | 58 | ... | ... |
app/views/notices/_backtrace_line.html.haml
spec/acceptance/app_regenerate_api_key_spec.rb
... | ... | @@ -52,8 +52,8 @@ feature "Create an application" do |
52 | 52 | fill_in 'app_name', :with => 'My new app' |
53 | 53 | click_on I18n.t('apps.new.add_app') |
54 | 54 | page.has_content?(I18n.t('controllers.apps.flash.create.success')) |
55 | - expect(App.where(:name => 'My new app').count).to eql 1 | |
56 | - expect(App.where(:name => 'My new app 2').count).to eql 0 | |
55 | + expect(App.where(:name => 'My new app').count).to eq 1 | |
56 | + expect(App.where(:name => 'My new app 2').count).to eq 0 | |
57 | 57 | |
58 | 58 | |
59 | 59 | click_on I18n.t('shared.navigation.apps') |
... | ... | @@ -62,8 +62,8 @@ feature "Create an application" do |
62 | 62 | fill_in 'app_name', :with => 'My new app 2' |
63 | 63 | click_on I18n.t('apps.edit.update') |
64 | 64 | page.has_content?(I18n.t('controllers.apps.flash.update.success')) |
65 | - expect(App.where(:name => 'My new app').count).to eql 0 | |
66 | - expect(App.where(:name => 'My new app 2').count).to eql 1 | |
65 | + expect(App.where(:name => 'My new app').count).to eq 0 | |
66 | + expect(App.where(:name => 'My new app 2').count).to eq 1 | |
67 | 67 | |
68 | 68 | end |
69 | 69 | ... | ... |
spec/controllers/problems_controller_spec.rb
... | ... | @@ -145,8 +145,6 @@ describe ProblemsController, type: 'controller' do |
145 | 145 | end |
146 | 146 | |
147 | 147 | describe "GET /apps/:app_id/problems/:id" do |
148 | - #render_views | |
149 | - | |
150 | 148 | context 'when logged in as an admin' do |
151 | 149 | before do |
152 | 150 | sign_in admin |
... | ... | @@ -250,8 +248,8 @@ describe ProblemsController, type: 'controller' do |
250 | 248 | before { sign_in admin } |
251 | 249 | |
252 | 250 | context "when app has a issue tracker" do |
253 | - let(:notice) { Fabricate :notice } | |
254 | - let(:problem) { notice.problem } | |
251 | + let(:notice) { NoticeDecorator.new(Fabricate :notice) } | |
252 | + let(:problem) { ProblemDecorator.new(notice.problem) } | |
255 | 253 | let(:issue_tracker) do |
256 | 254 | Fabricate(:issue_tracker).tap do |t| |
257 | 255 | t.instance_variable_set(:@tracker, ErrbitPlugin::MockIssueTracker.new(t.options)) | ... | ... |
spec/fabricators/err_fabricator.rb
... | ... | @@ -18,12 +18,11 @@ Fabricator :notice do |
18 | 18 | end |
19 | 19 | |
20 | 20 | Fabricator :backtrace do |
21 | - lines(:count => 99) { Fabricate.build(:backtrace_line) } | |
22 | -end | |
23 | - | |
24 | -Fabricator :backtrace_line do | |
25 | - number { rand(999) } | |
26 | - file { "/path/to/file/#{SecureRandom.hex(4)}.rb" } | |
27 | - method(:method) { ActiveSupport.methods.shuffle.first } | |
21 | + lines(:count => 99) do | |
22 | + { | |
23 | + number: rand(999), | |
24 | + file: "/path/to/file/#{SecureRandom.hex(4)}.rb", | |
25 | + method: ActiveSupport.methods.shuffle.first | |
26 | + } | |
27 | + end | |
28 | 28 | end |
29 | - | ... | ... |
spec/fabricators/problem_fabricator.rb
... | ... | @@ -23,8 +23,7 @@ end |
23 | 23 | |
24 | 24 | Fabricator(:problem_resolved, :from => :problem) do |
25 | 25 | after_create do |pr| |
26 | - Fabricate(:notice, | |
27 | - :err => Fabricate(:err, :problem => pr)) | |
26 | + Fabricate(:notice, :err => Fabricate(:err, :problem => pr)) | |
28 | 27 | pr.resolve! |
29 | 28 | end |
30 | 29 | end | ... | ... |
spec/mailers/mailer_spec.rb
1 | 1 | shared_examples "a notification email" do |
2 | 2 | it "should have X-Mailer header" do |
3 | - expect(@email).to have_header('X-Mailer', 'Errbit') | |
3 | + expect(email).to have_header('X-Mailer', 'Errbit') | |
4 | 4 | end |
5 | 5 | |
6 | 6 | it "should have X-Errbit-Host header" do |
7 | - expect(@email).to have_header('X-Errbit-Host', Errbit::Config.host) | |
7 | + expect(email).to have_header('X-Errbit-Host', Errbit::Config.host) | |
8 | 8 | end |
9 | 9 | |
10 | 10 | it "should have Precedence header" do |
11 | - expect(@email).to have_header('Precedence', 'bulk') | |
11 | + expect(email).to have_header('Precedence', 'bulk') | |
12 | 12 | end |
13 | 13 | |
14 | 14 | it "should have Auto-Submitted header" do |
15 | - expect(@email).to have_header('Auto-Submitted', 'auto-generated') | |
15 | + expect(email).to have_header('Auto-Submitted', 'auto-generated') | |
16 | 16 | end |
17 | 17 | |
18 | 18 | it "should have X-Auto-Response-Suppress header" do |
19 | 19 | # http://msdn.microsoft.com/en-us/library/ee219609(v=EXCHG.80).aspx |
20 | - expect(@email).to have_header('X-Auto-Response-Suppress', 'OOF, AutoReply') | |
20 | + expect(email).to have_header('X-Auto-Response-Suppress', 'OOF, AutoReply') | |
21 | 21 | end |
22 | 22 | |
23 | 23 | it "should send the email" do |
24 | ||
24 | 25 | expect(ActionMailer::Base.deliveries.size).to eq 1 |
25 | 26 | end |
26 | 27 | end |
... | ... | @@ -30,44 +31,63 @@ describe Mailer do |
30 | 31 | include EmailSpec::Helpers |
31 | 32 | include EmailSpec::Matchers |
32 | 33 | |
33 | - let(:notice) { Fabricate(:notice, :message => "class < ActionController::Base") } | |
34 | - let!(:user) { Fabricate(:admin) } | |
34 | + let(:notice) do | |
35 | + n = Fabricate(:notice, message: "class < ActionController::Base") | |
36 | + n.backtrace.lines.last[:file] = '[PROJECT_ROOT]/path/to/file.js' | |
37 | + # notice.backtrace.update_attributes(lines: lines) | |
38 | + n | |
39 | + end | |
35 | 40 | |
36 | - before do | |
37 | - ActionMailer::Base.deliveries = [] | |
38 | - notice.backtrace.lines.last.update_attributes(:file => "[PROJECT_ROOT]/path/to/file.js") | |
39 | - notice.app.update_attributes( | |
41 | + let(:app) do | |
42 | + a = notice.app | |
43 | + a.update_attributes( | |
40 | 44 | :asset_host => "http://example.com", |
41 | 45 | :notify_all_users => true |
42 | 46 | ) |
43 | - notice.problem.update_attributes :notices_count => 3 | |
44 | - | |
45 | - @email = Mailer.err_notification(notice).deliver | |
47 | + a | |
48 | + end | |
49 | + let(:problem) do | |
50 | + p = notice.problem | |
51 | + p.notices_count = 3 | |
52 | + p | |
53 | + end | |
54 | + let!(:user) { Fabricate(:admin) } | |
55 | + let(:error_report) do | |
56 | + instance_double( | |
57 | + 'ErrorReport', | |
58 | + notice: notice, | |
59 | + app: app, | |
60 | + problem: problem | |
61 | + ) | |
62 | + end | |
63 | + let(:email) do | |
64 | + Mailer.err_notification(error_report).deliver | |
46 | 65 | end |
47 | 66 | |
48 | - it_should_behave_like "a notification email" | |
67 | + before { email } | |
49 | 68 | |
69 | + it_should_behave_like "a notification email" | |
50 | 70 | |
51 | 71 | it "should html-escape the notice's message for the html part" do |
52 | - expect(@email).to have_body_text("class < ActionController::Base") | |
72 | + expect(email).to have_body_text("class < ActionController::Base") | |
53 | 73 | end |
54 | 74 | |
55 | 75 | it "should have inline css" do |
56 | - expect(@email).to have_body_text('<p class="backtrace" style="') | |
76 | + expect(email).to have_body_text('<p class="backtrace" style="') | |
57 | 77 | end |
58 | 78 | |
59 | 79 | it "should have links to source files" do |
60 | - expect(@email).to have_body_text('<a href="http://example.com/path/to/file.js" target="_blank">path/to/file.js') | |
80 | + expect(email).to have_body_text('<a href="http://example.com/path/to/file.js" target="_blank">path/to/file.js') | |
61 | 81 | end |
62 | 82 | |
63 | 83 | it "should have the error count in the subject" do |
64 | - expect(@email.subject).to match( /^\(3\) / ) | |
84 | + expect(email.subject).to match( /^\(3\) / ) | |
65 | 85 | end |
66 | 86 | |
67 | 87 | context 'with a very long message' do |
68 | 88 | let(:notice) { Fabricate(:notice, :message => 6.times.collect{|a| "0123456789" }.join('')) } |
69 | 89 | it "should truncate the long message" do |
70 | - expect(@email.subject).to match( / \d{47}\.{3}$/ ) | |
90 | + expect(email.subject).to match( / \d{47}\.{3}$/ ) | |
71 | 91 | end |
72 | 92 | end |
73 | 93 | end | ... | ... |
spec/models/backtrace_line_spec.rb
... | ... | @@ -1,10 +0,0 @@ |
1 | -describe BacktraceLine, type: 'model' do | |
2 | - subject { described_class.new(raw_line) } | |
3 | - | |
4 | - describe "root at the start of decorated filename" do | |
5 | - let(:raw_line) { { 'number' => rand(999), 'file' => '[PROJECT_ROOT]/app/controllers/pages_controller.rb', 'method' => ActiveSupport.methods.shuffle.first.to_s } } | |
6 | - it "should leave leading root symbol in filepath" do | |
7 | - expect(subject.decorated_path).to eq 'app/controllers/' | |
8 | - end | |
9 | - end | |
10 | -end |
spec/models/backtrace_spec.rb
1 | 1 | describe Backtrace, type: 'model' do |
2 | - subject { described_class.new } | |
3 | - | |
4 | - its(:fingerprint) { should be_present } | |
5 | - | |
6 | - describe "#similar" do | |
7 | - context "no similar backtrace" do | |
8 | - its(:similar) { should be_nil } | |
2 | + describe '.find_or_create' do | |
3 | + let(:lines) do | |
4 | + [ | |
5 | + { 'number' => '123', 'file' => '/some/path/to.rb', 'method' => 'abc' }, | |
6 | + { 'number' => '345', 'file' => '/path/to.rb', 'method' => 'dowhat' } | |
7 | + ] | |
9 | 8 | end |
9 | + let(:fingerprint) { Backtrace.generate_fingerprint(lines) } | |
10 | 10 | |
11 | - context "similar backtrace exist" do | |
12 | - let!(:similar_backtrace) { | |
13 | - b = Fabricate(:backtrace) | |
14 | - b.fingerprint = fingerprint | |
15 | - b.save! | |
16 | - b | |
17 | - } | |
18 | - let(:fingerprint) { "fingerprint" } | |
19 | - | |
20 | - before { allow(subject).to receive(:fingerprint).and_return(fingerprint) } | |
21 | - | |
22 | - its(:similar) { should == similar_backtrace } | |
23 | - end | |
24 | - end | |
25 | - | |
26 | - describe "find_or_create" do | |
27 | - subject { described_class.find_or_create(attributes) } | |
28 | - let(:attributes) { double :attributes } | |
29 | - let(:backtrace) { double :backtrace } | |
30 | - | |
31 | - before { allow(described_class).to receive(:new).and_return(backtrace) } | |
32 | - | |
33 | - context "no similar backtrace" do | |
34 | - before { allow(backtrace).to receive(:similar).and_return(nil) } | |
35 | - it "create new backtrace" do | |
36 | - expect(described_class).to receive(:create).with(attributes) | |
11 | + it 'create new backtrace' do | |
12 | + backtrace = described_class.find_or_create(lines) | |
37 | 13 | |
38 | - described_class.find_or_create(attributes) | |
39 | - end | |
14 | + expect(backtrace.lines).to eq(lines) | |
15 | + expect(backtrace.fingerprint).to eq(fingerprint) | |
40 | 16 | end |
41 | 17 | |
42 | - context "similar backtrace exist" do | |
43 | - let(:similar_backtrace) { double :similar_backtrace } | |
44 | - before { allow(backtrace).to receive(:similar).and_return(similar_backtrace) } | |
18 | + it 'creates one backtrace for two identical ones' do | |
19 | + described_class.find_or_create(lines) | |
20 | + described_class.find_or_create(lines) | |
45 | 21 | |
46 | - it { should == similar_backtrace } | |
22 | + expect(Backtrace.where(fingerprint: fingerprint).count).to eq(1) | |
47 | 23 | end |
48 | 24 | end |
49 | 25 | end | ... | ... |
spec/models/error_report_spec.rb
... | ... | @@ -21,9 +21,7 @@ describe ErrorReport do |
21 | 21 | Rails.root.join('spec','fixtures','hoptoad_test_notice.xml').read |
22 | 22 | } |
23 | 23 | |
24 | - let(:error_report) { | |
25 | - ErrorReport.new(xml) | |
26 | - } | |
24 | + let(:error_report) { ErrorReport.new(xml) } | |
27 | 25 | |
28 | 26 | let!(:app) { |
29 | 27 | Fabricate( |
... | ... | @@ -210,14 +208,13 @@ describe ErrorReport do |
210 | 208 | end |
211 | 209 | |
212 | 210 | it 'find the correct err for the notice' do |
213 | - err = Fabricate(:err, :problem => Fabricate(:problem, :resolved => true)) | |
214 | - | |
215 | - allow(error_report).to receive(:fingerprint).and_return(err.fingerprint) | |
211 | + error_report.generate_notice! | |
212 | + error_report.problem.resolve! | |
216 | 213 | |
217 | 214 | expect { |
218 | - error_report.generate_notice! | |
215 | + ErrorReport.new(xml).generate_notice! | |
219 | 216 | }.to change { |
220 | - error_report.error.resolved? | |
217 | + error_report.problem.reload.resolved? | |
221 | 218 | }.from(true).to(false) |
222 | 219 | end |
223 | 220 | |
... | ... | @@ -227,6 +224,7 @@ describe ErrorReport do |
227 | 224 | app.watchers.build(:email => 'foo@example.com') |
228 | 225 | app.save |
229 | 226 | end |
227 | + | |
230 | 228 | it 'send email' do |
231 | 229 | notice = error_report.generate_notice! |
232 | 230 | email = ActionMailer::Base.deliveries.last | ... | ... |
spec/models/fingerprint/md5_spec.rb
1 | 1 | describe Fingerprint::MD5, type: 'model' do |
2 | 2 | context 'being created' do |
3 | 3 | let(:backtrace) do |
4 | - Backtrace.create(:raw => [ | |
4 | + Backtrace.find_or_create([ | |
5 | 5 | { |
6 | 6 | "number"=>"17", |
7 | 7 | "file"=>"[GEM_ROOT]/gems/activesupport/lib/active_support/callbacks.rb", |
... | ... | @@ -14,10 +14,9 @@ describe Fingerprint::MD5, type: 'model' do |
14 | 14 | |
15 | 15 | context "with same backtrace" do |
16 | 16 | let(:backtrace_2) do |
17 | - backtrace | |
18 | - backtrace.lines.last.method = '_run__FRAGMENT__process_action__FRAGMENT__callbacks' | |
19 | - backtrace.save | |
20 | - backtrace | |
17 | + new_lines = backtrace.lines.dup | |
18 | + new_lines.last[:method] = '_run__FRAGMENT__process_action__FRAGMENT__callbacks' | |
19 | + Backtrace.find_or_create(new_lines) | |
21 | 20 | end |
22 | 21 | |
23 | 22 | it "normalizes the fingerprint of generated methods" do |
... | ... | @@ -27,10 +26,9 @@ describe Fingerprint::MD5, type: 'model' do |
27 | 26 | |
28 | 27 | context "with same backtrace where FRAGMENT has not been extracted" do |
29 | 28 | let(:backtrace_2) do |
30 | - backtrace | |
31 | - backtrace.lines.last.method = '_run__998857585768765__process_action__1231231312321313__callbacks' | |
32 | - backtrace.save | |
33 | - backtrace | |
29 | + new_lines = backtrace.lines.dup | |
30 | + new_lines.last[:method] = '_run__998857585768765__process_action__1231231312321313__callbacks' | |
31 | + Backtrace.find_or_create(new_lines) | |
34 | 32 | end |
35 | 33 | |
36 | 34 | it "normalizes the fingerprint of generated methods" do | ... | ... |
spec/models/fingerprint/sha1_spec.rb
1 | 1 | describe Fingerprint::Sha1, type: 'model' do |
2 | 2 | context '#generate' do |
3 | 3 | let(:backtrace) { |
4 | - Backtrace.create(:raw => [ | |
4 | + Backtrace.find_or_create([ | |
5 | 5 | {"number"=>"425", "file"=>"[GEM_ROOT]/gems/activesupport-3.0.0.rc/lib/active_support/callbacks.rb", "method"=>"_run__2115867319__process_action__262109504__callbacks"}, |
6 | 6 | {"number"=>"404", "file"=>"[GEM_ROOT]/gems/activesupport-3.0.0.rc/lib/active_support/callbacks.rb", "method"=>"send"}, |
7 | 7 | {"number"=>"404", "file"=>"[GEM_ROOT]/gems/activesupport-3.0.0.rc/lib/active_support/callbacks.rb", "method"=>"_run_process_action_callbacks"} |
... | ... | @@ -19,11 +19,9 @@ describe Fingerprint::Sha1, type: 'model' do |
19 | 19 | |
20 | 20 | context "with different backtrace with only last line change" do |
21 | 21 | let(:backtrace_2) { |
22 | - backtrace | |
23 | - backtrace.lines.last.number = 401 | |
24 | - backtrace.send(:generate_fingerprint) | |
25 | - backtrace.save | |
26 | - backtrace | |
22 | + new_lines = backtrace.lines.dup | |
23 | + new_lines.last[:number] = 401 | |
24 | + Backtrace.find_or_create backtrace.lines | |
27 | 25 | } |
28 | 26 | it 'should not same fingerprint' do |
29 | 27 | expect( | ... | ... |
spec/spec_helper.rb
spec/views/issue_trackers/issue.md.erb_spec.rb
... | ... | @@ -6,7 +6,7 @@ describe "issue_trackers/issue.md.erb", type: 'view' do |
6 | 6 | } |
7 | 7 | |
8 | 8 | before do |
9 | - allow(view).to receive(:problem).and_return(problem) | |
9 | + allow(view).to receive(:problem).and_return(ProblemDecorator.new(problem)) | |
10 | 10 | end |
11 | 11 | |
12 | 12 | it "has the problem url" do | ... | ... |
spec/views/issue_trackers/issue.txt.erb_spec.rb
... | ... | @@ -6,7 +6,8 @@ describe "issue_trackers/issue.txt.erb", type: 'view' do |
6 | 6 | } |
7 | 7 | |
8 | 8 | before do |
9 | - allow(view).to receive(:problem).and_return(problem) | |
9 | + allow(view).to receive(:problem).and_return( | |
10 | + ProblemDecorator.new(problem)) | |
10 | 11 | end |
11 | 12 | |
12 | 13 | it "has the problem url" do | ... | ... |