diff --git a/CHANGELOG.md b/CHANGELOG.md index d651459f4..14e694076 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,30 @@ Changelog ========= +## 6.12.0 (28 Aug 2019) + +### Enhancements + +* Add Ruby (and other framework) version strings to report and session payloads (device.runtimeVersions). + | [560](https://github.com/bugsnag/bugsnag-ruby/pull/560) + +* Allow symbols in breadcrumb meta data. + | [#563](https://github.com/bugsnag/bugsnag-ruby/pull/563) + | [directionless](https://github.com/directionless) + +### Fixes + +* Use `Module#prepend` for Rake integration when on a new enough Ruby version + to avoid infinite mutual recursion issues when something else monkey patches + `Rake::Task`. + | [#556](https://github.com/bugsnag/bugsnag-ruby/issues/556) + | [#559](https://github.com/bugsnag/bugsnag-ruby/issues/559) + +* Handle `nil` values for the `job` block parameter for the Que error notifier. + This occurs under some conditions such as database connection failures. + | [#545](https://github.com/bugsnag/bugsnag-ruby/issues/545) + | [#548](https://github.com/bugsnag/bugsnag-ruby/pull/548) + ## 6.11.1 (22 Jan 2019) ### Fixes diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8063f19a0..1aa92f2eb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,7 +18,12 @@ Thank you! - Run the tests with and make sure they all pass ``` - rake spec + bundle exec rake spec + ``` +- You will need to add the following test dependencies: + + ``` + bundle install --with test --binstubs ``` - For adding a new integration (like support for a web framework or worker queue), include an example in the `example/` directory showing off what diff --git a/VERSION b/VERSION index fac714a32..d4e6cb429 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.11.1 +6.12.0 diff --git a/lib/bugsnag/breadcrumbs/validator.rb b/lib/bugsnag/breadcrumbs/validator.rb index 64eabb434..c6893b50a 100644 --- a/lib/bugsnag/breadcrumbs/validator.rb +++ b/lib/bugsnag/breadcrumbs/validator.rb @@ -49,11 +49,11 @@ def validate(breadcrumb) ## # Tests whether the meta_data types are non-complex objects. # - # Acceptable types are String, Numeric, TrueClass, FalseClass, and nil. + # Acceptable types are String, Symbol, Numeric, TrueClass, FalseClass, and nil. # # @param value [Object] the object to be type checked def valid_meta_data_type?(value) - value.nil? || value.is_a?(String) || value.is_a?(Numeric) || value.is_a?(FalseClass) || value.is_a?(TrueClass) + value.nil? || value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(Numeric) || value.is_a?(FalseClass) || value.is_a?(TrueClass) end end end diff --git a/lib/bugsnag/configuration.rb b/lib/bugsnag/configuration.rb index b51880887..c5a919430 100644 --- a/lib/bugsnag/configuration.rb +++ b/lib/bugsnag/configuration.rb @@ -34,6 +34,7 @@ class Configuration attr_accessor :proxy_password attr_accessor :timeout attr_accessor :hostname + attr_accessor :runtime_versions attr_accessor :ignore_classes attr_accessor :auto_capture_sessions attr_accessor :track_sessions @@ -93,6 +94,9 @@ def initialize self.send_code = true self.meta_data_filters = Set.new(DEFAULT_META_DATA_FILTERS) self.hostname = default_hostname + self.runtime_versions = {} + self.runtime_versions["ruby"] = RUBY_VERSION + self.runtime_versions["jruby"] = JRUBY_VERSION if defined?(JRUBY_VERSION) self.timeout = 15 self.notify_release_stages = nil self.auto_capture_sessions = true diff --git a/lib/bugsnag/integrations/que.rb b/lib/bugsnag/integrations/que.rb index 666bed135..8cf701497 100644 --- a/lib/bugsnag/integrations/que.rb +++ b/lib/bugsnag/integrations/que.rb @@ -1,23 +1,24 @@ require 'que' - if defined?(::Que) handler = proc do |error, job| begin - job = job.dup # Make sure the original job object is not mutated. + job &&= job.dup # Make sure the original job object is not mutated. Bugsnag.notify(error, true) do |report| - job[:error_count] += 1 + if job + job[:error_count] += 1 - # If the job was scheduled using ActiveJob then unwrap the job details for clarity: - if job[:job_class] == "ActiveJob::QueueAdapters::QueAdapter::JobWrapper" - wrapped_job = job[:args].last - wrapped_job = wrapped_job.each_with_object({}) { |(k, v), result| result[k.to_sym] = v } # Symbolize keys + # If the job was scheduled using ActiveJob then unwrap the job details for clarity: + if job[:job_class] == "ActiveJob::QueueAdapters::QueAdapter::JobWrapper" + wrapped_job = job[:args].last + wrapped_job = wrapped_job.each_with_object({}) { |(k, v), result| result[k.to_sym] = v } # Symbolize keys - # Align key names with keys in `job` - wrapped_job[:queue] = wrapped_job.delete(:queue_name) - wrapped_job[:args] = wrapped_job.delete(:arguments) + # Align key names with keys in `job` + wrapped_job[:queue] = wrapped_job.delete(:queue_name) + wrapped_job[:args] = wrapped_job.delete(:arguments) - job.merge!(wrapper_job_class: job[:job_class], wrapper_job_id: job[:job_id]).merge!(wrapped_job) + job.merge!(wrapper_job_class: job[:job_class], wrapper_job_id: job[:job_id]).merge!(wrapped_job) + end end report.add_tab(:job, job) @@ -38,9 +39,11 @@ if Que.respond_to?(:error_notifier=) Bugsnag.configuration.app_type ||= "que" + Bugsnag.configuration.runtime_versions["que"] = ::Que::Version Que.error_notifier = handler elsif Que.respond_to?(:error_handler=) Bugsnag.configuration.app_type ||= "que" + Bugsnag.configuration.runtime_versions["que"] = ::Que::Version Que.error_handler = handler end -end \ No newline at end of file +end diff --git a/lib/bugsnag/integrations/rack.rb b/lib/bugsnag/integrations/rack.rb index 3d37875da..e7569b2a0 100644 --- a/lib/bugsnag/integrations/rack.rb +++ b/lib/bugsnag/integrations/rack.rb @@ -29,7 +29,10 @@ def initialize(app) config.middleware.insert_before(Bugsnag::Middleware::Callbacks, Bugsnag::Middleware::WardenUser) if defined?(Warden) config.middleware.insert_before(Bugsnag::Middleware::Callbacks, Bugsnag::Middleware::ClearanceUser) if defined?(Clearance) + # Set environment data for payload config.app_type ||= "rack" + config.runtime_versions["rack"] = ::Rack.release if defined?(::Rack) + config.runtime_versions["sinatra"] = ::Sinatra::VERSION if defined?(::Sinatra) end end diff --git a/lib/bugsnag/integrations/railtie.rb b/lib/bugsnag/integrations/railtie.rb index 294e94975..476d39990 100644 --- a/lib/bugsnag/integrations/railtie.rb +++ b/lib/bugsnag/integrations/railtie.rb @@ -28,6 +28,7 @@ class Railtie < ::Rails::Railtie config.release_stage = ENV["BUGSNAG_RELEASE_STAGE"] || ::Rails.env.to_s config.project_root = ::Rails.root.to_s config.middleware.insert_before Bugsnag::Middleware::Callbacks, Bugsnag::Middleware::Rails3Request + config.runtime_versions["rails"] = ::Rails::VERSION::STRING end ActiveSupport.on_load(:action_controller) do diff --git a/lib/bugsnag/integrations/rake.rb b/lib/bugsnag/integrations/rake.rb index a1cfbd397..dcd426451 100644 --- a/lib/bugsnag/integrations/rake.rb +++ b/lib/bugsnag/integrations/rake.rb @@ -2,36 +2,68 @@ Rake::TaskManager.record_task_metadata = true -class Rake::Task - - FRAMEWORK_ATTRIBUTES = { - :framework => "Rake" - } - - ## - # Executes the rake task with Bugsnag setup with contextual data. - def execute_with_bugsnag(args=nil) - Bugsnag.configuration.app_type ||= "rake" - old_task = Bugsnag.configuration.request_data[:bugsnag_running_task] - Bugsnag.configuration.set_request_data :bugsnag_running_task, self - - execute_without_bugsnag(args) - - rescue Exception => ex - Bugsnag.notify(ex, true) do |report| - report.severity = "error" - report.severity_reason = { - :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE, - :attributes => FRAMEWORK_ATTRIBUTES +if Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.0') + module Bugsnag + module RakeTask + FRAMEWORK_ATTRIBUTES = { + framework: 'Rake' } + + # Executes the rake task with Bugsnag setup with contextual data. + def execute(args = nil) + Bugsnag.configuration.app_type ||= "rake" + old_task = Bugsnag.configuration.request_data[:bugsnag_running_task] + Bugsnag.configuration.set_request_data :bugsnag_running_task, self + Bugsnag.configuration.runtime_versions["rake"] = ::Rake::VERSION + + super + rescue Exception => ex + Bugsnag.notify(ex, true) do |report| + report.severity = "error" + report.severity_reason = { + type: Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE, + attributes: FRAMEWORK_ATTRIBUTES + } + end + raise + ensure + Bugsnag.configuration.set_request_data :bugsnag_running_task, old_task + end end - raise - ensure - Bugsnag.configuration.set_request_data :bugsnag_running_task, old_task end - alias_method :execute_without_bugsnag, :execute - alias_method :execute, :execute_with_bugsnag + Rake::Task.send(:prepend, Bugsnag::RakeTask) +else + class Rake::Task + FRAMEWORK_ATTRIBUTES = { + framework: 'Rake' + } + + ## + # Executes the rake task with Bugsnag setup with contextual data. + def execute_with_bugsnag(args=nil) + Bugsnag.configuration.app_type ||= "rake" + old_task = Bugsnag.configuration.request_data[:bugsnag_running_task] + Bugsnag.configuration.set_request_data :bugsnag_running_task, self + Bugsnag.configuration.runtime_versions["rake"] = ::Rake::VERSION + + execute_without_bugsnag(args) + rescue Exception => ex + Bugsnag.notify(ex, true) do |report| + report.severity = "error" + report.severity_reason = { + type: Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE, + attributes: FRAMEWORK_ATTRIBUTES + } + end + raise + ensure + Bugsnag.configuration.set_request_data :bugsnag_running_task, old_task + end + + alias_method :execute_without_bugsnag, :execute + alias_method :execute, :execute_with_bugsnag + end end Bugsnag.configuration.internal_middleware.use(Bugsnag::Middleware::Rake) diff --git a/lib/bugsnag/integrations/resque.rb b/lib/bugsnag/integrations/resque.rb index 5382049da..913dd43c4 100644 --- a/lib/bugsnag/integrations/resque.rb +++ b/lib/bugsnag/integrations/resque.rb @@ -62,10 +62,12 @@ def save Resque.after_fork do Bugsnag.configuration.app_type = "resque" Bugsnag.configuration.default_delivery_method = :synchronous + Bugsnag.configuration.runtime_versions["resque"] = ::Resque::VERSION end else Resque.before_first_fork do Bugsnag.configuration.app_type = "resque" Bugsnag.configuration.default_delivery_method = :synchronous + Bugsnag.configuration.runtime_versions["resque"] = ::Resque::VERSION end end diff --git a/lib/bugsnag/integrations/sidekiq.rb b/lib/bugsnag/integrations/sidekiq.rb index 27f9ae994..f392bcc1e 100644 --- a/lib/bugsnag/integrations/sidekiq.rb +++ b/lib/bugsnag/integrations/sidekiq.rb @@ -15,6 +15,7 @@ def initialize Bugsnag.configuration.internal_middleware.use(Bugsnag::Middleware::Sidekiq) Bugsnag.configuration.app_type = "sidekiq" Bugsnag.configuration.default_delivery_method = :synchronous + Bugsnag.configuration.runtime_versions["sidekiq"] = ::Sidekiq::VERSION end def call(worker, msg, queue) diff --git a/lib/bugsnag/middleware/rails3_request.rb b/lib/bugsnag/middleware/rails3_request.rb index 30b45004a..16cc588d3 100644 --- a/lib/bugsnag/middleware/rails3_request.rb +++ b/lib/bugsnag/middleware/rails3_request.rb @@ -32,13 +32,6 @@ def call(report) }) report.user["id"] = client_ip - - # Add the rails version - if report.configuration.send_environment - report.add_tab(:environment, { - :railsVersion => Rails::VERSION::STRING - }) - end end @bugsnag.call(report) diff --git a/lib/bugsnag/report.rb b/lib/bugsnag/report.rb index c48666340..c3b1e54aa 100644 --- a/lib/bugsnag/report.rb +++ b/lib/bugsnag/report.rb @@ -29,6 +29,7 @@ class Report attr_accessor :delivery_method attr_accessor :exceptions attr_accessor :hostname + attr_accessor :runtime_versions attr_accessor :grouping_hash attr_accessor :meta_data attr_accessor :raw_exceptions @@ -55,6 +56,7 @@ def initialize(exception, passed_configuration, auto_notify=false) self.breadcrumbs = [] self.delivery_method = configuration.delivery_method self.hostname = configuration.hostname + self.runtime_versions = configuration.runtime_versions.dup self.meta_data = {} self.release_stage = configuration.release_stage self.severity = auto_notify ? "error" : "warning" @@ -97,7 +99,8 @@ def as_json }, context: context, device: { - hostname: hostname + hostname: hostname, + runtimeVersions: runtime_versions }, exceptions: exceptions, groupingHash: grouping_hash, diff --git a/lib/bugsnag/session_tracker.rb b/lib/bugsnag/session_tracker.rb index 30a7a54a9..d57e227c7 100644 --- a/lib/bugsnag/session_tracker.rb +++ b/lib/bugsnag/session_tracker.rb @@ -118,7 +118,8 @@ def deliver(session_payload) :version => Bugsnag::Report::NOTIFIER_VERSION }, :device => { - :hostname => Bugsnag.configuration.hostname + :hostname => Bugsnag.configuration.hostname, + :runtimeVersions => Bugsnag.configuration.runtime_versions }, :app => { :version => Bugsnag.configuration.app_version, diff --git a/spec/breadcrumbs/validator_spec.rb b/spec/breadcrumbs/validator_spec.rb index 34de2c20d..07e7b2c5b 100644 --- a/spec/breadcrumbs/validator_spec.rb +++ b/spec/breadcrumbs/validator_spec.rb @@ -64,6 +64,7 @@ meta_data = { :string => "This is a string", + :symbol => :this_is_a_symbol, :integer => 12345, :float => 12345.6789, :false => false, diff --git a/spec/configuration_spec.rb b/spec/configuration_spec.rb index aa8cbb08d..ae3690db2 100644 --- a/spec/configuration_spec.rb +++ b/spec/configuration_spec.rb @@ -161,6 +161,37 @@ end end + describe "#hostname" do + it "has a default value" do + expect(subject.hostname.length).to be > 0 + end + + it "has a value set by Socket" do + expect(subject.hostname).to eq(Socket.gethostname) + end + + it "has a value set by DYNO environment variable" do + ENV['DYNO'] = 'localhost' + expect(subject.hostname).to eq("localhost") + end + + after do + ENV['DYNO'] = nil + end + end + + describe "#runtime_versions" do + it "has a default value" do + expect(subject.runtime_versions.length).to be > 0 + expect(subject.runtime_versions["ruby"]).to eq(RUBY_VERSION) + end + + it "has a settable value" do + subject.runtime_versions["ruby"] = '9.9.9' + expect(subject.runtime_versions["ruby"]).to eq('9.9.9') + end + end + describe "logger" do class TestLogger attr_accessor :logs diff --git a/spec/fixtures/tasks/Rakefile b/spec/fixtures/tasks/Rakefile index 385b11049..236b9e290 100644 --- a/spec/fixtures/tasks/Rakefile +++ b/spec/fixtures/tasks/Rakefile @@ -1,3 +1,15 @@ +if Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.0') + # Mixing Module#prepend with alias_method can sometimes lead to infinite + # mutual recursion, so this is to test that it doesn't happen. + mod = Module.new do + def execute(args = nil) + puts 'In module prepended to Rake::Task' + super + end + end + Rake::Task.send(:prepend, mod) +end + require "bugsnag/integrations/rake" namespace :test do diff --git a/spec/integrations/que_spec.rb b/spec/integrations/que_spec.rb index 85d691ae3..5708179b2 100644 --- a/spec/integrations/que_spec.rb +++ b/spec/integrations/que_spec.rb @@ -6,6 +6,10 @@ unless defined?(::Que) @mocked_que = true class ::Que + Version = '9.9.9' + class << self + attr_accessor :error_notifier + end end module Kernel alias_method :old_require, :require @@ -31,7 +35,6 @@ def require(path) expect(report).to receive(:add_tab).with(:job, { :error_count => 1, :job_class => 'ActiveJob::QueueAdapters::QueAdapter::JobWrapper', - :args => [{"queue_name" => "foo", "arguments" => "bar"}], :job_id => "ID", :wrapper_job_class => 'ActiveJob::QueueAdapters::QueAdapter::JobWrapper', :wrapper_job_id => "ID", @@ -51,6 +54,8 @@ def require(path) allow(Bugsnag).to receive(:configuration).and_return(config) expect(config).to receive(:app_type) expect(config).to receive(:app_type=).with('que') + runtime = {} + expect(config).to receive(:runtime_versions).and_return(runtime) allow(config).to receive(:clear_request_data) expect(Que).to receive(:error_notifier=) do |handler| handler.call(error, job) @@ -58,6 +63,26 @@ def require(path) #Kick off load './lib/bugsnag/integrations/que.rb' + + expect(runtime).to eq("que" => "9.9.9") + end + + context 'when the job is nil' do + it 'notifies Bugsnag' do + load './lib/bugsnag/integrations/que.rb' + error = RuntimeError.new('nil job') + report = Bugsnag::Report.new(error, Bugsnag::Configuration.new) + expect(Bugsnag).to receive(:notify).with(error, true).and_yield(report) + + Que.error_notifier.call(error, nil) + + expect(report.meta_data['custom'].fetch('job')).to eq(nil) + expect(report.severity).to eq('error') + expect(report.severity_reason).to eq({ + :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE, + :attributes => {:framework => 'Que'}, + }) + end end after do diff --git a/spec/integrations/rack_spec.rb b/spec/integrations/rack_spec.rb index 18c0ee67c..95cc9f013 100644 --- a/spec/integrations/rack_spec.rb +++ b/spec/integrations/rack_spec.rb @@ -18,6 +18,19 @@ app = lambda { |env| raise exception } rack_stack = Bugsnag::Rack.new(app) + before do + unless defined?(::Rack) + @mocked_rack = true + class Rack + def self.release + '9.9.9' + end + class Request + end + end + end + end + it "re-raises the exception" do expect { rack_stack.call(rack_env) }.to raise_error(BugsnagTestException) end @@ -47,6 +60,13 @@ } end + it "applies the rack version" do + app = lambda { |env| raise BugsnagTestException.new("It crashed") } + rack_stack = Bugsnag::Rack.new(app) + + expect(Bugsnag.configuration.runtime_versions["rack"]).to eq '9.9.9' + end + it "does not deliver an exception if auto_notify is disabled" do Bugsnag.configure do |config| config.auto_notify = false @@ -63,6 +83,9 @@ unless defined?(::Rack) @mocked_rack = true class Rack + def self.release + '9.9.9' + end class Request end end @@ -196,7 +219,6 @@ class Request end it "don't mess with middlewares list on each req" do - stub_const('Rack', nil) app = lambda { |env| ['200', {}, ['']] } Bugsnag::Rack.new(app) diff --git a/spec/integrations/rake_spec.rb b/spec/integrations/rake_spec.rb index e97dd45fb..f9118cf7e 100644 --- a/spec/integrations/rake_spec.rb +++ b/spec/integrations/rake_spec.rb @@ -55,18 +55,17 @@ let(:request) { JSON.parse(queue.pop) } it 'should run the rake middleware when rake tasks crash' do - #Skips this test in ruby 1.9.3 with travis - unless ENV['TRAVIS'] && RUBY_VERSION == "1.9.3" - ENV['BUGSNAG_TEST_SERVER_PORT'] = server.config[:Port].to_s - task_fixtures_path = File.join(File.dirname(__FILE__), '../fixtures', 'tasks') - Dir.chdir(task_fixtures_path) do - system("bundle exec rake test:crash > /dev/null 2>&1") - end - - result = request() - expect(result["events"][0]["metaData"]["rake_task"]).not_to be_nil - expect(result["events"][0]["metaData"]["rake_task"]["name"]).to eq("test:crash") + ENV['BUGSNAG_TEST_SERVER_PORT'] = server.config[:Port].to_s + task_fixtures_path = File.join(File.dirname(__FILE__), '../fixtures', 'tasks') + Dir.chdir(task_fixtures_path) do + system("bundle exec rake test:crash > /dev/null 2>&1") end + + result = request() + expect(result["events"][0]["metaData"]["rake_task"]).not_to be_nil + expect(result["events"][0]["metaData"]["rake_task"]["name"]).to eq("test:crash") + expect(result["events"][0]["app"]["type"]).to eq("rake") + expect(result["events"][0]["device"]["runtimeVersions"]["rake"]).to match(/\A\d+\.\d+\.\d+/) end end -end \ No newline at end of file +end diff --git a/spec/integrations/resque_spec.rb b/spec/integrations/resque_spec.rb index 2e773abf8..ac04ad9e5 100644 --- a/spec/integrations/resque_spec.rb +++ b/spec/integrations/resque_spec.rb @@ -6,6 +6,7 @@ unless defined?(::Resque) @mocked_resque = true class ::Resque + VERSION = '9.9.9' class Worker end class Failure @@ -44,10 +45,14 @@ def require(path) expect(fork_check).to receive(:fork_per_job?).and_return(true) expect(::Resque).to receive(:after_fork).and_yield expect(Bugsnag.configuration).to receive(:app_type=).with("resque") + runtime = {} + expect(Bugsnag.configuration).to receive(:runtime_versions).and_return(runtime) expect(Bugsnag.configuration).to receive(:default_delivery_method=).with(:synchronous) #Kick off require './lib/bugsnag/integrations/resque' + + expect(runtime).to eq("resque" => "9.9.9") end it "can configure" do diff --git a/spec/integrations/sidekiq_spec.rb b/spec/integrations/sidekiq_spec.rb index 5e7076841..3ea757098 100644 --- a/spec/integrations/sidekiq_spec.rb +++ b/spec/integrations/sidekiq_spec.rb @@ -29,6 +29,8 @@ def perform(value) expect(event["metaData"]["sidekiq"]["msg"]["args"]).to eq([-0]) expect(event["metaData"]["sidekiq"]["msg"]["queue"]).to eq("default") expect(event["severity"]).to eq("error") + expect(event["app"]["type"]).to eq("sidekiq") + expect(event["device"]["runtimeVersions"]["sidekiq"]).to eq('2.0.0') } end end diff --git a/spec/report_spec.rb b/spec/report_spec.rb index 96c62d0ae..3d4b8530f 100644 --- a/spec/report_spec.rb +++ b/spec/report_spec.rb @@ -1252,7 +1252,7 @@ def gloops if defined?(JRUBY_VERSION) - it "should work with java.lang.Throwables" do + it "works with java.lang.Throwables" do begin JRubyException.raise! rescue @@ -1267,4 +1267,16 @@ def gloops } end end + + it 'includes device data when notify is called' do + Bugsnag.configuration.hostname = 'test-host' + Bugsnag.configuration.runtime_versions["ruby"] = '9.9.9' + Bugsnag.notify(BugsnagTestException.new("It crashed")) + + expect(Bugsnag).to have_sent_notification{ |payload, headers| + event = payload["events"][0] + expect(event["device"]["hostname"]).to eq('test-host') + expect(event["device"]["runtimeVersions"]["ruby"]).to eq('9.9.9') + } + end end diff --git a/spec/session_tracker_spec.rb b/spec/session_tracker_spec.rb index 50a249b0f..e208494c8 100644 --- a/spec/session_tracker_spec.rb +++ b/spec/session_tracker_spec.rb @@ -129,6 +129,7 @@ device = payload["device"] expect(device.include?("hostname")).to be true expect(device["hostname"]).to eq(Bugsnag.configuration.hostname) + expect(device["runtimeVersions"]["ruby"]).to eq(Bugsnag.configuration.runtime_versions["ruby"]) end it 'uses middleware to attach session to notification' do