airbrake-ruby 4.7.0
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 +515 -0
- data/lib/airbrake-ruby/async_sender.rb +80 -0
- data/lib/airbrake-ruby/backtrace.rb +196 -0
- data/lib/airbrake-ruby/benchmark.rb +39 -0
- data/lib/airbrake-ruby/code_hunk.rb +51 -0
- data/lib/airbrake-ruby/config.rb +229 -0
- data/lib/airbrake-ruby/config/validator.rb +91 -0
- data/lib/airbrake-ruby/deploy_notifier.rb +36 -0
- data/lib/airbrake-ruby/file_cache.rb +54 -0
- data/lib/airbrake-ruby/filter_chain.rb +95 -0
- data/lib/airbrake-ruby/filters/context_filter.rb +29 -0
- data/lib/airbrake-ruby/filters/dependency_filter.rb +31 -0
- data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +46 -0
- data/lib/airbrake-ruby/filters/gem_root_filter.rb +33 -0
- data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +92 -0
- data/lib/airbrake-ruby/filters/git_repository_filter.rb +64 -0
- data/lib/airbrake-ruby/filters/git_revision_filter.rb +66 -0
- data/lib/airbrake-ruby/filters/keys_blacklist.rb +49 -0
- data/lib/airbrake-ruby/filters/keys_filter.rb +140 -0
- data/lib/airbrake-ruby/filters/keys_whitelist.rb +48 -0
- data/lib/airbrake-ruby/filters/root_directory_filter.rb +28 -0
- data/lib/airbrake-ruby/filters/sql_filter.rb +125 -0
- data/lib/airbrake-ruby/filters/system_exit_filter.rb +23 -0
- data/lib/airbrake-ruby/filters/thread_filter.rb +92 -0
- data/lib/airbrake-ruby/hash_keyable.rb +37 -0
- data/lib/airbrake-ruby/ignorable.rb +44 -0
- data/lib/airbrake-ruby/inspectable.rb +39 -0
- data/lib/airbrake-ruby/loggable.rb +34 -0
- data/lib/airbrake-ruby/monotonic_time.rb +43 -0
- data/lib/airbrake-ruby/nested_exception.rb +38 -0
- data/lib/airbrake-ruby/notice.rb +162 -0
- data/lib/airbrake-ruby/notice_notifier.rb +134 -0
- data/lib/airbrake-ruby/performance_breakdown.rb +46 -0
- data/lib/airbrake-ruby/performance_notifier.rb +155 -0
- data/lib/airbrake-ruby/promise.rb +109 -0
- data/lib/airbrake-ruby/query.rb +54 -0
- data/lib/airbrake-ruby/request.rb +46 -0
- data/lib/airbrake-ruby/response.rb +74 -0
- data/lib/airbrake-ruby/stashable.rb +15 -0
- data/lib/airbrake-ruby/stat.rb +73 -0
- data/lib/airbrake-ruby/sync_sender.rb +113 -0
- data/lib/airbrake-ruby/tdigest.rb +393 -0
- data/lib/airbrake-ruby/thread_pool.rb +128 -0
- data/lib/airbrake-ruby/time_truncate.rb +17 -0
- data/lib/airbrake-ruby/timed_trace.rb +58 -0
- data/lib/airbrake-ruby/truncator.rb +115 -0
- data/lib/airbrake-ruby/version.rb +6 -0
- data/spec/airbrake_spec.rb +324 -0
- data/spec/async_sender_spec.rb +72 -0
- data/spec/backtrace_spec.rb +427 -0
- data/spec/benchmark_spec.rb +33 -0
- data/spec/code_hunk_spec.rb +115 -0
- data/spec/config/validator_spec.rb +184 -0
- data/spec/config_spec.rb +154 -0
- data/spec/deploy_notifier_spec.rb +48 -0
- data/spec/file_cache_spec.rb +34 -0
- data/spec/filter_chain_spec.rb +92 -0
- data/spec/filters/context_filter_spec.rb +23 -0
- data/spec/filters/dependency_filter_spec.rb +12 -0
- data/spec/filters/exception_attributes_filter_spec.rb +50 -0
- data/spec/filters/gem_root_filter_spec.rb +41 -0
- data/spec/filters/git_last_checkout_filter_spec.rb +46 -0
- data/spec/filters/git_repository_filter.rb +61 -0
- data/spec/filters/git_revision_filter_spec.rb +126 -0
- data/spec/filters/keys_blacklist_spec.rb +225 -0
- data/spec/filters/keys_whitelist_spec.rb +194 -0
- data/spec/filters/root_directory_filter_spec.rb +39 -0
- data/spec/filters/sql_filter_spec.rb +262 -0
- data/spec/filters/system_exit_filter_spec.rb +14 -0
- data/spec/filters/thread_filter_spec.rb +277 -0
- data/spec/fixtures/notroot.txt +7 -0
- data/spec/fixtures/project_root/code.rb +221 -0
- data/spec/fixtures/project_root/empty_file.rb +0 -0
- data/spec/fixtures/project_root/long_line.txt +1 -0
- data/spec/fixtures/project_root/short_file.rb +3 -0
- data/spec/fixtures/project_root/vendor/bundle/ignored_file.rb +5 -0
- data/spec/helpers.rb +9 -0
- data/spec/ignorable_spec.rb +14 -0
- data/spec/inspectable_spec.rb +45 -0
- data/spec/monotonic_time_spec.rb +12 -0
- data/spec/nested_exception_spec.rb +73 -0
- data/spec/notice_notifier/options_spec.rb +259 -0
- data/spec/notice_notifier_spec.rb +356 -0
- data/spec/notice_spec.rb +296 -0
- data/spec/performance_breakdown_spec.rb +12 -0
- data/spec/performance_notifier_spec.rb +491 -0
- data/spec/promise_spec.rb +197 -0
- data/spec/query_spec.rb +11 -0
- data/spec/request_spec.rb +11 -0
- data/spec/response_spec.rb +88 -0
- data/spec/spec_helper.rb +100 -0
- data/spec/stashable_spec.rb +23 -0
- data/spec/stat_spec.rb +47 -0
- data/spec/sync_sender_spec.rb +133 -0
- data/spec/tdigest_spec.rb +230 -0
- data/spec/thread_pool_spec.rb +158 -0
- data/spec/time_truncate_spec.rb +13 -0
- data/spec/timed_trace_spec.rb +125 -0
- data/spec/truncator_spec.rb +238 -0
- metadata +216 -0
@@ -0,0 +1,197 @@
|
|
1
|
+
RSpec.describe Airbrake::Promise do
|
2
|
+
describe ".then" do
|
3
|
+
let(:resolved_with) { [] }
|
4
|
+
let(:rejected_with) { [] }
|
5
|
+
|
6
|
+
context "when it is not resolved" do
|
7
|
+
it "returns self" do
|
8
|
+
expect(subject.then {}).to eq(subject)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "doesn't call the resolve callbacks yet" do
|
12
|
+
subject.then { resolved_with << 1 }.then { resolved_with << 2 }
|
13
|
+
expect(resolved_with).to be_empty
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "when it is resolved" do
|
18
|
+
shared_examples "then specs" do
|
19
|
+
it "returns self" do
|
20
|
+
expect(subject.then {}).to eq(subject)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "yields the resolved value" do
|
24
|
+
yielded = nil
|
25
|
+
subject.then { |value| yielded = value }
|
26
|
+
expect(yielded).to eq('id' => '123')
|
27
|
+
end
|
28
|
+
|
29
|
+
it "calls the resolve callbacks" do
|
30
|
+
expect(resolved_with).to match_array([1, 2])
|
31
|
+
end
|
32
|
+
|
33
|
+
it "doesn't call the reject callbacks" do
|
34
|
+
expect(rejected_with).to be_empty
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "and there are some resolve and reject callbacks in place" do
|
39
|
+
before do
|
40
|
+
subject.then { resolved_with << 1 }.then { resolved_with << 2 }
|
41
|
+
subject.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
|
42
|
+
subject.resolve('id' => '123')
|
43
|
+
end
|
44
|
+
|
45
|
+
include_examples "then specs"
|
46
|
+
|
47
|
+
it "registers the resolve callbacks" do
|
48
|
+
subject.resolve('id' => '456')
|
49
|
+
expect(resolved_with).to match_array([1, 2, 1, 2])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "and additional then callbacks are added" do
|
54
|
+
before do
|
55
|
+
subject.resolve('id' => '123')
|
56
|
+
subject.then { resolved_with << 1 }.then { resolved_with << 2 }
|
57
|
+
end
|
58
|
+
|
59
|
+
include_examples "then specs"
|
60
|
+
|
61
|
+
it "doesn't register new resolve callbacks" do
|
62
|
+
subject.resolve('id' => '456')
|
63
|
+
expect(resolved_with).to match_array([1, 2])
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe ".rescue" do
|
70
|
+
let(:resolved_with) { [] }
|
71
|
+
let(:rejected_with) { [] }
|
72
|
+
|
73
|
+
context "when it is not rejected" do
|
74
|
+
it "returns self" do
|
75
|
+
expect(subject.then {}).to eq(subject)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "doesn't call the reject callbacks yet" do
|
79
|
+
subject.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
|
80
|
+
expect(rejected_with).to be_empty
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context "when it is rejected" do
|
85
|
+
shared_examples "rescue specs" do
|
86
|
+
it "returns self" do
|
87
|
+
expect(subject.rescue {}).to eq(subject)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "yields the rejected value" do
|
91
|
+
yielded = nil
|
92
|
+
subject.rescue { |value| yielded = value }
|
93
|
+
expect(yielded).to eq('bingo')
|
94
|
+
end
|
95
|
+
|
96
|
+
it "doesn't call the resolve callbacks" do
|
97
|
+
expect(resolved_with).to be_empty
|
98
|
+
end
|
99
|
+
|
100
|
+
it "calls the reject callbacks" do
|
101
|
+
expect(rejected_with).to match_array([1, 2])
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context "and there are some resolve and reject callbacks in place" do
|
106
|
+
before do
|
107
|
+
subject.then { resolved_with << 1 }.then { resolved_with << 2 }
|
108
|
+
subject.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
|
109
|
+
subject.reject('bingo')
|
110
|
+
end
|
111
|
+
|
112
|
+
include_examples "rescue specs"
|
113
|
+
|
114
|
+
it "registers the reject callbacks" do
|
115
|
+
subject.reject('bingo again')
|
116
|
+
expect(rejected_with).to match_array([1, 2, 1, 2])
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context "and additional reject callbacks are added" do
|
121
|
+
before do
|
122
|
+
subject.reject('bingo')
|
123
|
+
subject.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
|
124
|
+
end
|
125
|
+
|
126
|
+
include_examples "rescue specs"
|
127
|
+
|
128
|
+
it "doesn't register new reject callbacks" do
|
129
|
+
subject.reject('bingo again')
|
130
|
+
expect(rejected_with).to match_array([1, 2])
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe ".resolve" do
|
137
|
+
it "returns self" do
|
138
|
+
expect(subject.resolve(1)).to eq(subject)
|
139
|
+
end
|
140
|
+
|
141
|
+
it "executes callbacks attached with .then" do
|
142
|
+
array = []
|
143
|
+
subject.then { |notice_id| array << notice_id }.rescue { array << 999 }
|
144
|
+
|
145
|
+
expect(array.size).to be_zero
|
146
|
+
subject.resolve(1)
|
147
|
+
expect(array).to match_array([1])
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe ".reject" do
|
152
|
+
it "returns self" do
|
153
|
+
expect(subject.reject(1)).to eq(subject)
|
154
|
+
end
|
155
|
+
|
156
|
+
it "executes callbacks attached with .rescue" do
|
157
|
+
array = []
|
158
|
+
subject.then { array << 1 }.rescue { |error| array << error }
|
159
|
+
|
160
|
+
expect(array.size).to be_zero
|
161
|
+
subject.reject(999)
|
162
|
+
expect(array).to match_array([999])
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe "#rejected?" do
|
167
|
+
context "when it was rejected" do
|
168
|
+
before { subject.reject(1) }
|
169
|
+
it { is_expected.to be_rejected }
|
170
|
+
end
|
171
|
+
|
172
|
+
context "when it wasn't rejected" do
|
173
|
+
it { is_expected.not_to be_rejected }
|
174
|
+
end
|
175
|
+
|
176
|
+
context "when it was resolved" do
|
177
|
+
before { subject.resolve }
|
178
|
+
it { is_expected.not_to be_rejected }
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
describe "#resolved?" do
|
183
|
+
context "when it was resolved" do
|
184
|
+
before { subject.resolve }
|
185
|
+
it { is_expected.to be_resolved }
|
186
|
+
end
|
187
|
+
|
188
|
+
context "when it wasn't resolved" do
|
189
|
+
it { is_expected.not_to be_resolved }
|
190
|
+
end
|
191
|
+
|
192
|
+
context "when it was rejected" do
|
193
|
+
before { subject.reject(1) }
|
194
|
+
it { is_expected.not_to be_resolved }
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
data/spec/query_spec.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
RSpec.describe Airbrake::Response do
|
2
|
+
describe ".parse" do
|
3
|
+
[200, 201, 204].each do |code|
|
4
|
+
context "when response code is #{code}" do
|
5
|
+
it "logs response body" do
|
6
|
+
expect(Airbrake::Loggable.instance).to receive(:debug).with(
|
7
|
+
/Airbrake::Response \(#{code}\): {}/
|
8
|
+
)
|
9
|
+
described_class.parse(OpenStruct.new(code: code, body: '{}'))
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
[400, 401, 403, 420].each do |code|
|
15
|
+
context "when response code is #{code}" do
|
16
|
+
it "logs response message" do
|
17
|
+
expect(Airbrake::Loggable.instance).to receive(:error).with(
|
18
|
+
/Airbrake: foo/
|
19
|
+
)
|
20
|
+
described_class.parse(
|
21
|
+
OpenStruct.new(code: code, body: '{"message":"foo"}')
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "when response code is 429" do
|
28
|
+
let(:response) { OpenStruct.new(code: 429, body: '{"message":"rate limited"}') }
|
29
|
+
|
30
|
+
it "logs response message" do
|
31
|
+
expect(Airbrake::Loggable.instance).to receive(:error).with(
|
32
|
+
/Airbrake: rate limited/
|
33
|
+
)
|
34
|
+
described_class.parse(response)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "returns an error response" do
|
38
|
+
time = Time.now
|
39
|
+
allow(Time).to receive(:now).and_return(time)
|
40
|
+
|
41
|
+
resp = described_class.parse(response)
|
42
|
+
expect(resp).to include(
|
43
|
+
'error' => '**Airbrake: rate limited',
|
44
|
+
'rate_limit_reset' => time
|
45
|
+
)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when response code is unhandled" do
|
50
|
+
let(:response) { OpenStruct.new(code: 500, body: 'foo') }
|
51
|
+
|
52
|
+
it "logs response body" do
|
53
|
+
expect(Airbrake::Loggable.instance).to receive(:error).with(
|
54
|
+
/Airbrake: unexpected code \(500\)\. Body: foo/
|
55
|
+
)
|
56
|
+
described_class.parse(response)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "returns an error response" do
|
60
|
+
resp = described_class.parse(response)
|
61
|
+
expect(resp).to eq('error' => 'foo')
|
62
|
+
end
|
63
|
+
|
64
|
+
it "truncates body" do
|
65
|
+
response.body *= 1000
|
66
|
+
resp = described_class.parse(response)
|
67
|
+
expect(resp).to eq('error' => ('foo' * 33) + 'fo...')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "when response body can't be parsed as JSON" do
|
72
|
+
let(:response) { OpenStruct.new(code: 201, body: 'foo') }
|
73
|
+
|
74
|
+
it "logs response body" do
|
75
|
+
expect(Airbrake::Loggable.instance).to receive(:error).with(
|
76
|
+
/Airbrake: error while parsing body \(.*unexpected token.*\)\. Body: foo/
|
77
|
+
)
|
78
|
+
described_class.parse(response)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "returns an error message" do
|
82
|
+
expect(described_class.parse(response)['error']).to match(
|
83
|
+
/\A#<JSON::ParserError.+>/
|
84
|
+
)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
SimpleCov.start if ENV['COVERAGE']
|
3
|
+
|
4
|
+
require 'airbrake-ruby'
|
5
|
+
|
6
|
+
require 'rspec/its'
|
7
|
+
|
8
|
+
require 'webmock'
|
9
|
+
require 'webmock/rspec'
|
10
|
+
require 'pry'
|
11
|
+
|
12
|
+
require 'pathname'
|
13
|
+
require 'webrick'
|
14
|
+
require 'English'
|
15
|
+
require 'base64'
|
16
|
+
require 'pp'
|
17
|
+
|
18
|
+
require 'helpers'
|
19
|
+
|
20
|
+
RSpec.configure do |c|
|
21
|
+
c.order = 'random'
|
22
|
+
c.color = true
|
23
|
+
c.disable_monkey_patching!
|
24
|
+
c.include Helpers
|
25
|
+
end
|
26
|
+
|
27
|
+
Thread.abort_on_exception = true
|
28
|
+
|
29
|
+
WebMock.disable_net_connect!(allow_localhost: true)
|
30
|
+
|
31
|
+
class AirbrakeTestError < RuntimeError
|
32
|
+
attr_reader :backtrace
|
33
|
+
|
34
|
+
def initialize(*)
|
35
|
+
super
|
36
|
+
# rubocop:disable Metrics/LineLength
|
37
|
+
@backtrace = [
|
38
|
+
"/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb:23:in `<top (required)>'",
|
39
|
+
"/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'",
|
40
|
+
"/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'",
|
41
|
+
"/home/kyrylo/code/airbrake/ruby/spec/airbrake_spec.rb:1:in `<top (required)>'",
|
42
|
+
"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb:1327:in `load'",
|
43
|
+
"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb:1327:in `block in load_spec_files'",
|
44
|
+
"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb:1325:in `each'",
|
45
|
+
"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb:1325:in `load_spec_files'",
|
46
|
+
"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/runner.rb:102:in `setup'",
|
47
|
+
"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/runner.rb:88:in `run'",
|
48
|
+
"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/runner.rb:73:in `run'",
|
49
|
+
"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/runner.rb:41:in `invoke'",
|
50
|
+
"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/exe/rspec:4:in `<main>'"
|
51
|
+
]
|
52
|
+
# rubocop:enable Metrics/LineLength
|
53
|
+
end
|
54
|
+
|
55
|
+
# rubocop:disable Naming/AccessorMethodName
|
56
|
+
def set_backtrace(backtrace)
|
57
|
+
@backtrace = backtrace
|
58
|
+
end
|
59
|
+
# rubocop:enable Naming/AccessorMethodName
|
60
|
+
|
61
|
+
def message
|
62
|
+
'App crashed!'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class JavaAirbrakeTestError < AirbrakeTestError
|
67
|
+
def initialize(*)
|
68
|
+
super
|
69
|
+
# rubocop:disable Metrics/LineLength
|
70
|
+
@backtrace = [
|
71
|
+
"org.jruby.java.invokers.InstanceMethodInvoker.call(InstanceMethodInvoker.java:26)",
|
72
|
+
"org.jruby.ir.interpreter.Interpreter.INTERPRET_EVAL(Interpreter.java:126)",
|
73
|
+
"org.jruby.RubyKernel$INVOKER$s$0$3$eval19.call(RubyKernel$INVOKER$s$0$3$eval19.gen)",
|
74
|
+
"org.jruby.RubyKernel$INVOKER$s$0$0$loop.call(RubyKernel$INVOKER$s$0$0$loop.gen)",
|
75
|
+
"org.jruby.runtime.IRBlockBody.doYield(IRBlockBody.java:139)",
|
76
|
+
"org.jruby.RubyKernel$INVOKER$s$rbCatch19.call(RubyKernel$INVOKER$s$rbCatch19.gen)",
|
77
|
+
"opt.rubies.jruby_minus_9_dot_0_dot_0_dot_0.bin.irb.invokeOther4:start(/opt/rubies/jruby-9.0.0.0/bin/irb)",
|
78
|
+
"opt.rubies.jruby_minus_9_dot_0_dot_0_dot_0.bin.irb.RUBY$script(/opt/rubies/jruby-9.0.0.0/bin/irb:13)",
|
79
|
+
"org.jruby.ir.Compiler$1.load(Compiler.java:111)",
|
80
|
+
"org.jruby.Main.run(Main.java:225)",
|
81
|
+
"org.jruby.Main.main(Main.java:197)"
|
82
|
+
]
|
83
|
+
# rubocop:enable Metrics/LineLength
|
84
|
+
end
|
85
|
+
|
86
|
+
def is_a?(*)
|
87
|
+
true
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class Ruby21Error < RuntimeError
|
92
|
+
attr_accessor :cause
|
93
|
+
|
94
|
+
def self.raise_error(msg)
|
95
|
+
ex = new(msg)
|
96
|
+
ex.cause = $ERROR_INFO
|
97
|
+
|
98
|
+
raise ex
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
RSpec.describe Airbrake::Stashable do
|
2
|
+
let(:klass) do
|
3
|
+
mod = described_class
|
4
|
+
Class.new { include(mod) }
|
5
|
+
end
|
6
|
+
|
7
|
+
describe "#stash" do
|
8
|
+
subject { klass.new }
|
9
|
+
|
10
|
+
it "returns a hash" do
|
11
|
+
expect(subject.stash).to be_a(Hash)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "returns an empty hash" do
|
15
|
+
expect(subject.stash).to be_empty
|
16
|
+
end
|
17
|
+
|
18
|
+
it "remembers what was put in the stash" do
|
19
|
+
subject.stash[:foo] = 1
|
20
|
+
expect(subject.stash[:foo]).to eq(1)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/spec/stat_spec.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
RSpec.describe Airbrake::Stat do
|
2
|
+
describe "#to_h" do
|
3
|
+
it "converts to a hash" do
|
4
|
+
expect(subject.to_h).to eq(
|
5
|
+
'count' => 0,
|
6
|
+
'sum' => 0.0,
|
7
|
+
'sumsq' => 0.0,
|
8
|
+
'tdigest' => 'AAAAAkA0AAAAAAAAAAAAAA=='
|
9
|
+
)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#increment" do
|
14
|
+
let(:start_time) { Time.new(2018, 1, 1, 0, 0, 20, 0) }
|
15
|
+
let(:end_time) { Time.new(2018, 1, 1, 0, 0, 22, 0) }
|
16
|
+
|
17
|
+
before { subject.increment(start_time, end_time) }
|
18
|
+
|
19
|
+
its(:sum) { is_expected.to eq(2000) }
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#increment_ms" do
|
23
|
+
before { subject.increment_ms(1000) }
|
24
|
+
|
25
|
+
its(:count) { is_expected.to eq(1) }
|
26
|
+
its(:sum) { is_expected.to eq(1000) }
|
27
|
+
its(:sumsq) { is_expected.to eq(1000000) }
|
28
|
+
|
29
|
+
it "updates tdigest" do
|
30
|
+
expect(subject.tdigest.size).to eq(1)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#inspect" do
|
35
|
+
it "provides custom inspect output" do
|
36
|
+
expect(subject.inspect).to eq(
|
37
|
+
'#<struct Airbrake::Stat count=0, sum=0.0, sumsq=0.0>'
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "#pretty_print" do
|
43
|
+
it "is an alias of #inspect" do
|
44
|
+
expect(subject.method(:pretty_print)).to eql(subject.method(:inspect))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|