Files
leadchat/spec/services/twilio/incoming_message_service_spec.rb
Muhsin Keloth 1577288843 fix: Twilo attachment download fallback to the step where authorization headers are not passed. (#8681)
In the previous release, we enabled "HTTP Basic Authentication" to secure all attachments requiring HTTP authentication. This is particularly important for media files that may contain sensitive data, as recommended by Twilio. However, some users experienced issues because they did not enable this option despite our alerts prompting them to do so.  If the authenticated attachment download call fails, add another call to download the attachment without authentication.
2024-01-11 17:23:31 -08:00

234 lines
8.5 KiB
Ruby

require 'rails_helper'
describe Twilio::IncomingMessageService do
let!(:account) { create(:account) }
let!(:twilio_channel) do
create(:channel_twilio_sms, account: account, account_sid: 'ACxxx',
inbox: create(:inbox, account: account, greeting_enabled: false))
end
let!(:contact) { create(:contact, account: account, phone_number: '+12345') }
let(:contact_inbox) { create(:contact_inbox, source_id: '+12345', contact: contact, inbox: twilio_channel.inbox) }
let!(:conversation) { create(:conversation, contact: contact, inbox: twilio_channel.inbox, contact_inbox: contact_inbox) }
describe '#perform' do
it 'creates a new message in existing conversation' do
params = {
SmsSid: 'SMxx',
From: '+12345',
AccountSid: 'ACxxx',
MessagingServiceSid: twilio_channel.messaging_service_sid,
Body: 'testing3'
}
described_class.new(params: params).perform
expect(conversation.reload.messages.last.content).to eq('testing3')
end
it 'removes null bytes' do
params = {
SmsSid: 'SMxx',
From: '+12345',
AccountSid: 'ACxxx',
MessagingServiceSid: twilio_channel.messaging_service_sid,
Body: "remove\u0000 null bytes\u0000"
}
described_class.new(params: params).perform
expect(conversation.reload.messages.last.content).to eq('remove null bytes')
end
it 'wont throw error when the body is empty' do
params = {
SmsSid: 'SMxx',
From: '+12345',
AccountSid: 'ACxxx',
MessagingServiceSid: twilio_channel.messaging_service_sid
}
described_class.new(params: params).perform
expect(conversation.reload.messages.last.content).to be_nil
end
it 'creates a new conversation when payload is from different number' do
params = {
SmsSid: 'SMxx',
From: '+123456',
AccountSid: 'ACxxx',
MessagingServiceSid: twilio_channel.messaging_service_sid,
Body: 'new conversation'
}
described_class.new(params: params).perform
expect(twilio_channel.inbox.conversations.count).to eq(2)
end
# Since we support the case with phone number as well. the previous case is with accoud_sid and messaging_service_sid
context 'with a phone number' do
let!(:twilio_channel) do
create(:channel_twilio_sms, :with_phone_number, account: account, account_sid: 'ACxxx',
inbox: create(:inbox, account: account, greeting_enabled: false))
end
it 'creates a new message in existing conversation' do
params = {
SmsSid: 'SMxx',
From: '+12345',
AccountSid: 'ACxxx',
To: twilio_channel.phone_number,
Body: 'testing3'
}
described_class.new(params: params).perform
expect(conversation.reload.messages.last.content).to eq('testing3')
end
it 'creates a new conversation when payload is from different number' do
params = {
SmsSid: 'SMxx',
From: '+123456',
AccountSid: 'ACxxx',
To: twilio_channel.phone_number,
Body: 'new conversation'
}
described_class.new(params: params).perform
expect(twilio_channel.inbox.conversations.count).to eq(2)
end
it 'reopen last conversation if last conversation is resolved and lock to single conversation is enabled' do
params = {
SmsSid: 'SMxx',
From: '+12345',
AccountSid: 'ACxxx',
To: twilio_channel.phone_number,
Body: 'testing3'
}
twilio_channel.inbox.update(lock_to_single_conversation: true)
conversation.update(status: 'resolved')
described_class.new(params: params).perform
# message appended to the last conversation
expect(conversation.reload.messages.last.content).to eq('testing3')
expect(conversation.reload.status).to eq('open')
end
it 'creates a new conversation if last conversation is resolved and lock to single conversation is disabled' do
params = {
SmsSid: 'SMxx',
From: '+12345',
AccountSid: 'ACxxx',
To: twilio_channel.phone_number,
Body: 'testing3'
}
twilio_channel.inbox.update(lock_to_single_conversation: false)
conversation.update(status: 'resolved')
described_class.new(params: params).perform
expect(twilio_channel.inbox.conversations.count).to eq(2)
expect(twilio_channel.inbox.conversations.last.messages.last.content).to eq('testing3')
end
it 'will not create a new conversation if last conversation is not resolved and lock to single conversation is disabled' do
params = {
SmsSid: 'SMxx',
From: '+12345',
AccountSid: 'ACxxx',
To: twilio_channel.phone_number,
Body: 'testing3'
}
twilio_channel.inbox.update(lock_to_single_conversation: false)
conversation.update(status: Conversation.statuses.except('resolved').keys.sample)
described_class.new(params: params).perform
expect(twilio_channel.inbox.conversations.count).to eq(1)
expect(twilio_channel.inbox.conversations.last.messages.last.content).to eq('testing3')
end
end
context 'with multiple channels configured' do
before do
2.times.each do
create(:channel_twilio_sms, :with_phone_number, account: account, account_sid: 'ACxxx', messaging_service_sid: nil,
inbox: create(:inbox, account: account, greeting_enabled: false))
end
end
it 'creates a new conversation in appropriate channel' do
twilio_sms_channel = create(:channel_twilio_sms, :with_phone_number, account: account, account_sid: 'ACxxx',
inbox: create(:inbox, account: account, greeting_enabled: false))
params = {
SmsSid: 'SMxx',
From: '+123456',
AccountSid: 'ACxxx',
To: twilio_sms_channel.phone_number,
Body: 'new conversation'
}
described_class.new(params: params).perform
expect(twilio_sms_channel.inbox.conversations.count).to eq(1)
end
end
context 'when a message with an attachment is received' do
before do
stub_request(:get, 'https://chatwoot-assets.local/sample.png')
.to_return(status: 200, body: 'image data', headers: {})
end
let(:params_with_attachment) do
{
SmsSid: 'SMxx',
From: '+12345',
AccountSid: 'ACxxx',
MessagingServiceSid: twilio_channel.messaging_service_sid,
Body: 'testing3',
NumMedia: '1',
MediaContentType0: 'image/jpeg',
MediaUrl0: 'https://chatwoot-assets.local/sample.png'
}
end
it 'creates a new message with media in existing conversation' do
described_class.new(params: params_with_attachment).perform
expect(conversation.reload.messages.last.content).to eq('testing3')
expect(conversation.reload.messages.last.attachments.count).to eq(1)
expect(conversation.reload.messages.last.attachments.first.file_type).to eq('image')
end
end
context 'when there is an error downloading the attachment' do
before do
stub_request(:get, 'https://chatwoot-assets.local/sample.png')
.to_raise(Down::Error.new('Download error'))
stub_request(:get, 'https://chatwoot-assets.local/sample.png')
.to_return(status: 200, body: 'image data', headers: {})
end
let(:params_with_attachment_error) do
{
SmsSid: 'SMxx',
From: '+12345',
AccountSid: 'ACxxx',
MessagingServiceSid: twilio_channel.messaging_service_sid,
Body: 'testing3',
NumMedia: '1',
MediaContentType0: 'image/jpeg',
MediaUrl0: 'https://chatwoot-assets.local/sample.png'
}
end
it 'retries downloading the attachment without a token after an error' do
expect do
described_class.new(params: params_with_attachment_error).perform
end.not_to raise_error
expect(conversation.reload.messages.last.content).to eq('testing3')
expect(conversation.reload.messages.last.attachments.count).to eq(1)
expect(conversation.reload.messages.last.attachments.first.file_type).to eq('image')
end
end
end
end