Commit 7f72028937f84decf962f57f014d1d2a06a8ec4b

Authored by Cyril Mougel
2 parents 64c33b85 482518e5
Exists in master and in 1 other branch production

Merge pull request #475 from shingara/features/issue_472

Return 422 HTTP status code when invalid API key
app/controllers/notices_controller.rb
... ... @@ -5,11 +5,17 @@ class NoticesController < ApplicationController
5 5  
6 6 def create
7 7 # params[:data] if the notice came from a GET request, raw_post if it came via POST
8   - notice = App.report_error!(params[:data] || request.raw_post)
9   - api_xml = notice.to_xml(:only => false, :methods => [:id]) do |xml|
10   - xml.url locate_url(notice.id, :host => Errbit::Config.host)
  8 + report = ErrorReport.new(params[:data] || request.raw_post)
  9 +
  10 + if report.valid?
  11 + report.generate_notice!
  12 + api_xml = report.notice.to_xml(:only => false, :methods => [:id]) do |xml|
  13 + xml.url locate_url(report.notice.id, :host => Errbit::Config.host)
  14 + end
  15 + render :xml => api_xml
  16 + else
  17 + render :text => "Your API key is unknown", :status => 422
11 18 end
12   - render :xml => api_xml
13 19 end
14 20  
15 21 # Redirects a notice to the problem page. Useful when using User Information at Airbrake gem.
... ... @@ -17,4 +23,5 @@ class NoticesController < ApplicationController
17 23 problem = Notice.find(params[:id]).problem
18 24 redirect_to app_problem_path(problem.app, problem)
19 25 end
  26 +
20 27 end
... ...
app/models/app.rb
... ... @@ -42,46 +42,11 @@ class App
42 42 accepts_nested_attributes_for :notification_service, :allow_destroy => true,
43 43 :reject_if => proc { |attrs| !NotificationService.subclasses.map(&:to_s).include?(attrs[:type].to_s) }
44 44  
45   - # Processes a new error report.
46   - #
47   - # Accepts either XML or a hash with the following attributes:
48   - #
49   - # * <tt>:error_class</tt> - the class of error
50   - # * <tt>:message</tt> - the error message
51   - # * <tt>:backtrace</tt> - an array of stack trace lines
52   - #
53   - # * <tt>:request</tt> - a hash of values describing the request
54   - # * <tt>:server_environment</tt> - a hash of values describing the server environment
55   - #
56   - # * <tt>:api_key</tt> - the API key with which the error was reported
57   - # * <tt>:notifier</tt> - information to identify the source of the error report
58   - #
59   - def self.report_error!(*args)
60   - report = ErrorReport.new(*args)
61   - report.generate_notice!
62   - end
63   -
64   -
65   - # Processes a new error report.
66   - #
67   - # Accepts a hash with the following attributes:
68   - #
69   - # * <tt>:error_class</tt> - the class of error
70   - # * <tt>:message</tt> - the error message
71   - # * <tt>:backtrace</tt> - an array of stack trace lines
72   - #
73   - # * <tt>:request</tt> - a hash of values describing the request
74   - # * <tt>:server_environment</tt> - a hash of values describing the server environment
75   - #
76   - # * <tt>:notifier</tt> - information to identify the source of the error report
77   - #
78   - def report_error!(hash)
79   - report = ErrorReport.new(hash.merge(:api_key => api_key))
80   - report.generate_notice!
81   - end
82   -
83 45 def find_or_create_err!(attrs)
84   - Err.where(:fingerprint => attrs[:fingerprint]).first || problems.create!.errs.create!(attrs)
  46 + Err.where(
  47 + :fingerprint => attrs[:fingerprint]
  48 + ).first ||
  49 + problems.create!.errs.create!(attrs)
