In this PR https://github.com/chatwoot/chatwoot/pull/11139, if there is an attempt to create a duplication session for the contact in the same inbox, we will anonymize the old session. This PR would prevent sending messages to the older sessions. The support agents will have to create a new conversation to continue messages with customer.
372 lines
14 KiB
Ruby
372 lines
14 KiB
Ruby
require 'rails_helper'
|
|
|
|
describe ContactInboxBuilder do
|
|
let(:account) { create(:account) }
|
|
let(:contact) { create(:contact, email: 'xyc@example.com', phone_number: '+23423424123', account: account) }
|
|
|
|
describe '#perform' do
|
|
describe 'twilio sms inbox' do
|
|
let!(:twilio_sms) { create(:channel_twilio_sms, account: account) }
|
|
let!(:twilio_inbox) { create(:inbox, channel: twilio_sms, account: account) }
|
|
|
|
it 'does not create contact inbox when contact inbox already exists with the source id provided' do
|
|
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: twilio_inbox, source_id: contact.phone_number)
|
|
contact_inbox = described_class.new(
|
|
contact: contact,
|
|
inbox: twilio_inbox,
|
|
source_id: contact.phone_number
|
|
).perform
|
|
|
|
expect(contact_inbox.id).to eq(existing_contact_inbox.id)
|
|
end
|
|
|
|
it 'does not create contact inbox when contact inbox already exists with phone number and source id is not provided' do
|
|
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: twilio_inbox, source_id: contact.phone_number)
|
|
contact_inbox = described_class.new(
|
|
contact: contact,
|
|
inbox: twilio_inbox
|
|
).perform
|
|
|
|
expect(contact_inbox.id).to eq(existing_contact_inbox.id)
|
|
end
|
|
|
|
it 'creates a new contact inbox when different source id is provided' do
|
|
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: twilio_inbox, source_id: contact.phone_number)
|
|
contact_inbox = described_class.new(
|
|
contact: contact,
|
|
inbox: twilio_inbox,
|
|
source_id: '+224213223422'
|
|
).perform
|
|
|
|
expect(contact_inbox.id).not_to eq(existing_contact_inbox.id)
|
|
expect(contact_inbox.source_id).to eq('+224213223422')
|
|
end
|
|
|
|
it 'creates a contact inbox with contact phone number when source id not provided and no contact inbox exists' do
|
|
contact_inbox = described_class.new(
|
|
contact: contact,
|
|
inbox: twilio_inbox
|
|
).perform
|
|
|
|
expect(contact_inbox.source_id).to eq(contact.phone_number)
|
|
end
|
|
|
|
it 'raises error when contact phone number is not present and no source id is provided' do
|
|
contact.update!(phone_number: nil)
|
|
|
|
expect do
|
|
described_class.new(
|
|
contact: contact,
|
|
inbox: twilio_inbox
|
|
).perform
|
|
end.to raise_error(ArgumentError, 'contact phone number required')
|
|
end
|
|
end
|
|
|
|
describe 'twilio whatsapp inbox' do
|
|
let!(:twilio_whatsapp) { create(:channel_twilio_sms, medium: :whatsapp, account: account) }
|
|
let!(:twilio_inbox) { create(:inbox, channel: twilio_whatsapp, account: account) }
|
|
|
|
it 'does not create contact inbox when contact inbox already exists with the source id provided' do
|
|
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: twilio_inbox, source_id: "whatsapp:#{contact.phone_number}")
|
|
contact_inbox = described_class.new(
|
|
contact: contact,
|
|
inbox: twilio_inbox,
|
|
source_id: "whatsapp:#{contact.phone_number}"
|
|
).perform
|
|
|
|
expect(contact_inbox.id).to eq(existing_contact_inbox.id)
|
|
end
|
|
|
|
it 'does not create contact inbox when contact inbox already exists with phone number and source id is not provided' do
|
|
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: twilio_inbox, source_id: "whatsapp:#{contact.phone_number}")
|
|
contact_inbox = described_class.new(
|
|
contact: contact,
|
|
inbox: twilio_inbox
|
|
).perform
|
|
|
|
expect(contact_inbox.id).to eq(existing_contact_inbox.id)
|
|
end
|
|
|
|
it 'creates a new contact inbox when different source id is provided' do
|
|
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: twilio_inbox, source_id: "whatsapp:#{contact.phone_number}")
|
|
contact_inbox = described_class.new(
|
|
contact: contact,
|
|
inbox: twilio_inbox,
|
|
source_id: 'whatsapp:+555555'
|
|
).perform
|
|
|
|
expect(contact_inbox.id).not_to eq(existing_contact_inbox.id)
|
|
expect(contact_inbox.source_id).to eq('whatsapp:+555555')
|
|
end
|
|
|
|
it 'creates a contact inbox with contact phone number when source id not provided and no contact inbox exists' do
|
|
contact_inbox = described_class.new(
|
|
contact: contact,
|
|
inbox: twilio_inbox
|
|
).perform
|
|
|
|
expect(contact_inbox.source_id).to eq("whatsapp:#{contact.phone_number}")
|
|
end
|
|
|
|
it 'raises error when contact phone number is not present and no source id is provided' do
|
|
contact.update!(phone_number: nil)
|
|
|
|
expect do
|
|
described_class.new(
|
|
contact: contact,
|
|
inbox: twilio_inbox
|
|
).perform
|
|
end.to raise_error(ArgumentError, 'contact phone number required')
|
|
end
|
|
end
|
|
|
|
describe 'whatsapp inbox' do
|
|
let(:whatsapp_inbox) { create(:channel_whatsapp, account: account, sync_templates: false, validate_provider_config: false).inbox }
|
|
|
|
it 'does not create contact inbox when contact inbox already exists with the source id provided' do
|
|
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: whatsapp_inbox, source_id: contact.phone_number&.delete('+'))
|
|
contact_inbox = described_class.new(
|
|
contact: contact,
|
|
inbox: whatsapp_inbox,
|
|
source_id: contact.phone_number&.delete('+')
|
|
).perform
|
|
|
|
expect(contact_inbox.id).to be(existing_contact_inbox.id)
|
|
end
|
|
|
|
it 'does not create contact inbox when contact inbox already exists with phone number and source id is not provided' do
|
|
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: whatsapp_inbox, source_id: contact.phone_number&.delete('+'))
|
|
contact_inbox = described_class.new(
|
|
contact: contact,
|
|
inbox: whatsapp_inbox
|
|
).perform
|
|
|
|
expect(contact_inbox.id).to be(existing_contact_inbox.id)
|
|
end
|
|
|
|
it 'creates a new contact inbox when different source id is provided' do
|
|
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: whatsapp_inbox, source_id: contact.phone_number&.delete('+'))
|
|
contact_inbox = described_class.new(
|
|
contact: contact,
|
|
inbox: whatsapp_inbox,
|
|
source_id: '555555'
|
|
).perform
|
|
|
|
expect(contact_inbox.id).not_to be(existing_contact_inbox.id)
|
|
expect(contact_inbox.source_id).not_to be('555555')
|
|
end
|
|
|
|
it 'creates a contact inbox with contact phone number when source id not provided and no contact inbox exists' do
|
|
contact_inbox = described_class.new(
|
|
contact: contact,
|
|
inbox: whatsapp_inbox
|
|
).perform
|
|
|
|
expect(contact_inbox.source_id).to eq(contact.phone_number&.delete('+'))
|
|
end
|
|
|
|
it 'raises error when contact phone number is not present and no source id is provided' do
|
|
contact.update!(phone_number: nil)
|
|
|
|
expect do
|
|
described_class.new(
|
|
contact: contact,
|
|
inbox: whatsapp_inbox
|
|
).perform
|
|
end.to raise_error(ArgumentError, 'contact phone number required')
|
|
end
|
|
end
|
|
|
|
describe 'sms inbox' do
|
|
let!(:sms_channel) { create(:channel_sms, account: account) }
|
|
let!(:sms_inbox) { create(:inbox, channel: sms_channel, account: account) }
|
|
|
|
it 'does not create contact inbox when contact inbox already exists with the source id provided' do
|
|
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: sms_inbox, source_id: contact.phone_number)
|
|
contact_inbox = described_class.new(
|
|
contact: contact,
|
|
inbox: sms_inbox,
|
|
source_id: contact.phone_number
|
|
).perform
|
|
|
|
expect(contact_inbox.id).to eq(existing_contact_inbox.id)
|
|
end
|
|
|
|
it 'does not create contact inbox when contact inbox already exists with phone number and source id is not provided' do
|
|
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: sms_inbox, source_id: contact.phone_number)
|
|
contact_inbox = described_class.new(
|
|
contact: contact,
|
|
inbox: sms_inbox
|
|
).perform
|
|
|
|
expect(contact_inbox.id).to eq(existing_contact_inbox.id)
|
|
end
|
|
|
|
it 'creates a new contact inbox when different source id is provided' do
|
|
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: sms_inbox, source_id: contact.phone_number)
|
|
contact_inbox = described_class.new(
|
|
contact: contact,
|
|
inbox: sms_inbox,
|
|
source_id: '+224213223422'
|
|
).perform
|
|
|
|
expect(contact_inbox.id).not_to eq(existing_contact_inbox.id)
|
|
expect(contact_inbox.source_id).to eq('+224213223422')
|
|
end
|
|
|
|
it 'creates a contact inbox with contact phone number when source id not provided and no contact inbox exists' do
|
|
contact_inbox = described_class.new(
|
|
contact: contact,
|
|
inbox: sms_inbox
|
|
).perform
|
|
|
|
expect(contact_inbox.source_id).to eq(contact.phone_number)
|
|
end
|
|
|
|
it 'raises error when contact phone number is not present and no source id is provided' do
|
|
contact.update!(phone_number: nil)
|
|
|
|
expect do
|
|
described_class.new(
|
|
contact: contact,
|
|
inbox: sms_inbox
|
|
).perform
|
|
end.to raise_error(ArgumentError, 'contact phone number required')
|
|
end
|
|
end
|
|
|
|
describe 'email inbox' do
|
|
let!(:email_channel) { create(:channel_email, account: account) }
|
|
let!(:email_inbox) { create(:inbox, channel: email_channel, account: account) }
|
|
|
|
it 'does not create contact inbox when contact inbox already exists with the source id provided' do
|
|
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: email_inbox, source_id: contact.email)
|
|
contact_inbox = described_class.new(
|
|
contact: contact,
|
|
inbox: email_inbox,
|
|
source_id: contact.email
|
|
).perform
|
|
|
|
expect(contact_inbox.id).to eq(existing_contact_inbox.id)
|
|
end
|
|
|
|
it 'does not create contact inbox when contact inbox already exists with email and source id is not provided' do
|
|
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: email_inbox, source_id: contact.email)
|
|
contact_inbox = described_class.new(
|
|
contact: contact,
|
|
inbox: email_inbox
|
|
).perform
|
|
|
|
expect(contact_inbox.id).to eq(existing_contact_inbox.id)
|
|
end
|
|
|
|
it 'creates a new contact inbox when different source id is provided' do
|
|
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: email_inbox, source_id: contact.email)
|
|
contact_inbox = described_class.new(
|
|
contact: contact,
|
|
inbox: email_inbox,
|
|
source_id: 'xyc@xyc.com'
|
|
).perform
|
|
|
|
expect(contact_inbox.id).not_to eq(existing_contact_inbox.id)
|
|
expect(contact_inbox.source_id).to eq('xyc@xyc.com')
|
|
end
|
|
|
|
it 'creates a contact inbox with contact email when source id not provided and no contact inbox exists' do
|
|
contact_inbox = described_class.new(
|
|
contact: contact,
|
|
inbox: email_inbox
|
|
).perform
|
|
|
|
expect(contact_inbox.source_id).to eq(contact.email)
|
|
end
|
|
|
|
it 'raises error when contact email is not present and no source id is provided' do
|
|
contact.update!(email: nil)
|
|
|
|
expect do
|
|
described_class.new(
|
|
contact: contact,
|
|
inbox: email_inbox
|
|
).perform
|
|
end.to raise_error(ArgumentError, 'contact email required')
|
|
end
|
|
end
|
|
|
|
describe 'api inbox' do
|
|
let!(:api_channel) { create(:channel_api, account: account) }
|
|
let!(:api_inbox) { create(:inbox, channel: api_channel, account: account) }
|
|
|
|
it 'does not create contact inbox when contact inbox already exists with the source id provided' do
|
|
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: api_inbox, source_id: 'test')
|
|
contact_inbox = described_class.new(
|
|
contact: contact,
|
|
inbox: api_inbox,
|
|
source_id: 'test'
|
|
).perform
|
|
|
|
expect(contact_inbox.id).to eq(existing_contact_inbox.id)
|
|
end
|
|
|
|
it 'creates a new contact inbox when different source id is provided' do
|
|
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: api_inbox, source_id: SecureRandom.uuid)
|
|
contact_inbox = described_class.new(
|
|
contact: contact,
|
|
inbox: api_inbox,
|
|
source_id: 'test'
|
|
).perform
|
|
|
|
expect(contact_inbox.id).not_to eq(existing_contact_inbox.id)
|
|
expect(contact_inbox.source_id).to eq('test')
|
|
end
|
|
|
|
it 'creates a contact inbox with SecureRandom.uuid when source id not provided and no contact inbox exists' do
|
|
contact_inbox = described_class.new(
|
|
contact: contact,
|
|
inbox: api_inbox
|
|
).perform
|
|
|
|
expect(contact_inbox.source_id).not_to be_nil
|
|
end
|
|
end
|
|
|
|
context 'when there is a race condition' do
|
|
let(:account) { create(:account) }
|
|
let(:contact) { create(:contact, account: account) }
|
|
let(:contact2) { create(:contact, account: account) }
|
|
let(:channel) { create(:channel_email, account: account) }
|
|
let(:channel_api) { create(:channel_api, account: account) }
|
|
let(:source_id) { 'source_123' }
|
|
|
|
it 'handles RecordNotUnique error by updating source_id and retrying' do
|
|
existing_contact_inbox = create(:contact_inbox, contact: contact2, inbox: channel.inbox, source_id: source_id)
|
|
|
|
described_class.new(
|
|
contact: contact,
|
|
inbox: channel.inbox,
|
|
source_id: source_id
|
|
).perform
|
|
|
|
expect(ContactInbox.last.source_id).to eq(source_id)
|
|
expect(ContactInbox.last.contact_id).to eq(contact.id)
|
|
expect(ContactInbox.last.inbox_id).to eq(channel.inbox.id)
|
|
expect(existing_contact_inbox.reload.source_id).to include(source_id)
|
|
expect(existing_contact_inbox.reload.source_id).not_to eq(source_id)
|
|
end
|
|
|
|
it 'does not update source_id for channels other than email or phone number' do
|
|
create(:contact_inbox, contact: contact2, inbox: channel_api.inbox, source_id: source_id)
|
|
|
|
expect do
|
|
described_class.new(
|
|
contact: contact,
|
|
inbox: channel_api.inbox,
|
|
source_id: source_id
|
|
).perform
|
|
end.to raise_error(ActiveRecord::RecordNotUnique)
|
|
end
|
|
end
|
|
end
|
|
end
|