require 'spec_helper' require 'stringio' describe Honeybadger::Backtrace do it "parses a backtrace into lines" do array = [ "app/models/user.rb:13:in `magic'", "app/controllers/users_controller.rb:8:in `index'" ] backtrace = Honeybadger::Backtrace.parse(array) line = backtrace.lines.first expect(line.number).to eq '13' expect(line.file).to eq 'app/models/user.rb' expect(line.method).to eq 'magic' line = backtrace.lines.last expect(line.number).to eq '8' expect(line.file).to eq 'app/controllers/users_controller.rb' expect(line.method).to eq 'index' end it "parses a windows backtrace into lines" do array = [ "C:/Program Files/Server/app/models/user.rb:13:in `magic'", "C:/Program Files/Server/app/controllers/users_controller.rb:8:in `index'" ] backtrace = Honeybadger::Backtrace.parse(array) line = backtrace.lines.first expect(line.number).to eq '13' expect(line.file).to eq 'C:/Program Files/Server/app/models/user.rb' expect(line.method).to eq 'magic' line = backtrace.lines.last expect(line.number).to eq '8' expect(line.file).to eq 'C:/Program Files/Server/app/controllers/users_controller.rb' expect(line.method).to eq 'index' end it "is equal with equal lines" do one = build_backtrace_array two = one.dup expect(Honeybadger::Backtrace.parse(one)).to eq Honeybadger::Backtrace.parse(two) end it "parses massive one-line exceptions into multiple lines" do original_backtrace = Honeybadger::Backtrace. parse(["one:1:in `one'\n two:2:in `two'\n three:3:in `three`"]) expected_backtrace = Honeybadger::Backtrace. parse(["one:1:in `one'", "two:2:in `two'", "three:3:in `three`"]) expect(expected_backtrace).to eq original_backtrace end context "when source file exists" do before(:each) do source = <<-RUBY $:<<'lib' require 'honeybadger' begin raise StandardError rescue => e puts Honeybadger::Notice.new(exception: e).backtrace.to_json end RUBY array = [ "app/models/user.rb:2:in `magic'", "app/concerns/authenticated_controller.rb:4:in `authorize'", "app/controllers/users_controller.rb:8:in `index'" ] ['app/models/user.rb', 'app/concerns/authenticated_controller.rb', 'app/controllers/users_controller.rb'].each do |file| File.should_receive(:exists?).with(file).and_return true File.should_receive(:open).with(file).and_yield StringIO.new(source) end @backtrace = Honeybadger::Backtrace.parse(array) end it "includes a snippet from the source file for each line of the backtrace" do expect(@backtrace.lines[0].source.keys.size).to eq(4) expect(@backtrace.lines[0].source[1]).to match(/\$:<</) expect(@backtrace.lines[0].source[2]).to match(/require/) expect(@backtrace.lines[0].source[3]).to match(/\n/) expect(@backtrace.lines[0].source[4]).to match(/begin/) expect(@backtrace.lines[1].source.keys.size).to eq(5) expect(@backtrace.lines[1].source[2]).to match(/require/) expect(@backtrace.lines[1].source[3]).to match(/\n/) expect(@backtrace.lines[1].source[4]).to match(/begin/) expect(@backtrace.lines[1].source[5]).to match(/StandardError/) expect(@backtrace.lines[1].source[6]).to match(/rescue/) expect(@backtrace.lines[2].source.keys.size).to eq(3) expect(@backtrace.lines[2].source[6]).to match(/rescue/) expect(@backtrace.lines[2].source[7]).to match(/Honeybadger/) expect(@backtrace.lines[2].source[8]).to match(/end/) end end it "fails gracefully when looking up snippet and file doesn't exist" do array = [ "app/models/user.rb:13:in `magic'", "app/controllers/users_controller.rb:8:in `index'" ] backtrace = Honeybadger::Backtrace.parse(array) expect(backtrace.lines[0].source).to be_empty expect(backtrace.lines[1].source).to be_empty end it "has an empty application trace by default" do backtrace = Honeybadger::Backtrace.parse(build_backtrace_array) expect(backtrace.application_lines).to be_empty end context "with a project root" do before(:each) do @project_root = '/some/path' Honeybadger.configure {|config| config.project_root = @project_root } @backtrace_with_root = Honeybadger::Backtrace.parse( ["#{@project_root}/app/models/user.rb:7:in `latest'", "#{@project_root}/app/controllers/users_controller.rb:13:in `index'", "#{@project_root}/vendor/plugins/foo/bar.rb:42:in `baz'", "/lib/something.rb:41:in `open'"], :filters => default_filters) @backtrace_without_root = Honeybadger::Backtrace.parse( ["[PROJECT_ROOT]/app/models/user.rb:7:in `latest'", "[PROJECT_ROOT]/app/controllers/users_controller.rb:13:in `index'", "[PROJECT_ROOT]/vendor/plugins/foo/bar.rb:42:in `baz'", "/lib/something.rb:41:in `open'"]) end it "filters out the project root" do expect(@backtrace_without_root).to eq @backtrace_with_root end it "has an application trace" do expect(@backtrace_without_root.application_lines).to eq @backtrace_without_root.lines[0..1] end it "filters ./vendor from application trace" do expect(@backtrace_without_root.application_lines).not_to include(@backtrace_without_root.lines[2]) end end context "with a project root equals to a part of file name" do before(:each) do # Heroku-like @project_root = '/app' Honeybadger.configure {|config| config.project_root = @project_root } end it "filters out the project root" do backtrace_with_root = Honeybadger::Backtrace.parse( ["#{@project_root}/app/models/user.rb:7:in `latest'", "#{@project_root}/app/controllers/users_controller.rb:13:in `index'", "/lib/something.rb:41:in `open'"], :filters => default_filters) backtrace_without_root = Honeybadger::Backtrace.parse( ["[PROJECT_ROOT]/app/models/user.rb:7:in `latest'", "[PROJECT_ROOT]/app/controllers/users_controller.rb:13:in `index'", "/lib/something.rb:41:in `open'"]) expect(backtrace_without_root).to eq backtrace_with_root end end context "with a blank project root" do before(:each) do Honeybadger.configure {|config| config.project_root = '' } end it "does not filter line numbers with respect to any project root" do backtrace = ["/app/models/user.rb:7:in `latest'", "/app/controllers/users_controller.rb:13:in `index'", "/lib/something.rb:41:in `open'"] backtrace_with_root = Honeybadger::Backtrace.parse(backtrace, :filters => default_filters) backtrace_without_root = Honeybadger::Backtrace.parse(backtrace) expect(backtrace_without_root).to eq backtrace_with_root end end it "removes notifier trace" do inside_notifier = ['lib/honeybadger.rb:13:in `voodoo`'] outside_notifier = ['users_controller:8:in `index`'] without_inside = Honeybadger::Backtrace.parse(outside_notifier) with_inside = Honeybadger::Backtrace.parse(inside_notifier + outside_notifier, :filters => default_filters) expect(without_inside).to eq with_inside end it "runs filters on the backtrace" do filters = [lambda { |line| line.sub('foo', 'bar') }] input = Honeybadger::Backtrace.parse(["foo:13:in `one'", "baz:14:in `two'"], :filters => filters) expected = Honeybadger::Backtrace.parse(["bar:13:in `one'", "baz:14:in `two'"]) expect(expected).to eq input end it "aliases #to_ary as #to_a" do backtrace = Honeybadger::Backtrace.parse(build_backtrace_array) expect(backtrace.to_a).to eq backtrace.to_ary end it "generates json from to_array template" do backtrace = Honeybadger::Backtrace.parse(build_backtrace_array) array = [{'foo' => 'bar'}] backtrace.should_receive(:to_ary).once.and_return(array) json = backtrace.to_json payload = nil expect { payload = JSON.parse(json) }.not_to raise_error expect(payload).to eq array end def build_backtrace_array ["app/models/user.rb:13:in `magic'", "app/controllers/users_controller.rb:8:in `index'"] end def default_filters Honeybadger::Configuration::DEFAULT_BACKTRACE_FILTERS end end