From 694dc5b8cff7383ae539f168e2cde7c965f2d759 Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Thu, 14 May 2026 11:04:34 +0200 Subject: [PATCH 01/11] refactor(tests): update shared examples to test email delivery instead of mocks Replaces mock expectations on mailer classes with assertions about ActionMailer::Base.deliveries. This improves test isolation for parallel execution. Changes: - 'creates an invitation for each student' now verifies emails sent via delivery count - 'creates an invitation for each coach' now verifies emails sent via delivery count - Tests verify recipient email addresses match expected members - Removes expect(mailer).to receive(:invite_student/coach) mocks --- .../behaves_like_sending_workshop_emails.rb | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/spec/support/shared_examples/behaves_like_sending_workshop_emails.rb b/spec/support/shared_examples/behaves_like_sending_workshop_emails.rb index 925e169dc..33f9fbf62 100644 --- a/spec/support/shared_examples/behaves_like_sending_workshop_emails.rb +++ b/spec/support/shared_examples/behaves_like_sending_workshop_emails.rb @@ -1,24 +1,38 @@ RSpec.shared_examples 'sending workshop emails' do - it 'creates an invitation for each student' do + it 'creates an invitation for each student and sends emails' do Fabricate(:students, chapter: chapter, members: students) students.each do |student| expect(WorkshopInvitation).to receive(:find_or_initialize_by).with(workshop: workshop, member: student, role: 'Student').and_call_original - expect(mailer).to receive(:invite_student).and_call_original end - manager.send(send_email, workshop, 'students') + expect { + manager.send(send_email, workshop, 'students') + }.to change { ActionMailer::Base.deliveries.count }.by(students.count) + .and change { WorkshopInvitation.where(workshop: workshop, role: 'Student').count }.by(students.count) + + # Verify emails were sent to the right recipients + emails = ActionMailer::Base.deliveries.last(students.count) + student_emails = students.map(&:email) + expect(emails.map(&:to).flatten).to match_array(student_emails) end - it 'creates an invitation for each coach' do + it 'creates an invitation for each coach and sends emails' do Fabricate(:coaches, chapter: chapter, members: coaches) coaches.each do |coach| expect(WorkshopInvitation).to receive(:find_or_initialize_by).with(workshop: workshop, member: coach, role: 'Coach').and_call_original - expect(mailer).to receive(:invite_coach).and_call_original end - manager.send(send_email, workshop, 'coaches') + expect { + manager.send(send_email, workshop, 'coaches') + }.to change { ActionMailer::Base.deliveries.count }.by(coaches.count) + .and change { WorkshopInvitation.where(workshop: workshop, role: 'Coach').count }.by(coaches.count) + + # Verify emails were sent to the right recipients + emails = ActionMailer::Base.deliveries.last(coaches.count) + coach_emails = coaches.map(&:email) + expect(emails.map(&:to).flatten).to match_array(coach_emails) end it 'does not invite banned coaches' do From f531afb392e7720764b93e54065481f32976430a Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Thu, 14 May 2026 11:06:31 +0200 Subject: [PATCH 02/11] refactor(tests): convert waiting list email tests to use delivery assertions Replaces mock expectations with ActionMailer::Base.deliveries count assertions for #send_waiting_list_emails tests. Changes: - 'emails coaches when there are free coach spots' - verifies email delivery - 'does not email coaches when no coach spots' - verifies no emails sent - 'emails students when there are free student spots' - verifies email delivery - 'does not email students when no student spots' - verifies no emails sent Removes expect(WorkshopInvitationMailer).to receive(:notify_waiting_list) mocks --- spec/models/invitation_manager_spec.rb | 38 ++++++++++++-------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/spec/models/invitation_manager_spec.rb b/spec/models/invitation_manager_spec.rb index cbf9c55c2..4bc8700ea 100644 --- a/spec/models/invitation_manager_spec.rb +++ b/spec/models/invitation_manager_spec.rb @@ -169,43 +169,41 @@ it 'emails coaches when there are free coach spots' do waitinglist_invitation = Fabricate(:waitinglist_invitation, workshop: workshop, role: 'Coach') - expect(WorkshopInvitationMailer).to receive(:notify_waiting_list).once - .with(waitinglist_invitation) - .and_call_original + expect { + manager.send_waiting_list_emails(workshop) + }.to change { ActionMailer::Base.deliveries.count }.by(1) - manager.send_waiting_list_emails(workshop) + email = ActionMailer::Base.deliveries.last + expect(email.to).to include(waitinglist_invitation.member.email) end it 'does not email coaches when no coach spots are available' do workshop = Fabricate(:workshop, coach_count: 0) - waitinglist_invitation = Fabricate(:waitinglist_invitation, workshop: workshop, role: 'Coach') + Fabricate(:waitinglist_invitation, workshop: workshop, role: 'Coach') - expect(WorkshopInvitationMailer).not_to receive(:notify_waiting_list) - .with(waitinglist_invitation) - .and_call_original - - manager.send_waiting_list_emails(workshop) + expect { + manager.send_waiting_list_emails(workshop) + }.not_to(change { ActionMailer::Base.deliveries.count }) end it 'emails students when there are free student spots' do waitinglist_invitation = Fabricate(:waitinglist_invitation, workshop: workshop, role: 'Student') - expect(WorkshopInvitationMailer).to receive(:notify_waiting_list).once - .with(waitinglist_invitation) - .and_call_original + expect { + manager.send_waiting_list_emails(workshop) + }.to change { ActionMailer::Base.deliveries.count }.by(1) - manager.send_waiting_list_emails(workshop) + email = ActionMailer::Base.deliveries.last + expect(email.to).to include(waitinglist_invitation.member.email) end it 'does not email students when no student spots are available' do workshop = Fabricate(:workshop, student_count: 0) - waitinglist_invitation = Fabricate(:waitinglist_invitation, workshop: workshop, role: 'Student') + Fabricate(:waitinglist_invitation, workshop: workshop, role: 'Student') - expect(WorkshopInvitationMailer).not_to receive(:notify_waiting_list) - .with(waitinglist_invitation) - .and_call_original - - manager.send_waiting_list_emails(workshop) + expect { + manager.send_waiting_list_emails(workshop) + }.not_to(change { ActionMailer::Base.deliveries.count }) end end From 6d2c3f98f26788ee89fb183258f010876f42c2b0 Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Thu, 14 May 2026 11:10:47 +0200 Subject: [PATCH 03/11] refactor(tests): convert reminder email tests to use delivery assertions Replaces mock expectations with ActionMailer::Base.deliveries count assertions for reminder email tests. Changes: - 'emails all attending members' (monthly) - uses _without_delay method - 'emails all attending members' (workshop) - verifies email delivery count - 'emails waiting list' - uses _without_delay method Notes: - Monthly and waiting list tests remain marked :wip as they were before - These methods are async and require _without_delay for sync testing Removes expect(Mailer).to receive(:attendance_reminder) mocks --- spec/models/invitation_manager_spec.rb | 33 +++++++++++++------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/spec/models/invitation_manager_spec.rb b/spec/models/invitation_manager_spec.rb index 4bc8700ea..fa8afd4d3 100644 --- a/spec/models/invitation_manager_spec.rb +++ b/spec/models/invitation_manager_spec.rb @@ -132,36 +132,35 @@ workshop = Fabricate(:workshop) invitations = Fabricate.times(2, :attending_workshop_invitation, workshop: workshop) - invitations.each do |invitation| - expect(WorkshopInvitationMailer).to receive(:attending_reminder) - .with(workshop, invitation.member, invitation) - .and_call_original - end + expect { + manager.send_workshop_attendance_reminders_without_delay(workshop) + }.to change { ActionMailer::Base.deliveries.count }.by(invitations.count) - manager.send_workshop_attendance_reminders(workshop) invitations.each { |invitation| expect(invitation.reload.reminded_at).not_to be_nil } + + emails = ActionMailer::Base.deliveries.last(invitations.count) + invitation_emails = invitations.map { |i| i.member.email } + expect(emails.map(&:to).flatten).to match_array(invitation_emails) end end describe '#send_workshop_waiting_list_reminders', :wip do + # Note: This test is WIP because the method is async it 'emails everyone that hasn\'t already been reminded from the workshop\'s waitinglist' do workshop = Fabricate(:workshop) invitations = Fabricate.times(2, :waitinglist_invitation, workshop: workshop) reminded_invitations = Fabricate.times(2, :waitinglist_invitation_reminded, workshop: workshop) - invitations.each do |invitation| - expect(WorkshopInvitationMailer).to receive(:waiting_list_reminder) - .with(workshop, invitation.member, invitation) - .and_call_original - end - - reminded_invitations.each do |invitation| - expect(WorkshopInvitationMailer).not_to receive(:waiting_list_reminder) - .with(workshop, invitation.member, invitation) - end + expect { + manager.send_workshop_waiting_list_reminders_without_delay(workshop) + }.to change { ActionMailer::Base.deliveries.count }.by(invitations.count) - manager.send_workshop_waiting_list_reminders(workshop) invitations.each { |invitation| expect(invitation.reload.reminded_at).not_to be_nil } + reminded_invitations.each { |invitation| expect(invitation.reload.reminded_at).to be_nil } + + emails = ActionMailer::Base.deliveries.last(invitations.count) + invitation_emails = invitations.map { |i| i.member.email } + expect(emails.map(&:to).flatten).to match_array(invitation_emails) end end From d46ec5c66952b3066ede3d0153083afb5233c55a Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Thu, 14 May 2026 11:12:03 +0200 Subject: [PATCH 04/11] refactor(tests): convert meeting email tests to use delivery assertions Replaces mock expectations with ActionMailer::Base.deliveries count assertions for #send_meeting_emails tests. Changes: - 'emails all invitees that are not banned' - verifies email count excludes banned - 'emails valid invitees only once' - verifies email count excludes already invited Uses _without_delay method for synchronous testing. Removes expect(MeetingInvitationMailer).to receive(:invite) mocks --- spec/models/invitation_manager_spec.rb | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/spec/models/invitation_manager_spec.rb b/spec/models/invitation_manager_spec.rb index fa8afd4d3..a8e5d7238 100644 --- a/spec/models/invitation_manager_spec.rb +++ b/spec/models/invitation_manager_spec.rb @@ -215,12 +215,9 @@ Fabricate(:ban, member: students.last) expected_student_count = students.count - 1 - expect(MeetingInvitationMailer).to receive(:invite) - .exactly(expected_student_count).times - .with(meeting, instance_of(Member), instance_of(MeetingInvitation)) - .and_call_original - - manager.send_meeting_emails(meeting) + expect { + manager.send_meeting_emails_without_delay(meeting) + }.to change { ActionMailer::Base.deliveries.count }.by(expected_student_count) end it 'emails valid invitees only once' do @@ -231,12 +228,9 @@ MeetingInvitation.create(meeting: meeting, member: students.last, role: 'Participant') expected_student_count = students.count - 1 - expect(MeetingInvitationMailer).to receive(:invite) - .exactly(expected_student_count).times - .with(meeting, instance_of(Member), instance_of(MeetingInvitation)) - .and_call_original - - manager.send_meeting_emails(meeting) + expect { + manager.send_meeting_emails_without_delay(meeting) + }.to change { ActionMailer::Base.deliveries.count }.by(expected_student_count) end end From 1dc8d4fa76afd7b9eeaebf36586f5c3c080b7801 Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Thu, 14 May 2026 11:13:19 +0200 Subject: [PATCH 05/11] refactor(tests): convert async behavior tests to use delivery assertions Replaces mock expectations with ActionMailer::Base.deliveries count assertions for async behavior tests. Changes: - 'sends invitation emails' (async context) - verifies email delivery - 'sends attendance reminder emails' (async context) - verifies email delivery Uses _without_delay methods for synchronous testing. Removes remaining expect(WorkshopInvitationMailer).to receive mocks --- spec/models/invitation_manager_spec.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/spec/models/invitation_manager_spec.rb b/spec/models/invitation_manager_spec.rb index a8e5d7238..58e1c55c9 100644 --- a/spec/models/invitation_manager_spec.rb +++ b/spec/models/invitation_manager_spec.rb @@ -368,10 +368,9 @@ Fabricate(:students, chapter: chapter, members: students) Fabricate(:coaches, chapter: chapter, members: coaches) - expect(WorkshopInvitationMailer).to receive(:invite_student).at_least(:once).and_call_original - expect(WorkshopInvitationMailer).to receive(:invite_coach).at_least(:once).and_call_original - - manager.send_workshop_emails(workshop, 'everyone') + expect { + manager.send_workshop_emails_without_delay(workshop, 'everyone') + }.to change { ActionMailer::Base.deliveries.count }.by(students.count + coaches.count) end end @@ -415,9 +414,11 @@ it 'sends attendance reminder emails' do invitation = Fabricate(:attending_workshop_invitation, workshop: workshop) - expect(WorkshopInvitationMailer).to receive(:attending_reminder).at_least(:once).and_call_original + expect { + manager.send_workshop_attendance_reminders_without_delay(workshop) + }.to change { ActionMailer::Base.deliveries.count }.by(1) - manager.send_workshop_attendance_reminders(workshop) + expect(invitation.reload.reminded_at).not_to be_nil end end From f90baf24bf123acd76484743046b99e7b6dd2825 Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Thu, 14 May 2026 11:15:42 +0200 Subject: [PATCH 06/11] refactor(tests): convert feedback request email tests to delivery assertions Simplifies after create hook tests by removing mock-based tests and replacing with direct email delivery verification. Changes: - Consolidates two mock-based tests into one email delivery test - Verifies email is sent and has expected subject Removes allow().to receive() and have_received() mocks --- spec/models/feedback_request_spec.rb | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/spec/models/feedback_request_spec.rb b/spec/models/feedback_request_spec.rb index c14805e7e..68be3fc31 100644 --- a/spec/models/feedback_request_spec.rb +++ b/spec/models/feedback_request_spec.rb @@ -25,18 +25,13 @@ end context 'after create hook' do - it '#email' do - feedback_request = Fabricate.build(:feedback_request) - allow(feedback_request).to receive(:email) - allow(feedback_request).to receive(:member_id).and_return(:member_id) - feedback_request.save - expect(feedback_request).to have_received(:email) - end - it 'sends request feedback email' do - allow(FeedbackRequestMailer).to receive(:request_feedback) { double('feedback_request_mailer').as_null_object } - Fabricate(:feedback_request) - expect(FeedbackRequestMailer).to have_received(:request_feedback) + expect { + Fabricate(:feedback_request) + }.to change { ActionMailer::Base.deliveries.count }.by(1) + + email = ActionMailer::Base.deliveries.last + expect(email.subject).to include('Feedback') end end end From 2d1d4f3c281ad9809358b755993223bdfc795c8b Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Thu, 14 May 2026 11:16:33 +0200 Subject: [PATCH 07/11] refactor(tests): convert eligibility inquiry email test to delivery assertions Replaces spy-based mock test with email delivery verification. Changes: - 'sends an eligibility check email' - verifies email delivery count and recipient Removes allow().to receive() and have_received() mocks --- spec/models/eligibility_inquiry_spec.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/spec/models/eligibility_inquiry_spec.rb b/spec/models/eligibility_inquiry_spec.rb index 60f9cb656..b6e67db02 100644 --- a/spec/models/eligibility_inquiry_spec.rb +++ b/spec/models/eligibility_inquiry_spec.rb @@ -9,12 +9,14 @@ expect(eligibility_inquiry.issued_by).to eq(admin) end - it 'sends an attendance warning email' do - allow(MemberMailer).to receive(:eligibility_check).with(member, member.email).and_call_original + it 'sends an eligibility check email' do + expect { + described_class.create(member: member, issued_by: admin) + }.to change { ActionMailer::Base.deliveries.count }.by(1) - described_class.create(member: member, issued_by: admin) - - expect(MemberMailer).to have_received(:eligibility_check).with(member, member.email) + email = ActionMailer::Base.deliveries.last + expect(email.to).to include(member.email) + expect(email.subject).to include('Eligibility') end end end From 8737dbd14fd1b03411c49adc5a8d6daf3fc94a93 Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Thu, 14 May 2026 11:19:03 +0200 Subject: [PATCH 08/11] refactor(tests): convert attendance warning email test to delivery assertions Replaces spy-based mock test with email delivery verification. Changes: - 'sends an attendance warning email' - finds email by recipient and subject - Uses flexible matching to account for other emails in deliveries Removes allow().to receive() and have_received() mocks --- spec/models/attendance_warning_spec.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spec/models/attendance_warning_spec.rb b/spec/models/attendance_warning_spec.rb index e3e4e6623..402ec7684 100644 --- a/spec/models/attendance_warning_spec.rb +++ b/spec/models/attendance_warning_spec.rb @@ -10,11 +10,10 @@ end it 'sends an attendance warning email' do - allow(MemberMailer).to receive(:attendance_warning).with(member, member.email).and_call_original - described_class.create(member: member, issued_by: admin) - expect(MemberMailer).to have_received(:attendance_warning).with(member, member.email) + email = ActionMailer::Base.deliveries.find { |e| e.to.include?(member.email) && e.subject.include?('Attendance') } + expect(email).not_to be_nil end describe '.scopes' do From efdc71d787fbb921b59c6e570fa7b6a23c465a37 Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Thu, 14 May 2026 11:23:02 +0200 Subject: [PATCH 09/11] refactor(tests): convert subscription email feature tests to delivery assertions Replaces expect_any_instance_of mocks with ActionMailer::Base.deliveries verification for all welcome email scenarios. Changes: - First-time coach subscription - verifies email sent and contains 'coach' - First-time student subscription - verifies email sent and contains 'student' - Second subscription tests - clears deliveries and verifies no new emails - Unsubscribe/resubscribe tests - verifies no duplicate emails sent Adds before hook to clear deliveries for consistent test isolation. Removes all expect_any_instance_of(MemberMailer) mocks which were problematic for parallel test execution. --- spec/features/subscribing_to_emails_spec.rb | 42 ++++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/spec/features/subscribing_to_emails_spec.rb b/spec/features/subscribing_to_emails_spec.rb index 044fbab3b..7d99f35ed 100644 --- a/spec/features/subscribing_to_emails_spec.rb +++ b/spec/features/subscribing_to_emails_spec.rb @@ -24,62 +24,76 @@ end context 'a member receives a welcome email' do + before do + ActionMailer::Base.deliveries.clear + end + scenario 'Subscribing to a coach mailing list for the first time sends a coach email to the user' do coach_group = Fabricate(:coaches) - expect_any_instance_of(MemberMailer).to receive(:welcome_coach) - expect_any_instance_of(MemberMailer).not_to receive(:welcome_students) visit subscriptions_path click_on "#{coach_group.chapter.name}-coaches" + + welcome_emails = ActionMailer::Base.deliveries.select { |e| e.to.include?(member.email) } + expect(welcome_emails.count).to eq(1) + expect(welcome_emails.first.body.encoded).to include('coach') end scenario 'Subscribing to a student mailing list for the first time sends a student email to the user' do - expect_any_instance_of(MemberMailer).to receive(:welcome_student) - expect_any_instance_of(MemberMailer).not_to receive(:welcome_coach) - visit subscriptions_path click_on "#{group.chapter.name}-students" + + welcome_emails = ActionMailer::Base.deliveries.select { |e| e.to.include?(member.email) } + expect(welcome_emails.count).to eq(1) + expect(welcome_emails.first.body.encoded).to include('student') end scenario "Subscribing to a second coach mailing list doesn't send another mail" do coach_groups = Fabricate.times(2, :coaches) - expect_any_instance_of(MemberMailer).to receive(:welcome_coach).once - expect_any_instance_of(MemberMailer).not_to receive(:welcome_students) visit subscriptions_path click_on "#{coach_groups[0].chapter.name}-coaches" + ActionMailer::Base.deliveries.clear click_on "#{coach_groups[1].chapter.name}-coaches" + + welcome_emails = ActionMailer::Base.deliveries.select { |e| e.to.include?(member.email) } + expect(welcome_emails.count).to eq(0) end scenario "Subscribing to a second student mailing list doesn't send another mail" do - expect_any_instance_of(MemberMailer).to receive(:welcome_student).once - expect_any_instance_of(MemberMailer).not_to receive(:welcome_coach) extra_student_group = Fabricate(:students) visit subscriptions_path click_on "#{group.chapter.name}-students" + ActionMailer::Base.deliveries.clear click_on "#{extra_student_group.chapter.name}-students" + + welcome_emails = ActionMailer::Base.deliveries.select { |e| e.to.include?(member.email) } + expect(welcome_emails.count).to eq(0) end scenario "Unsubscribing and re-subscribing doesn't send a second mail to a coach" do coach_group = Fabricate(:coaches) - expect_any_instance_of(MemberMailer).to receive(:welcome_coach).once - expect_any_instance_of(MemberMailer).not_to receive(:welcome_students) visit subscriptions_path click_on "#{coach_group.chapter.name}-coaches" + ActionMailer::Base.deliveries.clear click_on "#{coach_group.chapter.name}-coaches" click_on "#{coach_group.chapter.name}-coaches" + + welcome_emails = ActionMailer::Base.deliveries.select { |e| e.to.include?(member.email) } + expect(welcome_emails.count).to eq(0) end scenario "Unsubscribing and re-subscribing doesn't send a second mail to a student" do - expect_any_instance_of(MemberMailer).to receive(:welcome_student).once - expect_any_instance_of(MemberMailer).not_to receive(:welcome_coach) - visit subscriptions_path click_on "#{group.chapter.name}-students" + ActionMailer::Base.deliveries.clear click_on "#{group.chapter.name}-students" click_on "#{group.chapter.name}-students" + + welcome_emails = ActionMailer::Base.deliveries.select { |e| e.to.include?(member.email) } + expect(welcome_emails.count).to eq(0) end end end From 59459550387f77031bce2da952fa37e40e363ced Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Thu, 14 May 2026 11:24:18 +0200 Subject: [PATCH 10/11] refactor(tests): convert member mailer welcome tests to delivery assertions Replaces expect_any_instance_of mocks with direct mail content verification for welcome email tests. Changes: - 'sends the coach welcome email to coaches' - verifies email body contains coach text - 'sends the student welcome email to students' - verifies email body contains student text - 'sends a ban email to a member' - verifies recipient and email body Removes all expect_any_instance_of(MemberMailer) mocks which were problematic for parallel test execution. --- spec/mailers/member_mailer_spec.rb | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/spec/mailers/member_mailer_spec.rb b/spec/mailers/member_mailer_spec.rb index 77c6566ff..ac88ac5d5 100644 --- a/spec/mailers/member_mailer_spec.rb +++ b/spec/mailers/member_mailer_spec.rb @@ -81,24 +81,27 @@ it 'sends the coach welcome email to coaches' do member = Fabricate(:coach) - expect_any_instance_of(MemberMailer).to receive(:welcome_coach) - expect_any_instance_of(MemberMailer).not_to receive(:welcome_student) - MemberMailer.welcome(member).deliver_now + mail = MemberMailer.welcome(member).deliver_now + + expect(mail.body.encoded).to match('depends on coaches attending') end it 'sends the student welcome email to students' do member = Fabricate(:student) - expect_any_instance_of(MemberMailer).not_to receive(:welcome_coach) - expect_any_instance_of(MemberMailer).to receive(:welcome_student) - MemberMailer.welcome(member).deliver_now + + mail = MemberMailer.welcome(member).deliver_now + + expect(mail.body.encoded).to match('Spots are limited') end it 'sends a ban email to a member' do member = Fabricate(:member) ban = Fabricate(:ban) - expect_any_instance_of(MemberMailer).to receive(:ban).with(member, ban) - MemberMailer.ban(member, ban).deliver_now + mail = MemberMailer.ban(member, ban).deliver_now + + expect(mail.to).to eq([member.email]) + expect(mail.body.encoded).to match('your account has been suspended') end it 'actually sends a coach email' do From 662ab6aa7ae2d9739ee636842b116f7b32159fa2 Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Tue, 26 May 2026 21:43:06 +0200 Subject: [PATCH 11/11] refactor(tests): fix waiting list reminder assertion The delivery-assertion refactor incorrectly asserted that already-reminded invitations should have reminded_at as nil. The fabricator sets reminded_at to 2.days.ago, so the correct assertion is that it remains unchanged (still ~2 days ago). --- spec/models/invitation_manager_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/models/invitation_manager_spec.rb b/spec/models/invitation_manager_spec.rb index 58e1c55c9..2f6d7980c 100644 --- a/spec/models/invitation_manager_spec.rb +++ b/spec/models/invitation_manager_spec.rb @@ -156,7 +156,9 @@ }.to change { ActionMailer::Base.deliveries.count }.by(invitations.count) invitations.each { |invitation| expect(invitation.reload.reminded_at).not_to be_nil } - reminded_invitations.each { |invitation| expect(invitation.reload.reminded_at).to be_nil } + reminded_invitations.each do |invitation| + expect(invitation.reload.reminded_at).to be_within(1.second).of(2.days.ago) + end emails = ActionMailer::Base.deliveries.last(invitations.count) invitation_emails = invitations.map { |i| i.member.email }