Skip to content

Commit

Permalink
Improve SDK's error handling (#1298)
Browse files Browse the repository at this point in the history
* Add sentry/exceptions.rb

* Add Sentry::ExternalError

Sentry::Error represents errors SDK raises intentionally.
Sentry::ExternalError represents errors by the SDK, but casused by
external reasons, like networking issues.

* Transport#send_event shouldn't swallow any error

* Move event sending specs to their own file

* Implement error handling on Client#capture_event

* Fix background worker's logger setup

* Inline method calls should always swallow errors

The code invoked by the SDK itself, whether it's an external service
call (like sending events) or a user-defined callback (like async or
before_send), shouldn't cause the user's application to crash.

Although some may think errors caused by user-defined callbacks should
be raised for debugging purpose, it's actually a bad practice in reality.

Most of the users only activate the SDK in production and they don't write
tests for the callbacks. So if there's any bug in the callbacks,
they will usually be spotted in production.

Also, because the SDK is usually placed at the farthest layer of the
app to catch all the possible exceptions, there won't be anything to
capture exceptions triggered by the SDK.

So from the SDK's perspective, we can either raise them or swallow them:

- If we raise them, the user's customers will see broken apps. Good for
  spotting bugs, very bad user experience.
- If we rescue them, the user's customers won't spot a thing. It may be
  harder for the user to spot the buggy callback, but at least their
  customers won't be angry.

This is why I decided to swallow any errors happen in the
`Client#capture_event`.

* Refactor Client#capture_event

* Log event message when event sending failed

* Capitalize all event sending messages

* Change logging message base on event type

* Fix Event.get_log_message

* Update changelog
st0012 authored Mar 7, 2021

Verified

This commit was signed with the committer’s verified signature.
snyk-bot Snyk bot
1 parent b967ec5 commit e8652c6
Showing 13 changed files with 479 additions and 262 deletions.
3 changes: 3 additions & 0 deletions sentry-ruby/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -3,6 +3,9 @@
## Unreleased

