Files
chatwoot/spec/jobs/migration/validate_openai_hooks_job_spec.rb
3253e863ed fix: validate OpenAI hook credentials (#14068)
# Pull Request Template

## Description

- Validates openai key while configuring hooks
- added backfill logic

Fixes # (issue)

## Type of change

- [x] New feature (non-breaking change which adds functionality)


## How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
locally

<img width="1710" height="1234" alt="CleanShot 2026-04-15 at 16 15
02@2x"
src="https://github.com/user-attachments/assets/3d319fe0-19f9-4fd0-9308-74987daac2e1"
/>

<img width="2884" height="1136" alt="CleanShot 2026-05-11 at 19 22
53@2x"
src="https://github.com/user-attachments/assets/5eae8650-985b-4c4a-af42-35f7175ff52d"
/>



## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [x] Any dependent changes have been merged and published in downstream
modules

---------

Co-authored-by: Vishnu Narayanan <iamwishnu@gmail.com>
2026-05-18 14:08:57 +05:30

59 lines
2.5 KiB
Ruby

require 'rails_helper'
RSpec.describe Migration::ValidateOpenaiHooksJob do
let(:integrations_mailer) { instance_double(AdministratorNotifications::IntegrationsNotificationMailer) }
let(:mailer_response) { instance_double(ActionMailer::MessageDelivery, deliver_later: true) }
before do
allow(Integrations::Openai::KeyValidator).to receive(:valid?).and_return(true)
allow(AdministratorNotifications::IntegrationsNotificationMailer).to receive(:with).and_return(integrations_mailer)
allow(integrations_mailer).to receive(:openai_disconnect).and_return(mailer_response)
end
def create_openai_hook(account:, api_key: 'sk-good')
create(:integrations_hook, :openai, account: account, settings: { 'api_key' => api_key })
end
it 'destroys invalid hooks, preserves valid ones, sends disconnect email, and reports stats' do
account_a = create(:account)
account_b = create(:account)
valid_hook = create_openai_hook(account: account_a, api_key: 'sk-good')
invalid_hook = create_openai_hook(account: account_b, api_key: 'sk-bad')
allow(Integrations::Openai::KeyValidator).to receive(:valid?).with('sk-bad').and_return(false)
result = described_class.perform_now
expect(valid_hook.reload).to be_enabled
expect { invalid_hook.reload }.to raise_error(ActiveRecord::RecordNotFound)
expect(AdministratorNotifications::IntegrationsNotificationMailer).to have_received(:with).with(account: account_b)
expect(result).to eq(checked: 2, destroyed: 1)
end
it 'scopes to a specific account when provided' do
account_a = create(:account)
account_b = create(:account)
hook_a = create_openai_hook(account: account_a, api_key: 'sk-bad')
hook_b = create_openai_hook(account: account_b, api_key: 'sk-bad')
allow(Integrations::Openai::KeyValidator).to receive(:valid?).with('sk-bad').and_return(false)
described_class.perform_now(account: account_a)
expect { hook_a.reload }.to raise_error(ActiveRecord::RecordNotFound)
expect(hook_b.reload).to be_enabled
end
it 'only checks enabled OpenAI hooks' do
account = create(:account)
slack_hook = create(:integrations_hook, account: account, app_id: 'slack')
disabled_hook = create_openai_hook(account: account)
disabled_hook.disable
allow(Integrations::Openai::KeyValidator).to receive(:valid?).and_return(false)
described_class.perform_now
expect(slack_hook.reload).to be_enabled
expect(disabled_hook.reload).to be_disabled # still disabled, not re-checked
end
end