Skip to content

Commit db975df

Browse files
authored
Fix flaky specs (#2561)
* Set up webmock for specs * Replace faulty spec with something more useful Not sure what the intention was but this spec did not test any faraday error handling. What actually happened was that HTTPTransport really tried to hit the API and ended up with error. We *do not* want to hit any APIs like that in specs, this causes instability of the spec suite. That's why I added webmock to ensure we're not sending any HTTP requests in specs. * Stub requests in event sending spec This spec was very flaky * Fix faraday spec even though it sends HTTP request I'll figure it out eventually * Use sentry request stubbing when needed * DRY up toggling webmock in specs * Fix problematic spec
1 parent ef2ea49 commit db975df

12 files changed

+86
-58
lines changed

sentry-ruby/Gemfile

+1
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,4 @@ gem "yard", github: "lsegal/yard"
3333
gem "webrick"
3434
gem "faraday"
3535
gem "excon"
36+
gem "webmock"

sentry-ruby/lib/sentry/transport/http_transport.rb

-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ class HTTPTransport < Transport
2323
Zlib::BufError, Errno::EHOSTUNREACH, Errno::ECONNREFUSED
2424
].freeze
2525

26-
2726
def initialize(*args)
2827
super
2928
log_debug("Sentry HTTP Transport will connect to #{@dsn.server}") if @dsn

sentry-ruby/spec/contexts/with_request_mock.rb

+9-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@ def setsockopt(*args); end
1212
allow(TCPSocket).to receive(:open).and_return(FakeSocket.new)
1313
end
1414

15-
def stub_request(fake_response, &block)
15+
around do |example|
16+
WebMock.disable!
17+
example.run
18+
WebMock.enable!
19+
end
20+
21+
def sentry_stub_request(fake_response, &block)
1622
allow_any_instance_of(Net::HTTP).to receive(:connect)
1723
allow_any_instance_of(Net::HTTP).to receive(:transport_request) do |http_obj, request|
1824
block.call(request, http_obj) if block
@@ -32,10 +38,10 @@ def build_fake_response(status, body: {}, headers: {})
3238

3339
def stub_sentry_response
3440
# use bad request as an example is easier for verifying with error messages
35-
stub_request(build_fake_response("400", body: { data: "bad sentry DSN public key" }))
41+
sentry_stub_request(build_fake_response("400", body: { data: "bad sentry DSN public key" }))
3642
end
3743

3844
def stub_normal_response(code: "200", &block)
39-
stub_request(build_fake_response(code), &block)
45+
sentry_stub_request(build_fake_response(code), &block)
4046
end
4147
end

sentry-ruby/spec/sentry/client/event_sending_spec.rb

+21-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
config.transport.transport_class = Sentry::DummyTransport
1111
end
1212
end
13+
14+
before do
15+
stub_request(:post, Sentry::TestHelper::DUMMY_DSN)
16+
end
17+
1318
subject(:client) { Sentry::Client.new(configuration) }
1419

1520
let(:hub) do
@@ -470,13 +475,16 @@
470475
before do
471476
configuration.background_worker_threads = 0
472477
Sentry.background_worker = Sentry::BackgroundWorker.new(configuration)
478+
479+
stub_request(:post, "http://sentry.localdomain/sentry/api/42/envelope/")
480+
.to_raise(Timeout::Error)
473481
end
474482

475483
it "swallows and logs Sentry::ExternalError (caused by transport's networking error)" do
476484
expect(client.capture_event(event, scope)).to be_nil
477485

478-
expect(string_io.string).to match(/Event sending failed: Failed to open TCP connection/)
479-
expect(string_io.string).to match(/Event capturing failed: Failed to open TCP connection/)
486+
expect(string_io.string).to match(/Event sending failed: Exception from WebMock/)
487+
expect(string_io.string).to match(/Event capturing failed: Exception from WebMock/)
480488
end
481489

482490
it "swallows and logs errors caused by the user (like in before_send)" do
@@ -502,13 +510,16 @@
502510
context "when sending events in background causes error", retry: 3 do
503511
before do
504512
Sentry.background_worker = Sentry::BackgroundWorker.new(configuration)
513+
514+
stub_request(:post, "http://sentry.localdomain/sentry/api/42/envelope/")
515+
.to_raise(Timeout::Error)
505516
end
506517

507518
it "swallows and logs Sentry::ExternalError (caused by transport's networking error)" do
508519
expect(client.capture_event(event, scope)).to be_a(Sentry::ErrorEvent)
509520
sleep(0.2)
510521

