spec/standalone/ucblit/logging/loggers_spec.rb in berkeley_library-logging-0.2.0 vs spec/standalone/ucblit/logging/loggers_spec.rb in berkeley_library-logging-0.2.1

- old
+ new

@@ -7,19 +7,42 @@ attr_reader :out # rubocop:disable Lint/ConstantDefinitionInBlock before(:each) do @out = StringIO.new - class ::TestError < StandardError; end + class ::TestError < StandardError + attr_writer :cause + + def cause + @cause || super + end + end end # rubocop:enable Lint/ConstantDefinitionInBlock after(:each) do Object.send(:remove_const, :TestError) end describe :new_json_logger do + + # TODO: rewrite this as a matcher + # rubocop:disable Metrics/AbcSize + def assert_serialized_error(err_json, err) + expect(err_json).to be_a(Hash) + expect(err_json['name']).to eq(TestError.name) + expect(err_json['message']).to eq(err.message) + + err_stack = err_json['stack'] + backtrace = err.backtrace + expect(backtrace).not_to be_nil # just to be sure + backtrace.each do |line| + expect(err_stack).to include(line) + end + end + # rubocop:enable Metrics/AbcSize + it 'supports tagged logging' do logger = Loggers.new_json_logger(out) logger = ActiveSupport::TaggedLogging.new(logger) expected_tag = 'hello' @@ -36,29 +59,66 @@ msg = 'Help I am trapped in a unit test' begin raise TestError, msg rescue TestError => e - ex = e Loggers.new_json_logger(out).error(e) end logged_json = JSON.parse(out.string) - expect(logged_json['msg']).to eq(msg) + expect(logged_json['msg']).to eq(e.message) + assert_serialized_error(logged_json['err'], e) + end + + # rubocop:disable Naming/RescuedExceptionsVariableName + it 'includes the error cause' do + msg_outer = 'Help I am trapped in the outer part of a unit test' + msg_inner = 'Help I am trapped in the inner part of a unit test' + + begin + raise TestError, msg_inner + rescue TestError => ex_inner + begin + raise TestError, msg_outer + rescue TestError => ex_outer + Loggers.new_json_logger(out).error(ex_outer) + end + end + + expect(ex_outer.cause).to eq(ex_inner) # just to be sure + + logged_json = JSON.parse(out.string) + expect(logged_json['msg']).to eq(ex_outer.message) + err_json = logged_json['err'] - expect(err_json).to be_a(Hash) - expect(err_json['name']).to eq(TestError.name) - expect(err_json['message']).to eq(msg) + assert_serialized_error(err_json, ex_outer) - err_stack = err_json['stack'] - backtrace = ex.backtrace - expect(backtrace).not_to be_nil # just to be sure - backtrace.each do |line| - expect(err_stack).to include(line) + cause_json = err_json['cause'] + expect(cause_json).not_to be_nil + assert_serialized_error(cause_json, ex_inner) + end + end + # rubocop:enable Naming/RescuedExceptionsVariableName + + # rubocop:disable Naming/RescuedExceptionsVariableName + it 'handles pathological circular references' do + msg_outer = 'Help I am trapped in the outer part of a unit test' + msg_inner = 'Help I am trapped in the inner part of a unit test' + + begin + raise TestError, msg_inner + rescue TestError => ex_inner + begin + raise TestError, msg_outer + rescue TestError => ex_outer + ex_inner.cause = ex_outer end end + + expect { Loggers.new_json_logger(out).error(ex_outer) }.not_to raise_error(SystemStackError) end + # rubocop:enable Naming/RescuedExceptionsVariableName describe :default_logger do it 'returns a readable $stdout logger' do logger = Loggers.default_logger expect(logger).to be_a(Logger) @@ -130,11 +190,11 @@ expect(logged_txt).to include(k.inspect) expect(logged_txt).to include(v.inspect) end end - it 'logs an error with cause and backtrace' do + it 'logs an error with backtrace' do msg_txt = 'message text' ex_msg = 'Help I am trapped in a unit test' begin raise TestError, ex_msg @@ -152,10 +212,41 @@ backtrace.each do |line| expect(logged_txt).to include(line) end end + # rubocop:disable Naming/RescuedExceptionsVariableName + it 'includes the error cause' do + msg_outer = 'Help I am trapped in the outer part of a unit test' + msg_inner = 'Help I am trapped in the inner part of a unit test' + + begin + raise TestError, msg_inner + rescue TestError => ex_inner + begin + raise TestError, msg_outer + rescue TestError => ex_outer + Loggers.new_readable_logger(out).error(ex_outer) + end + end + + expect(ex_outer.cause).to eq(ex_inner) # just to be sure + + logged_txt = out.string + [ex_inner, ex_outer].each do |ex| + msg = ex.message + expect(logged_txt).to include(msg) + + backtrace = ex.backtrace + expect(backtrace).not_to be_nil # just to be sure + backtrace.each do |line| + expect(logged_txt).to include(line) + end + end + end + # rubocop:enable Naming/RescuedExceptionsVariableName + end describe 'messages with data and no text' do it 'logs an arbitrary hash in a reasonable way' do out = StringIO.new @@ -272,7 +363,8 @@ BerkeleyLibrary::Logging.env = 'some-unsupported-environment' expect { Loggers.new_default_logger(config) }.to raise_error(ArgumentError) end end end + end end