diff --git a/Gemfile b/Gemfile index 116aea08..57151f19 100644 --- a/Gemfile +++ b/Gemfile @@ -20,7 +20,6 @@ gem 'omniauth-rails_csrf_protection' gem 'blazer' # for querying the database # monitoring & performance -gem 'delayed_job_active_record' gem 'sentry-rails' gem 'sentry-ruby' gem 'skylight' # performance monitoring: https://www.skylight.io/app/applications/670fP418RH7v/recent/6h/endpoints @@ -48,6 +47,7 @@ group :development do # debugging gem 'better_errors' gem 'binding_of_caller' # FIX: commands such as continue not work while debugging with binding.pry + gem 'letter_opener' # previews outgoing emails in the browser instead of sending them # performance gem 'parity' # CLI commands to simplify integration with Heroku apps (e.g, database sync, deployment, etc) diff --git a/Gemfile.lock b/Gemfile.lock index 429c9ca7..395908a0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -106,6 +106,8 @@ GEM xpath (~> 3.2) cgi (0.5.1) chartkick (5.1.0) + childprocess (5.1.0) + logger (~> 1.5) coderay (1.1.3) concurrent-ruby (1.3.6) connection_pool (2.5.5) @@ -116,11 +118,6 @@ GEM irb (~> 1.10) reline (>= 0.3.8) debug_inspector (1.2.0) - delayed_job (4.1.11) - activesupport (>= 3.0, < 8.0) - delayed_job_active_record (4.1.8) - activerecord (>= 3.0, < 8.0) - delayed_job (>= 3.0, < 5) drb (2.2.3) dry-cli (1.0.0) erb (6.0.2) @@ -169,6 +166,12 @@ GEM kaminari-core (= 1.2.2) kaminari-core (1.2.2) language_server-protocol (3.17.0.3) + launchy (3.1.1) + addressable (~> 2.8) + childprocess (~> 5.0) + logger (~> 1.6) + letter_opener (1.10.0) + launchy (>= 2.2, < 4) logger (1.6.1) loofah (2.25.1) crass (~> 1.0.2) @@ -407,12 +410,12 @@ DEPENDENCIES bootsnap capybara debug - delayed_job_active_record factory_bot_rails faker groupdate heroicon kaminari + letter_opener mini_magick minitest-spec-rails minitest-stub-const diff --git a/Procfile b/Procfile index 2aef89a9..08545f28 100644 --- a/Procfile +++ b/Procfile @@ -1,3 +1,2 @@ web: bundle exec puma -C config/puma.rb release: rake db:migrate -worker: rake jobs:work diff --git a/app/controllers/user/authorization_requests_controller.rb b/app/controllers/user/authorization_requests_controller.rb index d04b1462..0b6557eb 100644 --- a/app/controllers/user/authorization_requests_controller.rb +++ b/app/controllers/user/authorization_requests_controller.rb @@ -18,13 +18,12 @@ def index def create authorization_request = AuthorizationRequest.find_or_create_by!(organization: organization, user: current_user) - # TODO: Move this to a background job. Once we have a background job, we can remove the `if` statement. if authorization_request.previously_new_record? organization.users.each do |user| UserMailer .with(user: user, authorization_request: authorization_request) .new_authorization_request - .deliver_later + .deliver_now end end diff --git a/app/controllers/user/authorizations_controller.rb b/app/controllers/user/authorizations_controller.rb index 92f173d2..2c5b0623 100644 --- a/app/controllers/user/authorizations_controller.rb +++ b/app/controllers/user/authorizations_controller.rb @@ -35,11 +35,11 @@ def send_notifications(authorization) end def notify_user(authorization) - UserMailer.with(granted_by_user: current_user, authorization: authorization).authorization_granted.deliver_later + UserMailer.with(granted_by_user: current_user, authorization: authorization).authorization_granted.deliver_now end def notify_admin(authorization) - UserMailer.with(granted_by_user: current_user, authorization: authorization).authorization_alert.deliver_later + UserMailer.with(granted_by_user: current_user, authorization: authorization).authorization_alert.deliver_now end def clear_related_authorization_requests diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb deleted file mode 100644 index bef39599..00000000 --- a/app/jobs/application_job.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -class ApplicationJob < ActiveJob::Base - # Automatically retry jobs that encountered a deadlock - # retry_on ActiveRecord::Deadlocked - - # Most jobs are safe to ignore if the underlying records are no longer available - # discard_on ActiveJob::DeserializationError -end diff --git a/app/models/user.rb b/app/models/user.rb index 3ae15b66..a2abe570 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -88,7 +88,7 @@ def find_or_create_with_omniauth(auth) user.update_dynamic_attributes(auth) report_sign_in(user) user.save! - UserMailer.with(user: user).welcome.deliver_later if user.new_record? && user.valid? # TODO: why check valid here? + UserMailer.with(user: user).welcome.deliver_now if user.new_record? && user.valid? # TODO: why check valid here? user end diff --git a/bin/delayed_job b/bin/delayed_job deleted file mode 100755 index 4f1b0b2b..00000000 --- a/bin/delayed_job +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment')) -require 'delayed/command' -Delayed::Command.new(ARGV).daemonize diff --git a/config/environments/development.rb b/config/environments/development.rb index 66b0766d..e14a718d 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -75,8 +75,8 @@ logger.formatter = config.log_formatter config.logger = ActiveSupport::TaggedLogging.new(logger) - # Do not send emails in development - config.action_mailer.perform_deliveries = false + # Preview outgoing emails in the browser via letter_opener (no real SMTP) + config.action_mailer.delivery_method = :letter_opener # Sets default host for email path and urls routes.default_url_options[:host] = 'http://localhost:3001' diff --git a/config/environments/production.rb b/config/environments/production.rb index 04308986..971c0461 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -51,10 +51,6 @@ # Prepend all log lines with the following tags. config.log_tags = [:request_id] - # Use a real queuing backend for Active Job (and separate queues per environment). - config.active_job.queue_adapter = :delayed_job - # config.active_job.queue_name_prefix = "cherry_production" - config.action_mailer.perform_caching = false # Ignore bad email addresses and do not raise email delivery errors. diff --git a/config/environments/test.rb b/config/environments/test.rb index cdb8476f..149c4596 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -42,7 +42,6 @@ # The :test delivery method accumulates sent emails in the # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test - config.action_mailer.perform_deliveries = false # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr diff --git a/db/migrate/20260420142918_drop_delayed_jobs.rb b/db/migrate/20260420142918_drop_delayed_jobs.rb new file mode 100644 index 00000000..44107d7c --- /dev/null +++ b/db/migrate/20260420142918_drop_delayed_jobs.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +class DropDelayedJobs < ActiveRecord::Migration[7.0] + def up + drop_table :delayed_jobs + end + + def down + create_table :delayed_jobs do |table| + table.integer :priority, default: 0, null: false + table.integer :attempts, default: 0, null: false + table.text :handler, null: false + table.text :last_error + table.datetime :run_at + table.datetime :locked_at + table.datetime :failed_at + table.string :locked_by + table.string :queue + table.timestamps null: true + end + + add_index :delayed_jobs, %i[priority run_at], name: 'delayed_jobs_priority' + end +end diff --git a/db/schema.rb b/db/schema.rb index 9e18b8ba..93a1c630 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2026_04_17_172507) do +ActiveRecord::Schema[7.2].define(version: 2026_04_20_142918) do # These are extensions that must be enabled in order to support this database enable_extension "pg_stat_statements" enable_extension "plpgsql" @@ -126,21 +126,6 @@ t.index ["project_id"], name: "index_dashboards_on_project_id" end - create_table "delayed_jobs", force: :cascade do |t| - t.integer "priority", default: 0, null: false - t.integer "attempts", default: 0, null: false - t.text "handler", null: false - t.text "last_error" - t.datetime "run_at" - t.datetime "locked_at" - t.datetime "failed_at" - t.string "locked_by" - t.string "queue" - t.datetime "created_at" - t.datetime "updated_at" - t.index ["priority", "run_at"], name: "delayed_jobs_priority" - end - create_table "metrics", force: :cascade do |t| t.string "name" t.bigint "project_id", null: false diff --git a/test/controllers/user/authorizations_controller_test.rb b/test/controllers/user/authorizations_controller_test.rb index a23c4800..508a3aca 100644 --- a/test/controllers/user/authorizations_controller_test.rb +++ b/test/controllers/user/authorizations_controller_test.rb @@ -19,17 +19,14 @@ class User::AuthorizationsControllerTest < ApplicationIntegrationTest it 'sends email to the user to whom the authorization has been granted' do Organization.any_instance.stubs(:can_create_new_authorizations?).returns(true) sign_in(user, controller_test: true) - post(user_authorizations_path(email: 'hello@example.com', organization_id: organization.id), as: :json) + assert_emails(1) do + post(user_authorizations_path(email: 'hello@example.com', organization_id: organization.id), as: :json) + end assert_response :success - assert_enqueued_email_with( - UserMailer, - :authorization_granted, - params: { - granted_by_user: user, - authorization: Authorization.last - } - ) + email = ActionMailer::Base.deliveries.last + assert_equal ['hello@example.com'], email.to + assert_equal 'Cherry - Authorization Granted', email.subject end it 'notifies admin about new authorizations' do @@ -37,34 +34,25 @@ class User::AuthorizationsControllerTest < ApplicationIntegrationTest authorized_user = create :user Authorization.create!(email: authorized_user.email, organization: organization) sign_in(authorized_user, controller_test: true) - post(user_authorizations_path(email: 'hello@example.com', organization_id: organization.id), as: :json) + assert_emails(2) do + post(user_authorizations_path(email: 'hello@example.com', organization_id: organization.id), as: :json) + end assert_response :success - assert_enqueued_email_with( - UserMailer, - :authorization_alert, - params: { - granted_by_user: authorized_user, - authorization: Authorization.last - } - ) + alert = ActionMailer::Base.deliveries.find { |e| e.subject == 'Cherry - New Authorization' } + assert_equal [user.email], alert.to end it 'does not notify admin when the admin is the creator of the authorization' do Organization.any_instance.stubs(:can_create_new_authorizations?).returns(true) sign_in(user, controller_test: true) - post(user_authorizations_path(email: 'hello@example.com', organization_id: organization.id), as: :json) + assert_emails(1) do + post(user_authorizations_path(email: 'hello@example.com', organization_id: organization.id), as: :json) + end assert_response :success - assert_equal 1, ActiveJob::Base.queue_adapter.enqueued_jobs.count - assert_enqueued_email_with( - UserMailer, - :authorization_granted, - params: { - granted_by_user: user, - authorization: Authorization.last - } - ) + email = ActionMailer::Base.deliveries.last + assert_equal 'Cherry - Authorization Granted', email.subject end end diff --git a/test/screenshots/dashboard-with-chart.png b/test/screenshots/dashboard-with-chart.png index e29a3def..53d73728 100644 Binary files a/test/screenshots/dashboard-with-chart.png and b/test/screenshots/dashboard-with-chart.png differ diff --git a/test/screenshots/metric-contributions.png b/test/screenshots/metric-contributions.png index d0c07808..2e0deee2 100644 Binary files a/test/screenshots/metric-contributions.png and b/test/screenshots/metric-contributions.png differ diff --git a/test/tasks/database_cleanup_test.rb b/test/tasks/database_cleanup_test.rb index 047f0131..56fe1c93 100644 --- a/test/tasks/database_cleanup_test.rb +++ b/test/tasks/database_cleanup_test.rb @@ -14,7 +14,6 @@ class DatabaseCleanupTest < ActionMailer::TestCase it 'deletes older project occurrences' do assert_equal 4, Occurrence.count Rake::Task['database:cleanup'].execute - assert_equal 0, Delayed::Job.count assert_equal 2, Occurrence.count end end