airbrake-ruby 4.15.0 → 6.1.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.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/lib/airbrake-ruby/async_sender.rb +4 -2
  3. data/lib/airbrake-ruby/backtrace.rb +6 -5
  4. data/lib/airbrake-ruby/config/processor.rb +77 -0
  5. data/lib/airbrake-ruby/config/validator.rb +6 -0
  6. data/lib/airbrake-ruby/config.rb +44 -35
  7. data/lib/airbrake-ruby/context.rb +51 -0
  8. data/lib/airbrake-ruby/file_cache.rb +1 -1
  9. data/lib/airbrake-ruby/filter_chain.rb +3 -0
  10. data/lib/airbrake-ruby/filters/context_filter.rb +4 -5
  11. data/lib/airbrake-ruby/filters/dependency_filter.rb +1 -0
  12. data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +1 -1
  13. data/lib/airbrake-ruby/filters/gem_root_filter.rb +1 -0
  14. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +3 -4
  15. data/lib/airbrake-ruby/filters/git_repository_filter.rb +11 -2
  16. data/lib/airbrake-ruby/filters/git_revision_filter.rb +3 -1
  17. data/lib/airbrake-ruby/filters/keys_filter.rb +23 -15
  18. data/lib/airbrake-ruby/filters/root_directory_filter.rb +1 -0
  19. data/lib/airbrake-ruby/filters/sql_filter.rb +11 -11
  20. data/lib/airbrake-ruby/filters/system_exit_filter.rb +1 -0
  21. data/lib/airbrake-ruby/filters/thread_filter.rb +4 -3
  22. data/lib/airbrake-ruby/grouppable.rb +1 -1
  23. data/lib/airbrake-ruby/ignorable.rb +1 -2
  24. data/lib/airbrake-ruby/mergeable.rb +1 -1
  25. data/lib/airbrake-ruby/monotonic_time.rb +1 -1
  26. data/lib/airbrake-ruby/notice.rb +1 -8
  27. data/lib/airbrake-ruby/notice_notifier.rb +4 -4
  28. data/lib/airbrake-ruby/performance_breakdown.rb +1 -6
  29. data/lib/airbrake-ruby/performance_notifier.rb +40 -54
  30. data/lib/airbrake-ruby/promise.rb +1 -0
  31. data/lib/airbrake-ruby/query.rb +1 -6
  32. data/lib/airbrake-ruby/queue.rb +1 -8
  33. data/lib/airbrake-ruby/remote_settings/callback.rb +44 -0
  34. data/lib/airbrake-ruby/remote_settings/settings_data.rb +116 -0
  35. data/lib/airbrake-ruby/remote_settings.rb +128 -0
  36. data/lib/airbrake-ruby/request.rb +1 -8
  37. data/lib/airbrake-ruby/stat.rb +2 -13
  38. data/lib/airbrake-ruby/sync_sender.rb +3 -2
  39. data/lib/airbrake-ruby/tdigest.rb +12 -9
  40. data/lib/airbrake-ruby/thread_pool.rb +9 -6
  41. data/lib/airbrake-ruby/time_truncate.rb +2 -2
  42. data/lib/airbrake-ruby/timed_trace.rb +1 -3
  43. data/lib/airbrake-ruby/truncator.rb +8 -2
  44. data/lib/airbrake-ruby/version.rb +11 -1
  45. data/lib/airbrake-ruby.rb +44 -54
  46. data/spec/airbrake_spec.rb +178 -92
  47. data/spec/async_sender_spec.rb +10 -8
  48. data/spec/backtrace_spec.rb +39 -36
  49. data/spec/benchmark_spec.rb +7 -5
  50. data/spec/code_hunk_spec.rb +26 -17
  51. data/spec/config/processor_spec.rb +167 -0
  52. data/spec/config/validator_spec.rb +23 -3
  53. data/spec/config_spec.rb +43 -55
  54. data/spec/context_spec.rb +54 -0
  55. data/spec/deploy_notifier_spec.rb +6 -4
  56. data/spec/file_cache_spec.rb +1 -0
  57. data/spec/filter_chain_spec.rb +29 -24
  58. data/spec/filters/context_filter_spec.rb +14 -5
  59. data/spec/filters/dependency_filter_spec.rb +3 -1
  60. data/spec/filters/exception_attributes_filter_spec.rb +5 -3
  61. data/spec/filters/gem_root_filter_spec.rb +9 -6
  62. data/spec/filters/git_last_checkout_filter_spec.rb +10 -12
  63. data/spec/filters/{git_repository_filter.rb → git_repository_filter_spec.rb} +26 -15
  64. data/spec/filters/git_revision_filter_spec.rb +20 -20
  65. data/spec/filters/keys_allowlist_spec.rb +26 -16
  66. data/spec/filters/keys_blocklist_spec.rb +35 -18
  67. data/spec/filters/root_directory_filter_spec.rb +7 -7
  68. data/spec/filters/sql_filter_spec.rb +28 -28
  69. data/spec/filters/system_exit_filter_spec.rb +4 -2
  70. data/spec/filters/thread_filter_spec.rb +16 -14
  71. data/spec/loggable_spec.rb +2 -2
  72. data/spec/monotonic_time_spec.rb +8 -6
  73. data/spec/nested_exception_spec.rb +46 -46
  74. data/spec/notice_notifier/options_spec.rb +25 -15
  75. data/spec/notice_notifier_spec.rb +54 -49
  76. data/spec/notice_spec.rb +7 -3
  77. data/spec/performance_breakdown_spec.rb +0 -12
  78. data/spec/performance_notifier_spec.rb +69 -87
  79. data/spec/promise_spec.rb +38 -32
  80. data/spec/query_spec.rb +1 -11
  81. data/spec/queue_spec.rb +1 -13
  82. data/spec/remote_settings/callback_spec.rb +162 -0
  83. data/spec/remote_settings/settings_data_spec.rb +348 -0
  84. data/spec/remote_settings_spec.rb +201 -0
  85. data/spec/request_spec.rb +1 -13
  86. data/spec/response_spec.rb +34 -12
  87. data/spec/spec_helper.rb +4 -4
  88. data/spec/stashable_spec.rb +5 -5
  89. data/spec/stat_spec.rb +7 -14
  90. data/spec/sync_sender_spec.rb +52 -17
  91. data/spec/tdigest_spec.rb +61 -56
  92. data/spec/thread_pool_spec.rb +67 -58
  93. data/spec/time_truncate_spec.rb +23 -6
  94. data/spec/timed_trace_spec.rb +32 -30
  95. data/spec/truncator_spec.rb +72 -43
  96. metadata +67 -51
