airbrake-ruby 1.0.0.rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/airbrake-ruby.rb +292 -0
- data/lib/airbrake-ruby/async_sender.rb +90 -0
- data/lib/airbrake-ruby/backtrace.rb +75 -0
- data/lib/airbrake-ruby/config.rb +120 -0
- data/lib/airbrake-ruby/filter_chain.rb +86 -0
- data/lib/airbrake-ruby/filters.rb +10 -0
- data/lib/airbrake-ruby/filters/keys_blacklist.rb +37 -0
- data/lib/airbrake-ruby/filters/keys_filter.rb +65 -0
- data/lib/airbrake-ruby/filters/keys_whitelist.rb +37 -0
- data/lib/airbrake-ruby/notice.rb +207 -0
- data/lib/airbrake-ruby/notifier.rb +145 -0
- data/lib/airbrake-ruby/payload_truncator.rb +141 -0
- data/lib/airbrake-ruby/response.rb +53 -0
- data/lib/airbrake-ruby/sync_sender.rb +76 -0
- data/lib/airbrake-ruby/version.rb +7 -0
- data/spec/airbrake_spec.rb +177 -0
- data/spec/async_sender_spec.rb +121 -0
- data/spec/backtrace_spec.rb +77 -0
- data/spec/config_spec.rb +67 -0
- data/spec/filter_chain_spec.rb +157 -0
- data/spec/notice_spec.rb +190 -0
- data/spec/notifier_spec.rb +690 -0
- data/spec/notifier_spec/options_spec.rb +217 -0
- data/spec/payload_truncator_spec.rb +458 -0
- data/spec/spec_helper.rb +98 -0
- metadata +158 -0
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Airbrake::AsyncSender do
|
4
|
+
before do
|
5
|
+
stub_request(:post, /.*/).to_return(status: 201, body: '{}')
|
6
|
+
@sender = described_class.new(Airbrake::Config.new)
|
7
|
+
@workers = @sender.instance_variable_get(:@workers)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "#new" do
|
11
|
+
context "workers_count parameter" do
|
12
|
+
let(:new_workers) { 5 }
|
13
|
+
let(:config) { Airbrake::Config.new(workers: new_workers) }
|
14
|
+
|
15
|
+
it "spawns alive threads in an enclosed ThreadGroup" do
|
16
|
+
expect(@workers).to be_a(ThreadGroup)
|
17
|
+
expect(@workers.list).to all(be_alive)
|
18
|
+
expect(@workers).to be_enclosed
|
19
|
+
end
|
20
|
+
|
21
|
+
it "controls the number of spawned threads" do
|
22
|
+
expect(@workers.list.size).to eq(1)
|
23
|
+
|
24
|
+
sender = described_class.new(config)
|
25
|
+
workers = sender.instance_variable_get(:@workers)
|
26
|
+
|
27
|
+
expect(workers.list.size).to eq(new_workers)
|
28
|
+
sender.close
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "queue" do
|
33
|
+
before do
|
34
|
+
@stdout = StringIO.new
|
35
|
+
end
|
36
|
+
|
37
|
+
let(:notices) { 1000 }
|
38
|
+
|
39
|
+
let(:config) do
|
40
|
+
Airbrake::Config.new(logger: Logger.new(@stdout), workers: 3, queue_size: 10)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "limits the size of the queue, but still sends all notices" do
|
44
|
+
sender = described_class.new(config)
|
45
|
+
|
46
|
+
notices.times { |i| sender.send(i) }
|
47
|
+
sender.close
|
48
|
+
|
49
|
+
log = @stdout.string.split("\n")
|
50
|
+
expect(log.grep(/\*\*Airbrake: \{\}/).size).to eq(notices)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#close" do
|
56
|
+
before do
|
57
|
+
@stderr = StringIO.new
|
58
|
+
config = Airbrake::Config.new(logger: Logger.new(@stderr))
|
59
|
+
@sender = described_class.new(config)
|
60
|
+
@workers = @sender.instance_variable_get(:@workers).list
|
61
|
+
end
|
62
|
+
|
63
|
+
context "when there are no unsent notices" do
|
64
|
+
it "joins the spawned thread" do
|
65
|
+
expect(@workers).to all(be_alive)
|
66
|
+
@sender.close
|
67
|
+
expect(@workers).to all(be_stop)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "when there are some unsent notices" do
|
72
|
+
before do
|
73
|
+
300.times { |i| @sender.send(i) }
|
74
|
+
expect(@sender.instance_variable_get(:@unsent).size).not_to be_zero
|
75
|
+
@sender.close
|
76
|
+
end
|
77
|
+
|
78
|
+
it "warns about the number of notices" do
|
79
|
+
expect(@stderr.string).to match(/waiting to send \d+ unsent notice/)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "prints the debug message the correct number of times" do
|
83
|
+
log = @stderr.string.split("\n")
|
84
|
+
expect(log.grep(/\*\*Airbrake: \{\}/).size).to eq(300)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "waits until the unsent notices queue is empty" do
|
88
|
+
expect(@sender.instance_variable_get(:@unsent).size).to be_zero
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context "when it was already closed" do
|
93
|
+
it "doesn't increase the unsent queue size" do
|
94
|
+
@sender.close
|
95
|
+
expect(@sender.instance_variable_get(:@unsent).size).to be_zero
|
96
|
+
|
97
|
+
expect { @sender.close }.
|
98
|
+
to raise_error(Airbrake::Error, 'attempted to close already closed sender')
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "#has_workers?" do
|
104
|
+
it "returns false when the sender is not closed, but has 0 workers" do
|
105
|
+
sender = described_class.new(Airbrake::Config.new)
|
106
|
+
expect(sender.has_workers?).to be_truthy
|
107
|
+
|
108
|
+
sender.instance_variable_get(:@workers).list.each(&:kill)
|
109
|
+
sleep 1
|
110
|
+
expect(sender.has_workers?).to be_falsey
|
111
|
+
end
|
112
|
+
|
113
|
+
it "returns false when the sender is closed" do
|
114
|
+
sender = described_class.new(Airbrake::Config.new)
|
115
|
+
expect(sender.has_workers?).to be_truthy
|
116
|
+
|
117
|
+
sender.close
|
118
|
+
expect(sender.has_workers?).to be_falsey
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Airbrake::Backtrace do
|
4
|
+
describe ".parse" do
|
5
|
+
context "UNIX backtrace" do
|
6
|
+
let(:backtrace) { described_class.new(AirbrakeTestError.new) }
|
7
|
+
|
8
|
+
let(:parsed_backtrace) do
|
9
|
+
# rubocop:disable Metrics/LineLength, Style/HashSyntax, Style/SpaceAroundOperators, Style/SpaceInsideHashLiteralBraces
|
10
|
+
[{:file=>"/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb", :line=>23, :function=>"<top (required)>"},
|
11
|
+
{:file=>"/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb", :line=>54, :function=>"require"},
|
12
|
+
{:file=>"/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb", :line=>54, :function=>"require"},
|
13
|
+
{:file=>"/home/kyrylo/code/airbrake/ruby/spec/airbrake_spec.rb", :line=>1, :function=>"<top (required)>"},
|
14
|
+
{:file=>"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb", :line=>1327, :function=>"load"},
|
15
|
+
{:file=>"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb", :line=>1327, :function=>"block in load_spec_files"},
|
16
|
+
{:file=>"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb", :line=>1325, :function=>"each"},
|
17
|
+
{:file=>"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb", :line=>1325, :function=>"load_spec_files"},
|
18
|
+
{:file=>"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/runner.rb", :line=>102, :function=>"setup"},
|
19
|
+
{:file=>"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/runner.rb", :line=>88, :function=>"run"},
|
20
|
+
{:file=>"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/runner.rb", :line=>73, :function=>"run"},
|
21
|
+
{:file=>"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/runner.rb", :line=>41, :function=>"invoke"},
|
22
|
+
{:file=>"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/exe/rspec", :line=>4, :function=>"<main>"}]
|
23
|
+
# rubocop:enable Metrics/LineLength, Style/HashSyntax,Style/SpaceAroundOperators, Style/SpaceInsideHashLiteralBraces
|
24
|
+
end
|
25
|
+
|
26
|
+
it "returns a properly formatted array of hashes" do
|
27
|
+
expect(described_class.parse(AirbrakeTestError.new)).
|
28
|
+
to eq(parsed_backtrace)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "Windows backtrace" do
|
33
|
+
let(:windows_bt) do
|
34
|
+
["C:/Program Files/Server/app/models/user.rb:13:in `magic'",
|
35
|
+
"C:/Program Files/Server/app/controllers/users_controller.rb:8:in `index'"]
|
36
|
+
end
|
37
|
+
|
38
|
+
let(:ex) { AirbrakeTestError.new.tap { |e| e.set_backtrace(windows_bt) } }
|
39
|
+
|
40
|
+
let(:parsed_backtrace) do
|
41
|
+
# rubocop:disable Metrics/LineLength, Style/HashSyntax, Style/SpaceInsideHashLiteralBraces, Style/SpaceAroundOperators
|
42
|
+
[{:file=>"C:/Program Files/Server/app/models/user.rb", :line=>13, :function=>"magic"},
|
43
|
+
{:file=>"C:/Program Files/Server/app/controllers/users_controller.rb", :line=>8, :function=>"index"}]
|
44
|
+
# rubocop:enable Metrics/LineLength, Style/HashSyntax, Style/SpaceInsideHashLiteralBraces, Style/SpaceAroundOperators
|
45
|
+
end
|
46
|
+
|
47
|
+
it "returns a properly formatted array of hashes" do
|
48
|
+
expect(described_class.parse(ex)).to eq(parsed_backtrace)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "JRuby Java exceptions" do
|
53
|
+
let(:backtrace_array) do
|
54
|
+
# rubocop:disable Metrics/LineLength, Style/HashSyntax, Style/SpaceInsideHashLiteralBraces, Style/SpaceAroundOperators
|
55
|
+
[{:file=>"InstanceMethodInvoker.java", :line=>26, :function=>"org.jruby.java.invokers.InstanceMethodInvoker.call"},
|
56
|
+
{:file=>"Interpreter.java", :line=>126, :function=>"org.jruby.ir.interpreter.Interpreter.INTERPRET_EVAL"},
|
57
|
+
{:file=>"RubyKernel$INVOKER$s$0$3$eval19.gen", :line=>nil, :function=>"org.jruby.RubyKernel$INVOKER$s$0$3$eval19.call"},
|
58
|
+
{:file=>"RubyKernel$INVOKER$s$0$0$loop.gen", :line=>nil, :function=>"org.jruby.RubyKernel$INVOKER$s$0$0$loop.call"},
|
59
|
+
{:file=>"IRBlockBody.java", :line=>139, :function=>"org.jruby.runtime.IRBlockBody.doYield"},
|
60
|
+
{:file=>"RubyKernel$INVOKER$s$rbCatch19.gen", :line=>nil, :function=>"org.jruby.RubyKernel$INVOKER$s$rbCatch19.call"},
|
61
|
+
{:file=>"/opt/rubies/jruby-9.0.0.0/bin/irb", :line=>nil, :function=>"opt.rubies.jruby_minus_9_dot_0_dot_0_dot_0.bin.irb.invokeOther4:start"},
|
62
|
+
{:file=>"/opt/rubies/jruby-9.0.0.0/bin/irb", :line=>13, :function=>"opt.rubies.jruby_minus_9_dot_0_dot_0_dot_0.bin.irb.RUBY$script"},
|
63
|
+
{:file=>"Compiler.java", :line=>111, :function=>"org.jruby.ir.Compiler$1.load"},
|
64
|
+
{:file=>"Main.java", :line=>225, :function=>"org.jruby.Main.run"},
|
65
|
+
{:file=>"Main.java", :line=>197, :function=>"org.jruby.Main.main"}]
|
66
|
+
# rubocop:enable Metrics/LineLength, Style/HashSyntax, Style/SpaceInsideHashLiteralBraces, Style/SpaceAroundOperators
|
67
|
+
end
|
68
|
+
|
69
|
+
it "returns a properly formatted array of hashes" do
|
70
|
+
allow(described_class).to receive(:java_exception?).and_return(true)
|
71
|
+
|
72
|
+
expect(described_class.parse(JavaAirbrakeTestError.new)).
|
73
|
+
to eq(backtrace_array)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/spec/config_spec.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Airbrake::Config do
|
4
|
+
let(:config) { described_class.new }
|
5
|
+
|
6
|
+
describe "#new" do
|
7
|
+
describe "options" do
|
8
|
+
it "doesn't set the default project_id" do
|
9
|
+
expect(config.project_id).to be_nil
|
10
|
+
end
|
11
|
+
|
12
|
+
it "doesn't set the default project_key" do
|
13
|
+
expect(config.project_key).to be_nil
|
14
|
+
end
|
15
|
+
|
16
|
+
it "doesn't set the default proxy" do
|
17
|
+
expect(config.proxy).to be_empty
|
18
|
+
end
|
19
|
+
|
20
|
+
it "sets the default logger" do
|
21
|
+
expect(config.logger).to be_a Logger
|
22
|
+
end
|
23
|
+
|
24
|
+
it "doesn't set the default app_version" do
|
25
|
+
expect(config.app_version).to be_nil
|
26
|
+
end
|
27
|
+
|
28
|
+
it "sets the default host" do
|
29
|
+
expect(config.host).to eq('https://airbrake.io')
|
30
|
+
end
|
31
|
+
|
32
|
+
it "sets the default endpoint" do
|
33
|
+
expect(config.endpoint).not_to be_nil
|
34
|
+
end
|
35
|
+
|
36
|
+
it "creates a new Config and merges it with the user config" do
|
37
|
+
cfg = described_class.new(logger: StringIO.new)
|
38
|
+
expect(cfg.logger).to be_a StringIO
|
39
|
+
end
|
40
|
+
|
41
|
+
it "raises error on unknown config options" do
|
42
|
+
expect { described_class.new(unknown_option: true) }.
|
43
|
+
to raise_error(Airbrake::Error, /unknown option/)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "sets the default number of workers" do
|
47
|
+
expect(config.workers).to eq(1)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "sets the default number of queue size" do
|
51
|
+
expect(config.queue_size).to eq(100)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "doesn't set the default root_directory" do
|
55
|
+
expect(config.root_directory).to be_nil
|
56
|
+
end
|
57
|
+
|
58
|
+
it "doesn't set the default environment" do
|
59
|
+
expect(config.environment).to be_nil
|
60
|
+
end
|
61
|
+
|
62
|
+
it "doesn't set default notify_environments" do
|
63
|
+
expect(config.ignore_environments).to be_empty
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Airbrake::FilterChain do
|
4
|
+
before do
|
5
|
+
@chain = described_class.new(config)
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:config) { Airbrake::Config.new }
|
9
|
+
|
10
|
+
describe "#refine" do
|
11
|
+
describe "execution order" do
|
12
|
+
let(:notice) do
|
13
|
+
Airbrake::Notice.new(config, AirbrakeTestError.new)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "executes filters starting from the oldest" do
|
17
|
+
nums = []
|
18
|
+
|
19
|
+
3.times do |i|
|
20
|
+
@chain.add_filter(proc { nums << i })
|
21
|
+
end
|
22
|
+
|
23
|
+
@chain.refine(notice)
|
24
|
+
|
25
|
+
expect(nums).to eq([0, 1, 2])
|
26
|
+
end
|
27
|
+
|
28
|
+
it "stops execution once a notice was ignored" do
|
29
|
+
nums = []
|
30
|
+
|
31
|
+
5.times do |i|
|
32
|
+
@chain.add_filter(proc do |notice|
|
33
|
+
nums << i
|
34
|
+
notice.ignore! if i == 2
|
35
|
+
end)
|
36
|
+
end
|
37
|
+
|
38
|
+
@chain.refine(notice)
|
39
|
+
|
40
|
+
expect(nums).to eq([0, 1, 2])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "default backtrace filters" do
|
45
|
+
let(:ex) { AirbrakeTestError.new.tap { |e| e.set_backtrace(backtrace) } }
|
46
|
+
let(:notice) { Airbrake::Notice.new(config, ex) }
|
47
|
+
|
48
|
+
before do
|
49
|
+
Gem.path << '/my/gem/root' << '/my/other/gem/root'
|
50
|
+
@chain.refine(notice)
|
51
|
+
@bt = notice[:errors].first[:backtrace].map { |frame| frame[:file] }
|
52
|
+
end
|
53
|
+
|
54
|
+
shared_examples 'root directories' do |root_directory, bt, expected_bt|
|
55
|
+
let(:backtrace) { bt }
|
56
|
+
|
57
|
+
before do
|
58
|
+
config = Airbrake::Config.new(root_directory: root_directory)
|
59
|
+
chain = described_class.new(config)
|
60
|
+
chain.refine(notice)
|
61
|
+
@bt = notice[:errors].first[:backtrace].map { |frame| frame[:file] }
|
62
|
+
end
|
63
|
+
|
64
|
+
it "filters it out" do
|
65
|
+
expect(@bt).to eq(expected_bt)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# rubocop:disable Metrics/LineLength
|
70
|
+
context "gem root" do
|
71
|
+
bt = [
|
72
|
+
"/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb:23:in `<top (required)>'",
|
73
|
+
"/my/gem/root/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb:1327:in `load'",
|
74
|
+
"/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'",
|
75
|
+
"/my/other/gem/root/gems/rspec-core-3.3.2/exe/rspec:4:in `<main>'"
|
76
|
+
]
|
77
|
+
|
78
|
+
expected_bt = [
|
79
|
+
"/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb",
|
80
|
+
"[GEM_ROOT]/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb",
|
81
|
+
"/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb",
|
82
|
+
"[GEM_ROOT]/gems/rspec-core-3.3.2/exe/rspec"
|
83
|
+
]
|
84
|
+
|
85
|
+
include_examples 'root directories', nil, bt, expected_bt
|
86
|
+
end
|
87
|
+
|
88
|
+
context "root directory" do
|
89
|
+
context "when normal string path" do
|
90
|
+
bt = [
|
91
|
+
"/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb:23:in `<top (required)>'",
|
92
|
+
"/var/www/project/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb:1327:in `load'",
|
93
|
+
"/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'",
|
94
|
+
"/var/www/project/gems/rspec-core-3.3.2/exe/rspec:4:in `<main>'"
|
95
|
+
]
|
96
|
+
|
97
|
+
expected_bt = [
|
98
|
+
"/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb",
|
99
|
+
"[PROJECT_ROOT]/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb",
|
100
|
+
"/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb",
|
101
|
+
"[PROJECT_ROOT]/gems/rspec-core-3.3.2/exe/rspec"
|
102
|
+
]
|
103
|
+
|
104
|
+
include_examples 'root directories', '/var/www/project', bt, expected_bt
|
105
|
+
end
|
106
|
+
|
107
|
+
context "when equals to a part of filename" do
|
108
|
+
bt = [
|
109
|
+
"/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb:23:in `<top (required)>'",
|
110
|
+
"/var/www/gems/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb:1327:in `load'",
|
111
|
+
"/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'",
|
112
|
+
"/var/www/gems/gems/rspec-core-3.3.2/exe/rspec:4:in `<main>'"
|
113
|
+
]
|
114
|
+
|
115
|
+
expected_bt = [
|
116
|
+
"/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb",
|
117
|
+
"[PROJECT_ROOT]/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb",
|
118
|
+
"/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb",
|
119
|
+
"[PROJECT_ROOT]/gems/rspec-core-3.3.2/exe/rspec"
|
120
|
+
]
|
121
|
+
|
122
|
+
include_examples 'root directories', '/var/www/gems', bt, expected_bt
|
123
|
+
end
|
124
|
+
|
125
|
+
context "when normal pathname path" do
|
126
|
+
bt = [
|
127
|
+
"/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb:23:in `<top (required)>'",
|
128
|
+
"/var/www/project/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb:1327:in `load'",
|
129
|
+
"/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'",
|
130
|
+
"/var/www/project/gems/rspec-core-3.3.2/exe/rspec:4:in `<main>'"
|
131
|
+
]
|
132
|
+
|
133
|
+
expected_bt = [
|
134
|
+
"/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb",
|
135
|
+
"[PROJECT_ROOT]/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb",
|
136
|
+
"/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb",
|
137
|
+
"[PROJECT_ROOT]/gems/rspec-core-3.3.2/exe/rspec"
|
138
|
+
]
|
139
|
+
|
140
|
+
include_examples 'root directories',
|
141
|
+
Pathname.new('/var/www/project'), bt, expected_bt
|
142
|
+
end
|
143
|
+
end
|
144
|
+
# rubocop:enable Metrics/LineLength
|
145
|
+
end
|
146
|
+
|
147
|
+
describe "default ignore filters" do
|
148
|
+
context "system exit filter" do
|
149
|
+
it "marks SystemExit exceptions as ignored" do
|
150
|
+
notice = Airbrake::Notice.new(config, SystemExit.new)
|
151
|
+
expect { @chain.refine(notice) }.
|
152
|
+
to(change { notice.ignored? }.from(false).to(true))
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
data/spec/notice_spec.rb
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Airbrake::Notice do
|
4
|
+
let(:notice) do
|
5
|
+
described_class.new(Airbrake::Config.new, AirbrakeTestError.new, bingo: '1')
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "#new" do
|
9
|
+
context "nested exceptions" do
|
10
|
+
it "unwinds nested exceptions" do
|
11
|
+
begin
|
12
|
+
begin
|
13
|
+
raise AirbrakeTestError
|
14
|
+
rescue AirbrakeTestError
|
15
|
+
Ruby21Error.raise_error('bingo')
|
16
|
+
end
|
17
|
+
rescue Ruby21Error => ex
|
18
|
+
notice = described_class.new(Airbrake::Config.new, ex)
|
19
|
+
|
20
|
+
expect(notice[:errors].size).to eq(2)
|
21
|
+
expect(notice[:errors][0][:message]).to eq('bingo')
|
22
|
+
expect(notice[:errors][1][:message]).to eq('App crashed!')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it "unwinds no more than 3 nested exceptions" do
|
27
|
+
begin
|
28
|
+
begin
|
29
|
+
raise AirbrakeTestError
|
30
|
+
rescue AirbrakeTestError
|
31
|
+
begin
|
32
|
+
Ruby21Error.raise_error('bongo')
|
33
|
+
rescue Ruby21Error
|
34
|
+
begin
|
35
|
+
Ruby21Error.raise_error('bango')
|
36
|
+
rescue Ruby21Error
|
37
|
+
Ruby21Error.raise_error('bingo')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
rescue Ruby21Error => ex
|
42
|
+
notice = described_class.new(Airbrake::Config.new, ex)
|
43
|
+
|
44
|
+
expect(notice[:errors].size).to eq(3)
|
45
|
+
expect(notice[:errors][0][:message]).to eq('bingo')
|
46
|
+
expect(notice[:errors][1][:message]).to eq('bango')
|
47
|
+
expect(notice[:errors][2][:message]).to eq('bongo')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#to_json" do
|
54
|
+
context "app_version" do
|
55
|
+
context "when missing" do
|
56
|
+
it "doesn't include app_version" do
|
57
|
+
expect(notice.to_json).not_to match(/"context":{"version":"1.2.3"/)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "when present" do
|
62
|
+
let(:config) do
|
63
|
+
Airbrake::Config.new(app_version: '1.2.3', root_directory: '/one/two')
|
64
|
+
end
|
65
|
+
|
66
|
+
let(:notice) { described_class.new(config, AirbrakeTestError.new) }
|
67
|
+
|
68
|
+
it "includes app_version" do
|
69
|
+
expect(notice.to_json).to match(/"context":{"version":"1.2.3"/)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "includes root_directory" do
|
73
|
+
expect(notice.to_json).to match(%r{"rootDirectory":"/one/two"})
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "truncation" do
|
79
|
+
shared_examples 'payloads' do |size, msg|
|
80
|
+
it msg do
|
81
|
+
ex = AirbrakeTestError.new
|
82
|
+
|
83
|
+
backtrace = []
|
84
|
+
size.times { backtrace << "bin/rails:3:in `<main>'" }
|
85
|
+
ex.set_backtrace(backtrace)
|
86
|
+
|
87
|
+
notice = described_class.new(Airbrake::Config.new, ex)
|
88
|
+
|
89
|
+
expect(notice.to_json.bytesize).to be < 64000
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
max_msg = 'truncates to the max allowed size'
|
94
|
+
|
95
|
+
context "with an extremely huge payload" do
|
96
|
+
include_examples 'payloads', 200_000, max_msg
|
97
|
+
end
|
98
|
+
|
99
|
+
context "with a big payload" do
|
100
|
+
include_examples 'payloads', 50_000, max_msg
|
101
|
+
end
|
102
|
+
|
103
|
+
small_msg = "doesn't truncate it"
|
104
|
+
|
105
|
+
context "with a small payload" do
|
106
|
+
include_examples 'payloads', 1000, small_msg
|
107
|
+
end
|
108
|
+
|
109
|
+
context "with a tiny payload" do
|
110
|
+
include_examples 'payloads', 300, small_msg
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "object replacement with its string version" do
|
114
|
+
let(:klass) { Class.new {} }
|
115
|
+
let(:ex) { AirbrakeTestError.new }
|
116
|
+
let(:params) { { bingo: [Object.new, klass.new] } }
|
117
|
+
let(:notice) { described_class.new(Airbrake::Config.new, ex, params) }
|
118
|
+
|
119
|
+
before do
|
120
|
+
backtrace = []
|
121
|
+
backtrace_size.times { backtrace << "bin/rails:3:in `<main>'" }
|
122
|
+
ex.set_backtrace(backtrace)
|
123
|
+
end
|
124
|
+
|
125
|
+
context "with payload within the limits" do
|
126
|
+
let(:backtrace_size) { 1000 }
|
127
|
+
|
128
|
+
it "doesn't happen" do
|
129
|
+
expect(notice.to_json).
|
130
|
+
to match(/bingo":\["#<Object:.+>","#<#<Class:.+>:.+>"/)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
context "with payload bigger than the limit" do
|
135
|
+
context "with payload within the limits" do
|
136
|
+
let(:backtrace_size) { 50_000 }
|
137
|
+
|
138
|
+
it "happens" do
|
139
|
+
expect(notice.to_json).
|
140
|
+
to match(/bingo":\[".+Object.+",".+Class.+"/)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
it "overwrites the 'notifier' payload with the default values" do
|
148
|
+
notice[:notifier] = { name: 'bingo', bango: 'bongo' }
|
149
|
+
|
150
|
+
expect(notice.to_json).
|
151
|
+
to match(/"notifier":{"name":"airbrake-ruby","version":".+","url":".+"}/)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe "#[]" do
|
156
|
+
it "accesses payload" do
|
157
|
+
expect(notice[:params]).to eq(bingo: '1')
|
158
|
+
end
|
159
|
+
|
160
|
+
it "raises error if notice is ignored" do
|
161
|
+
notice.ignore!
|
162
|
+
expect { notice[:params] }.
|
163
|
+
to raise_error(Airbrake::Error, 'cannot access ignored notice')
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe "#[]=" do
|
168
|
+
it "sets a payload value" do
|
169
|
+
hash = { bingo: 'bango' }
|
170
|
+
notice[:params] = hash
|
171
|
+
expect(notice[:params]).to equal(hash)
|
172
|
+
end
|
173
|
+
|
174
|
+
it "raises error if notice is ignored" do
|
175
|
+
notice.ignore!
|
176
|
+
expect { notice[:params] = {} }.
|
177
|
+
to raise_error(Airbrake::Error, 'cannot access ignored notice')
|
178
|
+
end
|
179
|
+
|
180
|
+
it "raises error when trying to assign unrecognized key" do
|
181
|
+
expect { notice[:bingo] = 1 }.
|
182
|
+
to raise_error(Airbrake::Error, /:bingo is not recognized among/)
|
183
|
+
end
|
184
|
+
|
185
|
+
it "raises when setting non-hash objects as the value" do
|
186
|
+
expect { notice[:params] = Object.new }.
|
187
|
+
to raise_error(Airbrake::Error, 'Got Object value, wanted a Hash')
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|