Aggiunge fascia oraria, prova manuale obbligatoria e cruscotto invii email.
Le campagne rispettano orario e giorni lavorativi, richiedono una mail di test con dati inseriti a mano prima dell'invio massivo e offrono una dashboard per monitorare l'avanzamento. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -11,9 +11,9 @@ class MailingsControllerTest < ActionDispatch::IntegrationTest
|
||||
login_as users(:admin)
|
||||
follow_redirect! if response.redirect?
|
||||
|
||||
get mailings_path(project_code: @project.code)
|
||||
get dashboard_mailings_path(project_code: @project.code)
|
||||
assert_response :success
|
||||
assert_match(/Email/, response.body)
|
||||
assert_match(/Invii email/, response.body)
|
||||
end
|
||||
|
||||
test "creates a draft and builds recipients" do
|
||||
@@ -48,18 +48,39 @@ class MailingsControllerTest < ActionDispatch::IntegrationTest
|
||||
assert_redirected_to mailing_path(mailing, project_code: @project.code)
|
||||
assert_equal "pending", recipient.reload.status
|
||||
|
||||
post test_send_mailing_path(mailing, project_code: @project.code), params: {
|
||||
to: users(:admin).email,
|
||||
vars: { societa: "ASD Test Calcio", contatto_nome: "Mario" }
|
||||
}
|
||||
assert mailing.reload.tested?
|
||||
|
||||
perform_enqueued_jobs do
|
||||
post queue_mailing_path(mailing, project_code: @project.code)
|
||||
end
|
||||
|
||||
assert_redirected_to dashboard_mailings_path(project_code: @project.code)
|
||||
assert_equal "sent", mailing.reload.status
|
||||
assert_equal "sent", recipient.reload.status
|
||||
assert_equal "sent", opportunities(:deal).reload.send_status
|
||||
assert_equal "contacted", opportunities(:deal).pipeline_stage
|
||||
assert_equal 1, ActionMailer::Base.deliveries.size
|
||||
assert_equal 2, ActionMailer::Base.deliveries.size
|
||||
assert_equal "Ciao ASD Test Calcio", ActionMailer::Base.deliveries.last.subject
|
||||
end
|
||||
|
||||
test "queue send requires test email first" do
|
||||
login_as users(:admin)
|
||||
follow_redirect! if response.redirect?
|
||||
mailing = create_mailing(identity: @identity, audience: "to_send")
|
||||
mailing.rebuild_recipients!
|
||||
recipient = mailing.mailing_recipients.first
|
||||
patch update_recipients_mailing_path(mailing, project_code: @project.code), params: { pending_ids: [recipient.id] }
|
||||
|
||||
post queue_mailing_path(mailing, project_code: @project.code)
|
||||
assert_redirected_to mailing_path(mailing, project_code: @project.code)
|
||||
assert_equal "draft", mailing.reload.status
|
||||
assert_not mailing.tested?
|
||||
end
|
||||
|
||||
test "test send goes to current user" do
|
||||
login_as users(:admin)
|
||||
follow_redirect! if response.redirect?
|
||||
@@ -67,11 +88,17 @@ class MailingsControllerTest < ActionDispatch::IntegrationTest
|
||||
mailing.rebuild_recipients!
|
||||
|
||||
assert_emails 1 do
|
||||
post test_send_mailing_path(mailing, project_code: @project.code)
|
||||
post test_send_mailing_path(mailing, project_code: @project.code), params: {
|
||||
to: users(:admin).email,
|
||||
vars: { societa: "Società Prova", contatto_nome: "Anna Test" }
|
||||
}
|
||||
end
|
||||
mail = ActionMailer::Base.deliveries.last
|
||||
assert_equal [users(:admin).email], mail.to
|
||||
assert_match(/\[TEST\]/, mail.subject)
|
||||
assert_match(/Società Prova/, mail.subject)
|
||||
assert mailing.reload.tested?
|
||||
assert_equal users(:admin).email, mailing.test_sent_to
|
||||
end
|
||||
|
||||
test "creates an A/B mailing and sends variant B as test" do
|
||||
@@ -97,7 +124,11 @@ class MailingsControllerTest < ActionDispatch::IntegrationTest
|
||||
assert_equal "A", mailing.mailing_recipients.first.ab_variant
|
||||
|
||||
assert_emails 1 do
|
||||
post test_send_mailing_path(mailing, project_code: @project.code, variant: "B")
|
||||
post test_send_mailing_path(mailing, project_code: @project.code), params: {
|
||||
to: users(:admin).email,
|
||||
variant: "B",
|
||||
vars: { societa: "ASD Test Calcio" }
|
||||
}
|
||||
end
|
||||
mail = ActionMailer::Base.deliveries.last
|
||||
assert_match(/\[TEST B\] Oggetto B ASD Test Calcio/, mail.subject)
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
require "test_helper"
|
||||
|
||||
class SendMailingRecipientJobTest < ActiveJob::TestCase
|
||||
include ActionMailer::TestHelper
|
||||
test "sends pending recipients in sequence" do
|
||||
opportunities(:deal).update!(send_status: "to_send")
|
||||
mailing = create_mailing(audience: "to_send")
|
||||
mailing.rebuild_recipients!
|
||||
mailing.update!(status: "sending", queued_at: Time.current)
|
||||
mailing.update!(status: "sending", queued_at: Time.current, test_sent_at: Time.current, test_sent_to: "test@example.com")
|
||||
|
||||
perform_enqueued_jobs do
|
||||
SendMailingRecipientJob.perform_later(mailing.mailing_recipients.first.id)
|
||||
@@ -14,4 +15,37 @@ class SendMailingRecipientJobTest < ActiveJob::TestCase
|
||||
assert_equal "sent", mailing.reload.status
|
||||
assert_equal "sent", mailing.mailing_recipients.first.status
|
||||
end
|
||||
|
||||
test "does not send outside the working window and resumes next workday" do
|
||||
opportunities(:deal).update!(send_status: "to_send")
|
||||
mailing = create_mailing(
|
||||
audience: "to_send",
|
||||
send_window_enabled: true,
|
||||
send_window_start_minutes: 10 * 60,
|
||||
send_window_end_minutes: 18 * 60,
|
||||
interval_seconds: 120
|
||||
)
|
||||
mailing.rebuild_recipients!
|
||||
recipient = mailing.mailing_recipients.first
|
||||
mailing.update!(test_sent_at: Time.current, test_sent_to: "test@example.com")
|
||||
|
||||
travel_to Time.zone.local(2026, 4, 3, 18, 30, 0) do
|
||||
mailing.update!(test_sent_at: Time.current, test_sent_to: "test@example.com")
|
||||
assert_no_emails do
|
||||
mailing.queue_send!
|
||||
end
|
||||
assert_equal "sending", mailing.reload.status
|
||||
assert_equal "pending", recipient.reload.status
|
||||
assert_equal Time.zone.local(2026, 4, 7, 10, 0, 0), mailing.next_send_at
|
||||
assert_enqueued_jobs 0, only: SendMailingRecipientJob
|
||||
end
|
||||
|
||||
travel_to Time.zone.local(2026, 4, 7, 10, 0, 0) do
|
||||
perform_enqueued_jobs do
|
||||
ResumeMailingsJob.perform_now
|
||||
end
|
||||
assert_equal "sent", recipient.reload.status
|
||||
assert_equal "sent", mailing.reload.status
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -33,4 +33,14 @@ class MailMergeTest < ActiveSupport::TestCase
|
||||
test "unknown tokens become empty string" do
|
||||
assert_equal "X Y", MailMerge.render("X {{sconosciuto}} Y")
|
||||
end
|
||||
|
||||
test "extras override variables for manual test data" do
|
||||
html = MailMerge.render(
|
||||
"Ciao {{contatto_nome}} di {{societa}}",
|
||||
organization: organizations(:acme),
|
||||
project: projects(:matchlivetv),
|
||||
extras: { societa: "Override SRL", contatto_nome: "Manual Test" }
|
||||
)
|
||||
assert_equal "Ciao Manual Test di Override SRL", html
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
require "test_helper"
|
||||
|
||||
class Mailings::SendClockTest < ActiveSupport::TestCase
|
||||
setup do
|
||||
@mailing = create_mailing(
|
||||
send_window_enabled: true,
|
||||
send_window_start_minutes: 10 * 60,
|
||||
send_window_end_minutes: 18 * 60,
|
||||
interval_seconds: 120
|
||||
)
|
||||
end
|
||||
|
||||
test "is open inside weekday window" do
|
||||
travel_to Time.zone.local(2026, 4, 1, 11, 0, 0) do
|
||||
clock = Mailings::SendClock.new(@mailing)
|
||||
assert clock.open?
|
||||
assert_equal Time.zone.now, clock.next_send_at
|
||||
end
|
||||
end
|
||||
|
||||
test "waits until window start the same weekday" do
|
||||
travel_to Time.zone.local(2026, 4, 1, 8, 30, 0) do
|
||||
clock = Mailings::SendClock.new(@mailing)
|
||||
assert_not clock.open?
|
||||
assert_equal Time.zone.local(2026, 4, 1, 10, 0, 0), clock.next_open_at
|
||||
end
|
||||
end
|
||||
|
||||
test "after window end skips weekend and Italian holiday to next workday" do
|
||||
# Venerdì 3 aprile 2026 18:30. Lunedi 6 è Pasquetta → martedì 7 alle 10:00.
|
||||
travel_to Time.zone.local(2026, 4, 3, 18, 30, 0) do
|
||||
clock = Mailings::SendClock.new(@mailing)
|
||||
assert_not clock.open?
|
||||
assert_equal Time.zone.local(2026, 4, 7, 10, 0, 0), clock.next_open_at
|
||||
end
|
||||
end
|
||||
|
||||
test "interval that would land after 18:00 moves to next workday" do
|
||||
travel_to Time.zone.local(2026, 4, 1, 17, 59, 0) do
|
||||
clock = Mailings::SendClock.new(@mailing)
|
||||
at = clock.next_send_at(earliest: Time.current + 120.seconds)
|
||||
assert_equal Time.zone.local(2026, 4, 2, 10, 0, 0), at
|
||||
end
|
||||
end
|
||||
|
||||
test "disabled window sends anytime including weekend" do
|
||||
@mailing.update!(send_window_enabled: false)
|
||||
travel_to Time.zone.local(2026, 4, 4, 21, 0, 0) do
|
||||
clock = Mailings::SendClock.new(@mailing)
|
||||
assert clock.open?
|
||||
assert_equal Time.zone.now, clock.next_send_at
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Italy::HolidaysTest < ActiveSupport::TestCase
|
||||
test "computes Easter and Italian fixed holidays" do
|
||||
assert_equal Date.new(2026, 4, 5), Italy::Holidays.easter_date(2026)
|
||||
assert Italy::Holidays.holiday?(Date.new(2026, 4, 5))
|
||||
assert Italy::Holidays.holiday?(Date.new(2026, 4, 6))
|
||||
assert Italy::Holidays.holiday?(Date.new(2026, 8, 15))
|
||||
assert_not Italy::Holidays.holiday?(Date.new(2026, 4, 7))
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user