iachat/spec/builders/contact_inbox_builder_spec.rb
Pranav d355801555
fix: Do not allow sending messages if merged contact has a duplicate session (#11152)
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.
2025-03-21 18:04:46 -07:00

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