85 50 end
86 51  
87 52 # Mongoid Bug: find(id) on association proxies returns an Enumerator
... ...
app/models/error_report.rb
1 1 require 'digest/sha1'
2 2 require 'hoptoad_notifier'
3 3  
  4 +##
  5 +# Processes a new error report.
  6 +#
  7 +# Accepts a hash with the following attributes:
  8 +#
  9 +# * <tt>:error_class</tt> - the class of error
  10 +# * <tt>:message</tt> - the error message
  11 +# * <tt>:backtrace</tt> - an array of stack trace lines
  12 +#
  13 +# * <tt>:request</tt> - a hash of values describing the request
  14 +# * <tt>:server_environment</tt> - a hash of values describing the server environment
  15 +#
  16 +# * <tt>:notifier</tt> - information to identify the source of the error report
  17 +#
4 18 class ErrorReport
5 19 attr_reader :error_class, :message, :request, :server_environment, :api_key, :notifier, :user_attributes, :framework
6 20  
... ... @@ -26,7 +40,7 @@ class ErrorReport
26 40 end
27 41  
28 42 def app
29   - @app ||= App.find_by_api_key!(api_key)
  43 + @app ||= App.where(:api_key => api_key).first
30 44 end
31 45  
32 46 def backtrace
... ... @@ -34,7 +48,9 @@ class ErrorReport
34 48 end
35 49  
36 50 def generate_notice!
37   - notice = Notice.new(
  51 + return unless valid?
  52 + return @notice if @notice
  53 + @notice = Notice.new(
38 54 :message => message,
39 55 :error_class => error_class,
40 56 :backtrace_id => backtrace.id,
... ... @@ -44,19 +60,33 @@ class ErrorReport
44 60 :user_attributes => user_attributes,
45 61 :framework => framework
46 62 )
  63 + error.notices << @notice
  64 + @notice
  65 + end
  66 + attr_reader :notice
47 67  
48   - err = app.find_or_create_err!(
  68 + ##
  69 + # Error associate to this error_report
  70 + #
  71 + # Can already exist or not
  72 + #
  73 + # @return [ Error ]
  74 + def error
  75 + @error ||= app.find_or_create_err!(
49 76 :error_class => error_class,
50 77 :component => component,
51 78 :action => action,
52 79 :environment => rails_env,
53   - :fingerprint => fingerprint)
  80 + :fingerprint => fingerprint
  81 + )
  82 + end
54 83  
55   - err.notices << notice
56   - notice
  84 + def valid?
  85 + !!app
57 86 end
58 87  
59 88 private
  89 +
60 90 def fingerprint_source
61 91 # Find the first backtrace line with a file and line number.
62 92 if line = backtrace.lines.detect {|l| l.number.present? && l.file.present? }
... ...
lib/tasks/errbit/demo.rake
... ... @@ -42,15 +42,15 @@ namespace :errbit do
42 42  
43 43 errors.each do |error_template|
44 44 rand(34).times do
45   -
46   - error_report = error_template.reverse_merge({
  45 + ErrorReport.new(error_template.reverse_merge({
  46 + :api_key => app.api_key,
47 47 :error_class => "StandardError",
48 48 :message => "Oops. Something went wrong!",
49 49 :backtrace => random_backtrace,
50 50 :request => {
51   - 'component' => 'main',
52   - 'action' => 'error'
53   - },
  51 + 'component' => 'main',
  52 + 'action' => 'error'
  53 + },
54 54 :server_environment => {'environment-name' => Rails.env.to_s},
55 55 :notifier => {:name => "seeds.rb"},
56 56 :app_user => {
... ... @@ -59,9 +59,7 @@ namespace :errbit do
59 59 :name => "John Smith",
60 60 :url => "http://www.example.com/users/jsmith"
61 61 }
62   - })
63   -
64   - app.report_error!(error_report)
  62 + })).generate_notice!
65 63 end
66 64 end
67 65  
... ...
spec/controllers/notices_controller_spec.rb
... ... @@ -3,87 +3,53 @@ require &#39;spec_helper&#39;
3 3 describe NoticesController do
4 4 it_requires_authentication :for => { :locate => :get }
5 5  
  6 + let(:notice) { Fabricate(:notice) }
  7 + let(:xml) { Rails.root.join('spec','fixtures','hoptoad_test_notice.xml').read }
