mirror of
https://github.com/lingble/chatwoot.git
synced 2025-10-31 19:17:48 +00:00
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>
87 lines
3.2 KiB
Ruby
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
|