Files
chatwoot/spec/jobs/send_reply_job_spec.rb
53b2a517d7 fix: resolve SendReplyJob flaky specs (#14394)
`SendReplyJob` was caching reloadable service class objects in
`CHANNEL_SERVICES`. In test, a request spec can trigger Rails constant
reloading after `SendReplyJob` has already been loaded, leaving the job
with stale class objects while later specs stub the reloaded constants.
This resolves the channel service at perform time so the job follows the
current Rails constant table.

How to reproduce
Run the CircleCI shard that contains send_reply_job_spec, or the
minimized order-dependent reproduction:
```sh
bundle exec rspec --format progress spec/builders/v2/reports/label_summary_builder_spec.rb spec/controllers/api/v1/accounts/bulk_actions_controller_spec.rb spec/jobs/send_reply_job_spec.rb:32
```

What changed
- Store service class names in `SendReplyJob::CHANNEL_SERVICES` instead
of class objects.
- Resolve the service with constantize inside perform so reloads do not
leave stale cached classes.

Co-authored-by: Sony Mathew <2040199+sony-mathew@users.noreply.github.com>
2026-05-07 22:47:09 +05:30

127 lines
5.9 KiB
Ruby

require 'rails_helper'
RSpec.describe SendReplyJob do
subject(:job) { described_class.perform_later(message) }
let(:message) { create(:message) }
it 'enqueues the job' do
expect { job }.to have_enqueued_job(described_class)
.with(message)
.on_queue('high')
end
context 'when the job is triggered on a new message' do
let(:process_service) { double }
before do
allow(process_service).to receive(:perform)
end
def expect_mapped_service_to_perform(message, service_class_name)
channel_name = message.conversation.inbox.channel.class.name
service_class = described_class::CHANNEL_SERVICES.fetch(channel_name)
expect(service_class.name).to eq(service_class_name)
expect(service_class).to receive(:new).with(message: message).and_return(process_service)
expect(process_service).to receive(:perform)
described_class.perform_now(message.id)
end
it 'calls Facebook::SendOnFacebookService when its facebook message' do
stub_request(:post, /graph.facebook.com/)
facebook_channel = create(:channel_facebook_page)
facebook_inbox = create(:inbox, channel: facebook_channel)
message = create(:message, conversation: create(:conversation, inbox: facebook_inbox))
allow(Facebook::SendOnFacebookService).to receive(:new).with(message: message).and_return(process_service)
expect(Facebook::SendOnFacebookService).to receive(:new).with(message: message)
expect(process_service).to receive(:perform)
described_class.perform_now(message.id)
end
it 'calls ::Twitter::SendOnTwitterService when its twitter message' do
twitter_channel = create(:channel_twitter_profile)
twitter_inbox = create(:inbox, channel: twitter_channel)
message = create(:message, conversation: create(:conversation, inbox: twitter_inbox))
expect_mapped_service_to_perform(message, 'Twitter::SendOnTwitterService')
end
it 'calls ::Twilio::SendOnTwilioService when its twilio message' do
twilio_channel = create(:channel_twilio_sms)
message = create(:message, conversation: create(:conversation, inbox: twilio_channel.inbox))
expect_mapped_service_to_perform(message, 'Twilio::SendOnTwilioService')
end
it 'calls ::Telegram::SendOnTelegramService when its telegram message' do
telegram_channel = create(:channel_telegram)
message = create(:message, conversation: create(:conversation, inbox: telegram_channel.inbox))
expect_mapped_service_to_perform(message, 'Telegram::SendOnTelegramService')
end
it 'calls ::Line:SendOnLineService when its line message' do
line_channel = create(:channel_line)
message = create(:message, conversation: create(:conversation, inbox: line_channel.inbox))
expect_mapped_service_to_perform(message, 'Line::SendOnLineService')
end
it 'calls ::Whatsapp:SendOnWhatsappService when its whatsapp message' do
stub_request(:post, 'https://waba.360dialog.io/v1/configs/webhook')
whatsapp_channel = create(:channel_whatsapp, sync_templates: false)
message = create(:message, conversation: create(:conversation, inbox: whatsapp_channel.inbox))
expect_mapped_service_to_perform(message, 'Whatsapp::SendOnWhatsappService')
end
it 'calls ::Sms::SendOnSmsService when its sms message' do
sms_channel = create(:channel_sms)
message = create(:message, conversation: create(:conversation, inbox: sms_channel.inbox))
expect_mapped_service_to_perform(message, 'Sms::SendOnSmsService')
end
it 'calls ::Instagram::Direct::SendOnInstagramService when its instagram message' do
instagram_channel = create(:channel_instagram)
message = create(:message, conversation: create(:conversation, inbox: instagram_channel.inbox))
expect_mapped_service_to_perform(message, 'Instagram::SendOnInstagramService')
end
it 'calls ::Instagram::Messenger::SendOnInstagramService when its an instagram_direct_message from facebook channel' do
stub_request(:post, /graph.facebook.com/)
facebook_channel = create(:channel_facebook_page)
facebook_inbox = create(:inbox, channel: facebook_channel)
conversation = create(:conversation,
inbox: facebook_inbox,
additional_attributes: { 'type' => 'instagram_direct_message' })
message = create(:message, conversation: conversation)
allow(Instagram::Messenger::SendOnInstagramService).to receive(:new).with(message: message).and_return(process_service)
expect(Instagram::Messenger::SendOnInstagramService).to receive(:new).with(message: message)
expect(process_service).to receive(:perform)
described_class.perform_now(message.id)
end
it 'calls ::Email::SendOnEmailService when its email message' do
email_channel = create(:channel_email)
message = create(:message, conversation: create(:conversation, inbox: email_channel.inbox))
expect_mapped_service_to_perform(message, 'Email::SendOnEmailService')
end
it 'calls ::Messages::SendEmailNotificationService when its webwidget message' do
webwidget_channel = create(:channel_widget)
message = create(:message, conversation: create(:conversation, inbox: webwidget_channel.inbox))
expect_mapped_service_to_perform(message, 'Messages::SendEmailNotificationService')
end
it 'calls ::Messages::SendEmailNotificationService when its api channel message' do
api_channel = create(:channel_api)
message = create(:message, conversation: create(:conversation, inbox: api_channel.inbox))
expect_mapped_service_to_perform(message, 'Messages::SendEmailNotificationService')
end
it 'calls ::Tiktok::SendOnTiktokService when its tiktok message' do
tiktok_channel = create(:channel_tiktok)
message = create(:message, conversation: create(:conversation, inbox: tiktok_channel.inbox))
expect_mapped_service_to_perform(message, 'Tiktok::SendOnTiktokService')
end
end
end