diff --git a/app/jobs/backtrace_job.rb b/app/jobs/backtrace_job.rb index 50ee155..eab9f22 100644 --- a/app/jobs/backtrace_job.rb +++ b/app/jobs/backtrace_job.rb @@ -30,13 +30,15 @@ class BacktraceJob < ApplicationJob # Encuentra el código fuente del error source = data.dig('sourcesContent', data['sources']&.index(backtrace['file']))&.split("\n") - backtrace['function'] = source[backtrace['line'] - 1] if source.present? + # XXX: Elimina la sangría aunque cambie las columnas porque + # eso lo vamos a ver en el archivo fuente directo. + backtrace['function'] = source[backtrace['line'] - 1].strip if source.present? end end end begin - raise BacktraceException, "#{origin}: #{params['errors']&.first&.dig('message')}" + raise BacktraceException, "#{origin}: #{message}" rescue BacktraceException => e ExceptionNotifier.notify_exception(e, data: { site: site.name, params: params, _backtrace: true }) end @@ -102,4 +104,9 @@ class BacktraceJob < ApplicationJob rescue URI::Error params.dig('context', 'url') end + + # @return [String,Nil] + def message + @message ||= params['errors']&.first&.dig('message') + end end diff --git a/app/views/exception_notifier/_data.text.erb b/app/views/exception_notifier/_data.text.erb index 3493d68..09313f4 100644 --- a/app/views/exception_notifier/_data.text.erb +++ b/app/views/exception_notifier/_data.text.erb @@ -3,7 +3,7 @@ # <%= error['type'] %>: <%= error['message'] %> ``` -<%= Terminal::Table.new headings: error['backtrace'].first.keys, rows: error['backtrace'].map(&:values).map(&:strip) %> +<%= Terminal::Table.new headings: error['backtrace'].first.keys, rows: error['backtrace'].map(&:values) %> ``` <% end %> diff --git a/config/environments/test.rb b/config/environments/test.rb index f816982..c58a65c 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -64,4 +64,13 @@ Rails.application.configure do # Annotate rendered view with file names. # config.action_view.annotate_rendered_view_with_filenames = true + + config.middleware.use ExceptionNotification::Rack, + error_grouping: true, + email: { + email_prefix: '', + sender_address: ENV.fetch('DEFAULT_FROM', "noreply@sutty.nl"), + exception_recipients: ENV.fetch('EXCEPTION_TO', "errors@sutty.nl"), + normalize_subject: true + } end diff --git a/test/jobs/backtrace_job_test.rb b/test/jobs/backtrace_job_test.rb new file mode 100644 index 0000000..89c6860 --- /dev/null +++ b/test/jobs/backtrace_job_test.rb @@ -0,0 +1,147 @@ +# frozen_string_literal: true + +require 'test_helper' + +class DeployJobTest < ActiveSupport::TestCase + def site + @site ||= create :site + end + + # Mockup + def job + job = BacktraceJob.new + job.instance_variable_set :@site, site + job.instance_variable_set :@params, notice + + job + end + + # setTimeout(() => { throw('Prueba') }, 1000) + def notice + @notice ||= { + "errors" => [ + { + "type" => "", + "message" => "Prueba", + "backtrace" => [ + { + "function" => "pt "https://tintalimon.com.ar/assets/js/pack.js", + "line" => 89, + "column" => 74094 + }, + { + "function" => "pt "https://tintalimon.com.ar/assets/js/pack.js", + "line" => 89, + "column" => 74731 + }, + { + "function" => "pt "https://tintalimon.com.ar/assets/js/pack.js", + "line" => 89, + "column" => 71925 + }, + { + "function" => "setTimeout handler*", + "file" => "debugger eval code", + "line" => 1, + "column" => 11 + } + ] + } + ], + "context" => { + "severity" => "error", + "history" => [ + { + "type" => "error", + "target" => "html. > head. > script.[type=\"text/javascript\"][src=\"//stats.habitapp.org/piwik.js\"]", + "date" => "2021-04-26T22:06:58.390Z" + }, + { + "type" => "DOMContentLoaded", + "target" => "[object HTMLDocument]", + "date" => "2021-04-26T22:06:58.510Z" + }, + { + "type" => "load", + "target" => "[object HTMLDocument]", + "date" => "2021-04-26T22:06:58.845Z" + }, + { + "type" => "xhr", + "date" => "2021-04-26T22:06:58.343Z", + "method" => "GET", + "url" => "assets/data/site.json", + "statusCode" => 200, + "duration" => 506 + }, + { + "type" => "xhr", + "date" => "2021-04-26T22:06:58.886Z", + "method" => "GET", + "url" => "assets/templates/cart.html", + "statusCode" => 200, + "duration" => 591 + } + ], + "windowError" => true, + "notifier" => { + "name" => "airbrake-js/browser", + "version" => "1.4.2", + "url" => "https://github.com/airbrake/airbrake-js/tree/master/packages/browser" + }, + "userAgent" => "Mozilla/5.0 (Windows NT 6.1; rv:85.0) Gecko/20100101 Firefox/85.0", + "url" => "https://tintalimon.com.ar/carrito/", + "rootDirectory" => "https://tintalimon.com.ar", + "language" => "JavaScript" + }, + "params" => {}, + "environment" => {}, + "session" => {} + } + + # XXX: Siempre devolvemos un duplicado porque BacktraceJob lo + # modifica + @notice.dup + end + + # Asegurarse que el sitio se destruye al terminar de usarlo + teardown do + site&.destroy + end + + test 'al recibir un backtrace enviamos un error' do + ActionMailer::Base.deliveries.clear + + assert BacktraceJob.perform_now site_id: site.id, params: notice + + email = ActionMailer::Base.deliveries.first + + assert email + assert_equal " (BacktraceJob::BacktraceException) \"tintalimon.com.ar: Prueba\"", email.subject + assert (%r{webpack://} =~ email.body.to_s) + end + + test 'los errores se basan en un sitio' do + assert_equal site, job.send(:site) + end + + test 'los errores tienen archivos fuente' do + assert_equal %w[https://tintalimon.com.ar/assets/js/pack.js], job.send(:sources) + end + + test 'los errores tienen una url de origen' do + assert_equal 'tintalimon.com.ar', job.send(:origin) + end + + test 'los errores tienen un sourcemap' do + local_job = job + sourcemap = local_job.send :sourcemap + + assert_equal SourceMap::Map, sourcemap.class + assert_equal 'assets/js/pack.js', sourcemap.filename + assert sourcemap.sources.size.positive? + end +end