data/spec/config_spec.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  RSpec.describe Airbrake::Config do
2
+ subject(:config) { described_class.new }
3
+
2
4
  let(:resolved_promise) { Airbrake::Promise.new.resolve }
3
5
  let(:rejected_promise) { Airbrake::Promise.new.reject }
4
6
 
@@ -10,7 +12,9 @@ RSpec.describe Airbrake::Config do
10
12
  its(:app_version) { is_expected.to be_nil }
11
13
  its(:versions) { is_expected.to be_empty }
12
14
  its(:host) { is_expected.to eq('https://api.airbrake.io') }
13
- its(:endpoint) { is_expected.not_to be_nil }
15
+ its(:error_host) { is_expected.to eq('https://api.airbrake.io') }
16
+ its(:apm_host) { is_expected.to eq('https://api.airbrake.io') }
17
+ its(:error_endpoint) { is_expected.not_to be_nil }
14
18
  its(:workers) { is_expected.to eq(1) }
15
19
  its(:queue_size) { is_expected.to eq(100) }
16
20
  its(:root_directory) { is_expected.to eq(Bundler.root.realpath.to_s) }
@@ -19,26 +23,36 @@ RSpec.describe Airbrake::Config do
19
23
  its(:timeout) { is_expected.to be_nil }
20
24
  its(:blocklist_keys) { is_expected.to be_empty }
21
25
  its(:allowlist_keys) { is_expected.to be_empty }
22
- its(:performance_stats) { is_expected.to eq(true) }
26
+ its(:performance_stats) { is_expected.to be(true) }
23
27
  its(:performance_stats_flush_period) { is_expected.to eq(15) }
24
- its(:query_stats) { is_expected.to eq(true) }
25
- its(:job_stats) { is_expected.to eq(true) }
28
+ its(:query_stats) { is_expected.to be(true) }
29
+ its(:job_stats) { is_expected.to be(true) }
30
+ its(:error_notifications) { is_expected.to be(true) }
31
+ its(:remote_config) { is_expected.to be(true) }
32
+
33
+ its(:remote_config_host) do
34
+ is_expected.to eq('https://notifier-configs.airbrake.io')
35
+ end
26
36
 
