airbrake-ruby 4.6.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 +513 -0
- data/lib/airbrake-ruby/async_sender.rb +142 -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 +48 -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 +104 -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 +45 -0
- data/lib/airbrake-ruby/performance_notifier.rb +125 -0
- data/lib/airbrake-ruby/promise.rb +109 -0
- data/lib/airbrake-ruby/query.rb +53 -0
- data/lib/airbrake-ruby/request.rb +45 -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/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 +155 -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.rb +36 -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 +219 -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_spec.rb +356 -0
- data/spec/notice_notifier_spec/options_spec.rb +259 -0
- data/spec/notice_spec.rb +296 -0
- data/spec/performance_breakdown_spec.rb +12 -0
- data/spec/performance_notifier_spec.rb +435 -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/time_truncate_spec.rb +13 -0
- data/spec/timed_trace_spec.rb +125 -0
- data/spec/truncator_spec.rb +238 -0
- metadata +213 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
module Airbrake
|
2
|
+
# TimeTruncate contains methods for truncating time.
|
3
|
+
#
|
4
|
+
# @api private
|
5
|
+
# @since v3.2.0
|
6
|
+
module TimeTruncate
|
7
|
+
# Truncate +time+ to floor minute and turn it into an RFC3339 timestamp.
|
8
|
+
#
|
9
|
+
# @param [Time] time
|
10
|
+
# @return [String]
|
11
|
+
def self.utc_truncate_minutes(time)
|
12
|
+
tm = time.getutc
|
13
|
+
|
14
|
+
Time.utc(tm.year, tm.month, tm.day, tm.hour, tm.min).to_datetime.rfc3339
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Airbrake
|
2
|
+
# TimedTrace represents a chunk of code performance of which was measured and
|
3
|
+
# stored under a label. The chunk is called a "span".
|
4
|
+
#
|
5
|
+
# @example
|
6
|
+
# timed_trace = TimedTrace.new
|
7
|
+
# timed_trace.span('http request') do
|
8
|
+
# http.get('example.com')
|
9
|
+
# end
|
10
|
+
# timed_trace.spans #=> { 'http request' => 0.123 }
|
11
|
+
#
|
12
|
+
# @api public
|
13
|
+
# @since v4.3.0
|
14
|
+
class TimedTrace
|
15
|
+
# @param [String] label
|
16
|
+
# @return [Airbrake::TimedTrace]
|
17
|
+
def self.span(label, &block)
|
18
|
+
new.tap { |timed_trace| timed_trace.span(label, &block) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
@spans = {}
|
23
|
+
end
|
24
|
+
|
25
|
+
# @param [String] label
|
26
|
+
# @return [Boolean]
|
27
|
+
def span(label)
|
28
|
+
start_span(label)
|
29
|
+
yield
|
30
|
+
stop_span(label)
|
31
|
+
end
|
32
|
+
|
33
|
+
# @param [String] label
|
34
|
+
# @return [Boolean]
|
35
|
+
def start_span(label)
|
36
|
+
return false if @spans.key?(label)
|
37
|
+
|
38
|
+
@spans[label] = Airbrake::Benchmark.new
|
39
|
+
true
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param [String] label
|
43
|
+
# @return [Boolean]
|
44
|
+
def stop_span(label)
|
45
|
+
return false unless @spans.key?(label)
|
46
|
+
|
47
|
+
@spans[label].stop
|
48
|
+
true
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [Hash<String=>Float>]
|
52
|
+
def spans
|
53
|
+
@spans.each_with_object({}) do |(label, benchmark), new_spans|
|
54
|
+
new_spans[label] = benchmark.duration
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module Airbrake
|
2
|
+
# This class is responsible for truncation of too big objects. Mainly, you
|
3
|
+
# should use it for simple objects such as strings, hashes, & arrays.
|
4
|
+
#
|
5
|
+
# @api private
|
6
|
+
# @since v1.0.0
|
7
|
+
class Truncator
|
8
|
+
# @return [Hash] the options for +String#encode+
|
9
|
+
ENCODING_OPTIONS = { invalid: :replace, undef: :replace }.freeze
|
10
|
+
|
11
|
+
# @return [String] the temporary encoding to be used when fixing invalid
|
12
|
+
# strings with +ENCODING_OPTIONS+
|
13
|
+
TEMP_ENCODING = 'utf-16'.freeze
|
14
|
+
|
15
|
+
# @return [String] what to append when something is a circular reference
|
16
|
+
CIRCULAR = '[Circular]'.freeze
|
17
|
+
|
18
|
+
# @return [String] what to append when something is truncated
|
19
|
+
TRUNCATED = '[Truncated]'.freeze
|
20
|
+
|
21
|
+
# @return [Array<Class>] The types that can contain references to itself
|
22
|
+
CIRCULAR_TYPES = [Array, Hash, Set].freeze
|
23
|
+
|
24
|
+
# @param [Integer] max_size maximum size of hashes, arrays and strings
|
25
|
+
def initialize(max_size)
|
26
|
+
@max_size = max_size
|
27
|
+
end
|
28
|
+
|
29
|
+
# Performs deep truncation of arrays, hashes, sets & strings. Uses a
|
30
|
+
# placeholder for recursive objects (`[Circular]`).
|
31
|
+
#
|
32
|
+
# @param [Object] object The object to truncate
|
33
|
+
# @param [Set] seen The cache that helps to detect recursion
|
34
|
+
# @return [Object] truncated object
|
35
|
+
def truncate(object, seen = Set.new)
|
36
|
+
if seen.include?(object.object_id)
|
37
|
+
return CIRCULAR if CIRCULAR_TYPES.any? { |t| object.is_a?(t) }
|
38
|
+
return object
|
39
|
+
end
|
40
|
+
truncate_object(object, seen << object.object_id)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Reduces maximum allowed size of hashes, arrays, sets & strings by half.
|
44
|
+
# @return [Integer] current +max_size+ value
|
45
|
+
def reduce_max_size
|
46
|
+
@max_size /= 2
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def truncate_object(object, seen)
|
52
|
+
case object
|
53
|
+
when Hash then truncate_hash(object, seen)
|
54
|
+
when Array then truncate_array(object, seen)
|
55
|
+
when Set then truncate_set(object, seen)
|
56
|
+
when String then truncate_string(object)
|
57
|
+
when Numeric, TrueClass, FalseClass, Symbol, NilClass then object
|
58
|
+
else
|
59
|
+
truncate_string(stringify_object(object))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def truncate_string(str)
|
64
|
+
fixed_str = replace_invalid_characters(str)
|
65
|
+
return fixed_str if fixed_str.length <= @max_size
|
66
|
+
(fixed_str.slice(0, @max_size) + TRUNCATED).freeze
|
67
|
+
end
|
68
|
+
|
69
|
+
def stringify_object(object)
|
70
|
+
object.to_json
|
71
|
+
rescue *Notice::JSON_EXCEPTIONS
|
72
|
+
object.to_s
|
73
|
+
end
|
74
|
+
|
75
|
+
def truncate_hash(hash, seen)
|
76
|
+
truncated_hash = {}
|
77
|
+
hash.each_with_index do |(key, val), idx|
|
78
|
+
break if idx + 1 > @max_size
|
79
|
+
truncated_hash[key] = truncate(val, seen)
|
80
|
+
end
|
81
|
+
|
82
|
+
truncated_hash.freeze
|
83
|
+
end
|
84
|
+
|
85
|
+
def truncate_array(array, seen)
|
86
|
+
array.slice(0, @max_size).map! { |elem| truncate(elem, seen) }.freeze
|
87
|
+
end
|
88
|
+
|
89
|
+
def truncate_set(set, seen)
|
90
|
+
truncated_set = Set.new
|
91
|
+
|
92
|
+
set.each do |elem|
|
93
|
+
truncated_set << truncate(elem, seen)
|
94
|
+
break if truncated_set.size >= @max_size
|
95
|
+
end
|
96
|
+
|
97
|
+
truncated_set.freeze
|
98
|
+
end
|
99
|
+
|
100
|
+
# Replaces invalid characters in a string with arbitrary encoding.
|
101
|
+
#
|
102
|
+
# @param [String] str The string to replace characters
|
103
|
+
# @return [String] a UTF-8 encoded string
|
104
|
+
# @see https://github.com/flori/json/commit/3e158410e81f94dbbc3da6b7b35f4f64983aa4e3
|
105
|
+
def replace_invalid_characters(str)
|
106
|
+
encoding = str.encoding
|
107
|
+
utf8_string = (encoding == Encoding::UTF_8 || encoding == Encoding::ASCII)
|
108
|
+
return str if utf8_string && str.valid_encoding?
|
109
|
+
|
110
|
+
temp_str = str.dup
|
111
|
+
temp_str.encode!(TEMP_ENCODING, ENCODING_OPTIONS) if utf8_string
|
112
|
+
temp_str.encode!('utf-8', ENCODING_OPTIONS)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,324 @@
|
|
1
|
+
RSpec.describe Airbrake do
|
2
|
+
it "gets initialized with a performance notifier" do
|
3
|
+
expect(described_class.performance_notifier).not_to be_nil
|
4
|
+
end
|
5
|
+
|
6
|
+
it "gets initialized with a notice notifier" do
|
7
|
+
expect(described_class.notice_notifier).not_to be_nil
|
8
|
+
end
|
9
|
+
|
10
|
+
it "gets initialized with a deploy notifier" do
|
11
|
+
expect(described_class.deploy_notifier).not_to be_nil
|
12
|
+
end
|
13
|
+
|
14
|
+
describe ".configure" do
|
15
|
+
before do
|
16
|
+
Airbrake::Config.instance = Airbrake::Config.new
|
17
|
+
described_class.reset
|
18
|
+
end
|
19
|
+
|
20
|
+
after { described_class.reset }
|
21
|
+
|
22
|
+
it "yields the config" do
|
23
|
+
expect do |b|
|
24
|
+
begin
|
25
|
+
described_class.configure(&b)
|
26
|
+
rescue Airbrake::Error
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
end.to yield_with_args(Airbrake::Config)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "sets logger to Airbrake::Loggable" do
|
33
|
+
logger = Logger.new(File::NULL)
|
34
|
+
described_class.configure do |c|
|
35
|
+
c.project_id = 1
|
36
|
+
c.project_key = '123'
|
37
|
+
c.logger = logger
|
38
|
+
end
|
39
|
+
|
40
|
+
expect(Airbrake::Loggable.instance).to eql(logger)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "makes Airbrake configured" do
|
44
|
+
expect(described_class).not_to be_configured
|
45
|
+
|
46
|
+
described_class.configure do |c|
|
47
|
+
c.project_id = 1
|
48
|
+
c.project_key = '2'
|
49
|
+
end
|
50
|
+
|
51
|
+
expect(described_class).to be_configured
|
52
|
+
end
|
53
|
+
|
54
|
+
context "when a notifier was configured" do
|
55
|
+
before do
|
56
|
+
expect(described_class).to receive(:configured?).and_return(true)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "closes previously configured notice notifier" do
|
60
|
+
expect(described_class).to receive(:close)
|
61
|
+
described_class.configure {}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "when a notifier wasn't configured" do
|
66
|
+
before do
|
67
|
+
expect(described_class).to receive(:configured?).and_return(false)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "doesn't close previously configured notice notifier" do
|
71
|
+
expect(described_class).not_to receive(:close)
|
72
|
+
described_class.configure {}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "when called multiple times" do
|
77
|
+
it "doesn't overwrite performance notifier" do
|
78
|
+
described_class.configure {}
|
79
|
+
performance_notifier = described_class.performance_notifier
|
80
|
+
|
81
|
+
described_class.configure {}
|
82
|
+
expect(described_class.performance_notifier).to eql(performance_notifier)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "doesn't overwrite notice notifier" do
|
86
|
+
described_class.configure {}
|
87
|
+
notice_notifier = described_class.notice_notifier
|
88
|
+
|
89
|
+
described_class.configure {}
|
90
|
+
expect(described_class.notice_notifier).to eql(notice_notifier)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "doesn't overwrite deploy notifier" do
|
94
|
+
described_class.configure {}
|
95
|
+
deploy_notifier = described_class.deploy_notifier
|
96
|
+
|
97
|
+
described_class.configure {}
|
98
|
+
expect(described_class.deploy_notifier).to eql(deploy_notifier)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context "when blacklist_keys gets configured" do
|
103
|
+
before { allow(Airbrake.notice_notifier).to receive(:add_filter) }
|
104
|
+
|
105
|
+
it "adds blacklist filter" do
|
106
|
+
expect(Airbrake.notice_notifier).to receive(:add_filter)
|
107
|
+
.with(an_instance_of(Airbrake::Filters::KeysBlacklist))
|
108
|
+
described_class.configure { |c| c.blacklist_keys = %w[password] }
|
109
|
+
end
|
110
|
+
|
111
|
+
it "initializes blacklist with specified parameters" do
|
112
|
+
expect(Airbrake::Filters::KeysBlacklist).to receive(:new).with(%w[password])
|
113
|
+
described_class.configure { |c| c.blacklist_keys = %w[password] }
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context "when whitelist_keys gets configured" do
|
118
|
+
before { allow(Airbrake.notice_notifier).to receive(:add_filter) }
|
119
|
+
|
120
|
+
it "adds whitelist filter" do
|
121
|
+
expect(Airbrake.notice_notifier).to receive(:add_filter)
|
122
|
+
.with(an_instance_of(Airbrake::Filters::KeysWhitelist))
|
123
|
+
described_class.configure { |c| c.whitelist_keys = %w[banana] }
|
124
|
+
end
|
125
|
+
|
126
|
+
it "initializes whitelist with specified parameters" do
|
127
|
+
expect(Airbrake::Filters::KeysWhitelist).to receive(:new).with(%w[banana])
|
128
|
+
described_class.configure { |c| c.whitelist_keys = %w[banana] }
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context "when root_directory gets configured" do
|
133
|
+
before { allow(Airbrake.notice_notifier).to receive(:add_filter) }
|
134
|
+
|
135
|
+
it "adds root directory filter" do
|
136
|
+
expect(Airbrake.notice_notifier).to receive(:add_filter)
|
137
|
+
.with(an_instance_of(Airbrake::Filters::RootDirectoryFilter))
|
138
|
+
described_class.configure { |c| c.root_directory = '/my/path' }
|
139
|
+
end
|
140
|
+
|
141
|
+
it "initializes root directory filter with specified path" do
|
142
|
+
expect(Airbrake::Filters::RootDirectoryFilter)
|
143
|
+
.to receive(:new).with('/my/path')
|
144
|
+
described_class.configure { |c| c.root_directory = '/my/path' }
|
145
|
+
end
|
146
|
+
|
147
|
+
it "adds git revision filter" do
|
148
|
+
expect(Airbrake.notice_notifier).to receive(:add_filter)
|
149
|
+
.with(an_instance_of(Airbrake::Filters::GitRevisionFilter))
|
150
|
+
described_class.configure { |c| c.root_directory = '/my/path' }
|
151
|
+
end
|
152
|
+
|
153
|
+
it "initializes git revision filter with correct root directory" do
|
154
|
+
expect(Airbrake::Filters::GitRevisionFilter)
|
155
|
+
.to receive(:new).with('/my/path')
|
156
|
+
described_class.configure { |c| c.root_directory = '/my/path' }
|
157
|
+
end
|
158
|
+
|
159
|
+
it "adds git repository filter" do
|
160
|
+
expect(Airbrake.notice_notifier).to receive(:add_filter)
|
161
|
+
.with(an_instance_of(Airbrake::Filters::GitRepositoryFilter))
|
162
|
+
described_class.configure { |c| c.root_directory = '/my/path' }
|
163
|
+
end
|
164
|
+
|
165
|
+
it "initializes git repository filter with correct root directory" do
|
166
|
+
expect(Airbrake::Filters::GitRepositoryFilter)
|
167
|
+
.to receive(:new).with('/my/path')
|
168
|
+
described_class.configure { |c| c.root_directory = '/my/path' }
|
169
|
+
end
|
170
|
+
|
171
|
+
it "adds git last checkout filter" do
|
172
|
+
expect(Airbrake.notice_notifier).to receive(:add_filter)
|
173
|
+
.with(an_instance_of(Airbrake::Filters::GitLastCheckoutFilter))
|
174
|
+
described_class.configure { |c| c.root_directory = '/my/path' }
|
175
|
+
end
|
176
|
+
|
177
|
+
it "initializes git last checkout filter with correct root directory" do
|
178
|
+
expect(Airbrake::Filters::GitLastCheckoutFilter)
|
179
|
+
.to receive(:new).with('/my/path')
|
180
|
+
described_class.configure { |c| c.root_directory = '/my/path' }
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe "#reset" do
|
186
|
+
context "when Airbrake was previously configured" do
|
187
|
+
before do
|
188
|
+
expect(described_class).to receive(:configured?).and_return(true)
|
189
|
+
end
|
190
|
+
|
191
|
+
it "closes notice notifier" do
|
192
|
+
expect(described_class).to receive(:close)
|
193
|
+
subject.reset
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
describe "#notify_request" do
|
199
|
+
context "when :stash key is not provided" do
|
200
|
+
it "doesn't add anything to the stash of the request" do
|
201
|
+
expect(described_class.performance_notifier).to receive(:notify) do |request|
|
202
|
+
expect(request.stash).to be_empty
|
203
|
+
end
|
204
|
+
|
205
|
+
described_class.notify_request(
|
206
|
+
method: 'GET',
|
207
|
+
route: '/',
|
208
|
+
status_code: 200,
|
209
|
+
start_time: Time.now
|
210
|
+
)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
context "when :stash key is provided" do
|
215
|
+
it "adds the value as the stash of the request" do
|
216
|
+
expect(described_class.performance_notifier).to receive(:notify) do |request|
|
217
|
+
expect(request.stash).to eq(request_id: 1)
|
218
|
+
end
|
219
|
+
|
220
|
+
described_class.notify_request(
|
221
|
+
{
|
222
|
+
method: 'GET',
|
223
|
+
route: '/',
|
224
|
+
status_code: 200,
|
225
|
+
start_time: Time.now
|
226
|
+
},
|
227
|
+
request_id: 1
|
228
|
+
)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
describe "#notify_query" do
|
234
|
+
context "when :stash key is not provided" do
|
235
|
+
it "doesn't add anything to the stash of the query" do
|
236
|
+
expect(described_class.performance_notifier).to receive(:notify) do |query|
|
237
|
+
expect(query.stash).to be_empty
|
238
|
+
end
|
239
|
+
|
240
|
+
described_class.notify_query(
|
241
|
+
method: 'GET',
|
242
|
+
route: '/',
|
243
|
+
query: '',
|
244
|
+
start_time: Time.now
|
245
|
+
)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
context "when :stash key is provided" do
|
250
|
+
it "adds the value as the stash of the query" do
|
251
|
+
expect(described_class.performance_notifier).to receive(:notify) do |query|
|
252
|
+
expect(query.stash).to eq(request_id: 1)
|
253
|
+
end
|
254
|
+
|
255
|
+
described_class.notify_query(
|
256
|
+
{
|
257
|
+
method: 'GET',
|
258
|
+
route: '/',
|
259
|
+
query: '',
|
260
|
+
start_time: Time.now
|
261
|
+
},
|
262
|
+
request_id: 1
|
263
|
+
)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
describe "#notify_performance_breakdown" do
|
269
|
+
context "when :stash key is not provided" do
|
270
|
+
it "doesn't add anything to the stash of the performance breakdown" do
|
271
|
+
expect(described_class.performance_notifier).to receive(:notify) do |query|
|
272
|
+
expect(query.stash).to be_empty
|
273
|
+
end
|
274
|
+
|
275
|
+
described_class.notify_query(
|
276
|
+
method: 'GET',
|
277
|
+
route: '/',
|
278
|
+
query: '',
|
279
|
+
start_time: Time.now
|
280
|
+
)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
context "when :stash key is provided" do
|
285
|
+
it "adds the value as the stash of the performance breakdown" do
|
286
|
+
expect(
|
287
|
+
described_class.performance_notifier
|
288
|
+
).to receive(:notify) do |performance_breakdown|
|
289
|
+
expect(performance_breakdown.stash).to eq(request_id: 1)
|
290
|
+
end
|
291
|
+
|
292
|
+
described_class.notify_performance_breakdown(
|
293
|
+
{
|
294
|
+
method: 'GET',
|
295
|
+
route: '/',
|
296
|
+
response_type: :html,
|
297
|
+
groups: {},
|
298
|
+
start_time: Time.now
|
299
|
+
},
|
300
|
+
request_id: 1
|
301
|
+
)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
describe ".performance_notifier" do
|
307
|
+
it "returns a performance notifier" do
|
308
|
+
expect(described_class.performance_notifier)
|
309
|
+
.to be_an(Airbrake::PerformanceNotifier)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
describe ".notice_notifier" do
|
314
|
+
it "returns a notice notifier" do
|
315
|
+
expect(described_class.notice_notifier).to be_an(Airbrake::NoticeNotifier)
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
describe ".deploy_notifier" do
|
320
|
+
it "returns a deploy notifier" do
|
321
|
+
expect(described_class.deploy_notifier).to be_an(Airbrake::DeployNotifier)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|