fix(voice): don't mint inbound calls for outbound connect webhooks

If Meta delivered a connect for an outbound call before the controller
committed the Call row (race between Meta's API response and our INSERT),
the handler treated the unknown call_id as inbound and either tripped the
unique (provider, provider_call_id) index or broadcast voice_call.incoming
for an outbound. Use session.sdp_type to gate inbound creation on 'offer'
only; 'answer' with no row is logged and skipped — the next status webhook
finds the now-committed row.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tanmay Deep Sharma
2026-05-07 15:39:29 +07:00
co-authored by Claude Opus 4.7
parent 74f9cd764b
commit 29fd83c1ea
2 changed files with 30 additions and 1 deletions
@@ -114,6 +114,21 @@ describe Whatsapp::IncomingCallService do
end
end
describe 'outbound connect with no local row yet' do
it 'does not mint an inbound call when sdp_type is answer' do
allow(inbox.channel).to receive(:voice_enabled?).and_return(true)
allow(Rails.logger).to receive(:warn)
allow(ActionCable.server).to receive(:broadcast)
params = call_payload(event: 'connect', session: { sdp: 'sdp_answer', sdp_type: 'answer' })
expect { described_class.new(inbox: inbox, params: params).perform }
.not_to change(Call, :count)
expect(Rails.logger).to have_received(:warn).with(/Outbound connect for unknown call/)
expect(ActionCable.server).not_to have_received(:broadcast)
end
end
describe 'duplicate inbound connect' do
let!(:call) do
conversation = create(:conversation, account: account, inbox: inbox)