27
37
  describe "#new" do
28
38
  context "when user config is passed" do
29
39
  subject { described_class.new(logger: StringIO.new) }
40
+
30
41
  its(:logger) { is_expected.to be_a(StringIO) }
31
42
  end
32
43
  end
33
44
 
34
45
  describe "#valid?" do
35
- context "when #validate returns a resolved promise" do
36
- before { expect(subject).to receive(:validate).and_return(resolved_promise) }
46
+ context "when the config is valid" do
47
+ before do
48
+ config.project_id = 123
49
+ config.project_key = 'abc'
50
+ end
51
+
37
52
  it { is_expected.to be_valid }
38
53
  end
39
54
 
40
- context "when #validate returns a rejected promise" do
41
- before { expect(subject).to receive(:validate).and_return(rejected_promise) }
55
+ context "when the config is invalid" do
42
56
  it { is_expected.not_to be_valid }
43
57
  end
44
58
  end
@@ -46,7 +60,7 @@ RSpec.describe Airbrake::Config do
46
60
  describe "#ignored_environment?" do
47
61
  context "when Validator returns a resolved promise" do
48
62
  before do
49
- expect(Airbrake::Config::Validator).to receive(:check_notify_ability)
63
+ allow(Airbrake::Config::Validator).to receive(:check_notify_ability)
50
64
  .and_return(resolved_promise)
51
65
  end
52
66
 
@@ -55,7 +69,7 @@ RSpec.describe Airbrake::Config do
55
69
 
56
70
  context "when Validator returns a rejected promise" do
57
71
  before do
58
- expect(Airbrake::Config::Validator).to receive(:check_notify_ability)
72
+ allow(Airbrake::Config::Validator).to receive(:check_notify_ability)
59
73
  .and_return(rejected_promise)
60
74
  end
61
75
 
@@ -63,13 +77,13 @@ RSpec.describe Airbrake::Config do
63
77
  end
64
78
  end
65
79
 
66
- describe "#endpoint" do
80
+ describe "#error_endpoint" do
67
81
  subject { described_class.new(valid_params.merge(user_config)) }
68
82
 
69
83
  context "when host ends with a URL with a slug with a trailing slash" do
70
84
  let(:user_config) { { host: 'https://localhost/bingo/' } }
71
85
 
72
- its(:endpoint) do
86
+ its(:error_endpoint) do
73
87
  is_expected.to eq(URI('https://localhost/bingo/api/v3/projects/1/notices'))
74
88
  end
75
89
  end
@@ -77,7 +91,7 @@ RSpec.describe Airbrake::Config do
77
91
  context "when host ends with a URL with a slug without a trailing slash" do
78
92
  let(:user_config) { { host: 'https://localhost/bingo' } }
79
93
 
80
- its(:endpoint) do
94
+ its(:error_endpoint) do
81
95
  is_expected.to eq(URI('https://localhost/api/v3/projects/1/notices'))
82
96
  end
83
97
  end
@@ -88,19 +102,21 @@ RSpec.describe Airbrake::Config do
88
102
  end
89
103
 
90
104
  describe "#check_configuration" do
91
- let(:user_config) { {} }
92
-
93
105
  subject { described_class.new(valid_params.merge(user_config)) }
94
106
 
107
+ let(:user_config) { {} }
108
+
95
109
  its(:check_configuration) { is_expected.to be_an(Airbrake::Promise) }
96
110
 
97
111
  context "when config is invalid" do
98
112
  let(:user_config) { { project_id: nil } }
113
+
99
114
  its(:check_configuration) { is_expected.to be_rejected }
100
115
  end
101
116
 
102
117
  context "when current environment is ignored" do
103
118
  let(:user_config) { { environment: 'test', ignore_environments: ['test'] } }
119
+
104
120
  its(:check_configuration) { is_expected.to be_rejected }
105
121
  end
106
122
 
@@ -111,22 +127,22 @@ RSpec.describe Airbrake::Config do
111
127
 
112
128
  describe "#check_performance_options" do
113
129
  it "returns a promise" do
