Merge pull request #12992 from mkllnk/errors

Add simpler Alert.raise interface to notify Bugsnag
This commit is contained in:
Maikel
2024-12-03 13:29:11 +11:00
committed by GitHub
31 changed files with 453 additions and 92 deletions

View File

@@ -77,7 +77,7 @@ module Admin
def log_and_notify_exception(exception)
Rails.logger.error exception.inspect
Bugsnag.notify(exception)
Alert.raise(exception)
end
def vine_params_empty?

View File

@@ -66,7 +66,7 @@ module Api
end
def error_during_processing(exception)
Bugsnag.notify(exception)
Alert.raise(exception)
render(json: { exception: exception.message },
status: :unprocessable_entity) && return

View File

@@ -52,7 +52,7 @@ module Api
end
def error_during_processing(exception)
Bugsnag.notify(exception)
Alert.raise(exception)
if Rails.env.development? || Rails.env.test?
render status: :unprocessable_entity,

View File

@@ -50,9 +50,7 @@ module OrderCompletion
end
def order_invalid!
Bugsnag.notify("Notice: invalid order loaded during checkout") do |payload|
payload.add_metadata :order, :order, @order
end
Alert.raise_with_record("Notice: invalid order loaded during checkout", @order)
flash[:error] = t('checkout.order_not_loaded')
redirect_to main_app.shop_path
@@ -92,9 +90,7 @@ module OrderCompletion
end
def notify_failure(error = RuntimeError.new(order_processing_error))
Bugsnag.notify(error) do |payload|
payload.add_metadata :order, @order
end
Alert.raise_with_record(error, @order)
flash[:error] = order_processing_error if flash.blank?
end

View File

@@ -20,9 +20,7 @@ module OrderStockCheck
def check_order_cycle_expiry
return unless current_order_cycle&.closed?
Bugsnag.notify("Notice: order cycle closed during checkout completion") do |payload|
payload.add_metadata :order, :order, current_order
end
Alert.raise_with_record("Notice: order cycle closed during checkout completion", current_order)
current_order.empty!
current_order.set_order_cycle! nil

View File

@@ -4,11 +4,6 @@ class ErrorsController < ApplicationController
layout "errors"
def not_found
Bugsnag.notify("404") do |event|
event.severity = "info"
event.add_metadata(:request, :env, request.env)
end
render status: :not_found, formats: :html
end

View File

@@ -60,7 +60,7 @@ module Spree
end
rescue StandardError => e
logger.error e.message
Bugsnag.notify(e)
Alert.raise(e)
flash[:error] = e.message
ensure
redirect_to request.referer

View File

@@ -213,7 +213,7 @@ module Spree
end
def notify_bugsnag(error, product, variant)
Bugsnag.notify(error) do |report|
Alert.raise(error) do |report|
report.add_metadata(:product,
{ product: product.attributes, variant: variant.attributes })
report.add_metadata(:product, :product_error, product.errors.first) unless product.valid?

View File

@@ -1,6 +1,5 @@
# frozen_string_literal: true
require 'open_food_network/error_logger'
require "spree/core/controller_helpers/auth"
require "spree/core/controller_helpers/common"
require "spree/core/controller_helpers/order"
@@ -37,7 +36,7 @@ class UserRegistrationsController < Devise::RegistrationsController
end
end
rescue StandardError => e
OpenFoodNetwork::ErrorLogger.notify(e)
Alert.raise(e)
render_error(message: I18n.t('unknown_error', scope: I18N_SCOPE))
end

View File

@@ -19,9 +19,7 @@ class BackorderJob < ApplicationJob
rescue StandardError => e
# Errors here shouldn't affect the checkout. So let's report them
# separately:
Bugsnag.notify(e) do |payload|
payload.add_metadata(:order, :order, order)
end
Alert.raise_with_record(e, order)
end
def perform(order)

View File

@@ -22,11 +22,7 @@ class ReportJob < ApplicationJob
broadcast_result(channel, format, blob) if channel
rescue StandardError => e
Bugsnag.notify(e) do |payload|
payload.add_metadata :report, {
report_class:, user:, params:, format:
}
end
Alert.raise(e, { report: { report_class:, user:, params:, format: } })
broadcast_error(channel)
end

View File

@@ -16,9 +16,7 @@ class StockSyncJob < ApplicationJob
rescue StandardError => e
# Errors here shouldn't affect the shopping. So let's report them
# separately:
Bugsnag.notify(e) do |payload|
payload.add_metadata(:order, :order, order)
end
Alert.raise_with_record(e, order)
end
def self.sync_linked_catalogs_now(order)
@@ -29,9 +27,7 @@ class StockSyncJob < ApplicationJob
rescue StandardError => e
# Errors here shouldn't affect the shopping. So let's report them
# separately:
Bugsnag.notify(e) do |payload|
payload.add_metadata(:order, :order, order)
end
Alert.raise_with_record(e, order)
end
def self.catalog_ids(order)

View File

@@ -55,9 +55,7 @@ class SubscriptionConfirmJob < ApplicationJob
if order.errors.any?
send_failed_payment_email(order)
else
Bugsnag.notify(e) do |payload|
payload.add_metadata :order, :order, order
end
Alert.raise_with_record(e, order)
send_failed_payment_email(order, e.message)
end
end
@@ -108,8 +106,6 @@ class SubscriptionConfirmJob < ApplicationJob
record_and_log_error(:failed_payment, order, error_message)
SubscriptionMailer.failed_payment_email(order).deliver_now
rescue StandardError => e
Bugsnag.notify(e) do |payload|
payload.add_metadata :subscription_data, { order:, error_message: }
end
Alert.raise(e, { subscription_data: { order:, error_message: } })
end
end

View File