511-
expect(string_io.string).to match(/Event sending failed: Failed to open TCP connection/)
522+
expect(string_io.string).to match(/Event sending failed: Exception from WebMock/)
512523
end
513524

514525
it "swallows and logs errors caused by the user (like in before_send)" do
@@ -560,11 +571,14 @@
560571
describe "#send_event" do
561572
context "error happens when sending the event" do
562573
it "raises the error" do
574+
stub_request(:post, "http://sentry.localdomain/sentry/api/42/envelope/")
575+
.to_raise(Timeout::Error)
576+
563577
expect do
564578
client.send_event(event)
565579
end.to raise_error(Sentry::ExternalError)
566580

567-
expect(string_io.string).to match(/Event sending failed: Failed to open TCP connection/)
581+
expect(string_io.string).to match(/Event sending failed: Exception from WebMock/)
568582
end
569583
end
570584

@@ -635,6 +649,9 @@
635649
end
636650

637651
it "records lost span delta client reports" do
652+
stub_request(:post, "http://sentry.localdomain/sentry/api/42/envelope/")
653+
.to_raise(Timeout::Error)
654+
638655
expect { client.send_event(transaction_event) }.to raise_error(Sentry::ExternalError)
639656
expect(client.transport).to have_recorded_lost_event(:before_send, 'span', num: 2)
640657
end

sentry-ruby/spec/sentry/faraday_spec.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@
271271

272272
let(:url) { "http://example.com" }
273273

274-
it "skips instrumentation" do
274+
it "skips instrumentation", webmock: false do
275275
transaction = Sentry.start_transaction
276276
Sentry.get_current_scope.set_span(transaction)
277277

sentry-ruby/spec/sentry/net/http_spec.rb

+16-22
Original file line numberDiff line numberDiff line change
@@ -11,38 +11,32 @@
1111
::Logger.new(string_io)
1212
end
1313

14-
context "with IPv6 addresses" do
14+
context "with tracing enabled" do
1515
before do
1616
perform_basic_setup do |config|
1717
config.traces_sample_rate = 1.0
18+
config.transport.transport_class = Sentry::HTTPTransport
19+
config.logger = logger
20+
# the dsn needs to have a real host so we can make a real connection before sending a failed request
21+
config.dsn = 'http://[email protected]/5434472'
1822
end
1923
end
2024

21-
it "correctly parses the short-hand IPv6 addresses" do
22-
stub_normal_response
23-
24-
transaction = Sentry.start_transaction
25-
Sentry.get_current_scope.set_span(transaction)
25+
context "with IPv6 addresses" do
26+
it "correctly parses the short-hand IPv6 addresses" do
27+
stub_normal_response
2628

27-
_ = Net::HTTP.get("::1", "/path", 8080)
29+
transaction = Sentry.start_transaction
30+
Sentry.get_current_scope.set_span(transaction)
2831

29-
expect(transaction.span_recorder.spans.count).to eq(2)
32+
_ = Net::HTTP.get("::1", "/path", 8080)
3033

31-
request_span = transaction.span_recorder.spans.last
32-
expect(request_span.data).to eq(
33-
{ "url" => "http://[::1]/path", "http.request.method" => "GET", "http.response.status_code" => 200 }
34-
)
35-
end
36-
end
34+
expect(transaction.span_recorder.spans.count).to eq(2)
3735

38-
context "with tracing enabled" do
39-
before do
40-
perform_basic_setup do |config|
41-
config.traces_sample_rate = 1.0
42-
config.transport.transport_class = Sentry::HTTPTransport
43-
config.logger = logger
44-
# the dsn needs to have a real host so we can make a real connection before sending a failed request
45-
config.dsn = 'http://[email protected]/5434472'
36+
request_span = transaction.span_recorder.spans.last
37+
expect(request_span.data).to eq(
38+
{ "url" => "http://[::1]/path", "http.request.method" => "GET", "http.response.status_code" => 200 }
39+
)
4640
end
4741
end
4842

sentry-ruby/spec/sentry/rspec/matchers_spec.rb

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
config.dsn = 'https://[email protected]/5434472'
1313
config.enabled_environments = ["production"]
1414
config.environment = :test
15+
config.transport.transport_class = Sentry::DummyTransport
1516
end
1617

1718
setup_sentry_test

sentry-ruby/spec/sentry/transport/http_transport_rate_limiting_spec.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@
107107

108108
describe "rate limit header processing" do
109109
before do
110-
stub_request(fake_response)
110+
sentry_stub_request(fake_response)
111111
end
112112