114
- resource = Airbrake::Query.new(method: '', route: '', query: '', timing: 1)
115
- expect(subject.check_performance_options(resource))
130
+ metric = Airbrake::Query.new(method: '', route: '', query: '', timing: 1)
131
+ expect(config.check_performance_options(metric))
116
132
  .to be_an(Airbrake::Promise)
117
133
  end
118
134
 
119
135
  context "when performance stats are disabled" do
120
- before { subject.performance_stats = false }
136
+ before { config.performance_stats = false }
121
137
 
122
- let(:resource) do
138
+ let(:metric) do
123
139
  Airbrake::Request.new(
124
140
  method: 'GET', route: '/foo', status_code: 200, timing: 1,
125
141
  )
126
142
  end
127
143
 
128
144
  it "returns a rejected promise" do
129
- promise = subject.check_performance_options(resource)
145
+ promise = config.check_performance_options(metric)
130
146
  expect(promise.value).to eq(
131
147
  'error' => "The Performance Stats feature is disabled",
132
148
  )
@@ -134,14 +150,14 @@ RSpec.describe Airbrake::Config do
134
150
  end
135
151
 
136
152
  context "when query stats are disabled" do
137
- before { subject.query_stats = false }
153
+ before { config.query_stats = false }
138
154
 
139
- let(:resource) do
155
+ let(:metric) do
140
156
  Airbrake::Query.new(method: 'GET', route: '/foo', query: '', timing: 1)
141
157
  end
142
158
 
143
159
  it "returns a rejected promise" do
144
- promise = subject.check_performance_options(resource)
160
+ promise = config.check_performance_options(metric)
145
161
  expect(promise.value).to eq(
146
162
  'error' => "The Query Stats feature is disabled",
147
163
  )
@@ -149,14 +165,14 @@ RSpec.describe Airbrake::Config do
149
165
  end
150
166
 
151
167
  context "when job stats are disabled" do
152
- before { subject.job_stats = false }
168
+ before { config.job_stats = false }
153
169
 
154
- let(:resource) do
170
+ let(:metric) do
155
171
  Airbrake::Queue.new(queue: 'foo_queue', error_count: 0, timing: 1)
156
172
  end
157
173
 
158
174
  it "returns a rejected promise" do
159
- promise = subject.check_performance_options(resource)
175
+ promise = config.check_performance_options(metric)
160
176
  expect(promise.value).to eq(
161
177
  'error' => "The Job Stats feature is disabled",
162
178
  )
@@ -166,35 +182,7 @@ RSpec.describe Airbrake::Config do
166
182
 
167
183
  describe "#logger" do
168
184
  it "sets logger level to Logger::WARN" do
169
- expect(subject.logger.level).to eq(Logger::WARN)
170
- end
171
- end
172
-
173
- describe "#blacklist_keys=" do
174
- before { allow(Kernel).to receive(:warn) }
175
-
176
- it "sets blocklist_keys instead" do
177
- subject.blacklist_keys = [1, 2, 3]
178
- expect(subject.blocklist_keys).to eq([1, 2, 3])
179
- end
180
-
181
- it "prints a warning" do
182
- expect(Kernel).to receive(:warn).with(/use blocklist_keys= instead/)
183
- subject.blacklist_keys = [1, 2, 3]
184
- end
185
- end
186
-
187
- describe "#whitelist_keys=" do
188
- before { allow(Kernel).to receive(:warn) }
189
-
190
- it "sets allowlist_keys instead" do
191
- subject.whitelist_keys = [1, 2, 3]
192
- expect(subject.allowlist_keys).to eq([1, 2, 3])
193
- end
194
-
195
- it "prints a warning" do
196
- expect(Kernel).to receive(:warn).with(/use allowlist_keys= instead/)
197
- subject.whitelist_keys = [1, 2, 3]
185
+ expect(config.logger.level).to eq(Logger::WARN)
198
186
  end
199
187
  end
200
188
  end