6 8 let(:app) { Fabricate(:app) }
  9 + let(:error_report) { mock(:valid? => true, :generate_notice! => true, :notice => notice) }
7 10  
8 11 context 'notices API' do
9 12 before do
10   - @xml = Rails.root.join('spec','fixtures','hoptoad_test_notice.xml').read
11   - @app = Fabricate(:app_with_watcher)
12   - App.stub(:find_by_api_key!).and_return(@app)
13   - @notice = App.report_error!(@xml)
  13 + ErrorReport.should_receive(:new).with(xml).and_return(error_report)
14 14 end
15 15  
16   - it "generates a notice from raw xml [POST]" do
17   - App.should_receive(:report_error!).with(@xml).and_return(@notice)
18   - request.should_receive(:raw_post).and_return(@xml)
19   - post :create, :format => :xml
20   - response.should be_success
21   - # Same RegExp from Airbrake::Sender#send_to_airbrake (https://github.com/airbrake/airbrake/blob/master/lib/airbrake/sender.rb#L53)
22   - # Inspired by https://github.com/airbrake/airbrake/blob/master/test/sender_test.rb
23   - response.body.should match(%r{<id[^>]*>#{@notice.id}</id>})
24   - response.body.should match(%r{<url[^>]*>(.+)#{locate_path(@notice.id)}</url>})
25   - end
26   -
27   - it "should trnasform xml <va> tags to hashes correctly" do
28   - App.should_receive(:report_error!).with(@xml).and_return(@notice)
29   - request.should_receive(:raw_post).and_return(@xml)
30   - post :create, :format => :xml
  16 + context "with xml pass in raw_port" do
  17 + before do
  18 + request.should_receive(:raw_post).and_return(xml)
  19 + post :create, :format => :xml
  20 + end
31 21  
32   - # XML: <var key="SCRIPT_NAME"/>
33   - @notice.env_vars.should have_key('SCRIPT_NAME')
34   - @notice.env_vars['SCRIPT_NAME'].should be_nil # blank ends up nil
  22 + it "generates a notice from raw xml [POST]" do
  23 + response.should be_success
  24 + # Same RegExp from Airbrake::Sender#send_to_airbrake (https://github.com/airbrake/airbrake/blob/master/lib/airbrake/sender.rb#L53)
  25 + # Inspired by https://github.com/airbrake/airbrake/blob/master/test/sender_test.rb
  26 + response.body.should match(%r{<id[^>]*>#{notice.id}</id>})
  27 + response.body.should match(%r{<url[^>]*>(.+)#{locate_path(notice.id)}</url>})
  28 + end
35 29  
36   - # XML representation:
37   - # <var key="rack.session.options">
38   - # <var key="secure">false</var>
39   - # <var key="httponly">true</var>
40   - # <var key="path">/</var>
41   - # <var key="expire_after"/>
42   - # <var key="domain"/>
43   - # <var key="id"/>
44   - # </var>
45   - expected = {
46   - 'secure' => 'false',
47   - 'httponly' => 'true',
48   - 'path' => '/',
49   - 'expire_after' => nil,
50   - 'domain' => nil,
51   - 'id' => nil
52   - }
53   - @notice.env_vars.should have_key('rack_session_options')
54   - @notice.env_vars['rack_session_options'].should eql(expected)
55 30 end
56 31  
57 32 it "generates a notice from xml in a data param [POST]" do
58   - App.should_receive(:report_error!).with(@xml).and_return(@notice)
59   - post :create, :data => @xml, :format => :xml
  33 + post :create, :data => xml, :format => :xml
60 34 response.should be_success
61 35 # Same RegExp from Airbrake::Sender#send_to_airbrake (https://github.com/airbrake/airbrake/blob/master/lib/airbrake/sender.rb#L53)
62 36 # Inspired by https://github.com/airbrake/airbrake/blob/master/test/sender_test.rb
63   - response.body.should match(%r{<id[^>]*>#{@notice.id}</id>})
64   - response.body.should match(%r{<url[^>]*>(.+)#{locate_path(@notice.id)}</url>})
  37 + response.body.should match(%r{<id[^>]*>#{notice.id}</id>})
  38 + response.body.should match(%r{<url[^>]*>(.+)#{locate_path(notice.id)}</url>})
65 39 end
66 40  
67 41 it "generates a notice from xml [GET]" do
68   - App.should_receive(:report_error!).with(@xml).and_return(@notice)
69   - get :create, :data => @xml, :format => :xml
  42 + get :create, :data => xml, :format => :xml
70 43 response.should be_success
71   - response.body.should match(%r{<id[^>]*>#{@notice.id}</id>})
72   - response.body.should match(%r{<url[^>]*>(.+)#{locate_path(@notice.id)}</url>})
  44 + response.body.should match(%r{<id[^>]*>#{notice.id}</id>})
  45 + response.body.should match(%r{<url[^>]*>(.+)#{locate_path(notice.id)}</url>})
73 46 end
74   -
75   - it "sends a notification email" do
76   - App.should_receive(:report_error!).with(@xml).and_return(@notice)
77   - request.should_receive(:raw_post).and_return(@xml)
78   - post :create, :format => :xml
79   - response.should be_success
80   - response.body.should match(%r{<id[^>]*>#{@notice.id}</id>})
81   - response.body.should match(%r{<url[^>]*>(.+)#{locate_path(@notice.id)}</url>})
82   - email = ActionMailer::Base.deliveries.last
83   - email.to.should include(@app.watchers.first.email)
84   - email.subject.should include(@notice.message.truncate(50))
85   - email.subject.should include("[#{@app.name}]")
86   - email.subject.should include("[#{@notice.environment_name}]")
  47 + context "with an invalid API_KEY" do
  48 + let(:error_report) { mock(:valid? => false) }
  49 + it 'return 422' do
  50 + post :create, :format => :xml, :data => xml
  51 + expect(response.status).to eq 422
  52 + end
87 53 end
88 54 end
89 55  
... ...
spec/fixtures/hoptoad_test_notice_without_line_of_backtrace.xml 0 → 100644
... ... @@ -0,0 +1,73 @@
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<notice version="2.0">
  3 + <api-key>APIKEY</api-key>
  4 + <notifier>
  5 + <name>Hoptoad Notifier</name>
  6 + <version>2.3.2</version>
  7 + <url>http://hoptoadapp.com</url>
  8 + </notifier>
  9 + <error>
  10 + <class>HoptoadTestingException</class>
  11 + <message>HoptoadTestingException: Testing hoptoad via "rake hoptoad:test". If you can see this, it works.</message>
  12 + <backtrace></backtrace>
  13 + </error>
  14 + <request>
  15 + <url>http://example.org/verify</url>
  16 + <component>application</component>
  17 + <action>verify</action>
  18 + <params>
  19 + <var key="action">verify</var>
  20 + <var key="controller">application</var>
  21 + </params>
  22 + <cgi-data>
  23 + <var key="rack.session"/>
  24 + <var key="action_dispatch.request.formats">text/html</var>
  25 + <var key="action_dispatch.request.parameters">
  26 + <var key="action">verify</var>
  27 + <var key="controller">application</var>
  28 + </var>
  29 + <var key="SERVER_NAME">example.org</var>
  30 + <var key="rack.url_scheme">http</var>
  31 + <var key="action_dispatch.remote_ip"/>
  32 + <var key="CONTENT_LENGTH">0</var>
  33 + <var key="rack.errors">#&lt;StringIO:0x103d9dec0&gt;</var>
  34 + <var key="action_dispatch.request.unsigned_session_cookie"/>
  35 + <var key="action_dispatch.request.query_parameters"/>
  36 + <var key="HTTPS">off</var>
  37 + <var key="rack.run_once">false</var>
  38 + <var key="PATH_INFO">/verify</var>
  39 + <var key="action_dispatch.secret_token">994f235e3372684bc736dd11842b754d2ddcffc8c2958d33a29527c3217becd6655fa4653a318bc7c34131f9baf2acc0c424ed07e48e0e5e87c6cd34d711e985</var>
  40 + <var key="rack.version">11</var>
  41 + <var key="SCRIPT_NAME"/>
  42 + <var key="action_dispatch.request.path_parameters">
  43 + <var key="action">verify</var>
  44 + <var key="controller">application</var>
  45 + </var>
  46 + <var key="rack.multithread">false</var>
  47 + <var key="action_dispatch.parameter_filter">password</var>
  48 + <var key="action_dispatch.cookies"/>
  49 + <var key="action_dispatch.request.request_parameters"/>
  50 + <var key="rack.multiprocess">true</var>
  51 + <var key="rack.request.query_hash"/>
  52 + <var key="SERVER_PORT">80</var>
  53 + <var key="REQUEST_METHOD">GET</var>
  54 + <var key="action_controller.instance">#&lt;ApplicationController:0x103d2f560&gt;</var>
  55 + <var key="rack.session.options">
  56 + <var key="secure">false</var>
  57 + <var key="httponly">true</var>
  58 + <var key="path">/</var>
  59 + <var key="expire_after"/>
  60 + <var key="domain"/>
  61 + <var key="id"/>
  62 + </var>
  63 + <var key="rack.input">#&lt;StringIO:0x103d9dc90&gt;</var>
  64 + <var key="action_dispatch.request.content_type"/>
  65 + <var key="rack.request.query_string"/>
  66 + <var key="QUERY_STRING"/>
  67 + </cgi-data>
  68 + </request>
  69 + <server-environment>
  70 + <project-root>/path/to/sample/project</project-root>
  71 + <environment-name>development</environment-name>
  72 + </server-environment>
  73 +</notice>
... ...
spec/models/app_spec.rb
... ... @@ -187,109 +187,17 @@ describe App do
187 187 end
188 188  
189 189  
190   - context '#report_error!' do
191   - before do
192   - @xml = Rails.root.join('spec','fixtures','hoptoad_test_notice.xml').read
193   - @app = Fabricate(:app, :api_key => 'APIKEY')
194   - ErrorReport.any_instance.stub(:fingerprint).and_return('fingerprintdigest')
195   - end
196   -
197   - it 'finds the correct app' do
198   - @notice = App.report_error!(@xml)
199   - @notice.err.app.should == @app
200   - end
201   -
202   - it 'finds the correct err for the notice' do
203   - App.should_receive(:find_by_api_key!).and_return(@app)
204   - @app.should_receive(:find_or_create_err!).with({
205   - :error_class => 'HoptoadTestingException',
206   - :component => 'application',
207   - :action => 'verify',
208   - :environment => 'development',
209   - :fingerprint => 'fingerprintdigest'
210   - }).and_return(err = Fabricate(:err))
211   - err.notices.stub(:create!)
212   - @notice = App.report_error!(@xml)
213   - end
214   -
215   - it 'marks the err as unresolved if it was previously resolved' do
216   - App.should_receive(:find_by_api_key!).and_return(@app)
217   - @app.should_receive(:find_or_create_err!).with({
218   - :error_class => 'HoptoadTestingException',
219   - :component => 'application',
220   - :action => 'verify',
221   - :environment => 'development',
222   - :fingerprint => 'fingerprintdigest'
223   - }).and_return(err = Fabricate(:err, :problem => Fabricate(:problem, :resolved => true)))
224   - err.should be_resolved
225   - @notice = App.report_error!(@xml)
226   - @notice.err.should == err
227   - @notice.err.should_not be_resolved
228   - end
229   -
230   - it 'should create a new notice' do
231   - @notice = App.report_error!(@xml)
232   - @notice.should be_persisted
233   - end
234   -
235   - it 'assigns an err to the notice' do
236   - @notice = App.report_error!(@xml)
237   - @notice.err.should be_a(Err)
238   - end
239   -
240   - it 'captures the err message' do
241   - @notice = App.report_error!(@xml)
242   - @notice.message.should == 'HoptoadTestingException: Testing hoptoad via "rake hoptoad:test". If you can see this, it works.'
243   - end
244   -
245   - it 'captures the backtrace' do
246   - @notice = App.report_error!(@xml)
247   - @notice.backtrace_lines.size.should == 73
248   - @notice.backtrace_lines.last['file'].should == '[GEM_ROOT]/bin/rake'
249   - end
250   -
251   - it 'captures the server_environment' do
252   - @notice = App.report_error!(@xml)
253   - @notice.server_environment['environment-name'].should == 'development'
254   - end
255   -
256   - it 'captures the request' do
257   - @notice = App.report_error!(@xml)
258   - @notice.request['url'].should == 'http://example.org/verify'
259   - @notice.request['params']['controller'].should == 'application'
260   - end
261   -
262   - it 'captures the notifier' do
263   - @notice = App.report_error!(@xml)
264   - @notice.notifier['name'].should == 'Hoptoad Notifier'
265   - end
266   -
267   - it "should handle params without 'request' section" do
268   - xml = Rails.root.join('spec','fixtures','hoptoad_test_notice_without_request_section.xml').read
269   - lambda { App.report_error!(xml) }.should_not raise_error
270   - end
271   -
272   - it "should handle params with only a single line of backtrace" do
273   - xml = Rails.root.join('spec','fixtures','hoptoad_test_notice_with_one_line_of_backtrace.xml').read
274   - lambda { @notice = App.report_error!(xml) }.should_not raise_error
275   - @notice.backtrace_lines.length.should == 1
  190 + describe ".find_by_api_key!" do
  191 + it 'return the app with api_key' do
  192 + app = Fabricate(:app)
  193 + expect(App.find_by_api_key!(app.api_key)).to eq app
276 194 end
277   -
278   - it 'captures the current_user' do
279   - @notice = App.report_error!(@xml)
280   - @notice.user_attributes['id'].should == '123'
281   - @notice.user_attributes['name'].should == 'Mr. Bean'
282   - @notice.user_attributes['email'].should == 'mr.bean@example.com'
283   - @notice.user_attributes['username'].should == 'mrbean'
  195 + it 'raise Mongoid::Errors::DocumentNotFound if not found' do
  196 + expect {
  197 + App.find_by_api_key!('foo')
  198 + }.to raise_error(Mongoid::Errors::DocumentNotFound)
284 199 end
285   -
286   - it 'captures the framework' do
287   - @notice = App.report_error!(@xml)
288   - @notice.framework.should == 'Rails: 3.2.11'
289   - end
290   -
291 200 end
292 201  
293   -
294 202 end
295 203  
... ...
spec/models/error_report_spec.rb 0 → 100644
... ... @@ -0,0 +1,204 @@
  1 +require 'spec_helper'
  2 +
  3 +describe ErrorReport do
  4 + context "with notice without line of backtrace" do
  5 + let(:xml){
  6 + Rails.root.join('spec','fixtures','hoptoad_test_notice.xml').read
  7 + }
  8 +
  9 + let(:error_report) {
  10 + ErrorReport.new(xml)
  11 + }
  12 +
  13 + let!(:app) {
  14 + Fabricate(
  15 + :app,
  16 + :api_key => 'APIKEY'
  17 + )
  18 + }
  19 +
  20 + describe "#app" do
  21 + it 'find the good app' do
  22 + expect(error_report.app).to eq app
  23 + end
  24 + end
  25 +
  26 + describe "#backtrace" do
  27 +
  28 + it 'should have valid backtrace' do
  29 + error_report.backtrace.should be_valid
  30 + end
  31 + end
  32 +
  33 + describe "#generate_notice!" do
  34 + it "save a notice" do
  35 + expect {
  36 + error_report.generate_notice!
  37 + }.to change {
  38 + app.reload.problems.count
  39 + }.by(1)
  40 + end
  41 + describe "notice create" do
  42 + before { error_report.generate_notice! }
  43 + subject { error_report.notice }
  44 + its(:message) { 'HoptoadTestingException: Testing hoptoad via "rake hoptoad:test". If you can see this, it works.' }
  45 + its(:framework) { should == 'Rails: 3.2.11' }
  46 +
  47 + it 'has complete backtrace' do
  48 + subject.backtrace_lines.size.should == 73
  49 + subject.backtrace_lines.last['file'].should == '[GEM_ROOT]/bin/rake'
  50 + end
  51 + it 'has server_environement' do
  52 + subject.server_environment['environment-name'].should == 'development'
  53 + end
  54 +
  55 + it 'has request' do
  56 + subject.request['url'].should == 'http://example.org/verify'
  57 + subject.request['params']['controller'].should == 'application'
  58 + end
  59 +
  60 + it 'has notifier' do
  61 + subject.notifier['name'].should == 'Hoptoad Notifier'
  62 + end
  63 +
  64 + it 'get user_attributes' do
  65 + subject.user_attributes['id'].should == '123'
  66 + subject.user_attributes['name'].should == 'Mr. Bean'
  67 + subject.user_attributes['email'].should == 'mr.bean@example.com'
  68 + subject.user_attributes['username'].should == 'mrbean'
  69 + end
  70 + it 'valid env_vars' do
  71 + # XML: <var key="SCRIPT_NAME"/>
  72 + subject.env_vars.should have_key('SCRIPT_NAME')
  73 + subject.env_vars['SCRIPT_NAME'].should be_nil # blank ends up nil
  74 +
  75 + # XML representation:
  76 + # <var key="rack.session.options">
  77 + # <var key="secure">false</var>
  78 + # <var key="httponly">true</var>
  79 + # <var key="path">/</var>
  80 + # <var key="expire_after"/>
  81 + # <var key="domain"/>
  82 + # <var key="id"/>
  83 + # </var>
  84 + expected = {
  85 + 'secure' => 'false',
  86 + 'httponly' => 'true',
  87 + 'path' => '/',
  88 + 'expire_after' => nil,
  89 + 'domain' => nil,
  90 + 'id' => nil
  91 + }
  92 + subject.env_vars.should have_key('rack_session_options')
  93 + subject.env_vars['rack_session_options'].should eql(expected)
  94 + end
  95 + end
  96 +
  97 + it 'save a notice assignes to err' do
  98 + error_report.generate_notice!
  99 + error_report.notice.err.should be_a(Err)
  100 + end
  101 +
  102 + it 'memoize the notice' do
  103 + expect {
  104 + error_report.generate_notice!
  105 + error_report.generate_notice!
  106 + }.to change {
  107 + Notice.count
  108 + }.by(1)
  109 + end
  110 +
  111 + it 'find the correct err for the notice' do
  112 + Fabricate(
  113 + :err, {
  114 + :fingerprint => 'eeb6cc484167899c061e7859008c4b23bae0851c',
  115 + :problem => Fabricate(:problem, :resolved => true)
  116 + }
  117 + )
  118 + expect {
  119 + error_report.generate_notice!
  120 + }.to change {
  121 + error_report.error.resolved?
  122 + }.from(true).to(false)
  123 + end
  124 +
  125 + context "with notification service configured" do
  126 + before do
  127 + app.notify_on_errs = true
  128 + app.watchers.build(:email => 'foo@example.com')
  129 + app.save
  130 + end
  131 + it 'send email' do
  132 + notice = error_report.generate_notice!
  133 + email = ActionMailer::Base.deliveries.last
  134 + email.to.should include(app.watchers.first.email)
  135 + email.subject.should include(notice.message.truncate(50))
  136 + email.subject.should include("[#{app.name}]")
  137 + email.subject.should include("[#{notice.environment_name}]")
  138 + end
  139 + end
  140 +
  141 + context "with xml without request section" do
  142 + let(:xml){
  143 + Rails.root.join('spec','fixtures','hoptoad_test_notice_without_request_section.xml').read
  144 + }
  145 + it "save a notice" do
  146 + expect {
  147 + error_report.generate_notice!
  148 + }.to change {
  149 + app.reload.problems.count
  150 + }.by(1)
  151 + end
  152 + end
  153 +
  154 + context "with xml with only a single line of backtrace" do
  155 + let(:xml){
  156 + Rails.root.join('spec','fixtures','hoptoad_test_notice_with_one_line_of_backtrace.xml').read
  157 + }
  158 + it "save a notice" do
  159 + expect {
  160 + error_report.generate_notice!
  161 + }.to change {
  162 + app.reload.problems.count
  163 + }.by(1)
  164 + end
  165 + end
  166 + end
  167 +
  168 + describe "#valid?" do
  169 + context "with valid error report" do
  170 + it "return true" do
  171 + expect(error_report.valid?).to be true
  172 + end
  173 + end
  174 + context "with not valid api_key" do
  175 + before do
  176 + App.where(:api_key => app.api_key).delete_all
  177 + end
  178 + it "return false" do
  179 + expect(error_report.valid?).to be false
  180 + end
  181 + end
  182 + end
  183 +
  184 + describe "#notice" do
  185 + context "before generate_notice!" do
  186 + it 'return nil' do
  187 + expect(error_report.notice).to be nil
  188 + end
  189 + end
  190 +
  191 + context "after generate_notice!" do
  192 + before do
  193 + error_report.generate_notice!
  194 + end
  195 +
  196 + it 'return the notice' do
  197 + expect(error_report.notice).to be_a Notice
  198 + end
  199 +
  200 + end
  201 + end
  202 +
  203 + end
  204 +end
... ...
spec/requests/notices_controller_spec.rb 0 → 100644
... ... @@ -0,0 +1,50 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "Notices management" do
  4 +
  5 + let(:errbit_app) { Fabricate(:app,
  6 + :api_key => 'APIKEY') }
  7 +
  8 + describe "create a new notice" do
  9 + context "with valide notice" do
  10 + let(:xml) { Rails.root.join('spec','fixtures','hoptoad_test_notice.xml').read }
  11 + it 'save a new notice' do
  12 + expect {
  13 + post '/notifier_api/v2/notices', :data => xml
  14 + expect(response).to be_success
  15 + }.to change {
  16 + errbit_app.problems.count
  17 + }.by(1)
  18 + end
  19 + end
  20 +
  21 + context "with notice with empty backtrace" do
  22 + let(:xml) { Rails.root.join('spec','fixtures','hoptoad_test_notice_without_line_of_backtrace.xml').read }
  23 + it 'save a new notice' do
  24 + expect {
  25 + post '/notifier_api/v2/notices', :data => xml
  26 + expect(response).to be_success
  27 + }.to change {
  28 + errbit_app.problems.count
  29 + }.by(1)
  30 + end
  31 + end
  32 +
  33 + context "with notice with bad api_key" do
  34 + let(:errbit_app) { Fabricate(:app) }
  35 + let(:xml) { Rails.root.join('spec','fixtures','hoptoad_test_notice.xml').read }
  36 + it 'not save a new notice and return 422' do
  37 + expect {
  38 + post '/notifier_api/v2/notices', :data => xml
  39 + expect(response.status).to eq 422
  40 + expect(response.body).to eq "Your API key is unknown"
  41 + }.to_not change {
  42 + errbit_app.problems.count
  43 + }.by(1)
  44 + end
  45 +
  46 + end
  47 +
  48 + end
  49 +
  50 +end
... ...