2019-08-22 06:57:44 -04:00
# frozen_string_literal: true
2017-07-20 02:53:57 -04:00
require 'spec_helper'
2021-06-04 11:10:25 -04:00
require 'raven/transports/dummy'
2020-06-12 08:08:56 -04:00
2020-06-24 14:09:03 -04:00
RSpec . describe Gitlab :: ErrorTracking do
2019-12-13 07:07:41 -05:00
let ( :exception ) { RuntimeError . new ( 'boom' ) }
let ( :issue_url ) { 'http://gitlab.com/gitlab-org/gitlab-foss/issues/1' }
2021-03-31 08:08:55 -04:00
let ( :extra ) { { issue_url : issue_url , some_other_info : 'info' } }
2019-12-13 07:07:41 -05:00
2021-03-03 07:11:16 -05:00
let ( :user ) { create ( :user ) }
let ( :sentry_payload ) do
{
tags : {
program : 'test' ,
locale : 'en' ,
feature_category : 'feature_a' ,
correlation_id : 'cid'
} ,
user : {
username : user . username
} ,
extra : {
some_other_info : 'info' ,
issue_url : 'http://gitlab.com/gitlab-org/gitlab-foss/issues/1'
}
}
2019-12-13 07:07:41 -05:00
end
2021-03-03 07:11:16 -05:00
let ( :logger_payload ) do
{
'exception.class' = > 'RuntimeError' ,
'exception.message' = > 'boom' ,
'tags.program' = > 'test' ,
'tags.locale' = > 'en' ,
'tags.feature_category' = > 'feature_a' ,
'tags.correlation_id' = > 'cid' ,
'user.username' = > user . username ,
'extra.some_other_info' = > 'info' ,
'extra.issue_url' = > 'http://gitlab.com/gitlab-org/gitlab-foss/issues/1'
}
end
2020-07-07 23:08:47 -04:00
2021-06-04 11:10:25 -04:00
let ( :sentry_event ) { Gitlab :: Json . parse ( Raven . client . transport . events . last [ 1 ] ) }
2021-03-31 08:08:55 -04:00
2019-12-13 07:07:41 -05:00
before do
stub_sentry_settings
2017-07-20 02:53:57 -04:00
2019-12-13 07:07:41 -05:00
allow ( described_class ) . to receive ( :sentry_dsn ) . and_return ( Gitlab . config . sentry . dsn )
allow ( Labkit :: Correlation :: CorrelationId ) . to receive ( :current_id ) . and_return ( 'cid' )
2021-03-03 07:11:16 -05:00
allow ( I18n ) . to receive ( :locale ) . and_return ( 'en' )
2019-12-13 07:07:41 -05:00
2020-06-12 08:08:56 -04:00
described_class . configure do | config |
2021-06-04 11:10:25 -04:00
config . encoding = 'json'
2020-06-12 08:08:56 -04:00
end
2019-12-13 07:07:41 -05:00
end
2021-03-03 07:11:16 -05:00
around do | example |
Gitlab :: ApplicationContext . with_context ( user : user , feature_category : 'feature_a' ) do
example . run
2018-04-13 12:32:54 -04:00
end
end
2019-12-13 07:07:41 -05:00
describe '.track_and_raise_for_dev_exception' do
context 'when exceptions for dev should be raised' do
before do
expect ( described_class ) . to receive ( :should_raise_for_dev? ) . and_return ( true )
end
2018-04-13 12:32:54 -04:00
2019-12-13 07:07:41 -05:00
it 'raises the exception' do
2021-06-04 11:10:25 -04:00
expect ( Raven ) . to receive ( :capture_exception ) . with ( exception , sentry_payload )
2021-03-03 07:11:16 -05:00
expect do
described_class . track_and_raise_for_dev_exception (
exception ,
issue_url : issue_url ,
some_other_info : 'info'
)
end . to raise_error ( RuntimeError , / boom / )
2019-12-13 07:07:41 -05:00
end
2018-04-13 12:32:54 -04:00
end
2019-12-13 07:07:41 -05:00
context 'when exceptions for dev should not be raised' do
2018-04-13 12:32:54 -04:00
before do
2019-12-13 07:07:41 -05:00
expect ( described_class ) . to receive ( :should_raise_for_dev? ) . and_return ( false )
2018-04-13 12:32:54 -04:00
end
it 'logs the exception with all attributes passed' do
2021-06-04 11:10:25 -04:00
expect ( Raven ) . to receive ( :capture_exception ) . with ( exception , sentry_payload )
2018-04-13 12:32:54 -04:00
2019-12-13 07:07:41 -05:00
described_class . track_and_raise_for_dev_exception (
2018-04-13 12:32:54 -04:00
exception ,
2019-12-13 07:07:41 -05:00
issue_url : issue_url ,
some_other_info : 'info'
2018-04-13 12:32:54 -04:00
)
end
2019-12-16 07:07:43 -05:00
it 'calls Gitlab::ErrorTracking::Logger.error with formatted payload' do
2021-03-03 07:11:16 -05:00
expect ( Gitlab :: ErrorTracking :: Logger ) . to receive ( :error ) . with ( logger_payload )
2018-04-13 12:32:54 -04:00
2019-12-13 07:07:41 -05:00
described_class . track_and_raise_for_dev_exception (
exception ,
issue_url : issue_url ,
some_other_info : 'info'
)
2018-04-13 12:32:54 -04:00
end
2017-07-20 02:53:57 -04:00
end
end
2018-11-14 06:10:36 -05:00
2019-12-13 07:07:41 -05:00
describe '.track_and_raise_exception' do
it 'always raises the exception' do
2021-06-04 11:10:25 -04:00
expect ( Raven ) . to receive ( :capture_exception ) . with ( exception , sentry_payload )
2019-12-13 07:07:41 -05:00
2021-03-03 07:11:16 -05:00
expect do
described_class . track_and_raise_for_dev_exception (
exception ,
issue_url : issue_url ,
some_other_info : 'info'
)
end . to raise_error ( RuntimeError , / boom / )
2019-12-13 07:07:41 -05:00
end
2018-11-14 06:10:36 -05:00
2019-12-16 07:07:43 -05:00
it 'calls Gitlab::ErrorTracking::Logger.error with formatted payload' do
2021-03-03 07:11:16 -05:00
expect ( Gitlab :: ErrorTracking :: Logger ) . to receive ( :error ) . with ( logger_payload )
2019-12-13 07:07:41 -05:00
expect do
described_class . track_and_raise_exception (
exception ,
issue_url : issue_url ,
some_other_info : 'info'
)
end . to raise_error ( RuntimeError )
2018-11-14 06:10:36 -05:00
end
2019-12-13 07:07:41 -05:00
end
2018-11-14 06:10:36 -05:00
2019-12-13 07:07:41 -05:00
describe '.track_exception' do
2020-10-23 05:08:41 -04:00
subject ( :track_exception ) { described_class . track_exception ( exception , extra ) }
2020-10-22 05:08:26 -04:00
2020-10-23 05:08:41 -04:00
before do
2021-06-04 11:10:25 -04:00
allow ( Raven ) . to receive ( :capture_exception ) . and_call_original
2020-10-23 05:08:41 -04:00
allow ( Gitlab :: ErrorTracking :: Logger ) . to receive ( :error )
end
2021-06-04 11:10:25 -04:00
it 'calls Raven.capture_exception' do
2020-10-23 05:08:41 -04:00
track_exception
2021-06-04 11:10:25 -04:00
expect ( Raven ) . to have_received ( :capture_exception ) . with (
2021-03-03 07:11:16 -05:00
exception ,
sentry_payload
)
2019-12-13 07:07:41 -05:00
end
2019-12-16 07:07:43 -05:00
it 'calls Gitlab::ErrorTracking::Logger.error with formatted payload' do
2020-10-23 05:08:41 -04:00
track_exception
2019-12-13 07:07:41 -05:00
2021-03-03 07:11:16 -05:00
expect ( Gitlab :: ErrorTracking :: Logger ) . to have_received ( :error ) . with ( logger_payload )
2018-11-14 06:10:36 -05:00
end
2020-02-07 10:09:52 -05:00
context 'with filterable parameters' do
let ( :extra ) { { test : 1 , my_token : 'test' } }
it 'filters parameters' do
2020-10-23 05:08:41 -04:00
track_exception
2020-02-07 10:09:52 -05:00
2021-03-03 07:11:16 -05:00
expect ( Gitlab :: ErrorTracking :: Logger ) . to have_received ( :error ) . with (
hash_including ( { 'extra.test' = > 1 , 'extra.my_token' = > '[FILTERED]' } )
)
2020-02-07 10:09:52 -05:00
end
end
2019-08-19 19:06:21 -04:00
context 'the exception implements :sentry_extra_data' do
let ( :extra_info ) { { event : 'explosion' , size : :massive } }
2021-06-04 11:10:25 -04:00
let ( :exception ) { double ( message : 'bang!' , sentry_extra_data : extra_info , backtrace : caller , cause : nil ) }
2019-08-19 19:06:21 -04:00
it 'includes the extra data from the exception in the tracking information' do
2020-10-23 05:08:41 -04:00
track_exception
2019-08-19 19:06:21 -04:00
2021-06-04 11:10:25 -04:00
expect ( Raven ) . to have_received ( :capture_exception ) . with (
2021-03-03 07:11:16 -05:00
exception , a_hash_including ( extra : a_hash_including ( extra_info ) )
)
2019-08-19 19:06:21 -04:00
end
end
context 'the exception implements :sentry_extra_data, which returns nil' do
2021-06-04 11:10:25 -04:00
let ( :exception ) { double ( message : 'bang!' , sentry_extra_data : nil , backtrace : caller , cause : nil ) }
2020-10-23 05:08:41 -04:00
let ( :extra ) { { issue_url : issue_url } }
2019-08-19 19:06:21 -04:00
it 'just includes the other extra info' do
2020-10-23 05:08:41 -04:00
track_exception
2019-08-19 19:06:21 -04:00
2021-06-04 11:10:25 -04:00
expect ( Raven ) . to have_received ( :capture_exception ) . with (
2021-03-03 07:11:16 -05:00
exception , a_hash_including ( extra : a_hash_including ( extra ) )
)
2019-08-19 19:06:21 -04:00
end
end
2020-05-29 11:08:14 -04:00
2021-03-31 08:08:55 -04:00
context 'when the error is kind of an `ActiveRecord::StatementInvalid`' do
let ( :exception ) { ActiveRecord :: StatementInvalid . new ( sql : 'SELECT "users".* FROM "users" WHERE "users"."id" = 1 AND "users"."foo" = $1' ) }
it 'injects the normalized sql query into extra' do
track_exception
2021-06-04 11:10:25 -04:00
expect ( sentry_event . dig ( 'extra' , 'sql' ) ) . to eq ( 'SELECT "users".* FROM "users" WHERE "users"."id" = $2 AND "users"."foo" = $1' )
2021-03-31 08:08:55 -04:00
end
end
context 'when the `ActiveRecord::StatementInvalid` is wrapped in another exception' do
it 'injects the normalized sql query into extra' do
allow ( exception ) . to receive ( :cause ) . and_return ( ActiveRecord :: StatementInvalid . new ( sql : 'SELECT "users".* FROM "users" WHERE "users"."id" = 1 AND "users"."foo" = $1' ) )
track_exception
2021-06-04 11:10:25 -04:00
expect ( sentry_event . dig ( 'extra' , 'sql' ) ) . to eq ( 'SELECT "users".* FROM "users" WHERE "users"."id" = $2 AND "users"."foo" = $1' )
2021-03-31 08:08:55 -04:00
end
end
end
2021-04-23 14:10:18 -04:00
context 'event processors' do
2021-03-31 08:08:55 -04:00
subject ( :track_exception ) { described_class . track_exception ( exception , extra ) }
before do
2021-06-04 11:10:25 -04:00
allow ( Raven ) . to receive ( :capture_exception ) . and_call_original
2021-03-31 08:08:55 -04:00
allow ( Gitlab :: ErrorTracking :: Logger ) . to receive ( :error )
end
2021-06-04 11:10:25 -04:00
context 'custom GitLab context when using Raven.capture_exception directly' do
subject ( :raven_capture_exception ) { Raven . capture_exception ( exception ) }
2021-03-31 08:08:55 -04:00
it 'merges a default set of tags into the existing tags' do
2021-06-04 11:10:25 -04:00
allow ( Raven . context ) . to receive ( :tags ) . and_return ( foo : 'bar' )
2021-03-31 08:08:55 -04:00
2021-06-04 11:10:25 -04:00
raven_capture_exception
2021-03-31 08:08:55 -04:00
2021-06-04 11:10:25 -04:00
expect ( sentry_event [ 'tags' ] ) . to include ( 'correlation_id' , 'feature_category' , 'foo' , 'locale' , 'program' )
2021-03-31 08:08:55 -04:00
end
it 'merges the current user information into the existing user information' do
2021-06-04 11:10:25 -04:00
Raven . user_context ( id : - 1 )
2021-03-31 08:08:55 -04:00
2021-06-04 11:10:25 -04:00
raven_capture_exception
2021-03-31 08:08:55 -04:00
2021-06-04 11:10:25 -04:00
expect ( sentry_event [ 'user' ] ) . to eq ( 'id' = > - 1 , 'username' = > user . username )
2021-03-31 08:08:55 -04:00
end
end
2020-05-29 11:08:14 -04:00
context 'with sidekiq args' do
2020-10-23 05:08:41 -04:00
context 'when the args does not have anything sensitive' do
let ( :extra ) { { sidekiq : { 'class' = > 'PostReceive' , 'args' = > [ 1 , { 'id' = > 2 , 'name' = > 'hello' } , 'some-value' , 'another-value' ] } } }
2020-06-12 08:08:56 -04:00
2020-10-23 05:08:41 -04:00
it 'ensures extra.sidekiq.args is a string' do
track_exception
2020-06-12 08:08:56 -04:00
2020-10-23 05:08:41 -04:00
expect ( Gitlab :: ErrorTracking :: Logger ) . to have_received ( :error ) . with (
2021-03-03 07:11:16 -05:00
hash_including (
'extra.sidekiq' = > {
'class' = > 'PostReceive' ,
'args' = > [ '1' , '{"id"=>2, "name"=>"hello"}' , 'some-value' , 'another-value' ]
}
)
)
2020-10-23 05:08:41 -04:00
end
2021-03-31 08:08:55 -04:00
it 'does not filter parameters when sending to Sentry' do
track_exception
2021-06-04 11:10:25 -04:00
expect ( sentry_event . dig ( 'extra' , 'sidekiq' , 'args' ) ) . to eq ( [ 1 , { 'id' = > 2 , 'name' = > 'hello' } , 'some-value' , 'another-value' ] )
2021-03-31 08:08:55 -04:00
end
2020-06-12 08:08:56 -04:00
end
2020-10-23 05:08:41 -04:00
context 'when the args has sensitive information' do
let ( :extra ) { { sidekiq : { 'class' = > 'UnknownWorker' , 'args' = > [ 'sensitive string' , 1 , 2 ] } } }
2021-03-31 08:08:55 -04:00
it 'filters sensitive arguments before sending and logging' do
2020-10-23 05:08:41 -04:00
track_exception
2021-06-04 11:10:25 -04:00
expect ( sentry_event . dig ( 'extra' , 'sidekiq' , 'args' ) ) . to eq ( [ '[FILTERED]' , 1 , 2 ] )
2020-10-23 05:08:41 -04:00
expect ( Gitlab :: ErrorTracking :: Logger ) . to have_received ( :error ) . with (
2021-03-03 07:11:16 -05:00
hash_including (
'extra.sidekiq' = > {
'class' = > 'UnknownWorker' ,
'args' = > [ '[FILTERED]' , '1' , '2' ]
}
)
)
2020-10-23 05:08:41 -04:00
end
end
end
2020-06-12 08:08:56 -04:00
2021-03-31 08:08:55 -04:00
context 'when the error is a GRPC error' do
context 'when the GRPC error contains a debug_error_string value' do
let ( :exception ) { GRPC :: DeadlineExceeded . new ( 'unknown cause' , { } , '{"hello":1}' ) }
2020-05-29 11:08:14 -04:00
2021-03-31 08:08:55 -04:00
it 'sets the GRPC debug error string in the Sentry event and adds a custom fingerprint' do
track_exception
2021-06-04 11:10:25 -04:00
expect ( sentry_event . dig ( 'extra' , 'grpc_debug_error_string' ) ) . to eq ( '{"hello":1}' )
expect ( sentry_event [ 'fingerprint' ] ) . to eq ( [ 'GRPC::DeadlineExceeded' , '4:unknown cause.' ] )
2021-01-06 04:10:31 -05:00
end
2021-03-31 08:08:55 -04:00
end
2021-01-06 04:10:31 -05:00
2021-03-31 08:08:55 -04:00
context 'when the GRPC error does not contain a debug_error_string value' do
let ( :exception ) { GRPC :: DeadlineExceeded . new }
it 'does not do any processing on the event' do
track_exception
2021-06-04 11:10:25 -04:00
expect ( sentry_event [ 'extra' ] ) . not_to include ( 'grpc_debug_error_string' )
expect ( sentry_event [ 'fingerprint' ] ) . to eq ( [ 'GRPC::DeadlineExceeded' , '4:unknown cause' ] )
2021-03-31 08:08:55 -04:00
end
2021-01-06 04:10:31 -05:00
end
end
2021-03-31 08:08:55 -04:00
end
2017-07-20 02:53:57 -04:00
end