@@ -0,0 +1,54 @@
1
+ RSpec.describe Airbrake::Context do
2
+ subject(:context) { described_class.current }
3
+
4
+ before { described_class.current.clear }
5
+
6
+ after { described_class.current.clear }
7
+
8
+ describe "#merge!" do
9
+ it "merges the given context with the current one" do
10
+ context.merge!(apples: 'oranges')
11
+ expect(context.to_h).to match(apples: 'oranges')
12
+ end
13
+ end
14
+
15
+ describe "#clear" do
16
+ it "clears the context" do
17
+ context.merge!(apples: 'oranges')
18
+ context.clear
19
+ expect(context.to_h).to be_empty
20
+ end
21
+ end
22
+
23
+ describe "#to_h" do
24
+ it "returns a hash representation of the context" do
25
+ expect(context.to_h).to be_a(Hash)
26
+ end
27
+ end
28
+
29
+ describe "#empty?" do
30
+ context "when the context has data" do
31
+ it "returns true" do
32
+ context.merge!(apples: 'oranges')
33
+ expect(context).not_to be_empty
34
+ end
35
+ end
36
+
37
+ context "when the context has NO data" do
38
+ it "returns false" do
39
+ expect(context).to be_empty
40
+ end
41
+ end
42
+ end
43
+
44
+ context "when another thread is spawned" do
45
+ it "doesn't clash with other threads' contexts" do
46
+ described_class.current.merge!(apples: 'oranges')
47
+ th = Thread.new do
48
+ described_class.current.merge!(foos: 'bars')
49
+ end
50
+ th.join
51
+ expect(described_class.current.to_h).to match(apples: 'oranges')
52
+ end
53
+ end
54
+ end
@@ -4,17 +4,19 @@ RSpec.describe Airbrake::DeployNotifier do
4
4
  end
5
5
 
6
6
  describe "#notify" do
7
+ subject(:deploy_notifier) { described_class.new }
8
+
7
9
  it "returns a promise" do
8
10
  stub_request(:post, 'https://api.airbrake.io/api/v4/projects/1/deploys')
9
11
  .to_return(status: 201, body: '{}')
10
- expect(subject.notify({})).to be_an(Airbrake::Promise)
12
+ expect(deploy_notifier.notify({})).to be_an(Airbrake::Promise)
11
13
  end
12
14
 
13
15
  context "when config is invalid" do
14
16
  before { Airbrake::Config.instance.merge(project_id: nil) }
15
17
 
16
18
  it "returns a rejected promise" do
17
- promise = subject.notify({})
19
+ promise = deploy_notifier.notify({})
18
20
  expect(promise).to be_rejected
19
21
  end
20
22
  end
@@ -28,7 +30,7 @@ RSpec.describe Airbrake::DeployNotifier do
28
30
  instance_of(Airbrake::Promise),
29
31
  URI('https://api.airbrake.io/api/v4/projects/1/deploys'),
30
32
  )
31
- subject.notify(environment: 'barenv')
33
+ deploy_notifier.notify(environment: 'barenv')
32
34
  end
33
35
  end
34
36
 
@@ -41,7 +43,7 @@ RSpec.describe Airbrake::DeployNotifier do
41
43
  instance_of(Airbrake::Promise),
42
44
  URI('https://api.airbrake.io/api/v4/projects/1/deploys'),
43
45
  )
44
- subject.notify({})
46
+ deploy_notifier.notify({})
45
47
  end
46
48
  end
47
49
  end
@@ -1,5 +1,6 @@
1
1
  RSpec.describe Airbrake::FileCache do
2
2
  before { described_class.reset }
3
+
3
4
  after { described_class.reset }
4
5
 
5
6
  describe ".[]=" do
@@ -1,4 +1,6 @@
1
1
  RSpec.describe Airbrake::FilterChain do
2
+ subject(:filter_chain) { described_class.new }
3
+
2
4
  let(:notice) { Airbrake::Notice.new(AirbrakeTestError.new) }
3
5
 
4
6
  describe "#refine" do
@@ -19,24 +21,27 @@ RSpec.describe Airbrake::FilterChain do
19
21
  it "executes filters from heaviest to lightest" do
20
22
  notice[:params][:bingo] = []
21
23
 
22
- (0...3).reverse_each { |i| subject.add_filter(filter.new(i)) }
23
- subject.refine(notice)
24
+ (0...3).reverse_each { |i| filter_chain.add_filter(filter.new(i)) }
25
+ filter_chain.refine(notice)
24
26
 
25
27
  expect(notice[:params][:bingo]).to eq([2, 1, 0])
26
28
  end
27
29
 
28
30
  it "stops execution once a notice was ignored" do
29
31
  f2 = filter.new(2)
30
- expect(f2).to receive(:call)
32
+ allow(f2).to receive(:call)
31
33
 
