iachat/spec/lib/webhooks/trigger_spec.rb
Pranav 8f95fafff4
feat: Add a setting to keep conversations pending on bot failures (#13512)
Adds an account-level setting `keep_pending_on_bot_failure` to control
whether conversations should move from pending to open when agent bot
webhooks fail.

Some users experience occasional message drops and don't want
conversations to automatically reopen due to transient bot failures.
This setting gives accounts control over that behavior. This is a
temporary setting which will be removed in future once a proper fix for
it is done, so it is not added in the UI.
2026-02-10 17:27:42 -08:00

110 lines
3.4 KiB
Ruby

require 'rails_helper'
describe Webhooks::Trigger do
include ActiveJob::TestHelper
subject(:trigger) { described_class }
let!(:account) { create(:account) }
let!(:inbox) { create(:inbox, account: account) }
let!(:conversation) { create(:conversation, inbox: inbox) }
let!(:message) { create(:message, account: account, inbox: inbox, conversation: conversation) }
let(:webhook_type) { :api_inbox_webhook }
let!(:url) { 'https://test.com' }
let(:agent_bot_error_content) { I18n.t('conversations.activity.agent_bot.error_moved_to_open') }
let(:default_timeout) { 5 }
let(:webhook_timeout) { default_timeout }
before do
ActiveJob::Base.queue_adapter = :test
allow(GlobalConfig).to receive(:get_value).and_call_original
allow(GlobalConfig).to receive(:get_value).with('WEBHOOK_TIMEOUT').and_return(webhook_timeout)
allow(GlobalConfig).to receive(:get_value).with('DEPLOYMENT_ENV').and_return(nil)
end
after do
clear_enqueued_jobs
clear_performed_jobs
end
describe '#execute' do
it 'triggers webhook' do
payload = { hello: :hello }
expect(RestClient::Request).to receive(:execute)
.with(
method: :post,
url: url,
payload: payload.to_json,
headers: { content_type: :json, accept: :json },
timeout: webhook_timeout
).once
trigger.execute(url, payload, webhook_type)
end
it 'raises RetriableError when webhook fails' do
payload = { event: 'message_created', conversation: { id: conversation.id }, id: message.id }
expect(RestClient::Request).to receive(:execute)
.with(
method: :post,
url: url,
payload: payload.to_json,
headers: { content_type: :json, accept: :json },
timeout: webhook_timeout
).and_raise(RestClient::ExceptionWithResponse.new('error', 500)).once
expect { trigger.execute(url, payload, webhook_type) }.to raise_error(CustomExceptions::Webhook::RetriableError)
end
it 'does not call ErrorHandler directly (deferred to job discard)' do
payload = { event: 'message_created', conversation: { id: conversation.id }, id: message.id }
expect(RestClient::Request).to receive(:execute)
.and_raise(RestClient::ExceptionWithResponse.new('error', 500))
expect(Webhooks::ErrorHandler).not_to receive(:perform)
expect { trigger.execute(url, payload, webhook_type) }.to raise_error(CustomExceptions::Webhook::RetriableError)
end
end
context 'when webhook timeout configuration is blank' do
let(:webhook_timeout) { nil }
it 'falls back to default timeout' do
payload = { hello: :hello }
expect(RestClient::Request).to receive(:execute)
.with(
method: :post,
url: url,
payload: payload.to_json,
headers: { content_type: :json, accept: :json },
timeout: default_timeout
).once
trigger.execute(url, payload, webhook_type)
end
end
context 'when webhook timeout configuration is invalid' do
let(:webhook_timeout) { -1 }
it 'falls back to default timeout' do
payload = { hello: :hello }
expect(RestClient::Request).to receive(:execute)
.with(
method: :post,
url: url,
payload: payload.to_json,
headers: { content_type: :json, accept: :json },
timeout: default_timeout
).once
trigger.execute(url, payload, webhook_type)
end
end
end