- Refactor interface construction [#1296](https://github.com/getsentry/sentry-ruby/pull/1296)
- Improve SDK's error handling [#1298](https://github.com/getsentry/sentry-ruby/pull/1298)
- Fixes [#1246](https://github.com/getsentry/sentry-ruby/issues/1246) and [#1289](https://github.com/getsentry/sentry-ruby/issues/1289)
- Please read [#1290](https://github.com/getsentry/sentry-ruby/issues/1290) to see the full specification
- Treat query string as pii too [#1302](https://github.com/getsentry/sentry-ruby/pull/1302)
- Fixes [#1301](https://github.com/getsentry/sentry-ruby/issues/1301)
- Ignore sentry-trace when tracing is not enabled [#1308](https://github.com/getsentry/sentry-ruby/pull/1308)
4 changes: 1 addition & 3 deletions sentry-ruby/lib/sentry-ruby.rb
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
require "time"

require "sentry/version"
require "sentry/exceptions"
require "sentry/core_ext/object/deep_dup"
require "sentry/utils/argument_checking_helper"
require "sentry/configuration"
@@ -26,9 +27,6 @@
end

module Sentry
class Error < StandardError
end

META = { "name" => "sentry.ruby", "version" => Sentry::VERSION }.freeze

LOGGER_PROGNAME = "sentry".freeze
1 change: 1 addition & 0 deletions sentry-ruby/lib/sentry/background_worker.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require "concurrent/executor/thread_pool_executor"
require "concurrent/executor/immediate_executor"
require "concurrent/configuration"

module Sentry
class BackgroundWorker
73 changes: 46 additions & 27 deletions sentry-ruby/lib/sentry/client.rb
Original file line number Diff line number Diff line change
@@ -2,10 +2,11 @@

module Sentry
class Client
attr_reader :transport, :configuration
attr_reader :transport, :configuration, :logger

def initialize(configuration)
@configuration = configuration
@logger = configuration.logger

if transport_class = configuration.transport.transport_class
@transport = transport_class.new(configuration)
@@ -26,32 +27,17 @@ def capture_event(event, scope, hint = {})
scope.apply_to_event(event, hint)

if async_block = configuration.async
begin
# We have to convert to a JSON-like hash, because background job
# processors (esp ActiveJob) may not like weird types in the event hash
event_hash = event.to_json_compatible

if async_block.arity == 2
hint = JSON.parse(JSON.generate(hint))
async_block.call(event_hash, hint)
else
async_block.call(event_hash)
end
rescue => e
configuration.logger.error(LOGGER_PROGNAME) { "async event sending failed: #{e.message}" }
send_event(event, hint)
end
dispatch_async_event(async_block, event, hint)
elsif hint.fetch(:background, true)
dispatch_background_event(event, hint)
else
if hint.fetch(:background, true)
Sentry.background_worker.perform do
send_event(event, hint)
end
else
send_event(event, hint)
end
send_event(event, hint)
end

event
rescue => e
logger.error(LOGGER_PROGNAME) { "Event capturing failed: #{e.message}" }
nil
end

def event_from_exception(exception, hint = {})
@@ -85,16 +71,49 @@ def event_from_transaction(transaction)

def send_event(event, hint = nil)
event_type = event.is_a?(Event) ? event.type : event["type"]
event = configuration.before_send.call(event, hint) if configuration.before_send && event_type == "event"

if event.nil?
configuration.logger.info(LOGGER_PROGNAME) { "Discarded event because before_send returned nil" }
return
if event_type == "event" && configuration.before_send
event = configuration.before_send.call(event, hint)

if event.nil?
logger.info(LOGGER_PROGNAME) { "Discarded event because before_send returned nil" }
return
end
end

transport.send_event(event)

event
rescue => e
logger.error(LOGGER_PROGNAME) { "#{event_type.capitalize} sending failed: #{e.message}" }
logger.error(LOGGER_PROGNAME) { "Unreported #{event_type.capitalize}: #{Event.get_log_message(event.to_hash)}" }
raise
end

private

def dispatch_background_event(event, hint)
Sentry.background_worker.perform do
send_event(event, hint)
end
end

def dispatch_async_event(async_block, event, hint)
# We have to convert to a JSON-like hash, because background job
# processors (esp ActiveJob) may not like weird types in the event hash
event_hash = event.to_json_compatible

if async_block.arity == 2
hint = JSON.parse(JSON.generate(hint))
async_block.call(event_hash, hint)
else
async_block.call(event_hash)
end
rescue => e
event_type = event_hash["type"]
logger.error(LOGGER_PROGNAME) { "Async #{event_type} sending failed: #{e.message}" }
send_event(event, hint)
end

end
end
27 changes: 17 additions & 10 deletions sentry-ruby/lib/sentry/event.rb
Original file line number Diff line number Diff line change
@@ -52,19 +52,26 @@ def initialize(configuration:, integration_meta: nil, message: nil)
class << self
def get_log_message(event_hash)
message = event_hash[:message] || event_hash['message']
message = get_message_from_exception(event_hash) if message.nil? || message.empty?
message = '<no message value>' if message.nil? || message.empty?
message

return message unless message.nil? || message.empty?

message = get_message_from_exception(event_hash)

return message unless message.nil? || message.empty?

message = event_hash[:transaction] || event_hash["transaction"]

return message unless message.nil? || message.empty?

'<no message value>'
end

def get_message_from_exception(event_hash)
(
event_hash &&
event_hash[:exception] &&
event_hash[:exception][:values] &&
event_hash[:exception][:values][0] &&
"#{event_hash[:exception][:values][0][:type]}: #{event_hash[:exception][:values][0][:value]}"
)
if exception = event_hash.dig(:exception, :values, 0)
"#{exception[:type]}: #{exception[:value]}"
elsif exception = event_hash.dig("exception", "values", 0)
"#{exception["type"]}: #{exception["value"]}"
end
end
end

7 changes: 7 additions & 0 deletions sentry-ruby/lib/sentry/exceptions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Sentry
class Error < StandardError
end

class ExternalError < StandardError
end
end
12 changes: 0 additions & 12 deletions sentry-ruby/lib/sentry/transport.rb
Original file line number Diff line number Diff line change
@@ -31,9 +31,6 @@ def send_event(event)
send_data(encoded_data)

event
rescue => e
failed_for_exception(e, event)
nil
end

def generate_auth_header
@@ -72,15 +69,6 @@ def prepare_encoded_event(event)
configuration.logger.info(LOGGER_PROGNAME) { "Sending #{event_type} #{event_id} to Sentry" }
encode(event_hash)
end

def failed_for_exception(e, event)
configuration.logger.warn(LOGGER_PROGNAME) { "Unable to record event with remote Sentry server (#{e.class} - #{e.message}):\n#{e.backtrace[0..10].join("\n")}" }
log_not_sending(event)
end

def log_not_sending(event)
configuration.logger.warn(LOGGER_PROGNAME) { "Failed to submit event. Unreported Event: #{Event.get_log_message(event.to_hash)}" }
end
end
end

2 changes: 1 addition & 1 deletion sentry-ruby/lib/sentry/transport/http_transport.rb
Original file line number Diff line number Diff line change
@@ -38,7 +38,7 @@ def send_data(data)
error_info += " Error in headers is: #{e.response[:headers]['x-sentry-error']}" if e.response[:headers]['x-sentry-error']
end

raise Sentry::Error, error_info
raise Sentry::ExternalError, error_info
end

private
331 changes: 331 additions & 0 deletions sentry-ruby/spec/sentry/client/event_sending_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,331 @@
require 'spec_helper'

RSpec.describe Sentry::Client do
let(:configuration) do
Sentry::Configuration.new.tap do |config|
config.dsn = DUMMY_DSN
config.transport.transport_class = Sentry::DummyTransport
end
end
subject { Sentry::Client.new(configuration) }

describe "#capture_event" do
let(:message) { "Test message" }
let(:scope) { Sentry::Scope.new }
let(:event) { subject.event_from_message(message) }

context 'with config.async set' do
let(:async_block) do
lambda do |event|
subject.send_event(event)
end
end

around do |example|
prior_async = configuration.async
configuration.async = async_block
example.run
configuration.async = prior_async
end

it "executes the given block" do
expect(async_block).to receive(:call).and_call_original

returned = subject.capture_event(event, scope)

expect(returned).to be_a(Sentry::Event)
expect(subject.transport.events.first).to eq(event.to_json_compatible)
end

it "doesn't call the async block if not allow sending events" do
allow(configuration).to receive(:sending_allowed?).and_return(false)

expect(async_block).not_to receive(:call)

returned = subject.capture_event(event, scope)

expect(returned).to eq(nil)
end

context "with false as value (the legacy way to disable it)" do
let(:async_block) { false }

it "doesn't cause any issue" do
returned = subject.capture_event(event, scope, { background: false })

expect(returned).to be_a(Sentry::Event)
expect(subject.transport.events.first).to eq(event)
end
end

context "with 2 arity block" do
let(:async_block) do
lambda do |event, hint|
event["tags"]["hint"] = hint
subject.send_event(event)
end
end

it "serializes hint and supplies it as the second argument" do
expect(configuration.async).to receive(:call).and_call_original

returned = subject.capture_event(event, scope, { foo: "bar" })

expect(returned).to be_a(Sentry::Event)
event = subject.transport.events.first
expect(event.dig("tags", "hint")).to eq({ "foo" => "bar" })
end
end
end

context "with background_worker enabled (default)" do
before do
Sentry.background_worker = Sentry::BackgroundWorker.new(configuration)
configuration.before_send = lambda do |event, _hint|
sleep 0.1
event
end
end

let(:transport) do
subject.transport
end

it "sends events asynchronously" do
subject.capture_event(event, scope)

expect(transport.events.count).to eq(0)

sleep(0.2)

expect(transport.events.count).to eq(1)
end

context "with hint: { background: false }" do
it "sends the event immediately" do
subject.capture_event(event, scope, { background: false })

expect(transport.events.count).to eq(1)
end
end
end
end

describe "#send_event" do
let(:event_object) do
subject.event_from_exception(ZeroDivisionError.new("divided by 0"))
end
let(:transaction_event_object) do
subject.event_from_transaction(Sentry::Transaction.new)
end

shared_examples "Event in send_event" do
context "when there's an exception" do
before do
expect(subject.transport).to receive(:send_event).and_raise(Sentry::ExternalError.new("networking error"))
end

it "raises the error" do
expect do
subject.send_event(event)
end.to raise_error(Sentry::ExternalError, "networking error")
end
end
it "sends data through the transport" do
expect(subject.transport).to receive(:send_event).with(event)
subject.send_event(event)
end

it "applies before_send callback before sending the event" do
configuration.before_send = lambda do |event, _hint|
if event.is_a?(Sentry::Event)
event.tags[:called] = true
else
event["tags"]["called"] = true
end

event
end

subject.send_event(event)

if event.is_a?(Sentry::Event)
expect(event.tags[:called]).to eq(true)
else
expect(event["tags"]["called"]).to eq(true)
end
end
end

it_behaves_like "Event in send_event" do
let(:event) { event_object }
end

it_behaves_like "Event in send_event" do
let(:event) { event_object.to_json_compatible }
end

shared_examples "TransactionEvent in send_event" do
it "sends data through the transport" do
subject.send_event(event)
end

it "doesn't apply before_send to TransactionEvent" do
configuration.before_send = lambda do |event, _hint|
raise "shouldn't trigger me"
end

subject.send_event(event)
end
end

it_behaves_like "TransactionEvent in send_event" do
let(:event) { transaction_event_object }
end

it_behaves_like "TransactionEvent in send_event" do
let(:event) { transaction_event_object.to_json_compatible }
end
end

describe "integrated error handling testing with HTTPTransport" do
let(:string_io) { StringIO.new }
let(:logger) do
::Logger.new(string_io)
end
let(:configuration) do
Sentry::Configuration.new.tap do |config|
config.dsn = DUMMY_DSN
config.logger = logger
end
end

let(:message) { "Test message" }
let(:scope) { Sentry::Scope.new }
let(:event) { subject.event_from_message(message) }

describe "#capture_event" do
around do |example|
prior_async = configuration.async
example.run
configuration.async = prior_async
end

context "when scope.apply_to_event fails" do
before do
scope.add_event_processor do
raise TypeError
end
end

it "swallows the event and logs the failure" do
expect(subject.capture_event(event, scope)).to be_nil

expect(string_io.string).to match(/Event capturing failed: TypeError/)
end
end

context "when sending events inline causes error" do
before do
configuration.background_worker_threads = 0
Sentry.background_worker = Sentry::BackgroundWorker.new(configuration)
end

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

expect(string_io.string).to match(/Event sending failed: Failed to open TCP connection/)
expect(string_io.string).to match(/Unreported Event: Test message/)
expect(string_io.string).to match(/Event capturing failed: Failed to open TCP connection/)
end

it "swallows and logs errors caused by the user (like in before_send)" do
configuration.before_send = -> (_, _) { raise TypeError }

expect(subject.capture_event(event, scope)).to be_nil

expect(string_io.string).to match(/Event sending failed: TypeError/)
expect(string_io.string).to match(/Unreported Event: Test message/)
end
end

context "when sending events in background causes error" do
before do
Sentry.background_worker = Sentry::BackgroundWorker.new(configuration)
end

it "swallows and logs Sentry::ExternalError (caused by transport's networking error)" do
expect(subject.capture_event(event, scope)).to be_a(Sentry::Event)
sleep(0.1)

expect(string_io.string).to match(/Event sending failed: Failed to open TCP connection/)
expect(string_io.string).to match(/Unreported Event: Test message/)
end

it "swallows and logs errors caused by the user (like in before_send)" do
configuration.before_send = -> (_, _) { raise TypeError }

expect(subject.capture_event(event, scope)).to be_a(Sentry::Event)
sleep(0.1)

expect(string_io.string).to match(/Event sending failed: TypeError/)
expect(string_io.string).to match(/Unreported Event: Test message/)
end
end

context "when config.async causes error" do
before do
expect(subject).to receive(:send_event)
end

it "swallows Redis related error and send the event synchronizely" do
class Redis
class ConnectionError < StandardError; end
end

configuration.async = -> (_, _) { raise Redis::ConnectionError }

subject.capture_event(event, scope)

expect(string_io.string).to match(/Async event sending failed: Redis::ConnectionError/)
end

it "swallows and logs the exception" do
configuration.async = -> (_, _) { raise TypeError }

subject.capture_event(event, scope)

expect(string_io.string).to match(/Async event sending failed: TypeError/)
end
end
end

describe "#send_event" do
context "error happens when sending the event" do
it "raises the error" do
expect do
subject.send_event(event)
end.to raise_error(Sentry::ExternalError)

expect(string_io.string).to match(/Event sending failed: Failed to open TCP connection/)
expect(string_io.string).to match(/Unreported Event: Test message/)
end
end

context "error happens in the before_send callback" do
it "raises the error" do
configuration.before_send = lambda do |event, _hint|
raise TypeError
end

expect do
subject.send_event(event)
end.to raise_error(TypeError)

expect(string_io.string).to match(/Event sending failed: TypeError/)
expect(string_io.string).to match(/Unreported Event: Test message/)
end
end
end
end
end
188 changes: 0 additions & 188 deletions sentry-ruby/spec/sentry/client_spec.rb
Original file line number Diff line number Diff line change
@@ -23,194 +23,6 @@ def sentry_context
allow(Time).to receive(:now).and_return fake_time
end

describe "#capture_event" do
let(:message) { "Test message" }
let(:scope) { Sentry::Scope.new }
let(:event) { subject.event_from_message(message) }

context 'with config.async set' do
let(:async_block) do
lambda do |event|
subject.send_event(event)
end
end

around do |example|
prior_async = configuration.async
configuration.async = async_block
example.run
configuration.async = prior_async
end

it "executes the given block" do
expect(async_block).to receive(:call).and_call_original

returned = subject.capture_event(event, scope)

expect(returned).to be_a(Sentry::Event)
expect(subject.transport.events.first).to eq(event.to_json_compatible)
end

it "doesn't call the async block if not allow sending events" do
allow(configuration).to receive(:sending_allowed?).and_return(false)

expect(async_block).not_to receive(:call)

returned = subject.capture_event(event, scope)

expect(returned).to eq(nil)
end

context "with false as value (the legacy way to disable it)" do
let(:async_block) { false }

it "doesn't cause any issue" do
returned = subject.capture_event(event, scope, { background: false })

expect(returned).to be_a(Sentry::Event)
expect(subject.transport.events.first).to eq(event)
end
end

context "with 2 arity block" do
let(:async_block) do
lambda do |event, hint|
event["tags"]["hint"] = hint
subject.send_event(event)
end
end

it "serializes hint and supplies it as the second argument" do
expect(configuration.async).to receive(:call).and_call_original

returned = subject.capture_event(event, scope, { foo: "bar" })

expect(returned).to be_a(Sentry::Event)
event = subject.transport.events.first
expect(event.dig("tags", "hint")).to eq({ "foo" => "bar" })
end
end

context "when async raises an exception" do
around do |example|
prior_async = configuration.async
configuration.async = proc { raise TypeError }
example.run
configuration.async = prior_async
end

it 'sends the result of Event.capture_exception via fallback' do
expect(configuration.logger).to receive(:error).with(Sentry::LOGGER_PROGNAME) { "async event sending failed: TypeError" }
expect(configuration.async).to receive(:call).and_call_original
expect(subject).to receive(:send_event)

subject.capture_event(event, scope)
end
end
end

context "with background_worker enabled (default)" do
before do
Sentry.background_worker = Sentry::BackgroundWorker.new(configuration)
configuration.before_send = lambda do |event, _hint|
sleep 0.1
event
end
end

let(:transport) do
subject.transport
end

it "sends events asynchronously" do
subject.capture_event(event, scope)

expect(transport.events.count).to eq(0)

sleep(0.2)

expect(transport.events.count).to eq(1)
end

context "with hint: { background: false }" do
it "sends the event immediately" do
subject.capture_event(event, scope, { background: false })

expect(transport.events.count).to eq(1)
end
end
end
end

describe "#send_event" do
let(:event_object) do
subject.event_from_exception(ZeroDivisionError.new("divided by 0"))
end
let(:transaction_event_object) do
subject.event_from_transaction(Sentry::Transaction.new)
end

before do
expect(subject.transport).to receive(:send_event).with(event)
end

shared_examples "Event in send_event" do
it "sends data through the transport" do
subject.send_event(event)
end

it "applies before_send callback before sending the event" do
configuration.before_send = lambda do |event, _hint|
if event.is_a?(Sentry::Event)
event.tags[:called] = true
else
event["tags"]["called"] = true
end

event
end

subject.send_event(event)

if event.is_a?(Sentry::Event)
expect(event.tags[:called]).to eq(true)
else
expect(event["tags"]["called"]).to eq(true)
end
end
end

it_behaves_like "Event in send_event" do
let(:event) { event_object }
end

it_behaves_like "Event in send_event" do
let(:event) { event_object.to_json_compatible }
end

shared_examples "TransactionEvent in send_event" do
it "sends data through the transport" do
subject.send_event(event)
end

it "doesn't apply before_send to TransactionEvent" do
configuration.before_send = lambda do |event, _hint|
raise "shouldn't trigger me"
end

subject.send_event(event)
end
end

it_behaves_like "TransactionEvent in send_event" do
let(:event) { transaction_event_object }
end

it_behaves_like "TransactionEvent in send_event" do
let(:event) { transaction_event_object.to_json_compatible }
end
end

describe "#transport" do
let(:configuration) { Sentry::Configuration.new }

51 changes: 51 additions & 0 deletions sentry-ruby/spec/sentry/event_spec.rb
Original file line number Diff line number Diff line change
@@ -154,4 +154,55 @@
expect(json["extra"]['anonymous_module']).not_to be_a(Class)
end
end

describe ".get_log_message" do
let(:client) do
Sentry::Client.new(configuration)
end

context "with adnormal event" do
subject do
Sentry::Event.new(configuration: configuration)
end

it "returns placeholder message" do
expect(described_class.get_log_message(subject.to_hash)).to eq('<no message value>')
expect(described_class.get_log_message(subject.to_json_compatible)).to eq('<no message value>')
end
end
context "with transaction event" do
let(:transaction) do
Sentry::Transaction.new(name: "test transaction", op: "sql.active_record", sampled: true)
end

subject do
client.event_from_transaction(transaction)
end

it "returns the transaction's name" do
expect(described_class.get_log_message(subject.to_hash)).to eq("test transaction")
expect(described_class.get_log_message(subject.to_json_compatible)).to eq("test transaction")
end
end
context "with exception event" do
subject do
client.event_from_exception(Exception.new("error!"))
end

it "returns the exception's message" do
expect(described_class.get_log_message(subject.to_hash)).to eq("Exception: error!")
expect(described_class.get_log_message(subject.to_json_compatible)).to eq("Exception: error!")
end
end
context "with message event" do
subject do
client.event_from_message("test")
end

it "returns the event's message" do
expect(described_class.get_log_message(subject.to_hash)).to eq("test")
expect(described_class.get_log_message(subject.to_json_compatible)).to eq("test")
end
end
end
end
6 changes: 3 additions & 3 deletions sentry-ruby/spec/sentry/transport/http_transport_spec.rb
Original file line number Diff line number Diff line change
@@ -95,7 +95,7 @@
end

it 'raises an error' do
expect { subject.send_data(data) }.to raise_error(Sentry::Error, /the server responded with status 404/)
expect { subject.send_data(data) }.to raise_error(Sentry::ExternalError, /the server responded with status 404/)

stubs.verify_stubbed_calls
end
@@ -109,7 +109,7 @@
end

it 'raises an error' do
expect { subject.send_data(data) }.to raise_error(Sentry::Error, /the server responded with status 500/)
expect { subject.send_data(data) }.to raise_error(Sentry::ExternalError, /the server responded with status 500/)

stubs.verify_stubbed_calls
end
@@ -123,7 +123,7 @@
end

it 'raises an error with header' do
expect { subject.send_data(data) }.to raise_error(Sentry::Error, /error_in_header/)
expect { subject.send_data(data) }.to raise_error(Sentry::ExternalError, /error_in_header/)

stubs.verify_stubbed_calls
end
36 changes: 18 additions & 18 deletions sentry-ruby/spec/sentry/transport_spec.rb
Original file line number Diff line number Diff line change
@@ -11,10 +11,10 @@
end
let(:fake_time) { Time.now }

subject { described_class.new(configuration) }
let(:client) { Sentry::Client.new(configuration) }
subject { client.transport }

describe "#encode" do
let(:client) { Sentry::Client.new(configuration) }

before do
Sentry.init do |config|
@@ -121,24 +121,24 @@
end

context "when failed" do
before do
allow(subject).to receive(:send_data).and_raise(StandardError)
context "with normal error" do
before do
allow(subject).to receive(:send_data).and_raise(StandardError)
end

it "raises the error" do
expect do
subject.send_event(event)
end.to raise_error(StandardError)
end
end

it "returns nil" do
expect(subject.send_event(event)).to eq(nil)
end

it "logs correct message" do
subject.send_event(event)

log = io.string
expect(log).to match(
/WARN -- sentry: Unable to record event with remote Sentry server \(StandardError - StandardError\):/
)
expect(log).to match(
/WARN -- sentry: Failed to submit event. Unreported Event: ZeroDivisionError: divided by 0/
)
context "with Faraday::Error" do
it "raises the error" do
expect do
subject.send_event(event)
end.to raise_error(Sentry::ExternalError)
end
end
end
end

0 comments on commit e8652c6

Please sign in to comment.