32
34
  f1 = proc { |notice| notice.ignore! }
33
35
 
34
36
  f0 = filter.new(-1)
35
- expect(f0).not_to receive(:call)
37
+ allow(f0).to receive(:call)
38
+
39
+ [f2, f1, f0].each { |f| filter_chain.add_filter(f) }
36
40
 
37
- [f2, f1, f0].each { |f| subject.add_filter(f) }
41
+ filter_chain.refine(notice)
38
42
 
39
- subject.refine(notice)
43
+ expect(f2).to have_received(:call)
44
+ expect(f0).not_to have_received(:call)
40
45
  end
41
46
  end
42
47
 
@@ -63,56 +68,56 @@ RSpec.describe Airbrake::FilterChain do
63
68
  notice[:params][:foo] = []
64
69
 
65
70
  f1 = filter.new(1)
66
- subject.add_filter(f1)
71
+ filter_chain.add_filter(f1)
67
72
 
68
73
  foo_filter_mock = double
69
- expect(foo_filter_mock).to(
70
- receive(:name).at_least(:once).and_return('FooFilter'),
71
- )
72
- subject.delete_filter(foo_filter_mock)
74
+ allow(foo_filter_mock).to receive(:name).at_least(:once).and_return('FooFilter')
75
+ filter_chain.delete_filter(foo_filter_mock)
76
+
77
+ expect(foo_filter_mock).to have_received(:name)
73
78
 
74
79
  f2 = filter.new(2)
75
- subject.add_filter(f2)
80
+ filter_chain.add_filter(f2)
76
81
 
77
- subject.refine(notice)
82
+ filter_chain.refine(notice)
78
83
  expect(notice[:params][:foo]).to eq([2])
79
84
  end
80
85
  end
81
86
 
82
87
  describe "#inspect" do
83
88
  it "returns a string representation of an empty FilterChain" do
84
- expect(subject.inspect).to eq('[]')
89
+ expect(filter_chain.inspect).to eq('[]')
85
90
  end
86
91
 
87
92
  it "returns a string representation of a non-empty FilterChain" do
88
- subject.add_filter(proc {})
89
- expect(subject.inspect).to eq('[Proc]')
93
+ filter_chain.add_filter(proc {})
94
+ expect(filter_chain.inspect).to eq('[Proc]')
90
95
  end
91
96
  end
92
97
 
93
98
  describe "#includes?" do
94
99
  context "when a custom filter class is included in the filter chain" do
95
100
  it "returns true" do
96
- klass = Class.new {}
101
+ klass = Class.new
97
102
 
98
- subject.add_filter(klass.new)
99
- expect(subject.includes?(klass)).to eq(true)
103
+ filter_chain.add_filter(klass.new)
104
+ expect(filter_chain.includes?(klass)).to be(true)
100
105
  end
101
106
  end
102
107
 
103
108
  context "when Proc filter class is included in the filter chain" do
104
109
  it "returns true" do
105
- subject.add_filter(proc {})
106
- expect(subject.includes?(Proc)).to eq(true)
110
+ filter_chain.add_filter(proc {})
111
+ expect(filter_chain.includes?(Proc)).to be(true)
107
112
  end
108
113
  end
109
114
 
110
115
  context "when filter class is NOT included in the filter chain" do
111
116
  it "returns false" do
112
- klass = Class.new {}
117
+ klass = Class.new
113
118
 
114
- subject.add_filter(proc {})
115
- expect(subject.includes?(klass)).to eq(false)
119
+ filter_chain.add_filter(proc {})
120
+ expect(filter_chain.includes?(klass)).to be(false)
116
121
  end
117
122
  end
118
123
  end
@@ -3,21 +3,30 @@ RSpec.describe Airbrake::Filters::ContextFilter do
3
3
 
4
4
  context "when the current context is empty" do
5
5
  it "doesn't merge anything with params" do
6
- described_class.new({}).call(notice)
6
+ described_class.new.call(notice)
7
7
  expect(notice[:params]).to be_empty
8
8
  end
9
9
  end
10
10
 
11
11
  context "when the current context has some data" do
12
12
  it "merges the data with params" do
13
- described_class.new(apples: 'oranges').call(notice)
13
+ Airbrake.merge_context(apples: 'oranges')
14
+ described_class.new.call(notice)
14
15
  expect(notice[:params]).to eq(airbrake_context: { apples: 'oranges' })
