This upgrades Chatwoot to Rails 7.2.3.1 while retaining the current Rails 7.0 framework defaults, so the runtime upgrade can be deployed and observed independently from default-behavior changes. ## What changed - Upgrade Rails and the compatible dependency set to Rails 7.2.3.1. - Keep `config.load_defaults 7.0` for a staged, lower-risk rollout. - Replace the unmaintained Azure Active Storage fork with the maintained `azure-blob` adapter while preserving the `microsoft` service name. - Pin Sidekiq 7.3.10 with `connection_pool` 2.x after validating scheduled-job execution against Redis. - Update Rails 7.2 compatibility surfaces in Active Record, strong parameters, migrations, storage, and tests. - Add read-only production preflight checks, an opt-in Active Storage smoke script, a deployment runbook, and the full Rails 7.2/8.0/8.1 assessment. ## How to test 1. Sign in and verify the dashboard and conversation UI load normally. 2. Open the agent-management modal and confirm agent data is rendered. 3. Create an API inbox and wait for the asynchronous deletion flow to complete. 4. Open Super Admin pages, including instance status and account-user management. 5. Upload and download an attachment using the configured Active Storage service. 6. Confirm recurring Sidekiq Cron jobs register and execute after startup. ## Rollout Follow `docs/rails_upgrades/7_2.md` for pre-deploy checks, deployment order, smoke tests, monitoring, and rollback. Run `bundle exec rails runner script/rails_upgrade/preflight.rb` against a production-equivalent environment before rollout. ## Tracking - [CW-5863 — Upgrade Rails to 8+](https://linear.app/chatwoot/issue/CW-5863/upgrade-rails-to-8) - [Rails 7.2 to 8.1 upgrade and production rollout plan](https://linear.app/chatwoot/document/chatwoot-rails-72-to-81-upgrade-and-production-rollout-plan-44e9f4964cb2) --------- Co-authored-by: Shivam Mishra <scm.mymail@gmail.com> Co-authored-by: Sony Mathew <2040199+sony-mathew@users.noreply.github.com> Co-authored-by: Sony Mathew <sony@chatwoot.com> Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
132 lines
6.8 KiB
Ruby
132 lines
6.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'rails_helper'
|
|
|
|
RSpec.describe ContactInbox do
|
|
describe 'pubsub_token' do
|
|
let(:contact_inbox) { create(:contact_inbox) }
|
|
|
|
it 'gets created on object create' do
|
|
obj = contact_inbox
|
|
expect(obj.pubsub_token).not_to be_nil
|
|
end
|
|
|
|
it 'does not get updated on object update' do
|
|
obj = contact_inbox
|
|
old_token = obj.pubsub_token
|
|
obj.update(source_id: '234234323')
|
|
expect(obj.pubsub_token).to eq(old_token)
|
|
end
|
|
|
|
it 'backfills pubsub_token on call for older objects' do
|
|
obj = create(:contact_inbox)
|
|
# to replicate an object with out pubsub_token
|
|
# rubocop:disable Rails/SkipsModelValidations
|
|
obj.update_column(:pubsub_token, nil)
|
|
# rubocop:enable Rails/SkipsModelValidations
|
|
|
|
obj.reload
|
|
|
|
# ensure the column is nil in database
|
|
expect(described_class.where(id: obj.id).pick(:pubsub_token)).to be_nil
|
|
|
|
new_token = obj.pubsub_token
|
|
obj.update(source_id: '234234323')
|
|
# the generated token shoul be persisted in db
|
|
expect(obj.pubsub_token).to eq(new_token)
|
|
end
|
|
end
|
|
|
|
describe 'validations' do
|
|
context 'when source_id' do
|
|
it 'allows source_id longer than 255 characters for channels without format restrictions' do
|
|
long_source_id = 'a' * 300
|
|
email_inbox = create(:inbox, channel: create(:channel_email))
|
|
contact = create(:contact, account: email_inbox.account)
|
|
contact_inbox = build(:contact_inbox, contact: contact, inbox: email_inbox, source_id: long_source_id)
|
|
|
|
expect(contact_inbox.valid?).to be(true)
|
|
expect { contact_inbox.save! }.not_to raise_error
|
|
expect(contact_inbox.reload.source_id).to eq(long_source_id)
|
|
expect(contact_inbox.source_id.length).to eq(300)
|
|
end
|
|
|
|
it 'validates whatsapp channel source_id' do
|
|
whatsapp_inbox = create(:channel_whatsapp, sync_templates: false, validate_provider_config: false).inbox
|
|
contact = create(:contact)
|
|
valid_source_id = build(:contact_inbox, contact: contact, inbox: whatsapp_inbox, source_id: '1234567890')
|
|
valid_bsuid_source_id = build(:contact_inbox, contact: contact, inbox: whatsapp_inbox, source_id: 'IN.2081978709342942')
|
|
valid_parent_bsuid_source_id = build(:contact_inbox, contact: contact, inbox: whatsapp_inbox, source_id: 'IN.ENT.9081726354')
|
|
ci_character_in_source_id = build(:contact_inbox, contact: contact, inbox: whatsapp_inbox, source_id: '1234567890aaa')
|
|
ci_plus_in_source_id = build(:contact_inbox, contact: contact, inbox: whatsapp_inbox, source_id: '+1234567890')
|
|
expect(valid_source_id.valid?).to be(true)
|
|
expect(valid_bsuid_source_id.valid?).to be(true)
|
|
expect(valid_parent_bsuid_source_id.valid?).to be(true)
|
|
expect(ci_character_in_source_id.valid?).to be(false)
|
|
expect(ci_character_in_source_id.errors.full_messages).to eq(
|
|
["Source invalid source id for whatsapp inbox. valid Regex #{RegexHelper::WHATSAPP_CHANNEL_REGEX}"]
|
|
)
|
|
expect(ci_plus_in_source_id.valid?).to be(false)
|
|
expect(ci_plus_in_source_id.errors.full_messages).to eq(
|
|
["Source invalid source id for whatsapp inbox. valid Regex #{RegexHelper::WHATSAPP_CHANNEL_REGEX}"]
|
|
)
|
|
end
|
|
|
|
it 'validates twilio sms channel source_id' do
|
|
twilio_sms_inbox = create(:channel_twilio_sms).inbox
|
|
contact = create(:contact)
|
|
valid_source_id = build(:contact_inbox, contact: contact, inbox: twilio_sms_inbox, source_id: '+1234567890')
|
|
ci_character_in_source_id = build(:contact_inbox, contact: contact, inbox: twilio_sms_inbox, source_id: '+1234567890aaa')
|
|
ci_without_plus_in_source_id = build(:contact_inbox, contact: contact, inbox: twilio_sms_inbox, source_id: '1234567890')
|
|
expect(valid_source_id.valid?).to be(true)
|
|
expect(ci_character_in_source_id.valid?).to be(false)
|
|
expect(ci_character_in_source_id.errors.full_messages).to eq(
|
|
["Source invalid source id for twilio sms inbox. valid Regex #{RegexHelper::TWILIO_CHANNEL_SMS_REGEX}"]
|
|
)
|
|
expect(ci_without_plus_in_source_id.valid?).to be(false)
|
|
expect(ci_without_plus_in_source_id.errors.full_messages).to eq(
|
|
["Source invalid source id for twilio sms inbox. valid Regex #{RegexHelper::TWILIO_CHANNEL_SMS_REGEX}"]
|
|
)
|
|
end
|
|
|
|
it 'validates twilio whatsapp channel source_id' do
|
|
twilio_whatsapp_inbox = create(:channel_twilio_sms, medium: :whatsapp).inbox
|
|
contact = create(:contact)
|
|
valid_source_id = build(:contact_inbox, contact: contact, inbox: twilio_whatsapp_inbox, source_id: 'whatsapp:+1234567890')
|
|
valid_bsuid_source_id = build(:contact_inbox, contact: contact, inbox: twilio_whatsapp_inbox, source_id: 'whatsapp:IN.2081978709342942')
|
|
valid_parent_bsuid_source_id = build(:contact_inbox, contact: contact, inbox: twilio_whatsapp_inbox, source_id: 'whatsapp:IN.ENT.9081726354')
|
|
ci_character_in_source_id = build(:contact_inbox, contact: contact, inbox: twilio_whatsapp_inbox, source_id: 'whatsapp:+1234567890aaa')
|
|
ci_without_plus_in_source_id = build(:contact_inbox, contact: contact, inbox: twilio_whatsapp_inbox, source_id: 'whatsapp:1234567890')
|
|
expect(valid_source_id.valid?).to be(true)
|
|
expect(valid_bsuid_source_id.valid?).to be(true)
|
|
expect(valid_parent_bsuid_source_id.valid?).to be(true)
|
|
expect(ci_character_in_source_id.valid?).to be(false)
|
|
expect(ci_character_in_source_id.errors.full_messages).to eq(
|
|
["Source invalid source id for twilio whatsapp inbox. valid Regex #{RegexHelper::TWILIO_CHANNEL_WHATSAPP_REGEX}"]
|
|
)
|
|
expect(ci_without_plus_in_source_id.valid?).to be(false)
|
|
expect(ci_without_plus_in_source_id.errors.full_messages).to eq(
|
|
["Source invalid source id for twilio whatsapp inbox. valid Regex #{RegexHelper::TWILIO_CHANNEL_WHATSAPP_REGEX}"]
|
|
)
|
|
end
|
|
|
|
it 'rejects whatsapp BSUID source_id values longer than 128 alphanumeric characters' do
|
|
whatsapp_inbox = create(:channel_whatsapp, sync_templates: false, validate_provider_config: false).inbox
|
|
contact = create(:contact)
|
|
contact_inbox = build(:contact_inbox, contact: contact, inbox: whatsapp_inbox, source_id: "IN.#{'1' * 129}")
|
|
|
|
expect(contact_inbox.valid?).to be(false)
|
|
end
|
|
|
|
it 'rejects twilio whatsapp parent BSUID source_id values longer than 128 alphanumeric characters' do
|
|
twilio_whatsapp_inbox = create(:channel_twilio_sms, medium: :whatsapp).inbox
|
|
contact = create(:contact)
|
|
contact_inbox = build(:contact_inbox, contact: contact, inbox: twilio_whatsapp_inbox,
|
|
source_id: "whatsapp:IN.ENT.#{'1' * 129}")
|
|
|
|
expect(contact_inbox.valid?).to be(false)
|
|
end
|
|
end
|
|
end
|
|
end
|