113113
shared_examples "rate limiting headers handling" do

sentry-ruby/spec/sentry/transport/http_transport_spec.rb

+19-19
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
subject { client.transport }
2323

2424
it "logs a debug message only during initialization" do
25-
stub_request(build_fake_response("200"))
25+
sentry_stub_request(build_fake_response("200"))
2626
string_io = StringIO.new
2727
configuration.logger = Logger.new(string_io)
2828

@@ -39,7 +39,7 @@
3939
end
4040

4141
it "initializes new Net::HTTP instance for every request" do
42-
stub_request(build_fake_response("200")) do |request|
42+
sentry_stub_request(build_fake_response("200")) do |request|
4343
expect(request["User-Agent"]).to eq("sentry-ruby/#{Sentry::VERSION}")
4444
end
4545

@@ -87,7 +87,7 @@
8787
let(:fake_response) { build_fake_response("200") }
8888

8989
it 'sets default User-Agent' do
90-
stub_request(fake_response) do |request|
90+
sentry_stub_request(fake_response) do |request|
9191
expect(request["User-Agent"]).to eq("sentry-ruby/#{Sentry::VERSION}")
9292
end
9393

@@ -97,7 +97,7 @@
9797
it "accepts custom proxy" do
9898
configuration.transport.proxy = { uri: URI("https://example.com"), user: "stan", password: "foobar" }
9999

100-
stub_request(fake_response) do |_, http_obj|
100+
sentry_stub_request(fake_response) do |_, http_obj|
101101
expect(http_obj.proxy_address).to eq("example.com")
102102
expect(http_obj.proxy_user).to eq("stan")
103103
expect(http_obj.proxy_pass).to eq("foobar")
@@ -109,7 +109,7 @@
109109
it "accepts a custom proxy string" do
110110
configuration.transport.proxy = "https://stan:[email protected]:8080"
111111

112-
stub_request(fake_response) do |_, http_obj|
112+
sentry_stub_request(fake_response) do |_, http_obj|
113113
expect(http_obj.proxy_address).to eq("example.com")
114114
expect(http_obj.proxy_user).to eq("stan")
115115
expect(http_obj.proxy_pass).to eq("foobar")
@@ -122,7 +122,7 @@
122122
it "accepts a custom proxy URI" do
123123
configuration.transport.proxy = URI("https://stan:[email protected]:8080")
124124

125-
stub_request(fake_response) do |_, http_obj|
125+
sentry_stub_request(fake_response) do |_, http_obj|
126126
expect(http_obj.proxy_address).to eq("example.com")
127127
expect(http_obj.proxy_user).to eq("stan")
128128
expect(http_obj.proxy_pass).to eq("foobar")
@@ -136,7 +136,7 @@
136136
begin
137137
ENV["http_proxy"] = "https://stan:[email protected]:8080"
138138

139-
stub_request(fake_response) do |_, http_obj|
139+
sentry_stub_request(fake_response) do |_, http_obj|
140140
expect(http_obj.proxy_address).to eq("example.com")
141141
expect(http_obj.proxy_port).to eq(8080)
142142

@@ -155,7 +155,7 @@
155155
it "accepts custom timeout" do
156156
configuration.transport.timeout = 10
157157

158-
stub_request(fake_response) do |_, http_obj|
158+
sentry_stub_request(fake_response) do |_, http_obj|
159159
expect(http_obj.read_timeout).to eq(10)
160160

161161
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.6")
@@ -169,7 +169,7 @@
169169
it "accepts custom open_timeout" do
170170
configuration.transport.open_timeout = 10
171171

172-
stub_request(fake_response) do |_, http_obj|
172+
sentry_stub_request(fake_response) do |_, http_obj|
173173
expect(http_obj.open_timeout).to eq(10)
174174
end
175175

@@ -178,7 +178,7 @@
178178

179179
describe "ssl configurations" do
180180
it "has the corrent default" do
181-
stub_request(fake_response) do |_, http_obj|
181+
sentry_stub_request(fake_response) do |_, http_obj|
182182
expect(http_obj.verify_mode).to eq(1)
183183
expect(http_obj.ca_file).to eq(nil)
184184
end
@@ -189,7 +189,7 @@
189189
it "accepts custom ssl_verification configuration" do
190190
configuration.transport.ssl_verification = false
191191

192-
stub_request(fake_response) do |_, http_obj|
192+
sentry_stub_request(fake_response) do |_, http_obj|
193193
expect(http_obj.verify_mode).to eq(0)
194194
expect(http_obj.ca_file).to eq(nil)
195195
end
@@ -200,7 +200,7 @@
200200
it "accepts custom ssl_ca_file configuration" do
201201
configuration.transport.ssl_ca_file = "/tmp/foo"
202202