15
16
  end
16
17
 
17
- it "clears the data from the provided context" do
18
+ it "clears the data from the current context" do
18
19
  context = { apples: 'oranges' }
19
- described_class.new(context).call(notice)
20
- expect(context).to be_empty
20
+ Airbrake.merge_context(context)
21
+ described_class.new.call(notice)
22
+ expect(Airbrake::Context.current).to be_empty
23
+ end
24
+
25
+ it "does not mutate the provided context object" do
26
+ context = { apples: 'oranges' }
27
+ Airbrake.merge_context(context)
28
+ described_class.new.call(notice)
29
+ expect(context).to match(apples: 'oranges')
21
30
  end
22
31
  end
23
32
  end
@@ -1,9 +1,11 @@
1
1
  RSpec.describe Airbrake::Filters::DependencyFilter do
2
+ subject(:dependency_filter) { described_class.new }
3
+
2
4
  let(:notice) { Airbrake::Notice.new(AirbrakeTestError.new) }
3
5
 
4
6
  describe "#call" do
5
7
  it "attaches loaded dependencies to context/versions/dependencies" do
6
- subject.call(notice)
8
+ dependency_filter.call(notice)
7
9
  expect(notice[:context][:versions][:dependencies]).to include(
8
10
  'airbrake-ruby' => Airbrake::AIRBRAKE_RUBY_VERSION,
9
11
  )
@@ -1,4 +1,6 @@
1
1
  RSpec.describe Airbrake::Filters::ExceptionAttributesFilter do
2
+ subject(:exception_attributes_filter) { described_class.new }
3
+
2
4
  describe "#call" do
3
5
  let(:notice) { Airbrake::Notice.new(ex) }
4
6
 
@@ -12,7 +14,7 @@ RSpec.describe Airbrake::Filters::ExceptionAttributesFilter do
12
14
  end
13
15
 
14
16
  it "doesn't raise" do
15
- expect { subject.call(notice) }.not_to raise_error
17
+ expect { exception_attributes_filter.call(notice) }.not_to raise_error
16
18
  expect(notice[:params]).to be_empty
17
19
  end
18
20
  end
@@ -27,7 +29,7 @@ RSpec.describe Airbrake::Filters::ExceptionAttributesFilter do
27
29
  end
28
30
 
29
31
  it "doesn't raise" do
30
- expect { subject.call(notice) }.not_to raise_error
32
+ expect { exception_attributes_filter.call(notice) }.not_to raise_error
31
33
  expect(notice[:params]).to be_empty
32
34
  end
33
35
  end
@@ -42,7 +44,7 @@ RSpec.describe Airbrake::Filters::ExceptionAttributesFilter do
42
44
  end
43
45
 
44
46
  it "merges parameters with the notice" do
45
- subject.call(notice)
47
+ exception_attributes_filter.call(notice)
46
48
  expect(notice[:params]).to eq(foo: '1')
47
49
  end
48
50
  end
@@ -1,24 +1,27 @@
1
1
  RSpec.describe Airbrake::Filters::GemRootFilter do
2
+ subject(:gem_root_filter) { described_class.new }
3
+
2
4
  let(:notice) { Airbrake::Notice.new(AirbrakeTestError.new) }
3
5
  let(:root1) { '/my/gem/root' }
4
6
  let(:root2) { '/my/other/gem/root' }
5
7
 
6
8
  before { Gem.path << root1 << root2 }
9
+
7
10
  after { 2.times { Gem.path.pop } }
8
11
 
9
12
  it "replaces gem root in the backtrace with a label" do
10
- # rubocop:disable Metrics/LineLength
13
+ # rubocop:disable Layout/LineLength
11
14
  notice[:errors].first[:backtrace] = [
12
15
  { file: "/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb" },
13
16
  { file: "#{root1}/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb" },
14
17
  { file: "/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb" },
15
18
  { file: "#{root2}/gems/rspec-core-3.3.2/exe/rspec" },
16
19
  ]
17
- # rubocop:enable Metrics/LineLength
20
+ # rubocop:enable Layout/LineLength
18
21
 
19
- subject.call(notice)
22
+ gem_root_filter.call(notice)
20
23
 
