Adds scheduled WhatsApp Cloud API phone-number health synchronization and expands the Account Health page with phone, capacity, business-account, webhook, coexistence, and recovery details. Authorization failures now guide administrators to the appropriate Configuration flow without exposing technical Meta error codes. Fixes [CW-7621](https://linear.app/chatwoot/issue/CW-7621/store-whatsapp-phone-number-health-status) **Preview** <img width="2594" height="1676" alt="CleanShot 2026-07-22 at 10 55 01@2x" src="https://github.com/user-attachments/assets/de606cb4-5682-4178-87e9-c18752d299b5" /> <img width="2576" height="1506" alt="CleanShot 2026-07-22 at 10 55 08@2x" src="https://github.com/user-attachments/assets/4eb1810f-be12-4fbc-bcb0-9e2906785c48" /> <img width="1748" height="1150" alt="CleanShot 2026-07-22 at 11 10 05@2x" src="https://github.com/user-attachments/assets/64f5be5a-c127-4372-889f-d392947c13b8" /> ### How to test 1. Open **Settings → Inboxes → a WhatsApp Cloud API inbox → Account Health**. 2. Confirm the page shows separate Phone number, Health and capacity, Business account, and Webhook configuration sections. 3. Confirm the configured webhook URL can be copied and the expected URL appears only when it differs. 4. For a coexistence number, confirm **Coexistence · Active** appears; confirm it is hidden for standard Cloud API numbers. 5. With an invalid Embedded Signup token, confirm the page asks to refresh the WhatsApp connection and **Go to Configuration** opens the Configuration tab. 6. With an invalid manually configured token, confirm the page asks the administrator to verify or replace the access token. 7. Confirm authorization states do not display Meta error codes or the manual-migration recommendation. ### What changed - Persists the latest successful phone health snapshot, check time, and most recent error while retaining the last successful data after a failed refresh. - Refreshes stale active Cloud API channels every six hours through low-priority jobs. - Fetches phone, WABA, business portfolio, webhook, and coexistence details. - Uses Meta's current `whatsapp_business_manager_messaging_limit` field while preserving the existing UI response key. - Classifies authorization failures for setup-specific recovery guidance and logs new risky quality/status transitions. - Adds focused service, scheduler, job, trigger, and API coverage. Internal alerts, throttling, automatic inbox disablement, and customer notifications remain outside this PR and are tracked separately in CW-7622. --------- Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com>
59 lines
2.1 KiB
Ruby
59 lines
2.1 KiB
Ruby
require 'rails_helper'
|
|
|
|
RSpec.describe TriggerScheduledItemsJob do
|
|
subject(:job) { described_class.perform_later }
|
|
|
|
let(:account) { create(:account) }
|
|
|
|
it 'enqueues the job' do
|
|
expect { job }.to have_enqueued_job(described_class)
|
|
.on_queue('scheduled_jobs')
|
|
end
|
|
|
|
it 'triggers Conversations::ReopenSnoozedConversationsJob' do
|
|
expect(Conversations::ReopenSnoozedConversationsJob).to receive(:perform_later).once
|
|
described_class.perform_now
|
|
end
|
|
|
|
it 'triggers Notification::ReopenSnoozedNotificationsJob' do
|
|
expect(Notification::ReopenSnoozedNotificationsJob).to receive(:perform_later).once
|
|
described_class.perform_now
|
|
end
|
|
|
|
it 'triggers Account::ConversationsResolutionSchedulerJob' do
|
|
expect(Account::ConversationsResolutionSchedulerJob).to receive(:perform_later).once
|
|
described_class.perform_now
|
|
end
|
|
|
|
it 'triggers Channels::Whatsapp::TemplatesSyncSchedulerJob' do
|
|
expect(Channels::Whatsapp::TemplatesSyncSchedulerJob).to receive(:perform_later).once
|
|
described_class.perform_now
|
|
end
|
|
|
|
it 'does not trigger the hourly WhatsApp health scheduler' do
|
|
expect(Channels::Whatsapp::HealthSyncSchedulerJob).not_to receive(:perform_later)
|
|
|
|
described_class.perform_now
|
|
end
|
|
|
|
context 'when unexecuted Scheduled campaign jobs' do
|
|
let!(:twilio_sms) { create(:channel_twilio_sms, account: account) }
|
|
let!(:twilio_inbox) { create(:inbox, channel: twilio_sms, account: account) }
|
|
|
|
it 'triggers Campaigns::TriggerOneoffCampaignJob' do
|
|
campaign = create(:campaign, inbox: twilio_inbox, account: account)
|
|
create(:campaign, inbox: twilio_inbox, account: account, scheduled_at: 10.days.after)
|
|
expect(Campaigns::TriggerOneoffCampaignJob).to receive(:perform_later).with(campaign).once
|
|
described_class.perform_now
|
|
end
|
|
|
|
it 'does not trigger campaigns that are already processing' do
|
|
create(:campaign, inbox: twilio_inbox, account: account, campaign_status: :processing)
|
|
|
|
expect(Campaigns::TriggerOneoffCampaignJob).not_to receive(:perform_later)
|
|
|
|
described_class.perform_now
|
|
end
|
|
end
|
|
end
|