diff --git a/app/models/newsletter.rb b/app/models/newsletter.rb index 8c3a83cfa..e5aac95bf 100644 --- a/app/models/newsletter.rb +++ b/app/models/newsletter.rb @@ -20,9 +20,41 @@ class Newsletter < ActiveRecord::Base sent_at.nil? end + def deliver + run_at = first_batch_run_at + list_of_recipient_emails_in_batches.each do |recipient_emails| + recipient_emails.each do |recipient_email| + Mailer.delay(run_at: run_at).newsletter(self, recipient_email) + if valid_email?(recipient_email) + end + end + run_at += batch_interval + end + end + + def batch_size + 10000 + end + + def batch_interval + 20.minutes + end + + def first_batch_run_at + Time.current + end + + def list_of_recipient_emails_in_batches + list_of_recipient_emails.in_groups_of(batch_size, false) + end + private def validate_segment_recipient errors.add(:segment_recipient, :invalid) unless valid_segment_recipient? end + + def valid_email?(email) + email.match(/\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i) + end end diff --git a/spec/models/newsletter_spec.rb b/spec/models/newsletter_spec.rb index bd42e9177..788326e88 100644 --- a/spec/models/newsletter_spec.rb +++ b/spec/models/newsletter_spec.rb @@ -63,4 +63,80 @@ describe Newsletter do expect(newsletter.list_of_recipient_emails).not_to include('erased_user@consul.dev') end end + + describe "#deliver" do + let!(:proposals) { Array.new(3) { create(:proposal) } } + let!(:debate) { create(:debate) } + + let!(:recipients) { proposals.map(&:author).map(&:email) } + let!(:newsletter) { create(:newsletter, segment_recipient: "proposal_authors") } + + before do + reset_mailer + Delayed::Worker.delay_jobs = true + end + + after do + Delayed::Worker.delay_jobs = false + end + + it "sends an email with the newsletter to every recipient" do + newsletter.deliver + + recipients.each do |recipient| + email = Mailer.newsletter(newsletter, recipient) + expect(email).to deliver_to(recipient) + end + + Delayed::Job.all.map(&:invoke_job) + expect(ActionMailer::Base.deliveries.count).to eq(3) + end + + it "sends emails in batches" do + allow(newsletter).to receive(:batch_size).and_return(1) + + newsletter.deliver + + expect(Delayed::Job.count).to eq(3) + end + + it "sends batches in time intervals" do + allow(newsletter).to receive(:batch_size).and_return(1) + allow(newsletter).to receive(:batch_interval).and_return(1.second) + allow(newsletter).to receive(:first_batch_run_at).and_return(Time.current) + + newsletter.deliver + + now = newsletter.first_batch_run_at + first_batch_run_at = now.change(usec: 0) + second_batch_run_at = (now + 1.second).change(usec: 0) + third_batch_run_at = (now + 2.seconds).change(usec: 0) + + expect(Delayed::Job.count).to eq(3) + expect(Delayed::Job.first.run_at.change(usec: 0)).to eq(first_batch_run_at) + expect(Delayed::Job.second.run_at.change(usec: 0)).to eq(second_batch_run_at) + expect(Delayed::Job.third.run_at.change(usec: 0)).to eq(third_batch_run_at) + end + + it "skips invalid emails" do + Proposal.destroy_all + + valid_email = "john@gmail.com" + invalid_email = "john@gmail..com" + + valid_email_user = create(:user, email: valid_email) + proposal = create(:proposal, author: valid_email_user) + + invalid_email_user = create(:user, email: invalid_email) + proposal = create(:proposal, author: invalid_email_user) + + newsletter.deliver + + expect(Activity.count).to eq(1) + expect(Activity.first.user_id).to eq(valid_email_user.id) + expect(Activity.first.action).to eq("email") + expect(Activity.first.actionable).to eq(newsletter) + end + + end end