Files
chatwoot/spec/mailboxes/reply_mailbox_spec.rb
T
d1c482cb64 fix(email): strip null bytes from inbound mailbox messages (#14546)
Inbound emails containing null bytes could fail while Chatwoot persisted
the incoming message, causing IMAP sync to drop and repeatedly retry the
same malformed email. This strips null bytes at the inbound mailbox
message persistence boundary so the rest of the email can be saved
normally.

Fixes
https://linear.app/chatwoot/issue/CW-7165/argumenterror-string-contains-null-byte-argumenterror
Sentry: https://chatwoot-p3.sentry.io/issues/7405946944/

## Test
- [x] added specs
- [x] manually tested the logic on prod for the inbox(email) which was
throwing the error

## What changed

- Sanitizes null bytes only while building attributes for inbound
mailbox message persistence.
- Uses the sanitized source ID for both duplicate lookup and message
creation.
- Adds regression coverage for the mailbox message creation path.

---------

Co-authored-by: Sony Mathew <2040199+sony-mathew@users.noreply.github.com>
Co-authored-by: Sony Mathew <sony@chatwoot.com>
2026-06-08 17:12:25 +05:30

730 lines
29 KiB
Ruby

require 'rails_helper'
RSpec.describe ReplyMailbox do
include ActionMailbox::TestHelper
describe 'add mail as reply in a conversation' do
let(:account) { create(:account) }
let(:agent) { create(:user, email: 'agent1@example.com', account: account) }
let(:reply_mail) { create_inbound_email_from_fixture('reply.eml') }
let(:reply_to_mail) { create_inbound_email_from_fixture('reply_to.eml') }
let(:mail_with_quote) { create_inbound_email_from_fixture('mail_with_quote.eml') }
let(:conversation) { create(:conversation, assignee: agent, inbox: create(:inbox, account: account, greeting_enabled: false), account: account) }
let(:described_subject) { described_class.receive reply_mail }
let(:serialized_attributes) do
%w[bcc cc content_type date from headers html_content in_reply_to message_id multipart number_of_attachments references subject text_content
to auto_reply]
end
context 'with reply uuid present' do
before do
# this UUID is hardcoded in the reply.eml, that's why we are updating this
conversation.uuid = '6bdc3f4d-0bec-4515-a284-5d916fdde489'
conversation.save!
described_subject
end
it 'add the mail content as new message on the conversation' do
expect(conversation.messages.last.content).to eq("Let's talk about these images:")
end
it 'add the attachments' do
expect(conversation.messages.last.attachments.count).to eq(2)
end
it 'have proper content_attributes with details of email' do
expect(conversation.messages.last.content_attributes[:email].keys).to eq(serialized_attributes)
end
it 'set proper content_type' do
expect(conversation.messages.last.content_type).to eq('incoming_email')
end
end
context 'with in reply to email' do
let(:reply_mail_without_uuid) { create_inbound_email_from_fixture('reply_mail_without_uuid.eml') }
let(:described_subject) { described_class.receive reply_mail_without_uuid }
let(:email_channel) { create(:channel_email, email: 'test@example.com', account: account) }
let(:conversation_1) do
create(
:conversation,
assignee: agent,
inbox: email_channel.inbox,
account: account,
additional_attributes: { mail_subject: "Discussion: Let's debate these attachments" }
)
end
before do
conversation_1.update!(uuid: '6bdc3f4d-0bec-4515-a284-5d916fdde489')
reply_mail_without_uuid.mail['In-Reply-To'] = '<conversation/6bdc3f4d-0bec-4515-a284-5d916fdde489/messages/123@test.com>'
end
it 'find channel with in-reply-to mail' do
described_subject
expect(conversation_1.messages.last.content).to include("Let's talk about these images:")
end
end
context 'when new conversation email contains null bytes' do
let(:email_channel) { create(:channel_email, email: 'test@example.com', account: account) }
let(:null_byte_mail) { create_inbound_email_from_mail(from: 'sender@example.com', to: email_channel.email, subject: 'Hello') }
let(:mail_with_null_bytes) do
Mail.new.tap do |mail|
mail.from = 'sender@example.com'
mail.to = email_channel.email
mail.subject = "Hello\u0000"
mail.message_id = "message\u0000@example.com"
mail['In-Reply-To'] = "source\u0000@example.com"
mail.references = ["reference\u0000@example.com"]
mail.content_type = 'text/plain'
mail.body = "Body\u0000 text"
end
end
before do
allow(null_byte_mail).to receive(:mail).and_return(mail_with_null_bytes)
end
it 'creates sanitized conversation and message records' do
expect { described_class.receive null_byte_mail }.to change(Conversation, :count).by(1)
conversation = Conversation.last
message = conversation.messages.last
expect(conversation.additional_attributes['in_reply_to']).to eq('source@example.com')
expect(conversation.additional_attributes['mail_subject']).to eq('Hello')
expect(message.source_id).to eq('message@example.com')
expect(message.content).to eq('Body text')
expect(message.content_attributes.to_json).not_to include('\u0000')
end
end
context 'with inline attachments' do
let(:mail_with_inline_images) { create_inbound_email_from_fixture('mail_with_inline_images.eml') }
let(:described_subject) { described_class.receive mail_with_inline_images }
before do
conversation.uuid = '6bdc3f4d-0bed-4515-a284-5d916fdde489'
conversation.save!
described_subject
end
it 'mail content contains img source' do
expect(conversation.messages.last.content).to include('HTML content and inline images')
end
it 'will not add the attachments' do
expect(conversation.messages.last.attachments.count).to eq(0)
html_full_content = conversation.messages.last.content_attributes[:email][:html_content][:full]
expect(html_full_content).to include('img')
expect(html_full_content).not_to include('cid:ii_lm7fuura0')
expect(html_full_content).not_to include('cid:ii_lm7fuuvm1')
expect(html_full_content).not_to include('cid:ii_lm7fuuwn2')
end
end
context 'with inline attachments and plain text' do
let(:mail_with_plain_text_and_inline_image) { create_inbound_email_from_fixture('mail_with_plain_text_and_inline_image.eml') }
let(:described_subject) { described_class.receive mail_with_plain_text_and_inline_image }
before do
conversation.uuid = '6bdc3f4d-0bed-4515-a284-5d916fdde489'
conversation.save!
described_subject
end
it 'will not add the attachments' do
described_class.receive mail_with_plain_text_and_inline_image
text_full_content = conversation.messages.last.content_attributes[:email][:text_content][:full]
expect(text_full_content).to include('img')
expect(text_full_content).not_to include('[image: poster (8).jpg]')
expect(text_full_content).not_to include('[image: poster (7).jpg]')
expect(text_full_content).not_to include('[image: poster (1).jpg]')
expect(conversation.messages.last.content).to include("Let's add no HTML content here, just plain text and images")
expect(conversation.messages.last.attachments.count).to eq(0)
end
end
context 'with inline attachments and html_part' do
let(:mail_with_html_part_no_html_content) { create_inbound_email_from_fixture('mail_with_html_part_no_html_content.eml') }
let(:described_subject) { described_class.receive mail_with_html_part_no_html_content }
before do
conversation.uuid = '6bdc3f4d-0bed-4515-a284-5d916fdde489'
conversation.save!
described_subject
end
it 'mail content will create message' do
expect(conversation.messages.last.content).to include('This is test message.')
end
it 'html_content is empty' do
expect(conversation.messages.last.attachments.count).to eq(0)
html_full_content = conversation.messages.last.content_attributes[:email][:html_content][:full]
expect(html_full_content).to be_nil
end
end
context 'with reply_to email address present' do
let(:email_channel) { create(:channel_email, email: 'test@example.com', account: account) }
let(:conversation_1) do
create(
:conversation,
assignee: agent,
inbox: email_channel.inbox,
account: account,
additional_attributes: { mail_subject: "Discussion: Let's debate these attachments" }
)
end
before do
conversation_1.update!(uuid: '6bdc3f4d-0bec-4515-a284-5d916fdde489')
end
it 'prefer reply-to over from address' do
described_class.receive reply_to_mail
expect(conversation_1.messages.last.content).to eq("Let's talk about these images:")
email = conversation_1.messages.last.content_attributes['email']
expect(reply_to_mail.mail['Reply-To'].value).to include(email['from'][0])
end
end
context 'without email to address' do
let(:forwarder_email) { create_inbound_email_from_fixture('forwarder_email.eml') }
let(:in_reply_to_email) { create_inbound_email_from_fixture('in_reply_to.eml') }
let(:described_subject) { described_class.receive forwarder_email }
let(:email_channel) { create(:channel_email, email: 'test@example.com', account: account) }
let(:conversation_1) do
create(
:conversation,
assignee: agent,
inbox: email_channel.inbox,
account: account,
additional_attributes: { mail_subject: "Discussion: Let's debate these attachments" }
)
end
before do
conversation_1.update!(uuid: '6bdc3f4d-0bec-4515-a284-5d916fdde489')
end
it 'find channel with forwarded to mail' do
described_subject
expect(conversation_1.messages.last.content).to eq("Let's talk about these images:")
end
it 'find channel with in message source id stated in in_reply_to' do
conversation_1.messages.new(source_id: '0CB459E0-0336-41DA-BC88-E6E28C697DDB@chatwoot.com', account_id: account.id, message_type: 'incoming',
inbox_id: email_channel.inbox.id).save!
described_class.receive in_reply_to_email
expect(conversation_1.messages.last.content).to eq("Let's talk about these images:")
end
end
context 'with quotes in email' do
let(:described_subject) { described_class.receive mail_with_quote }
before do
# this UUID is hardcoded in the reply.eml, that's why we are updating this
conversation.uuid = '6bdc3f4d-0bec-4515-a284-5d916fdde489'
conversation.save!
end
it 'add the mail content as new message on the conversation' do
described_subject
current_message = conversation.messages.last
expect(current_message.content).to eq(
<<~BODY.chomp
Yes, I am providing you step how to reproduce this issue
On Thu, Aug 19, 2021 at 2:07 PM Tejaswini from Email sender test < tejaswini@chatwoot.com> wrote:
> Any update on this?
>
>
--
* Sony Mathew*
Software developer
*Mob:9999999999
BODY
)
end
it 'add the mail content as new message on the conversation with broken html' do
described_subject
current_message = conversation.messages.last
expect(current_message.reload.content_attributes[:email][:text_content][:reply]).to eq(
<<~BODY.chomp
Yes, I am providing you step how to reproduce this issue
On Thu, Aug 19, 2021 at 2:07 PM Tejaswini from Email sender test < tejaswini@chatwoot.com> wrote:
> Any update on this?
>
>
--
* Sony Mathew*
Software developer
*Mob:9999999999
BODY
)
end
end
context 'with references header' do
let(:reply_mail_with_references) { create_inbound_email_from_fixture('reply_mail_without_uuid.eml') }
let(:email_channel) { create(:channel_email, email: 'test@example.com', account: account) }
let(:conversation_1) do
create(
:conversation,
assignee: agent,
inbox: email_channel.inbox,
account: account,
additional_attributes: { mail_subject: "Discussion: Let's debate these attachments" }
)
end
before do
conversation_1.update!(uuid: '6bdc3f4d-0bec-4515-a284-5d916fdde489')
end
context 'with message-specific pattern in references' do
before do
reply_mail_with_references.mail['References'] = '<conversation/6bdc3f4d-0bec-4515-a284-5d916fdde489/messages/123@test.com>'
end
it 'finds conversation from references header with message pattern' do
described_class.receive reply_mail_with_references
expect(conversation_1.messages.last.content).to include("Let's talk about these images:")
end
end
context 'with conversation fallback pattern in references' do
before do
reply_mail_with_references.mail['References'] = "<account/#{account.id}/conversation/6bdc3f4d-0bec-4515-a284-5d916fdde489@test.com>"
end
it 'finds conversation from references header with fallback pattern' do
described_class.receive reply_mail_with_references
expect(conversation_1.messages.last.content).to include("Let's talk about these images:")
end
end
context 'with multiple references including conversation pattern' do
before do
reply_mail_with_references.mail['References'] = [
'<some-random-message-id@gmail.com>',
"<account/#{account.id}/conversation/6bdc3f4d-0bec-4515-a284-5d916fdde489@test.com>",
'<another-random-message-id@outlook.com>'
].join("\r\n ")
end
it 'finds conversation from any reference in the chain' do
described_class.receive reply_mail_with_references
expect(conversation_1.messages.last.content).to include("Let's talk about these images:")
end
end
context 'with message source_id in references' do
before do
conversation_1.messages.create!(
source_id: 'original-message-id@test.com',
account_id: account.id,
message_type: 'outgoing',
inbox_id: email_channel.inbox.id,
content: 'Original message'
)
reply_mail_with_references.mail['References'] = '<original-message-id@test.com>'
end
it 'finds conversation from message source_id in references' do
described_class.receive reply_mail_with_references
expect(conversation_1.messages.last.content).to include("Let's talk about these images:")
end
end
context 'with conversation from different channel in references' do
let(:other_email_channel) { create(:channel_email, email: 'other@example.com', account: account) }
let(:other_conversation) do
create(
:conversation,
assignee: agent,
inbox: other_email_channel.inbox,
account: account
)
end
before do
other_conversation.update!(uuid: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee')
reply_mail_with_references.mail['References'] = '<conversation/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/messages/456@test.com>'
end
it 'does not use conversation from different channel' do
described_class.receive reply_mail_with_references
expect(other_conversation.messages.count).to eq(0)
end
end
end
end
describe 'when a chatwoot notification email is received' do
let(:account) { create(:account) }
let!(:channel_email) { create(:channel_email, email: 'sojan@chatwoot.com', account: account) }
let(:notification_mail) { create_inbound_email_from_fixture('notification.eml') }
let(:described_subject) { described_class.receive notification_mail }
let(:conversation) { Conversation.where(inbox_id: channel_email.inbox).last }
it 'shouldnt create a conversation in the channel' do
described_subject
expect(conversation.present?).to be(false)
end
end
describe 'when bounced email with out a sender is recieved' do
let(:account) { create(:account) }
let(:bounced_email) { create_inbound_email_from_fixture('bounced_with_no_from.eml') }
let(:described_subject) { described_class.receive bounced_email }
it 'shouldnt throw an error' do
create(:channel_email, email: 'support@example.com', account: account)
expect { described_subject }.not_to raise_error
end
end
describe 'when an account is suspended' do
let(:account) { create(:account, status: :suspended) }
let(:agent) { create(:user, email: 'agent1@example.com', account: account) }
let!(:channel_email) { create(:channel_email, account: account) }
let(:support_mail) { create_inbound_email_from_fixture('support.eml') }
let(:described_subject) { described_class.receive support_mail }
let(:conversation) { Conversation.where(inbox_id: channel_email.inbox).last }
before do
# this email is hardcoded in the support.eml, that's why we are updating this
channel_email.email = 'care@example.com'
channel_email.save!
end
it 'shouldnt create a conversation in the channel' do
described_subject
expect(conversation.present?).to be(false)
end
end
describe 'add mail as a new ticket in the email inbox' do
let(:account) { create(:account) }
let(:agent) { create(:user, email: 'agent1@example.com', account: account) }
let!(:channel_email) { create(:channel_email, account: account) }
let(:support_mail) { create_inbound_email_from_fixture('support.eml') }
let(:support_in_reply_to_mail) { create_inbound_email_from_fixture('support_in_reply_to.eml') }
let(:described_subject) { described_class.receive support_mail }
let(:serialized_attributes) do
%w[bcc cc content_type date from headers html_content in_reply_to message_id multipart number_of_attachments references subject
text_content to auto_reply]
end
let(:conversation) { Conversation.where(inbox_id: channel_email.inbox).last }
before do
# this email is hardcoded in the support.eml, that's why we are updating this
channel_email.email = 'care@example.com'
channel_email.save!
end
describe 'covers email address format' do
before do
described_class.receive support_in_reply_to_mail
end
it 'creates contact with proper email address' do
expect(support_in_reply_to_mail.mail['reply_to'].try(:value)).to eq('Sony Mathew <sony@chatwoot.com>')
expect(conversation.contact.email).to eq('sony@chatwoot.com')
end
end
describe 'covers basic ticket creation' do
before do
described_subject
end
it 'create the conversation in the inbox of the email channel' do
expect(conversation.inbox.id).to eq(channel_email.inbox.id)
expect(conversation.additional_attributes['source']).to eq('email')
expect(conversation.contact.email).to eq(support_mail.mail.from.first)
end
it 'create a new contact as the sender of the email' do
email_sender = Mail::Address.new(support_mail.mail[:from].value).name
expect(conversation.messages.last.sender.email).to eq(support_mail.mail.from.first)
expect(conversation.contact.name).to eq(email_sender)
end
it 'add the mail content as new message on the conversation' do
expect(conversation.messages.last.content).to eq("Let's talk about these images:")
end
it 'add the attachments' do
expect(conversation.messages.last.attachments.count).to eq(2)
end
it 'have proper content_attributes with details of email' do
expect(conversation.messages.last.content_attributes[:email].keys).to eq(serialized_attributes)
end
it 'set proper content_type' do
expect(conversation.messages.last.content_type).to eq('incoming_email')
end
end
describe 'email with references header' do
let(:mail_with_references) { create_inbound_email_from_fixture('mail_with_references.eml') }
let(:described_subject) { described_class.receive mail_with_references }
before do
# reuse the existing channel_email that's already set to 'care@example.com'
described_subject
end
it 'includes references in the message content_attributes' do
message = conversation.messages.last
email_attributes = message.content_attributes['email']
expect(email_attributes['references']).to be_present
expect(email_attributes['references']).to eq(['4e6e35f5a38b4_479f13bb90078178@small-app-01.mail', 'test-reference-id'])
end
it 'includes references in serialized email attributes' do
message = conversation.messages.last
expect(message.content_attributes['email'].keys).to include('references')
end
end
describe 'Sender without name' do
let(:support_mail_without_sender_name) { create_inbound_email_from_fixture('support_without_sender_name.eml') }
let(:described_subject) { described_class.receive support_mail_without_sender_name }
it 'create a new contact with the email' do
described_subject
email_sender = support_mail_without_sender_name.mail.from.first.split('@').first
expect(conversation.messages.last.sender.email).to eq(support_mail.mail.from.first)
expect(conversation.contact.name).to eq(email_sender)
end
end
describe 'Sender with upcase mail address' do
let(:support_mail_without_sender_name) { create_inbound_email_from_fixture('support_without_sender_name.eml') }
let(:described_subject) { described_class.receive support_mail_without_sender_name }
it 'create a new inbox with the email case insensitive' do
described_subject
expect(conversation.inbox.id).to eq(channel_email.inbox.id)
end
end
describe 'handle inbox contacts' do
let!(:contact) { create(:contact, account: account, email: support_mail.mail.from.first) }
let!(:contact_inbox) { create(:contact_inbox, inbox: channel_email.inbox, contact: contact) }
it 'does not create new contact if that contact exists in the inbox' do
expect do
described_subject
end
.to(not_change { Contact.count }
.and(not_change { ContactInbox.count }))
expect(conversation.messages.last.sender.id).to eq(contact.id)
expect(conversation.contact_inbox).to eq(contact_inbox)
end
context 'with uppercase reply-to' do
let(:support_mail) { create_inbound_email_from_fixture('support_uppercase.eml') }
let!(:contact) { create(:contact, account: account, email: support_mail.mail.from.first) }
let!(:contact_inbox) { create(:contact_inbox, inbox: channel_email.inbox, contact: contact) }
it 'does not create new contact if that contact exists in the inbox' do
expect do
described_subject
end
.to(not_change { Contact.count }
.and(not_change { ContactInbox.count }))
expect(conversation.messages.last.sender.id).to eq(contact.id)
expect(conversation.contact_inbox).to eq(contact_inbox)
end
end
end
describe 'group email sender' do
let(:group_sender_support_mail) { create_inbound_email_from_fixture('group_sender_support.eml') }
let(:described_subject) { described_class.receive group_sender_support_mail }
before do
# this email is hardcoded eml fixture file that's why we are updating this
channel_email.email = 'support@chatwoot.com'
channel_email.save!
end
it 'create new contact with original sender' do
described_subject
email_sender = Mail::Address.new(group_sender_support_mail.mail[:from].value).name
expect(conversation.contact.email).to eq(group_sender_support_mail.mail['X-Original-Sender'].value)
expect(conversation.contact.name).to eq(email_sender)
end
end
describe 'when mail has in reply to email' do
let(:reply_mail_without_uuid) { create_inbound_email_from_fixture('reply_mail_without_uuid.eml') }
let(:described_subject) { described_class.receive reply_mail_without_uuid }
let(:email_channel) { create(:channel_email, email: 'test@example.com', account: account) }
before do
email_channel
reply_mail_without_uuid.mail['In-Reply-To'] = 'conversation/6bdc3f4d-0bec-4515-a284-5d916fdde489/messages/123'
end
it 'create channel with reply to mail' do
described_subject
conversation_1 = Conversation.last
expect(conversation_1.messages.last.content).to eq("Let's talk about these images:")
expect(conversation_1.additional_attributes['in_reply_to']).to eq('conversation/6bdc3f4d-0bec-4515-a284-5d916fdde489/messages/123')
end
it 'append message to email conversation with same in reply to' do
described_subject
conversation_1 = Conversation.last
expect(conversation_1.messages.last.content).to eq("Let's talk about these images:")
expect(conversation_1.additional_attributes['in_reply_to']).to eq('conversation/6bdc3f4d-0bec-4515-a284-5d916fdde489/messages/123')
expect(conversation_1.messages.count).to eq(1)
reply_mail_without_uuid.mail['In-Reply-To'] = 'conversation/6bdc3f4d-0bec-4515-a284-5d916fdde489/messages/123'
reply_mail_without_uuid.mail['Message-Id'] = '0CB459E0-0336-41DA-BC88-E6E28C697SFC@chatwoot.com'
described_class.receive reply_mail_without_uuid
expect(conversation_1.messages.last.content).to eq("Let's talk about these images:")
expect(conversation_1.additional_attributes['in_reply_to']).to eq('conversation/6bdc3f4d-0bec-4515-a284-5d916fdde489/messages/123')
expect(conversation_1.messages.count).to eq(2)
end
end
describe 'Sender with reply_to email address' do
let(:reply_to_mail) { create_inbound_email_from_fixture('reply_to.eml') }
let(:email_channel) { create(:channel_email, email: 'test@example.com', account: account) }
it 'prefer reply-to over from address' do
email_channel
described_class.receive reply_to_mail
conversation_1 = Conversation.last
email = conversation_1.messages.last.content_attributes['email']
expect(reply_to_mail.mail['From'].value).to be_present
expect(conversation_1.messages.last.content).to eq("Let's talk about these images:")
expect(reply_to_mail.mail['Reply-To'].value).to include(email['from'][0])
expect(reply_to_mail.mail['Reply-To'].value).to include(conversation_1.contact.email)
expect(reply_to_mail.mail['From'].value).not_to include(conversation_1.contact.email)
end
end
describe 'when mail part is not present' do
let(:support_mail) { create_inbound_email_from_fixture('support_1.eml') }
let(:only_text) { create_inbound_email_from_fixture('only_text.eml') }
let(:only_html) { create_inbound_email_from_fixture('only_html.eml') }
let(:only_attachments) { create_inbound_email_from_fixture('only_attachments.eml') }
let(:html_and_attachments) { create_inbound_email_from_fixture('html_and_attachments.eml') }
let(:described_subject) { described_class.receive support_mail }
it 'Considers raw html mail body' do
described_subject
expect(conversation.inbox.id).to eq(channel_email.inbox.id)
expect(conversation.messages.last.content_attributes['email']['html_content']['reply']).to include(
<<~BODY.chomp
Hi,
We are providing you platform from here you can sell paid posts on your website.
Chatwoot | CS team | [C](https://d33wubrfki0l68.cloudfront.net/973467c532160fd8b940300a43fa85fa2d060307/dc9a0/static/brand-73f58cdefae282ae74cebfa74c1d7003.svg)
Skype: live:.cid.something
[]
BODY
)
expect(conversation.messages.last.content_attributes['email']['subject']).to eq('Get Paid to post an article')
end
it 'Considers only text body' do
described_class.receive only_text
expect(conversation.inbox.id).to eq(channel_email.inbox.id)
expect(conversation.messages.last.content).to eq('text only mail')
expect(conversation.messages.last.content_attributes['email']['subject']).to eq('test text only mail')
end
it 'Considers only html body' do
described_class.receive only_html
expect(conversation.inbox.id).to eq(channel_email.inbox.id)
expect(conversation.messages.last.content).to eq(
<<~BODY.chomp
This is html only mail
BODY
)
expect(conversation.messages.last.content_attributes['email']['subject']).to eq('test html only mail')
end
it 'Considers only attachments' do
described_class.receive only_attachments
expect(conversation.inbox.id).to eq(channel_email.inbox.id)
expect(conversation.messages.last.content).to be_nil
expect(conversation.messages.last.attachments.count).to eq(1)
expect(conversation.messages.last.content_attributes['email']['subject']).to eq('only attachments')
end
it 'Considers html and attachments' do
described_class.receive html_and_attachments
expect(conversation.inbox.id).to eq(channel_email.inbox.id)
expect(conversation.messages.last.content).to eq('This is html and attachments only mail')
expect(conversation.messages.last.attachments.count).to eq(1)
expect(conversation.messages.last.content_attributes['email']['subject']).to eq('attachment with html')
end
end
describe 'when BCC processing is disabled for account' do
before do
allow(GlobalConfigService).to receive(:load).with('SKIP_INCOMING_BCC_PROCESSING', '').and_return(account.id.to_s)
end
it 'does not process BCC-only emails' do
bcc_mail = create_inbound_email_from_fixture('support.eml')
bcc_mail.mail['to'] = nil
bcc_mail.mail['bcc'] = 'care@example.com'
described_class.receive bcc_mail
expect(conversation.present?).to be(false)
end
end
end
end