@@ -28,7 +28,7 @@ module Calculator
# In theory it should never be called any more after this has been deployed.
# If the message below doesn't show up in Bugsnag, we can safely delete this method and all
# the related methods below it.
Bugsnag.notify("Calculator::DefaultTax was called with legacy tax calculations")
Alert.raise("Calculator::DefaultTax was called with legacy tax calculations")
calculator = OpenFoodNetwork::EnterpriseFeeCalculator.new(order.distributor,
order.order_cycle)

View File

@@ -481,7 +481,7 @@ class Enterprise < ApplicationRecord
image_variant_url_for(image.variant(name))
rescue StandardError => e
Bugsnag.notify "Enterprise ##{id} #{image.try(:name)} error: #{e.message}"
Alert.raise "Enterprise ##{id} #{image.try(:name)} error: #{e.message}"
Rails.logger.error(e.message)
nil

View File

@@ -34,7 +34,7 @@ module Spree
image_variant_url_for(variant(size))
rescue StandardError => e
Bugsnag.notify "Product ##{viewable_id} Image ##{id} error: #{e.message}"
Alert.raise "Product ##{viewable_id} Image ##{id} error: #{e.message}"
Rails.logger.error(e.message)
self.class.default_image_url(size)

View File

@@ -535,7 +535,7 @@ module Spree
# because an outdated shipping fee is not as bad as a lost payment.
# And the shipping fee is already up-to-date when this error occurs.
# https://github.com/openfoodfoundation/openfoodnetwork/issues/3924
Bugsnag.notify(e) do |report|
Alert.raise(e) do |report|
report.add_metadata(:order, attributes)
report.add_metadata(:shipment, shipment.attributes)
report.add_metadata(:shipment_in_db, Spree::Shipment.find_by(id: shipment.id).attributes)

View File

