airbrake-ruby 4.15.0 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
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