Files
chatwoot/spec/services/email/send_on_email_service_spec.rb
Pranav 254d5dcf9a chore: Migrate mailers from the worker to jobs (#12331)
Previously, email replies were handled inside workers. There was no
execution logs. This meant if emails silently failed (as reported by a
customer), we had no way to trace where the issue happened, the only
assumption was “no error = mail sent.”

By moving email handling into jobs, we now have proper execution logs
for each attempt. This makes it easier to debug delivery issues and
would have better visibility when investigating customer reports.

Fixes
https://linear.app/chatwoot/issue/CW-5538/emails-are-not-sentdelivered-to-the-contact

---------

Co-authored-by: Sojan Jose <sojan@pepalo.com>
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
2025-10-21 16:36:37 -07:00

87 lines
3.2 KiB
Ruby

require 'rails_helper'
describe Email::SendOnEmailService do
let(:account) { create(:account) }
let(:email_channel) { create(:channel_email, account: account) }
let(:inbox) { create(:inbox, account: account, channel: email_channel) }
let(:conversation) { create(:conversation, account: account, inbox: inbox) }
let(:message) { create(:message, conversation: conversation, message_type: 'outgoing') }
let(:service) { described_class.new(message: message) }
describe '#perform' do
let(:mailer_context) { instance_double(ConversationReplyMailer) }
let(:delivery) { instance_double(ActionMailer::MessageDelivery) }
let(:email_message) { instance_double(Mail::Message) }
before do
allow(ConversationReplyMailer).to receive(:with).with(account: message.account).and_return(mailer_context)
end
context 'when message is email notifiable' do
before do
allow(mailer_context).to receive(:email_reply).with(message).and_return(delivery)
allow(delivery).to receive(:deliver_now).and_return(email_message)
allow(email_message).to receive(:message_id).and_return(
"conversation/#{conversation.uuid}/messages/" \
"#{message.id}@#{conversation.account.domain}"
)
end
it 'sends email via ConversationReplyMailer' do
service.perform
expect(ConversationReplyMailer).to have_received(:with).with(account: message.account)
expect(mailer_context).to have_received(:email_reply).with(message)
expect(delivery).to have_received(:deliver_now)
end
it 'updates message source id on success' do
service.perform
expect(message.reload.source_id).to eq("conversation/#{conversation.uuid}/messages/#{message.id}@#{conversation.account.domain}")
end
end
context 'when message is not email notifiable' do
let(:message) { create(:message, conversation: conversation, message_type: 'incoming') }
before do
allow(mailer_context).to receive(:email_reply)
end
it 'does not send email' do
service.perform
expect(ConversationReplyMailer).not_to have_received(:with)
expect(mailer_context).not_to have_received(:email_reply)
end
end
context 'when an error occurs' do
let(:error_message) { 'SMTP connection failed' }
let(:error) { StandardError.new(error_message) }
let(:exception_tracker) { instance_double(ChatwootExceptionTracker, capture_exception: true) }
let(:status_service) { instance_double(Messages::StatusUpdateService, perform: true) }
before do
allow(mailer_context).to receive(:email_reply).with(message).and_return(delivery)
allow(delivery).to receive(:deliver_now).and_raise(error)
allow(ChatwootExceptionTracker).to receive(:new).and_return(exception_tracker)
end
it 'captures the exception' do
expect(ChatwootExceptionTracker).to receive(:new).with(error, account: message.account)
service.perform
end
it 'updates message status to failed' do
service.perform
expect(message.reload.status).to eq('failed')
expect(message.reload.external_error).to eq(error_message)
end
end
end
end