203-
stub_request(fake_response) do |_, http_obj|
203+
sentry_stub_request(fake_response) do |_, http_obj|
204204
expect(http_obj.verify_mode).to eq(1)
205205
expect(http_obj.ca_file).to eq("/tmp/foo")
206206
end
@@ -211,7 +211,7 @@
211211
it "accepts custom ssl configuration" do
212212
configuration.transport.ssl = { verify: false, ca_file: "/tmp/foo" }
213213

214-
stub_request(fake_response) do |_, http_obj|
214+
sentry_stub_request(fake_response) do |_, http_obj|
215215
expect(http_obj.verify_mode).to eq(0)
216216
expect(http_obj.ca_file).to eq("/tmp/foo")
217217
end
@@ -225,7 +225,7 @@
225225
let(:fake_response) { build_fake_response("200") }
226226

227227
it "compresses data by default" do
228-
stub_request(fake_response) do |request|
228+
sentry_stub_request(fake_response) do |request|
229229
expect(request["Content-Type"]).to eq("application/x-sentry-envelope")
230230
expect(request["Content-Encoding"]).to eq("gzip")
231231

@@ -238,7 +238,7 @@
238238
end
239239

240240
it "doesn't compress small event" do
241-
stub_request(fake_response) do |request|
241+
sentry_stub_request(fake_response) do |request|
242242
expect(request["Content-Type"]).to eq("application/x-sentry-envelope")
243243
expect(request["Content-Encoding"]).to eq("")
244244

@@ -255,7 +255,7 @@
255255
it "doesn't compress data if the encoding is not gzip" do
256256
configuration.transport.encoding = "json"
257257

258-
stub_request(fake_response) do |request|
258+
sentry_stub_request(fake_response) do |request|
259259
expect(request["Content-Type"]).to eq("application/x-sentry-envelope")
260260
expect(request["Content-Encoding"]).to eq("")
261261

@@ -296,7 +296,7 @@
296296
let(:fake_response) { build_fake_response("404") }
297297

298298
it "raises an error" do
299-
stub_request(fake_response)
299+
sentry_stub_request(fake_response)
300300

301301
expect { subject.send_data(data) }.to raise_error(Sentry::ExternalError, /the server responded with status 404/)
302302
end
@@ -306,7 +306,7 @@
306306
let(:fake_response) { build_fake_response("500") }
307307

308308
it "raises an error" do
309-
stub_request(fake_response)
309+
sentry_stub_request(fake_response)
310310

311311
expect { subject.send_data(data) }.to raise_error(Sentry::ExternalError, /the server responded with status 500/)
312312
end
@@ -318,7 +318,7 @@
318318
end
319319

320320
it "raises an error with header" do
321-
stub_request(error_response)
321+
sentry_stub_request(error_response)
322322

323323
expect { subject.send_data(data) }.to raise_error(Sentry::ExternalError, /error_in_header/)
324324
end

sentry-ruby/spec/sentry/transport_spec.rb

+8-5
Original file line numberDiff line numberDiff line change
@@ -531,11 +531,14 @@
531531
end
532532
end
533533

534-
context "with Faraday::Error" do
535-
it "raises the error" do
536-
expect do
537-
subject.send_event(event)
538-
end.to raise_error(Sentry::ExternalError)
534+
context "with an HTTP exception" do
535+
before do
536+
stub_request(:post, "http://sentry.localdomain:80/sentry/api/42/envelope/").
537+
to_raise(Timeout::Error)
538+
end
539+
540+
it "raises Sentry::ExternalError" do
541+
expect { subject.send_event(event) }.to raise_error(Sentry::ExternalError)
539542
end
540543
end
541544
end

sentry-ruby/spec/sentry_spec.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,8 @@
183183
context "with spotlight" do
184184
before { perform_basic_setup { |c| c.spotlight = true } }
185185

186-
it "sends the event to spotlight too" do
187-
stub_request(build_fake_response("200")) do |request, http_obj|
186+
it "sends the event to spotlight too", webmock: false do
187+
sentry_stub_request(build_fake_response("200")) do |request, http_obj|
188188
expect(request["Content-Type"]).to eq("application/x-sentry-envelope")
189189
expect(request["Content-Encoding"]).to eq("gzip")
190190
expect(http_obj.address).to eq("localhost")

0 commit comments

Comments
 (0)