Restructure omni services and add Chatwoot research snapshot

This commit is contained in:
Ruslan Bakiev
2026-02-21 11:11:27 +07:00
parent edea7a0034
commit b73babbbf6
7732 changed files with 978203 additions and 32 deletions

View File

@@ -0,0 +1,149 @@
require 'rails_helper'
describe ContactIdentifyAction do
subject(:contact_identify) { described_class.new(contact: contact, params: params).perform }
let!(:account) { create(:account) }
let(:custom_attributes) { { test: 'test', test1: 'test1' } }
let!(:contact) { create(:contact, account: account, custom_attributes: custom_attributes) }
let(:params) do
{ name: 'test', identifier: 'test_id', additional_attributes: { location: 'Bengaulru', company_name: 'Meta' },
custom_attributes: { test: 'new test', test2: 'test2' } }
end
describe '#perform' do
it 'updates the contact' do
expect(Avatar::AvatarFromUrlJob).not_to receive(:perform_later).with(contact, params[:avatar_url])
contact_identify
expect(contact.reload.name).to eq 'test'
# custom attributes are merged properly without overwriting existing ones
expect(contact.custom_attributes).to eq({ 'test' => 'new test', 'test1' => 'test1', 'test2' => 'test2' })
expect(contact.additional_attributes).to eq({ 'company_name' => 'Meta', 'location' => 'Bengaulru' })
expect(contact.reload.identifier).to eq 'test_id'
end
it 'will not call avatar job if avatar is already attached' do
contact.avatar.attach(io: Rails.root.join('spec/assets/avatar.png').open, filename: 'avatar.png', content_type: 'image/png')
expect(Avatar::AvatarFromUrlJob).not_to receive(:perform_later).with(contact, params[:avatar_url])
contact_identify
end
it 'merge deeply nested additional attributes' do
create(:contact, account: account, identifier: '', email: 'test@test.com',
additional_attributes: { location: 'Bengaulru', company_name: 'Meta', social_profiles: { linkedin: 'saras' } })
params = { email: 'test@test.com', additional_attributes: { social_profiles: { twitter: 'saras' } } }
result = described_class.new(contact: contact, params: params).perform
expect(result.additional_attributes['social_profiles']).to eq({ 'linkedin' => 'saras', 'twitter' => 'saras' })
end
it 'enqueues avatar job when valid avatar url parameter is passed' do
params = { name: 'test', avatar_url: 'https://chatwoot-assets.local/sample.png' }
expect(Avatar::AvatarFromUrlJob).to receive(:perform_later).with(contact, params[:avatar_url]).once
described_class.new(contact: contact, params: params).perform
end
it 'does not enqueue avatar job when invalid avatar url parameter is passed' do
params = { name: 'test', avatar_url: 'invalid-url' }
expect(Avatar::AvatarFromUrlJob).not_to receive(:perform_later)
described_class.new(contact: contact, params: params).perform
end
context 'when contact with same identifier exists' do
it 'merges the current contact to identified contact' do
existing_identified_contact = create(:contact, account: account, identifier: 'test_id')
result = contact_identify
expect(result.id).to eq existing_identified_contact.id
expect(result.name).to eq params[:name]
expect { contact.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
end
context 'when contact with same email exists' do
it 'merges the current contact to email contact' do
existing_email_contact = create(:contact, account: account, email: 'test@test.com', name: 'old name')
params = { name: 'new name', email: 'test@test.com' }
result = described_class.new(contact: contact, params: params).perform
expect(result.id).to eq existing_email_contact.id
expect(result.name).to eq 'new name'
expect { contact.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
it 'will not merge the current contact to email contact if identifier of email contact is different' do
existing_email_contact = create(:contact, account: account, identifier: '1', email: 'test@test.com')
params = { identifier: '2', email: 'test@test.com' }
result = described_class.new(contact: contact, params: params).perform
expect(result.id).not_to eq existing_email_contact.id
expect(result.identifier).to eq params[:identifier]
expect(result.email).to be_nil
end
end
context 'when contact with same phone_number exists' do
it 'merges the current contact to phone_number contact' do
existing_phone_number_contact = create(:contact, account: account, phone_number: '+919999888877')
params = { phone_number: '+919999888877' }
result = described_class.new(contact: contact, params: params).perform
expect(result.id).to eq existing_phone_number_contact.id
expect(result.name).to eq existing_phone_number_contact.name
expect { contact.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
it 'will not merge the current contact to phone contact if identifier of phone contact is different' do
existing_phone_number_contact = create(:contact, account: account, identifier: '1', phone_number: '+919999888877')
params = { identifier: '2', phone_number: '+919999888877' }
result = described_class.new(contact: contact, params: params).perform
expect(result.id).not_to eq existing_phone_number_contact.id
expect(result.identifier).to eq params[:identifier]
expect(result.email).to be_nil
end
it 'will not overide the phone contacts email when params contains different email' do
existing_phone_number_contact = create(:contact, account: account, email: '1@test.com', phone_number: '+919999888877')
params = { email: '2@test.com', phone_number: '+919999888877' }
result = described_class.new(contact: contact, params: params).perform
expect(result.id).not_to eq existing_phone_number_contact.id
expect(result.email).to eq params[:email]
expect(result.phone_number).to be_nil
end
end
context 'when contacts with blank identifiers exist and identify action is called with blank identifier' do
it 'updates the attributes of contact passed in to identify action' do
create(:contact, account: account, identifier: '')
params = { identifier: '', name: 'new name' }
result = described_class.new(contact: contact, params: params).perform
expect(result.id).to eq contact.id
expect(result.name).to eq 'new name'
end
end
context 'when retain_original_contact_name is set to true' do
it 'will not update the name of the existing contact' do
existing_email_contact = create(:contact, account: account, name: 'old name', email: 'test@test.com')
params = { email: 'test@test.com', name: 'new name' }
result = described_class.new(contact: contact, params: params, retain_original_contact_name: true).perform
expect(result.id).to eq existing_email_contact.id
expect(result.name).to eq 'old name'
expect { contact.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
end
context 'when discard_invalid_attrs is set to false' do
it 'will not update the name of the existing contact' do
params = { email: 'blah blah blah', name: 'new name' }
expect do
described_class.new(contact: contact, params: params, retain_original_contact_name: true).perform
end.to raise_error(ActiveRecord::RecordInvalid)
end
end
context 'when discard_invalid_attrs is set to true' do
it 'will not update the name of the existing contact' do
params = { phone_number: 'blahblah blah', name: 'new name' }
described_class.new(contact: contact, params: params, discard_invalid_attrs: true).perform
expect(contact.reload.name).to eq 'new name'
expect(contact.phone_number).to be_nil
end
end
end
end

View File

@@ -0,0 +1,93 @@
require 'rails_helper'
describe ContactMergeAction do
subject(:contact_merge) { described_class.new(account: account, base_contact: base_contact, mergee_contact: mergee_contact).perform }
let!(:account) { create(:account) }
let!(:base_contact) do
create(:contact, identifier: 'base_contact', email: 'old@old.com', phone_number: '', custom_attributes: { val_test: 'old', val_empty_old: '' },
account: account)
end
let!(:mergee_contact) do
create(:contact, identifier: '', email: 'new@new.com', phone_number: '+12212345',
custom_attributes: { val_test: 'new', val_new: 'new', val_empty_new: '' }, account: account)
end
before do
2.times.each do
create(:conversation, contact: base_contact)
create(:conversation, contact: mergee_contact)
create(:message, sender: mergee_contact)
create(:note, contact: mergee_contact, account: mergee_contact.account)
end
end
describe '#perform' do
it 'deletes mergee_contact' do
contact_merge
expect { mergee_contact.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
it 'copies information from mergee contact to base contact' do
contact_merge
base_contact.reload
expect(base_contact.identifier).to eq('base_contact')
expect(base_contact.email).to eq('old@old.com')
expect(base_contact.phone_number).to eq('+12212345')
expect(base_contact.custom_attributes['val_test']).to eq('old')
expect(base_contact.custom_attributes['val_new']).to eq('new')
expect(base_contact.custom_attributes['val_empty_old']).to eq('')
expect(base_contact.custom_attributes['val_empty_new']).to eq('')
end
context 'when base contact and merge contact are same' do
it 'does not delete contact' do
mergee_contact = base_contact
contact_merge
expect(mergee_contact.reload).not_to be_nil
end
end
context 'when mergee contact has conversations' do
it 'moves the conversations to base contact' do
contact_merge
expect(base_contact.conversations.count).to be 4
end
end
context 'when mergee contact has contact inboxes' do
it 'moves the contact inboxes to base contact' do
contact_merge
expect(base_contact.contact_inboxes.count).to be 4
end
end
context 'when mergee contact has messages' do
it 'moves the messages to base contact' do
contact_merge
expect(base_contact.messages.count).to be 2
end
end
context 'when mergee contact has notes' do
it 'moves the notes to base contact' do
expect(base_contact.notes.count).to be 0
expect(mergee_contact.notes.count).to be 2
contact_merge
expect(base_contact.reload.notes.count).to be 2
end
end
context 'when contacts belong to a different account' do
it 'throws an exception' do
new_account = create(:account)
expect do
described_class.new(account: new_account, base_contact: base_contact,
mergee_contact: mergee_contact).perform
end.to raise_error('contact does not belong to the account')
end
end
end
end