Serializza gli invii SMTP su una coda unica per non far chiudere Aruba.
CI / scan_js (push) Has been cancelled
CI / scan_ruby (push) Has been cancelled
CI / lint (push) Has been cancelled

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-07 12:06:23 +02:00
co-authored by Cursor
parent a9470d7e77
commit 403fe80d37
12 changed files with 423 additions and 29 deletions
@@ -0,0 +1,106 @@
require "test_helper"
class Mailings::OutboundQueueTest < ActiveSupport::TestCase
include ActionMailer::TestHelper
setup do
Mailings::OutboundQueue.min_interval = 0
Mailings::OutboundQueue.reset!
Mailings::SmtpGate.min_gap = 0
Mailings::SmtpGate.backoff_base = 0
Mailings::SmtpGate.reset!
end
teardown do
Mailings::OutboundQueue.min_interval = 0
Mailings::OutboundQueue.reset!
end
test "campaign jobs only enqueue, drain sends one email at a time" do
first = create_campaign_org(name: "Club Uno", email: "uno@example.com")
second = create_campaign_org(name: "Club Due", email: "due@example.com")
mailing_a = create_sending_mailing(name: "Campagna A")
mailing_b = create_sending_mailing(name: "Campagna B")
recipient_a = enqueue_org(mailing_a, first)
recipient_b = enqueue_org(mailing_b, second)
assert_equal "queued", recipient_a.reload.status
assert_equal "queued", recipient_b.status
assert_emails 1 do
assert_equal :sent, Mailings::OutboundQueue.drain_one!
end
assert_equal "sent", recipient_a.reload.status
assert_equal "queued", recipient_b.reload.status
assert_emails 1 do
assert_equal :sent, Mailings::OutboundQueue.drain_one!
end
assert_equal "sent", recipient_b.reload.status
assert_equal :empty, Mailings::OutboundQueue.drain_one!
end
test "does not start a second SMTP session while the lock is held" do
org = create_campaign_org(name: "Club Lock", email: "lock@example.com")
mailing = create_sending_mailing(name: "Campagna lock")
enqueue_org(mailing, org)
started = Queue.new
release = Queue.new
result = nil
thread = Thread.new do
Mailings::OutboundQueue.exclusive do
started << true
release.pop
end
end
started.pop
result = Mailings::OutboundQueue.drain_one!
release << true
thread.join
assert_equal :busy, result
assert_equal "queued", mailing.mailing_recipients.first.reload.status
end
test "transient SMTP errors stay queued and the next drain tries another recipient" do
first = create_campaign_org(name: "Club EOF", email: "eof@example.com")
second = create_campaign_org(name: "Club Ok", email: "ok@example.com")
mailing = create_sending_mailing(name: "Campagna eof")
recipient_a = enqueue_org(mailing, first)
recipient_b = enqueue_org(mailing, second)
gate = Mailings::SmtpGate
original = gate.method(:deliver)
gate.define_singleton_method(:deliver) { raise EOFError, "end of file reached" }
begin
assert_equal :deferred, Mailings::OutboundQueue.drain_one!
ensure
gate.define_singleton_method(:deliver, original)
end
recipient_a.reload
assert_equal "queued", recipient_a.status
assert_match(/Da ritentare \(1\/8\)/, recipient_a.error_message)
assert_equal recipient_b.id, Mailings::OutboundQueue.next_queued.id
end
private
def create_sending_mailing(name:)
mailing = create_mailing(name: name, audience: "to_send", interval_seconds: 0)
mailing.update!(status: "sending", queued_at: Time.current, test_sent_at: Time.current, test_sent_to: "test@example.com")
mailing
end
def enqueue_org(mailing, org)
recipient = mailing.mailing_recipients.create!(
organization: org,
contact: org.contacts.first,
email: org.email,
status: "pending"
)
assert Mailings::OutboundQueue.enqueue(recipient.reload)
recipient
end
end
+69
View File
@@ -0,0 +1,69 @@
require "test_helper"
class Mailings::SmtpGateTest < ActiveSupport::TestCase
setup do
Mailings::SmtpGate.min_gap = 0
Mailings::SmtpGate.backoff_base = 0
Mailings::SmtpGate.max_attempts = 4
Mailings::SmtpGate.reset!
end
teardown do
Mailings::SmtpGate.min_gap = 0
Mailings::SmtpGate.backoff_base = 0
Mailings::SmtpGate.max_attempts = 4
Mailings::SmtpGate.reset!
end
test "retries EOFError then succeeds" do
hits = 0
result = Mailings::SmtpGate.deliver do
hits += 1
raise EOFError, "end of file reached" if hits < 3
:sent
end
assert_equal :sent, result
assert_equal 3, hits
end
test "gives up after max attempts" do
hits = 0
error = assert_raises(EOFError) do
Mailings::SmtpGate.deliver do
hits += 1
raise EOFError, "end of file reached"
end
end
assert_equal "end of file reached", error.message
assert_equal Mailings::SmtpGate.max_attempts, hits
end
test "does not retry permanent SMTP errors" do
hits = 0
assert_raises(Net::SMTPAuthenticationError) do
Mailings::SmtpGate.deliver do
hits += 1
raise Net::SMTPAuthenticationError, "535 authentication failed"
end
end
assert_equal 1, hits
end
test "serializes concurrent deliveries" do
order = Queue.new
threads = 2.times.map do |i|
Thread.new do
Mailings::SmtpGate.deliver do
order << i
sleep 0.05
end
end
end
threads.each(&:join)
assert_equal 2, order.size
end
end