Fixes company-contact name drift when a company is renamed or deleted. Closes: N/A ## Why Contacts keep a denormalized `additional_attributes.company_name` for display and filtering. Company rename/delete flows could leave that copied value stale even though the actual `company_id` relationship changed. ## What changed - Enqueues an async company contact-name sync job when a company name changes. - Moves company deletion into `Companies::DeleteJob`. - The delete job unlinks linked contacts, clears only the copied `company_name`, and then deletes the company. - Uses bulk JSON updates for the cleanup path so contact records are not saved, which avoids contact update callbacks, webhook dispatch, and automation side effects. ## How to test - Link a contact to a company, rename the company, and confirm the contact company name updates after the job runs. - Delete a company with linked contacts and confirm the delete job removes the company, unassigns linked contacts, and preserves other contact additional attributes. --------- Co-authored-by: Sony Mathew <sony@chatwoot.com> Co-authored-by: Sony Mathew <2040199+sony-mathew@users.noreply.github.com>
61 lines
2.3 KiB
Ruby
61 lines
2.3 KiB
Ruby
require 'rails_helper'
|
|
|
|
RSpec.describe Company, type: :model do
|
|
context 'with validations' do
|
|
it { is_expected.to validate_presence_of(:account_id) }
|
|
it { is_expected.to validate_presence_of(:name) }
|
|
it { is_expected.to validate_length_of(:name).is_at_most(100) }
|
|
it { is_expected.to validate_length_of(:description).is_at_most(1000) }
|
|
|
|
describe 'domain validation' do
|
|
it { is_expected.to allow_value('example.com').for(:domain) }
|
|
it { is_expected.to allow_value('sub.example.com').for(:domain) }
|
|
it { is_expected.to allow_value('').for(:domain) }
|
|
it { is_expected.to allow_value(nil).for(:domain) }
|
|
it { is_expected.not_to allow_value('invalid-domain').for(:domain) }
|
|
it { is_expected.not_to allow_value('.example.com').for(:domain) }
|
|
end
|
|
end
|
|
|
|
context 'with associations' do
|
|
it { is_expected.to belong_to(:account) }
|
|
it { is_expected.to have_many(:contacts).dependent(:nullify) }
|
|
end
|
|
|
|
describe 'scopes' do
|
|
let(:account) { create(:account) }
|
|
let!(:company_b) { create(:company, name: 'B Company', account: account) }
|
|
let!(:company_a) { create(:company, name: 'A Company', account: account) }
|
|
let!(:company_c) { create(:company, name: 'C Company', account: account) }
|
|
|
|
describe '.ordered_by_name' do
|
|
it 'orders companies by name alphabetically' do
|
|
companies = described_class.where(account: account).ordered_by_name
|
|
expect(companies.map(&:name)).to eq([company_a.name, company_b.name, company_c.name])
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#record_activity_at!' do
|
|
it 'does not move company activity backwards' do
|
|
company = create(:company, last_activity_at: Time.zone.now)
|
|
original_activity_at = company.last_activity_at
|
|
|
|
company.record_activity_at!(1.hour.ago)
|
|
|
|
expect(company.reload.last_activity_at).to be_within(1.second).of(original_activity_at)
|
|
end
|
|
end
|
|
|
|
describe 'contact company name sync' do
|
|
let(:account) { create(:account) }
|
|
let(:company) { create(:company, account: account, name: 'Acme') }
|
|
|
|
it 'enqueues contact company name sync when the company name changes' do
|
|
expect do
|
|
company.update!(name: 'Acme Labs')
|
|
end.to have_enqueued_job(Companies::SyncContactNamesJob).with(company_id: company.id)
|
|
end
|
|
end
|
|
end
|