From 2acd2c790addf845dd74ef39e0aac84f9df6f6d8 Mon Sep 17 00:00:00 2001 From: KoushikSagarr Date: Sun, 1 Nov 2026 19:00:49 +0530 Subject: [PATCH] Add monthly guards to scheduled rake tasks (#7284) --- lib/tasks/monthly_learning_hours_report.rake | 4 +- lib/tasks/youth_birthday_reminder.rake | 4 +- .../monthly_learning_hours_report_spec.rb | 71 +++++++++++++++++++ .../lib/tasks/youth_birthday_reminder_spec.rb | 71 ++++++++++++------- 4 files changed, 124 insertions(+), 26 deletions(-) create mode 100644 spec/lib/tasks/monthly_learning_hours_report_spec.rb diff --git a/lib/tasks/monthly_learning_hours_report.rake b/lib/tasks/monthly_learning_hours_report.rake index 60352a8a88..ca900e6a83 100644 --- a/lib/tasks/monthly_learning_hours_report.rake +++ b/lib/tasks/monthly_learning_hours_report.rake @@ -1,5 +1,7 @@ -desc "Scheduled once per month in Heroku Scheduler, this task will send a learning hours report to all Casa Admins and Supervisors" +desc "Send learning hours report to all Casa Admins and Supervisors; run daily in Heroku Scheduler, only acts on the 1st" task send_learning_hour_reports: :environment do + next unless Date.current.day == 1 || ENV["FORCE"] == "true" + admins = CasaAdmin.active.where(monthly_learning_hours_report: true) supervisors = Supervisor.active.where(monthly_learning_hours_report: true) diff --git a/lib/tasks/youth_birthday_reminder.rake b/lib/tasks/youth_birthday_reminder.rake index 75188f7052..4dca2cd85c 100644 --- a/lib/tasks/youth_birthday_reminder.rake +++ b/lib/tasks/youth_birthday_reminder.rake @@ -1,5 +1,7 @@ -desc "Create a notification for volunteers when a youth has a birthday coming in the next month, scheduled for the 15th of each month in Heroku Scheduler" +desc "Create a notification for volunteers when a youth has a birthday coming in the next month; run daily in Heroku Scheduler, only acts on the 15th" task youth_birthday_reminder: :environment do + next unless Date.current.day == 15 || ENV["FORCE"] == "true" + CasaCase.birthday_next_month.find_each do |casa_case| casa_case.case_assignments.active.find_each do |case_assignment| YouthBirthdayNotifier diff --git a/spec/lib/tasks/monthly_learning_hours_report_spec.rb b/spec/lib/tasks/monthly_learning_hours_report_spec.rb new file mode 100644 index 0000000000..8920f2fce5 --- /dev/null +++ b/spec/lib/tasks/monthly_learning_hours_report_spec.rb @@ -0,0 +1,71 @@ +require "rails_helper" +require "rake" + +RSpec.describe "send_learning_hour_reports rake task" do + before(:all) do + Rails.application.load_tasks if Rake::Task.tasks.empty? + end + + before(:each) do + Rake::Task["send_learning_hour_reports"].reenable + travel_to Date.new(2026, 1, 1) + end + + subject(:run_task) { Rake::Task["send_learning_hour_reports"].invoke } + + let!(:active_admin_with_report) { create(:casa_admin, active: true, monthly_learning_hours_report: true) } + let!(:active_admin_without_report) { create(:casa_admin, active: true, monthly_learning_hours_report: false) } + let!(:inactive_admin_with_report) { create(:casa_admin, active: false, monthly_learning_hours_report: true) } + + let!(:active_supervisor_with_report) { create(:supervisor, active: true, monthly_learning_hours_report: true) } + let!(:active_supervisor_without_report) { create(:supervisor, active: true, monthly_learning_hours_report: false) } + let!(:inactive_supervisor_with_report) { create(:supervisor, active: false, monthly_learning_hours_report: true) } + + context "when today is the 1st of the month" do + it "enqueues report emails only for active admins and supervisors with monthly reports enabled" do + expect { run_task } + .to have_enqueued_mail(LearningHoursMailer, :learning_hours_report_email).with(active_admin_with_report) + .and have_enqueued_mail(LearningHoursMailer, :learning_hours_report_email).with(active_supervisor_with_report) + .and have_enqueued_mail(LearningHoursMailer, :learning_hours_report_email).twice + end + + it "does not enqueue report emails for inactive users or users with report disabled" do + expect { run_task } + .not_to have_enqueued_mail(LearningHoursMailer, :learning_hours_report_email).with(active_admin_without_report) + + Rake::Task["send_learning_hour_reports"].reenable + expect { run_task } + .not_to have_enqueued_mail(LearningHoursMailer, :learning_hours_report_email).with(inactive_admin_with_report) + + Rake::Task["send_learning_hour_reports"].reenable + expect { run_task } + .not_to have_enqueued_mail(LearningHoursMailer, :learning_hours_report_email).with(active_supervisor_without_report) + + Rake::Task["send_learning_hour_reports"].reenable + expect { run_task } + .not_to have_enqueued_mail(LearningHoursMailer, :learning_hours_report_email).with(inactive_supervisor_with_report) + end + end + + context "when today is not the 1st of the month" do + before { travel_to Date.new(2026, 1, 2) } + + it "does not enqueue any report emails" do + expect { run_task } + .not_to have_enqueued_mail(LearningHoursMailer, :learning_hours_report_email) + end + + context "when FORCE=true" do + before do + stub_const("ENV", ENV.to_h.merge("FORCE" => "true")) + end + + it "enqueues report emails for active admins and supervisors with monthly reports enabled" do + expect { run_task } + .to have_enqueued_mail(LearningHoursMailer, :learning_hours_report_email).with(active_admin_with_report) + .and have_enqueued_mail(LearningHoursMailer, :learning_hours_report_email).with(active_supervisor_with_report) + .and have_enqueued_mail(LearningHoursMailer, :learning_hours_report_email).twice + end + end + end +end diff --git a/spec/lib/tasks/youth_birthday_reminder_spec.rb b/spec/lib/tasks/youth_birthday_reminder_spec.rb index 23bbf35267..81f91ad26f 100644 --- a/spec/lib/tasks/youth_birthday_reminder_spec.rb +++ b/spec/lib/tasks/youth_birthday_reminder_spec.rb @@ -8,6 +8,7 @@ before(:each) do Rake::Task["youth_birthday_reminder"].reenable + travel_to Date.new(2026, 1, 15) end subject(:run_task) { Rake::Task["youth_birthday_reminder"].invoke } @@ -15,48 +16,70 @@ # birthday_next_month matches on the birth MONTH; the youth's birth year must be # in the past (a future birth date is rejected by the model), so use a past year # with next calendar month. - let!(:casa_case) { create(:casa_case, birth_month_year_youth: Date.new(2012, Time.zone.today.next_month.month, 15)) } + let!(:casa_case) { create(:casa_case, birth_month_year_youth: Date.new(2012, 2, 15)) } - context "when a birthday-next-month case has no active assignment" do - it "does not raise" do - expect { run_task }.not_to raise_error + context "when today is the 15th of the month" do + context "when a birthday-next-month case has no active assignment" do + it "does not raise" do + expect { run_task }.not_to raise_error + end + + it "creates no notification" do + expect { run_task }.not_to change(Noticed::Notification, :count) + end end - it "creates no notification" do - expect { run_task }.not_to change(Noticed::Notification, :count) + context "when a birthday-next-month case has an active assignment" do + let!(:assignment) { create(:case_assignment, casa_case: casa_case, active: true) } + + it "notifies the assigned volunteer" do + expect { run_task }.to change(Noticed::Notification, :count).by(1) + end end - end - context "when a birthday-next-month case has an active assignment" do - let!(:assignment) { create(:case_assignment, casa_case: casa_case, active: true) } + context "when a birthday-next-month case has multiple active assignments" do + let!(:assignments) { create_list(:case_assignment, 2, casa_case: casa_case, active: true) } - it "notifies the assigned volunteer" do - expect { run_task }.to change(Noticed::Notification, :count).by(1) + it "notifies each active volunteer" do + expect { run_task }.to change(Noticed::Notification, :count).by(2) + end end - end - context "when a birthday-next-month case has multiple active assignments" do - let!(:assignments) { create_list(:case_assignment, 2, casa_case: casa_case, active: true) } + context "when a case has both an active and an inactive assignment" do + let!(:active_assignment) { create(:case_assignment, casa_case: casa_case, active: true) } + let!(:inactive_assignment) { create(:case_assignment, casa_case: casa_case, active: false) } - it "notifies each active volunteer" do - expect { run_task }.to change(Noticed::Notification, :count).by(2) + it "notifies only the active volunteer" do + expect { run_task }.to change(Noticed::Notification, :count).by(1) + end end - end - context "when a case has both an active and an inactive assignment" do - let!(:active_assignment) { create(:case_assignment, casa_case: casa_case, active: true) } - let!(:inactive_assignment) { create(:case_assignment, casa_case: casa_case, active: false) } + context "when the only assignment is inactive" do + let!(:assignment) { create(:case_assignment, casa_case: casa_case, active: false) } - it "notifies only the active volunteer" do - expect { run_task }.to change(Noticed::Notification, :count).by(1) + it "does not notify" do + expect { run_task }.not_to change(Noticed::Notification, :count) + end end end - context "when the only assignment is inactive" do - let!(:assignment) { create(:case_assignment, casa_case: casa_case, active: false) } + context "when today is not the 15th of the month" do + before { travel_to Date.new(2026, 1, 14) } + + let!(:assignment) { create(:case_assignment, casa_case: casa_case, active: true) } it "does not notify" do expect { run_task }.not_to change(Noticed::Notification, :count) end + + context "when FORCE=true" do + before do + stub_const("ENV", ENV.to_h.merge("FORCE" => "true")) + end + + it "notifies the assigned volunteer" do + expect { run_task }.to change(Noticed::Notification, :count).by(1) + end + end end end