@@ -106,7 +106,7 @@ module Spree
calculator.compute(item)
else
# Tax refund should not be possible with the way our production server are configured
Bugsnag.notify(
Alert.raise(
"Notice: Tax refund should not be possible, please check the default zone and " \
"the tax rate zone configuration"
) do |payload|

View File

@@ -293,7 +293,7 @@ module Spree
end
def ensure_unit_value
Bugsnag.notify("Trying to set unit_value to NaN") if unit_value&.nan?
Alert.raise("Trying to set unit_value to NaN") if unit_value&.nan?
return unless (variant_unit == "items" && unit_value.nil?) || unit_value&.nan?
self.unit_value = 1.0

View File

@@ -15,7 +15,7 @@ class StripeAccount < ApplicationRecord
destroy && Stripe::OAuth.deauthorize(stripe_user_id:)
rescue Stripe::OAuth::OAuthError => e
Bugsnag.notify(
Alert.raise(
e,
stripe_account: stripe_user_id,
enterprise_id:

View File

@@ -48,8 +48,8 @@ class VariantOverride < ApplicationRecord
def move_stock!(quantity)
unless stock_overridden?
Bugsnag.notify RuntimeError.new "Attempting to move stock of a VariantOverride " \
"without a count_on_hand specified."
Alert.raise "Attempting to move stock of a VariantOverride " \
"without a count_on_hand specified."
return
end
@@ -73,8 +73,8 @@ class VariantOverride < ApplicationRecord
self.attributes = { on_demand: false, count_on_hand: default_stock }
save
else
Bugsnag.notify RuntimeError.new "Attempting to reset stock level for a variant " \
"with no default stock level."
Alert.raise "Attempting to reset stock level for a variant " \
"with no default stock level."
end
end
self

38
app/services/alert.rb Normal file
View File

@@ -0,0 +1,38 @@
# frozen_string_literal: true
# A handy wrapper around error reporting libraries like Bugsnag.
#
# Bugsnag's API is great for general purpose but overly complex for our use.
# It also changes over time and we often make mistakes using it. So this class
# aims at:
#
# * Abstracting from Bugsnag, open for other services.
# * Simpler interface to reduce user error.
# * Central place to update Bugsnag API usage when it changes.
#
class Alert
# Alert Bugsnag with additional metadata to appear in tabs.
#
# Alert.raise(
# "Invalid order during checkout",
# {
# order: { number: "ABC123", state: "awaiting_return" },
# env: { referer: "example.com" }
# }
# )
def self.raise(error, metadata = {}, &block)
Bugsnag.notify(error) do |payload|
metadata.each do |name, data|
payload.add_metadata(name, data)
end
block.call(payload)
end
end
def self.raise_with_record(error, record, &)
metadata = {
record.class.name => record&.attributes || { record_was_nil: true }
}
self.raise(error, metadata, &)
end
end

View File

@@ -24,9 +24,7 @@ class PlaceProxyOrder
send_placement_email
rescue StandardError => e
summarizer.record_and_log_error(:processing, order, e.message)
Bugsnag.notify(e) do |payload|
payload.add_metadata :order, :order, order
end
Alert.raise_with_record(e, order)
end
private
@@ -56,9 +54,7 @@ class PlaceProxyOrder
true
rescue StandardError => e
Bugsnag.notify(e) do |payload|
payload.add_metadata(:proxy_order, { subscription:, proxy_order: })
end
Alert.raise(e, { proxy_order: { subscription:, proxy_order: } })
false
end

View File

@@ -145,7 +145,7 @@ module Sets
end
def notify_bugsnag(error, product, variant, variant_attributes)
Bugsnag.notify(error) do |report|
Alert.raise(error) do |report|
report.add_metadata( :product_set,
{ product: product.attributes, variant_attributes:,
variant: variant.attributes } )

View File

@@ -6,5 +6,9 @@ Bugsnag.configure do |config|
config.logger = Logger.new(STDOUT)
config.logger.level = Logger::ERROR
end
config.notify_release_stages = %w(production staging)
# If you want to notify Bugsnag in dev or test then set the env var:
# spring stop
# BUGSNAG=true ./bin/rails console
config.notify_release_stages = %w(production staging) unless ENV["BUGSNAG"]
end

View File

@@ -1,15 +0,0 @@
# frozen_string_literal: true
# Our error logging API currently wraps Bugsnag.
# It makes us more flexible if we wanted to replace Bugsnag or change logging
# behaviour.
module OpenFoodNetwork
module ErrorLogger
# Tries to escalate the error to a developer.
# If Bugsnag is configured, it will notify it. It would be nice to implement
# some kind of fallback.
def self.notify(error)
Bugsnag.notify(error)
end
end
end

View File

@@ -27,7 +27,7 @@ RSpec.describe UserRegistrationsController, type: :controller do
it "returns error when emailing fails" do
allow(Spree::UserMailer).to receive(:confirmation_instructions).and_raise("Some error")
expect(OpenFoodNetwork::ErrorLogger).to receive(:notify)
expect(Alert).to receive(:raise)
post :create, xhr: true, params: { spree_user: user_params, use_route: :spree }

View File

@@ -0,0 +1,291 @@
---
http_interactions:
- request:
method: post
uri: https://notify.bugsnag.com/
body:
encoding: UTF-8
string: '{"apiKey":"<HIDDEN-BUGSNAG_API_KEY>","notifier":{"name":"Ruby Bugsnag
Notifier","version":"6.26.4","url":"https://www.bugsnag.com"},"payloadVersion":"4.0","events":[{"app":{"version":null,"releaseStage":"test","type":"rails"},"breadcrumbs":[{"name":"ActiveRecord
SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.385Z"},{"name":"ActiveRecord
SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.387Z"},{"name":"ActiveRecord
SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.389Z"},{"name":"ActiveRecord
SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.390Z"},{"name":"ActiveRecord
SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.437Z"},{"name":"ActiveRecord
SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.492Z"},{"name":"ActiveRecord
SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.527Z"},{"name":"ActiveRecord
SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.546Z"},{"name":"ActiveRecord
SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.585Z"},{"name":"ActiveRecord
SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.593Z"},{"name":"ActiveRecord
SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.796Z"},{"name":"ActiveRecord
SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.814Z"},{"name":"ActiveRecord
SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.886Z"},{"name":"ActiveRecord
SQL query","type":"process","metaData":{"name":"Spree::Country Exists?","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","binds":"{\"name\":\"?\",\"LIMIT\":\"?\"}","connection_id":42100},"timestamp":"2024-11-20T04:51:11.888Z"},{"name":"ActiveRecord
SQL query","type":"process","metaData":{"name":"Spree::Country Exists?","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","binds":"{\"name\":\"?\",\"LIMIT\":\"?\"}","connection_id":42100},"timestamp":"2024-11-20T04:51:11.890Z"},{"name":"ActiveRecord
SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.890Z"},{"name":"ActiveRecord
SQL query","type":"process","metaData":{"name":"Spree::Country Load","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","binds":"{\"name\":\"?\",\"LIMIT\":\"?\"}","connection_id":42100},"timestamp":"2024-11-20T04:51:11.891Z"},{"name":"ActiveRecord
SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.894Z"},{"name":"ActiveRecord
SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.901Z"},{"name":"ActiveRecord
SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.904Z"},{"name":"ActiveRecord
SQL query","type":"process","metaData":{"name":"Spree::Preference Load","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","binds":"{\"key\":\"?\",\"LIMIT\":\"?\"}","connection_id":42100},"timestamp":"2024-11-20T04:51:11.905Z"},{"name":"ActiveRecord
SQL query","type":"process","metaData":{"name":"Spree::Country Load","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","binds":"{\"iso\":\"?\",\"LIMIT\":\"?\"}","connection_id":42100},"timestamp":"2024-11-20T04:51:11.933Z"},{"name":"Read
cache","type":"process","metaData":{"hit":true,"event_name":"cache_read.active_support","event_id":"4779ed9ba580ae3d3290"},"timestamp":"2024-11-20T04:51:11.933Z"},{"name":"Read
cache","type":"process","metaData":{"hit":true,"event_name":"cache_read.active_support","event_id":"4779ed9ba580ae3d3290"},"timestamp":"2024-11-20T04:51:11.933Z"},{"name":"ActiveRecord
SQL query","type":"process","metaData":{"name":"TRANSACTION","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.970Z"}],"device":{"hostname":"blackbox","runtimeVersions":{"ruby":"3.1.4","rails":"7.0.8","rack":"2.2.9"},"time":"2024-11-20T04:51:11.978Z"},"exceptions":[{"errorClass":"RuntimeError","message":"Testing
Bugsnag from RSpec","stacktrace":[{"lineNumber":129,"file":"gems/bugsnag-6.26.4/lib/bugsnag/report.rb","method":"initialize","code":{"126":" self.configuration
= passed_configuration","127":"","128":" @original_error = exception","129":" self.raw_exceptions
= generate_raw_exceptions(exception)","130":" self.exceptions = generate_exception_list","131":" @errors
= generate_error_list","132":""}},{"lineNumber":90,"file":"gems/bugsnag-6.26.4/lib/bugsnag.rb","method":"new","code":{"87":"","88":" exception
= NIL_EXCEPTION_DESCRIPTION if exception.nil?","89":"","90":" report
= Report.new(exception, configuration, auto_notify)","91":"","92":" #
If this is an auto_notify we yield the block before the any middleware is
run","93":" begin"}},{"lineNumber":90,"file":"gems/bugsnag-6.26.4/lib/bugsnag.rb","method":"notify","code":{"87":"","88":" exception
= NIL_EXCEPTION_DESCRIPTION if exception.nil?","89":"","90":" report
= Report.new(exception, configuration, auto_notify)","91":"","92":" #
If this is an auto_notify we yield the block before the any middleware is
run","93":" begin"}},{"lineNumber":29,"inProject":true,"file":"app/services/alert.rb","method":"raise","code":{"26":" # }","27":" # )","28":" def
self.raise(error, metadata = {}, &block)","29":" Bugsnag.notify(error)
do |payload|","30":" metadata.each do |name, data|","31":" payload.add_metadata(name,
data)","32":" end"}},{"lineNumber":34,"inProject":true,"file":"spec/services/alert_spec.rb","method":"block
(2 levels) in <main>","code":{"31":" config.delivery_method = :synchronous","32":" end","33":"","34":" Alert.raise(","35":" \"Testing
Bugsnag from RSpec\",","36":" { RSpec: { file: __FILE__ }, env: { BUGSNAG:
ENV[\"BUGSNAG\"] } }","37":" )"}},{"lineNumber":263,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"instance_exec","code":{"260":" begin","261":" run_before_example","262":" RSpec.current_scope
= :example","263":" @example_group_instance.instance_exec(self,
&@example_block)","264":"","265":" if pending?","266":" Pending.mark_fixed!
self"}},{"lineNumber":263,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"block
in run","code":{"260":" begin","261":" run_before_example","262":" RSpec.current_scope
= :example","263":" @example_group_instance.instance_exec(self,
&@example_block)","264":"","265":" if pending?","266":" Pending.mark_fixed!
self"}},{"lineNumber":511,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"block
in with_around_and_singleton_context_hooks","code":{"508":" def with_around_and_singleton_context_hooks","509":" singleton_context_hooks_host
= example_group_instance.singleton_class","510":" singleton_context_hooks_host.run_before_context_hooks(example_group_instance)","511":" with_around_example_hooks
{ yield }","512":" ensure","513":" singleton_context_hooks_host.run_after_context_hooks(example_group_instance)","514":" end"}},{"lineNumber":468,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"block
in with_around_example_hooks","code":{"465":"","466":" def with_around_example_hooks","467":" RSpec.current_scope
= :before_example_hook","468":" hooks.run(:around, :example, self)
{ yield }","469":" rescue Support::AllExceptionsExceptOnesWeMustNotRescue
=> e","470":" set_exception(e)","471":" end"}},{"lineNumber":486,"file":"gems/rspec-core-3.13.0/lib/rspec/core/hooks.rb","method":"block
in run","code":{"483":" case position","484":" when
:before then run_example_hooks_for(example_or_group, :before, :reverse_each)","485":" when
:after then run_example_hooks_for(example_or_group, :after, :each)","486":" when
:around then run_around_example_hooks_for(example_or_group) { yield }","487":" end","488":" end","489":" end"}},{"lineNumber":626,"file":"gems/rspec-core-3.13.0/lib/rspec/core/hooks.rb","method":"block
in run_around_example_hooks_for","code":{"623":"","624":" return
yield if hooks.empty? # exit early to avoid the extra allocation cost of `Example::Procsy`","625":"","626":" initial_procsy
= Example::Procsy.new(example) { yield }","627":" hooks.inject(initial_procsy)
do |procsy, around_hook|","628":" procsy.wrap { around_hook.execute_with(example,
procsy) }","629":" end.call"}},{"lineNumber":352,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"call","code":{"349":" #
Calls the proc and notes that the example has been executed.","350":" def
call(*args, &block)","351":" @executed = true","352":" @proc.call(*args,
&block)","353":" end","354":" alias run call","355":""}},{"lineNumber":75,"file":"gems/rspec-rails-6.1.2/lib/rspec/rails/adapters.rb","method":"block
(2 levels) in <module:MinitestLifecycleAdapter>","code":{"72":"","73":" group.around
do |example|","74":" before_setup","75":" example.run","76":" after_teardown","77":" end","78":" end"}},{"lineNumber":457,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"instance_exec","code":{"454":"","455":" #
@private","456":" def instance_exec(*args, &block)","457":" @example_group_instance.instance_exec(*args,
&block)","458":" end","459":"","460":" private"}},{"lineNumber":457,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"instance_exec","code":{"454":"","455":" #
@private","456":" def instance_exec(*args, &block)","457":" @example_group_instance.instance_exec(*args,
&block)","458":" end","459":"","460":" private"}},{"lineNumber":390,"file":"gems/rspec-core-3.13.0/lib/rspec/core/hooks.rb","method":"execute_with","code":{"387":" #
@private","388":" class AroundHook < Hook","389":" def execute_with(example,
procsy)","390":" example.instance_exec(procsy, &block)","391":" return
if procsy.executed?","392":" Pending.mark_skipped!(example,","393":" \"#{hook_description}
did not execute the example\")"}},{"lineNumber":628,"file":"gems/rspec-core-3.13.0/lib/rspec/core/hooks.rb","method":"block
(2 levels) in run_around_example_hooks_for","code":{"625":"","626":" initial_procsy
= Example::Procsy.new(example) { yield }","627":" hooks.inject(initial_procsy)
do |procsy, around_hook|","628":" procsy.wrap { around_hook.execute_with(example,
procsy) }","629":" end.call","630":" end","631":""}},{"lineNumber":352,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"call","code":{"349":" #
Calls the proc and notes that the example has been executed.","350":" def
call(*args, &block)","351":" @executed = true","352":" @proc.call(*args,
&block)","353":" end","354":" alias run call","355":""}},{"lineNumber":153,"inProject":true,"file":"spec/base_spec_helper.rb","method":"block
(3 levels) in <main>","code":{"150":"","151":" # Reset locale for all specs.","152":" config.around(:each)
do |example|","153":" I18n.with_locale(:en_AU) { example.run }","154":" end","155":"","156":" #
Reset all feature toggles to prevent leaking."}},{"lineNumber":351,"file":"gems/i18n-1.14.5/lib/i18n.rb","method":"with_locale","code":{"348":" current_locale
= self.locale","349":" self.locale = tmp_locale","350":" begin","351":" yield","352":" ensure","353":" self.locale
= current_locale","354":" end"}},{"lineNumber":153,"inProject":true,"file":"spec/base_spec_helper.rb","method":"block
(2 levels) in <main>","code":{"150":"","151":" # Reset locale for all specs.","152":" config.around(:each)
do |example|","153":" I18n.with_locale(:en_AU) { example.run }","154":" end","155":"","156":" #
Reset all feature toggles to prevent leaking."}},{"lineNumber":457,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"instance_exec","code":{"454":"","455":" #
@private","456":" def instance_exec(*args, &block)","457":" @example_group_instance.instance_exec(*args,
&block)","458":" end","459":"","460":" private"}},{"lineNumber":457,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"instance_exec","code":{"454":"","455":" #
@private","456":" def instance_exec(*args, &block)","457":" @example_group_instance.instance_exec(*args,
&block)","458":" end","459":"","460":" private"}},{"lineNumber":390,"file":"gems/rspec-core-3.13.0/lib/rspec/core/hooks.rb","method":"execute_with","code":{"387":" #
@private","388":" class AroundHook < Hook","389":" def execute_with(example,
procsy)","390":" example.instance_exec(procsy, &block)","391":" return
if procsy.executed?","392":" Pending.mark_skipped!(example,","393":" \"#{hook_description}
did not execute the example\")"}},{"lineNumber":628,"file":"gems/rspec-core-3.13.0/lib/rspec/core/hooks.rb","method":"block
(2 levels) in run_around_example_hooks_for","code":{"625":"","626":" initial_procsy
= Example::Procsy.new(example) { yield }","627":" hooks.inject(initial_procsy)
do |procsy, around_hook|","628":" procsy.wrap { around_hook.execute_with(example,
procsy) }","629":" end.call","630":" end","631":""}},{"lineNumber":352,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"call","code":{"349":" #
Calls the proc and notes that the example has been executed.","350":" def
call(*args, &block)","351":" @executed = true","352":" @proc.call(*args,
&block)","353":" end","354":" alias run call","355":""}},{"lineNumber":39,"file":"gems/webmock-3.23.1/lib/webmock/rspec.rb","method":"block
(2 levels) in <main>","code":{"36":" end","37":"","38":" config.around(:each)
do |example|","39":" example.run","40":" WebMock.reset!","41":" end","42":"}"}},{"lineNumber":457,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"instance_exec","code":{"454":"","455":" #
@private","456":" def instance_exec(*args, &block)","457":" @example_group_instance.instance_exec(*args,
&block)","458":" end","459":"","460":" private"}},{"lineNumber":457,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"instance_exec","code":{"454":"","455":" #
@private","456":" def instance_exec(*args, &block)","457":" @example_group_instance.instance_exec(*args,
&block)","458":" end","459":"","460":" private"}},{"lineNumber":390,"file":"gems/rspec-core-3.13.0/lib/rspec/core/hooks.rb","method":"execute_with","code":{"387":" #
@private","388":" class AroundHook < Hook","389":" def execute_with(example,
procsy)","390":" example.instance_exec(procsy, &block)","391":" return
if procsy.executed?","392":" Pending.mark_skipped!(example,","393":" \"#{hook_description}
did not execute the example\")"}},{"lineNumber":628,"file":"gems/rspec-core-3.13.0/lib/rspec/core/hooks.rb","method":"block
(2 levels) in run_around_example_hooks_for","code":{"625":"","626":" initial_procsy
= Example::Procsy.new(example) { yield }","627":" hooks.inject(initial_procsy)
do |procsy, around_hook|","628":" procsy.wrap { around_hook.execute_with(example,
procsy) }","629":" end.call","630":" end","631":""}},{"lineNumber":352,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"call","code":{"349":" #
Calls the proc and notes that the example has been executed.","350":" def
call(*args, &block)","351":" @executed = true","352":" @proc.call(*args,
&block)","353":" end","354":" alias run call","355":""}},{"lineNumber":124,"file":"gems/rspec-retry-0.6.2/lib/rspec/retry.rb","method":"block
in run","code":{"121":" example.metadata[:retry_exceptions] ||= []","122":"","123":" example.clear_exception","124":" ex.run","125":"","126":" self.attempts
+= 1","127":""}},{"lineNumber":110,"file":"gems/rspec-retry-0.6.2/lib/rspec/retry.rb","method":"loop","code":{"107":" def
run","108":" example = current_example","109":"","110":" loop do","111":" if
attempts > 0","112":" RSpec.configuration.formatters.each { |f| f.retry(example)
if f.respond_to? :retry }","113":" if verbose_retry?"}},{"lineNumber":110,"file":"gems/rspec-retry-0.6.2/lib/rspec/retry.rb","method":"run","code":{"107":" def
run","108":" example = current_example","109":"","110":" loop do","111":" if
attempts > 0","112":" RSpec.configuration.formatters.each { |f| f.retry(example)
if f.respond_to? :retry }","113":" if verbose_retry?"}},{"lineNumber":12,"file":"gems/rspec-retry-0.6.2/lib/rspec_ext/rspec_ext.rb","method":"run_with_retry","code":{"9":"","10":" class
Procsy","11":" def run_with_retry(opts = {})","12":" RSpec::Retry.new(self,
opts).run","13":" end","14":"","15":" def attempts"}},{"lineNumber":37,"file":"gems/rspec-retry-0.6.2/lib/rspec/retry.rb","method":"block
(2 levels) in setup","code":{"34":" config.add_setting :retry_callback,
:default => nil","35":"","36":" config.around(:each) do |ex|","37":" ex.run_with_retry","38":" end","39":" end","40":" end"}},{"lineNumber":457,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"instance_exec","code":{"454":"","455":" #
@private","456":" def instance_exec(*args, &block)","457":" @example_group_instance.instance_exec(*args,
&block)","458":" end","459":"","460":" private"}},{"lineNumber":457,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"instance_exec","code":{"454":"","455":" #
@private","456":" def instance_exec(*args, &block)","457":" @example_group_instance.instance_exec(*args,
&block)","458":" end","459":"","460":" private"}},{"lineNumber":390,"file":"gems/rspec-core-3.13.0/lib/rspec/core/hooks.rb","method":"execute_with","code":{"387":" #
@private","388":" class AroundHook < Hook","389":" def execute_with(example,
procsy)","390":" example.instance_exec(procsy, &block)","391":" return
if procsy.executed?","392":" Pending.mark_skipped!(example,","393":" \"#{hook_description}
did not execute the example\")"}},{"lineNumber":628,"file":"gems/rspec-core-3.13.0/lib/rspec/core/hooks.rb","method":"block
(2 levels) in run_around_example_hooks_for","code":{"625":"","626":" initial_procsy
= Example::Procsy.new(example) { yield }","627":" hooks.inject(initial_procsy)
do |procsy, around_hook|","628":" procsy.wrap { around_hook.execute_with(example,
procsy) }","629":" end.call","630":" end","631":""}},{"lineNumber":352,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"call","code":{"349":" #
Calls the proc and notes that the example has been executed.","350":" def
call(*args, &block)","351":" @executed = true","352":" @proc.call(*args,
&block)","353":" end","354":" alias run call","355":""}},{"lineNumber":629,"file":"gems/rspec-core-3.13.0/lib/rspec/core/hooks.rb","method":"run_around_example_hooks_for","code":{"626":" initial_procsy
= Example::Procsy.new(example) { yield }","627":" hooks.inject(initial_procsy)
do |procsy, around_hook|","628":" procsy.wrap { around_hook.execute_with(example,
procsy) }","629":" end.call","630":" end","631":"","632":" if
respond_to?(:singleton_class) && singleton_class.ancestors.include?(singleton_class)"}},{"lineNumber":486,"file":"gems/rspec-core-3.13.0/lib/rspec/core/hooks.rb","method":"run","code":{"483":" case
position","484":" when :before then run_example_hooks_for(example_or_group,
:before, :reverse_each)","485":" when :after then run_example_hooks_for(example_or_group,
:after, :each)","486":" when :around then run_around_example_hooks_for(example_or_group)
{ yield }","487":" end","488":" end","489":" end"}},{"lineNumber":468,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"with_around_example_hooks","code":{"465":"","466":" def
with_around_example_hooks","467":" RSpec.current_scope = :before_example_hook","468":" hooks.run(:around,
:example, self) { yield }","469":" rescue Support::AllExceptionsExceptOnesWeMustNotRescue
=> e","470":" set_exception(e)","471":" end"}},{"lineNumber":511,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"with_around_and_singleton_context_hooks","code":{"508":" def
with_around_and_singleton_context_hooks","509":" singleton_context_hooks_host
= example_group_instance.singleton_class","510":" singleton_context_hooks_host.run_before_context_hooks(example_group_instance)","511":" with_around_example_hooks
{ yield }","512":" ensure","513":" singleton_context_hooks_host.run_after_context_hooks(example_group_instance)","514":" end"}},{"lineNumber":259,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"run","code":{"256":" if
skipped?","257":" Pending.mark_pending! self, skip","258":" elsif
!RSpec.configuration.dry_run?","259":" with_around_and_singleton_context_hooks
do","260":" begin","261":" run_before_example","262":" RSpec.current_scope
= :example"}},{"lineNumber":646,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example_group.rb","method":"block
in run_examples","code":{"643":" next if RSpec.world.wants_to_quit","644":" instance
= new(example.inspect_output)","645":" set_ivars(instance, before_context_ivars)","646":" succeeded
= example.run(instance, reporter)","647":" if !succeeded && reporter.fail_fast_limit_met?","648":" RSpec.world.wants_to_quit
= true","649":" end"}},{"lineNumber":642,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example_group.rb","method":"map","code":{"639":"","640":" #
@private","641":" def self.run_examples(reporter)","642":" ordering_strategy.order(filtered_examples).map
do |example|","643":" next if RSpec.world.wants_to_quit","644":" instance
= new(example.inspect_output)","645":" set_ivars(instance, before_context_ivars)"}},{"lineNumber":642,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example_group.rb","method":"run_examples","code":{"639":"","640":" #
@private","641":" def self.run_examples(reporter)","642":" ordering_strategy.order(filtered_examples).map
do |example|","643":" next if RSpec.world.wants_to_quit","644":" instance
= new(example.inspect_output)","645":" set_ivars(instance, before_context_ivars)"}},{"lineNumber":607,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example_group.rb","method":"run","code":{"604":" begin","605":" RSpec.current_scope
= :before_context_hook","606":" run_before_context_hooks(new(''before(:context)
hook'')) if should_run_context_hooks","607":" result_for_this_group
= run_examples(reporter)","608":" results_for_descendants = ordering_strategy.order(children).map
{ |child| child.run(reporter) }.all?","609":" result_for_this_group
&& results_for_descendants","610":" rescue Pending::SkipDeclaredInExample
=> ex"}},{"lineNumber":121,"file":"gems/rspec-core-3.13.0/lib/rspec/core/runner.rb","method":"block
(3 levels) in run_specs","code":{"118":" return @configuration.failure_exit_code","119":" end","120":"","121":" example_groups.map
{ |g| g.run(reporter) }.all?","122":" end","123":" end","124":""}},{"lineNumber":121,"file":"gems/rspec-core-3.13.0/lib/rspec/core/runner.rb","method":"map","code":{"118":" return
@configuration.failure_exit_code","119":" end","120":"","121":" example_groups.map
{ |g| g.run(reporter) }.all?","122":" end","123":" end","124":""}},{"lineNumber":121,"file":"gems/rspec-core-3.13.0/lib/rspec/core/runner.rb","method":"block
(2 levels) in run_specs","code":{"118":" return @configuration.failure_exit_code","119":" end","120":"","121":" example_groups.map
{ |g| g.run(reporter) }.all?","122":" end","123":" end","124":""}},{"lineNumber":2091,"file":"gems/rspec-core-3.13.0/lib/rspec/core/configuration.rb","method":"with_suite_hooks","code":{"2088":" begin","2089":" RSpec.current_scope
= :before_suite_hook","2090":" run_suite_hooks(\"a `before(:suite)`
hook\", @before_suite_hooks)","2091":" yield","2092":" ensure","2093":" RSpec.current_scope
= :after_suite_hook","2094":" run_suite_hooks(\"an `after(:suite)`
hook\", @after_suite_hooks)"}},{"lineNumber":116,"file":"gems/rspec-core-3.13.0/lib/rspec/core/runner.rb","method":"block
in run_specs","code":{"113":" def run_specs(example_groups)","114":" examples_count
= @world.example_count(example_groups)","115":" examples_passed = @configuration.reporter.report(examples_count)
do |reporter|","116":" @configuration.with_suite_hooks do","117":" if
examples_count == 0 && @configuration.fail_if_no_examples","118":" return
@configuration.failure_exit_code","119":" end"}},{"lineNumber":74,"file":"gems/rspec-core-3.13.0/lib/rspec/core/reporter.rb","method":"report","code":{"71":" def
report(expected_example_count)","72":" start(expected_example_count)","73":" begin","74":" yield
self","75":" ensure","76":" finish","77":" end"}},{"lineNumber":115,"file":"gems/rspec-core-3.13.0/lib/rspec/core/runner.rb","method":"run_specs","code":{"112":" # failed.","113":" def
run_specs(example_groups)","114":" examples_count = @world.example_count(example_groups)","115":" examples_passed
= @configuration.reporter.report(examples_count) do |reporter|","116":" @configuration.with_suite_hooks
do","117":" if examples_count == 0 && @configuration.fail_if_no_examples","118":" return
@configuration.failure_exit_code"}},{"lineNumber":89,"file":"gems/rspec-core-3.13.0/lib/rspec/core/runner.rb","method":"run","code":{"86":" setup(err,
out)","87":" return @configuration.reporter.exit_early(exit_code) if
RSpec.world.wants_to_quit","88":"","89":" run_specs(@world.ordered_example_groups).tap
do","90":" persist_example_statuses","91":" end","92":" end"}},{"lineNumber":71,"file":"gems/rspec-core-3.13.0/lib/rspec/core/runner.rb","method":"run","code":{"68":" if
options.options[:runner]","69":" options.options[:runner].call(options,
err, out)","70":" else","71":" new(options).run(err, out)","72":" end","73":" end","74":""}},{"lineNumber":45,"file":"gems/rspec-core-3.13.0/lib/rspec/core/runner.rb","method":"invoke","code":{"42":" #
code.","43":" def self.invoke","44":" disable_autorun!","45":" status
= run(ARGV, $stderr, $stdout).to_i","46":" exit(status) if status !=
0","47":" end","48":""}},{"lineNumber":4,"file":"gems/rspec-core-3.13.0/exe/rspec","method":"<main>","code":{"1":"#!/usr/bin/env
ruby","2":"","3":"require ''rspec/core''","4":"RSpec::Core::Runner.invoke"}},{"lineNumber":18,"file":"gems/spring-commands-rspec-1.0.4/lib/spring/commands/rspec.rb","method":"load","code":{"15":"","16":" def
call","17":" ::RSpec.configuration.start_time = Time.now if defined?(::RSpec.configuration.start_time)","18":" load
Gem.bin_path(gem_name, exec_name)","19":" end","20":" end","21":""}},{"lineNumber":18,"file":"gems/spring-commands-rspec-1.0.4/lib/spring/commands/rspec.rb","method":"call","code":{"15":"","16":" def
call","17":" ::RSpec.configuration.start_time = Time.now if defined?(::RSpec.configuration.start_time)","18":" load
Gem.bin_path(gem_name, exec_name)","19":" end","20":" end","21":""}},{"lineNumber":38,"file":"gems/spring-4.2.1/lib/spring/command_wrapper.rb","method":"call","code":{"35":"","36":" def
call","37":" if command.respond_to?(:call)","38":" command.call","39":" else","40":" load
exec","41":" end"}},{"lineNumber":226,"file":"gems/spring-4.2.1/lib/spring/application.rb","method":"block
in serve","code":{"223":" invoke_after_fork_callbacks","224":" shush_backtraces","225":"","226":" command.call","227":" }","228":"","229":" disconnect_database"}},{"lineNumber":190,"file":"gems/spring-4.2.1/lib/spring/application.rb","method":"fork","code":{"187":" Rails.application.reloader.reload!","188":" end","189":"","190":" pid
= fork {","191":" # Make sure to close other clients otherwise their
graceful termination","192":" # will be impossible due to reference
from this fork.","193":" @clients.each_key { |c| c.close if c != client
}"}},{"lineNumber":190,"file":"gems/spring-4.2.1/lib/spring/application.rb","method":"serve","code":{"187":" Rails.application.reloader.reload!","188":" end","189":"","190":" pid
= fork {","191":" # Make sure to close other clients otherwise their
graceful termination","192":" # will be impossible due to reference
from this fork.","193":" @clients.each_key { |c| c.close if c != client
}"}},{"lineNumber":153,"file":"gems/spring-4.2.1/lib/spring/application.rb","method":"block
in run","code":{"150":" if terminating? || watcher_stale? || preload_failed?","151":" exit","152":" else","153":" serve
manager.recv_io(UNIXSocket)","154":" end","155":" end","156":" end"}},{"lineNumber":147,"file":"gems/spring-4.2.1/lib/spring/application.rb","method":"loop","code":{"144":" state
:running","145":" manager.puts","146":"","147":" loop do","148":" IO.select
[manager, @interrupt.first]","149":"","150":" if terminating? || watcher_stale?
|| preload_failed?"}},{"lineNumber":147,"file":"gems/spring-4.2.1/lib/spring/application.rb","method":"run","code":{"144":" state
:running","145":" manager.puts","146":"","147":" loop do","148":" IO.select
[manager, @interrupt.first]","149":"","150":" if terminating? || watcher_stale?
|| preload_failed?"}},{"lineNumber":25,"file":"gems/spring-4.2.1/lib/spring/application/boot.rb","method":"<top
(required)>","code":{"19":" app.spawn_env,","20":" ].compact","21":" \"spring
app | #{attributes.join(\" | \")}\"","22":"end","23":"","24":"app.eager_preload
if ENV.delete(\"SPRING_PRELOAD\") == \"1\"","25":"app.run"}},{"lineNumber":1,"file":"-e","method":"<main>","code":null}]}],"featureFlags":[],"metaData":{"RSpec":{"file":"/home/maikel/code/openfoodnetwork/spec/services/alert_spec.rb"},"env":{}},"severity":"warning","severityReason":{"type":"handledException"},"unhandled":false,"user":{}}]}'
headers:
Bugsnag-Api-Key:
- "<HIDDEN-BUGSNAG_API_KEY>"
Bugsnag-Payload-Version:
- '4.0'
Bugsnag-Sent-At:
- '2024-11-20T04:51:11.998Z'
Content-Type:
- application/json
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
- "*/*"
User-Agent:
- Ruby
response:
status:
code: 200
message: OK
headers:
Access-Control-Allow-Origin:
- "*"
Bugsnag-Event-Id:
- 673d6ac0010cfda42d8e0000
Date:
- Wed, 20 Nov 2024 04:51:12 GMT
Content-Length:
- '2'
Content-Type:
- text/plain; charset=utf-8
Via:
- 1.1 google
Alt-Svc:
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
body:
encoding: UTF-8
string: OK
recorded_at: Wed, 20 Nov 2024 04:51:12 GMT
recorded_with: VCR 6.2.0

View File

@@ -1,16 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
require 'open_food_network/error_logger'
module OpenFoodNetwork
RSpec.describe ErrorLogger do
let(:error) { StandardError.new("Test") }
it "notifies Bugsnag" do
expect(Bugsnag).to receive(:notify).with(error)
ErrorLogger.notify(error)
end
end
end

View File

@@ -0,0 +1,88 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Alert do
around do |example|
original_config = nil
Bugsnag.configure do |config|
original_config = config.dup
config.api_key ||= "dummy-key"
config.notify_release_stages = ["test"]
config.delivery_method = :synchronous
end
example.run
Bugsnag.configure do |config|
config.api_key ||= original_config.api_key
config.notify_release_stages = original_config.notify_release_stages
config.delivery_method = original_config.delivery_method
end
end
it "notifies Bugsnag" do
expect(Bugsnag).to receive(:notify).with("hey")
Alert.raise("hey")
end
it "adds context" do
pending "Bugsnag calls in CI" if ENV.fetch("CI", false)
expect_any_instance_of(Bugsnag::Report).to receive(:add_metadata).with(
:order, { number: "ABC123" }
)
expect_any_instance_of(Bugsnag::Report).to receive(:add_metadata).with(
:env, { referer: "example.com" }
)
Alert.raise(
"hey",
{ order: { number: "ABC123" }, env: { referer: "example.com" } }
)
end
it "is compatible with Bugsnag API" do
pending "Bugsnag calls in CI" if ENV.fetch("CI", false)
expect_any_instance_of(Bugsnag::Report).to receive(:add_metadata).with(
:order, { number: "ABC123" }
)
Alert.raise("hey") do |payload|
payload.add_metadata(:order, { number: "ABC123" })
end
end
it "sends ActiveRecord objects" do
pending "Bugsnag calls in CI" if ENV.fetch("CI", false)
order = Spree::Order.new(number: "ABC123")
expect_any_instance_of(Bugsnag::Report).to receive(:add_metadata).with(
"Spree::Order", hash_including("number" => "ABC123")
)
Alert.raise_with_record("Wrong order", order)
end
it "notifies Bugsnag when ActiveRecord object is missing" do
pending "Bugsnag calls in CI" if ENV.fetch("CI", false)
expect_any_instance_of(Bugsnag::Report).to receive(:add_metadata).with(
"NilClass", { record_was_nil: true }
)
Alert.raise_with_record("Wrong order", nil)
end
it "reaches the Bugsnag service for real", :vcr do
# You need to have a valid Bugsnag API key to record this test.
# And after recording, you need to check the Bugsnag account for the right
# data.
Alert.raise(
"Testing Bugsnag from RSpec",
{ RSpec: { file: __FILE__ }, env: { BUGSNAG: ENV.fetch("BUGSNAG", nil) } }
)
end
end

View File

@@ -11,6 +11,7 @@ VCR.configure do |config|
# Filter sensitive environment variables
%w[
BUGSNAG_API_KEY
STRIPE_INSTANCE_SECRET_KEY
STRIPE_CUSTOMER
STRIPE_ACCOUNT