RSpec.describe BalancerDeliveryMethod do class ExampleMailer < ApplicationMailer include BalancedDeliveryConcern def greet(name, bypass_unverified_mail_protection: true, **mail_args) mail(to: name, from: "smtp_from", body: "Hello #{name}", **mail_args) bypass_unverified_mail_protection! if bypass_unverified_mail_protection end end class ImportantEmail < ApplicationMailer include BalancedDeliveryConcern before_action :set_x_deliver_with def greet(name) mail(to: name, from: "smtp_from", body: "Hello #{name}") bypass_unverified_mail_protection! end private def set_x_deliver_with headers['X-deliver-with'] = :mock_smtp end end class TestMail def self.deliveries @deliveries ||= [] end def self.deliveries=(val) @deliveries = val end attr_accessor :settings def initialize(values) @settings = values.dup end def deliver!(mail) Mail::SmtpEnvelope.new(mail) self.class.deliveries << mail end end class MockSmtp < TestMail; end class MockSendmail < TestMail; end class FixedSequence def initialize(sequence) @enumerator = sequence.each end def rand(_) @enumerator.next end end before do ActionMailer::Base.add_delivery_method :mock_smtp, MockSmtp ActionMailer::Base.add_delivery_method :mock_sendmail, MockSendmail ActionMailer::Base.add_delivery_method :balancer, BalancerDeliveryMethod ExampleMailer.delivery_method = :balancer ImportantEmail.delivery_method = :balancer end context 'when a single delivery method is provided' do before do ActionMailer::Base.balancer_settings = { mock_smtp: 10 } end it 'sends emails to the selected delivery method' do mail = ExampleMailer.greet('Joshua').deliver_now expect(mail).to have_been_delivered_using(MockSmtp) end end context 'when multiple delivery methods are provided' do before do ActionMailer::Base.balancer_settings = { mock_smtp: 10, mock_sendmail: 5 } rng_sequence = [3, 14, 1] BalancerDeliveryMethod.random = FixedSequence.new(rng_sequence) end after do BalancerDeliveryMethod.random = Random.new end it 'sends emails randomly, given the provided weights' do mail1 = ExampleMailer.greet('Lucia').deliver_now expect(mail1).to have_been_delivered_using(MockSmtp) mail2 = ExampleMailer.greet('Damian').deliver_now expect(mail2).to have_been_delivered_using(MockSendmail) mail3 = ExampleMailer.greet('Rahwa').deliver_now expect(mail3).to have_been_delivered_using(MockSmtp) end end context 'when observers are configured' do let(:observer) { double("Observer") } before do allow(observer).to receive(:delivered_email) ActionMailer::Base.register_observer(observer) end after do ActionMailer::Base.unregister_observer(observer) end it 'invoke the observer exactly once' do mail = ExampleMailer.greet('Joshua').deliver_now expect(observer).to have_received(:delivered_email).with(mail).once end end context 'SafeMailer.important_email_use_delivery_method is present' do before do allow(SafeMailer).to receive(:important_email_use_delivery_method).and_return(delivery_method) ActionMailer::Base.balancer_settings = { mock_smtp: 10, mock_sendmail: 5 } rng_sequence = [3, 14, 1] BalancerDeliveryMethod.random = FixedSequence.new(rng_sequence) end after do BalancerDeliveryMethod.random = Random.new end context 'known delivery_method & email is important' do let(:delivery_method) { :mock_smtp } it 'sends emails given the forced_delivery_method' do mail1 = ImportantEmail.greet('Lucia').deliver_now expect(mail1).to have_been_delivered_using(MockSmtp) mail2 = ImportantEmail.greet('Damian').deliver_now expect(mail2).to have_been_delivered_using(MockSmtp) mail3 = ImportantEmail.greet('Rahwa').deliver_now expect(mail3).to have_been_delivered_using(MockSmtp) end end end context 'when the email does not bypass unverified mail protection' do let(:mail) { ExampleMailer.greet(email, bypass_unverified_mail_protection:) } let(:bypass_unverified_mail_protection) { false } before do ActionMailer::Base.balancer_settings = { mock_smtp: 10 } mail.deliver_now end context 'when the email belongs to a user' do let(:email) { user.email } let(:user) { create(:user, email: 'u@a.com', email_verified_at:) } context 'and the email is not verified' do let(:email_verified_at) { nil } it { expect(mail).not_to have_been_delivered_using(MockSmtp) } end context 'and the email is not verified but a bypass flag is added' do let(:email_verified_at) { nil } let(:bypass_unverified_mail_protection) { true } it { expect(mail).to have_been_delivered_using(MockSmtp) } end context 'and the email is verified' do let(:email_verified_at) { Time.current } it { expect(mail).to have_been_delivered_using(MockSmtp) } end end context 'when the email belongs to a individual' do let(:email) { individual.email } let(:individual) { create(:individual, email: 'u@a.com', email_verified_at:) } context 'and the email is not verified' do let(:email_verified_at) { nil } it { expect(mail).not_to have_been_delivered_using(MockSmtp) } end context 'and the email is verified' do let(:email_verified_at) { Time.current } it { expect(mail).to have_been_delivered_using(MockSmtp) } end end context 'when there are only bcc recipients' do let(:bypass_unverified_mail_protection) { false } let(:mail) { ExampleMailer.greet(nil, bypass_unverified_mail_protection: false, bcc: ["'u@a.com'"]) } it { expect(mail).to have_been_delivered_using(MockSmtp) } end end # Helpers def have_been_delivered_using(delivery_class) satisfy("have been delivered using #{delivery_class}") do |mail| delivery_class.deliveries.include?(mail) end end end