fix(voice): keep terminate handling idempotent for ended calls
A retried terminate webhook for an answered short call (duration=0) would re-enter handle_terminate, find call.in_progress? false, fall through to no_answer, and corrupt a completed call's status. Bail when the call is already terminal so retries don't recompute the status.
This commit is contained in:
@@ -52,6 +52,10 @@ class Whatsapp::IncomingCallService
|
||||
def handle_terminate(payload)
|
||||
call = Call.whatsapp.find_by(provider_call_id: payload[:id])
|
||||
return unless call
|
||||
# Webhook retries can re-deliver terminate after we've already finalized the
|
||||
# call; don't recompute status or a duration=0 retry can flip a completed
|
||||
# short call back to no_answer.
|
||||
return if call.terminal?
|
||||
|
||||
duration = payload[:duration]&.to_i
|
||||
status = answered?(call, duration) ? 'completed' : 'no_answer'
|
||||
|
||||
@@ -101,6 +101,17 @@ describe Whatsapp::IncomingCallService do
|
||||
described_class.new(inbox: inbox, params: params).perform
|
||||
expect(call.reload.status).to eq('no_answer')
|
||||
end
|
||||
|
||||
it 'is a no-op when the call is already terminal so retries cannot flip a completed call to no_answer' do
|
||||
call.update!(status: 'completed', duration_seconds: 5, direction: :outgoing, accepted_by_agent: nil)
|
||||
allow(ActionCable.server).to receive(:broadcast)
|
||||
params = call_payload(event: 'terminate', duration: 0, terminate_reason: 'completed_normally')
|
||||
|
||||
described_class.new(inbox: inbox, params: params).perform
|
||||
|
||||
expect(call.reload).to have_attributes(status: 'completed', duration_seconds: 5)
|
||||
expect(ActionCable.server).not_to have_received(:broadcast)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'duplicate inbound connect' do
|
||||
|
||||
Reference in New Issue
Block a user