21
- # rubocop:disable Metrics/LineLength
24
+ # rubocop:disable Layout/LineLength
22
25
  expect(notice[:errors].first[:backtrace]).to(
23
26
  eq(
24
27
  [
@@ -29,12 +32,12 @@ RSpec.describe Airbrake::Filters::GemRootFilter do
29
32
  ],
30
33
  ),
31
34
  )
32
- # rubocop:enable Metrics/LineLength
35
+ # rubocop:enable Layout/LineLength
33
36
  end
34
37
 
35
38
  it "does not filter file when it is nil" do
36
39
  expect(notice[:errors].first[:file]).to be_nil
37
- expect { subject.call(notice) }.not_to(
40
+ expect { gem_root_filter.call(notice) }.not_to(
38
41
  change { notice[:errors].first[:file] },
39
42
  )
40
43
  end
@@ -1,21 +1,21 @@
1
1
  RSpec.describe Airbrake::Filters::GitLastCheckoutFilter do
2
- subject { described_class.new('.') }
2
+ subject(:git_last_checkout_filter) { described_class.new('.') }
3
3
 
4
4
  let(:notice) { Airbrake::Notice.new(AirbrakeTestError.new) }
5
5
 
6
6
  context "when context/lastCheckout is defined" do
7
7
  it "doesn't attach anything to context/lastCheckout" do
8
8
  notice[:context][:lastCheckout] = '123'
9
- subject.call(notice)
9
+ git_last_checkout_filter.call(notice)
10
10
  expect(notice[:context][:lastCheckout]).to eq('123')
11
11
  end
12
12
  end
13
13
 
14
14
  context "when .git directory doesn't exist" do
15
- subject { described_class.new('root/dir') }
15
+ subject(:git_last_checkout_without_git_dir_filter) { described_class.new('root/dir') }
16
16
 
17
17
  it "doesn't attach anything to context/lastCheckout" do
18
- subject.call(notice)
18
+ git_last_checkout_without_git_dir_filter.call(notice)
19
19
  expect(notice[:context][:lastCheckout]).to be_nil
20
20
  end
21
21
  end
@@ -25,7 +25,7 @@ RSpec.describe Airbrake::Filters::GitLastCheckoutFilter do
25
25
  before { ENV['AIRBRAKE_DEPLOY_USERNAME'] = 'deployer' }
26
26
 
27
27
  it "attaches username from the environment" do
28
- subject.call(notice)
28
+ git_last_checkout_filter.call(notice)
29
29
  expect(notice[:context][:lastCheckout][:username]).to eq('deployer')
30
30
  end
31
31
  end
@@ -34,7 +34,7 @@ RSpec.describe Airbrake::Filters::GitLastCheckoutFilter do
34
34
  before { ENV['AIRBRAKE_DEPLOY_USERNAME'] = nil }
35
35
 
36
36
  it "attaches last checkouted username" do
37
- subject.call(notice)
37
+ git_last_checkout_filter.call(notice)
38
38
  username = notice[:context][:lastCheckout][:username]
39
39
  expect(username).not_to be_empty
40
40
  expect(username).not_to be_nil
@@ -42,20 +42,18 @@ RSpec.describe Airbrake::Filters::GitLastCheckoutFilter do
42
42
  end
43
43
 
44
44
  it "attaches last checkouted email" do
45
- subject.call(notice)
46
- expect(notice[:context][:lastCheckout][:email]).to(
47
- match(/\A\w+[\w.-]*@\w+\.?\w+?\z/),
48
- )
45
+ git_last_checkout_filter.call(notice)
46
+ expect(notice[:context][:lastCheckout][:email]).to(match(/\A\w+@[\w\-.]+\z/))
49
47
  end
50
48
 
51
49
  it "attaches last checkouted revision" do
52
- subject.call(notice)
50
+ git_last_checkout_filter.call(notice)
53
51
  expect(notice[:context][:lastCheckout][:revision]).not_to be_empty
54
52
  expect(notice[:context][:lastCheckout][:revision].size).to eq(40)
55
53
  end
56
54
 
57
55
  it "attaches last checkouted time" do
58
- subject.call(notice)
56
+ git_last_checkout_filter.call(notice)
59
57
  expect(notice[:context][:lastCheckout][:time]).not_to be_empty
60
58
  expect(notice[:context][:lastCheckout][:time].size).to eq(25)
61
59
  end