chore: preserve terminal statuses; skip early in-progress

This commit is contained in:
Muhsin
2026-03-18 16:44:51 +04:00
parent e096804803
commit e585c84778
3 changed files with 50 additions and 0 deletions
@@ -9,6 +9,7 @@ class Voice::CallStatus::Manager
current_status = conversation.additional_attributes&.dig('call_status')
return if current_status == status
return if preserve_existing_terminal_status?(current_status, status)
apply_status(status, duration: duration, timestamp: timestamp)
update_message(status)
@@ -82,4 +83,9 @@ class Voice::CallStatus::Manager
data['data']['meta'].delete('duration')
end
end
def preserve_existing_terminal_status?(current_status, next_status)
TERMINAL_STATUSES.include?(current_status) &&
TERMINAL_STATUSES.include?(next_status)
end
end
@@ -21,6 +21,7 @@ class Voice::StatusUpdateService
conversation = account.conversations.find_by(identifier: call_sid)
return unless conversation
return if ignore_premature_in_progress?(conversation, normalized_status)
Voice::CallStatus::Manager.new(
conversation: conversation,
@@ -57,4 +58,10 @@ class Voice::StatusUpdateService
rescue ArgumentError
nil
end
def ignore_premature_in_progress?(conversation, normalized_status)
normalized_status == 'in-progress' &&
conversation.additional_attributes&.dig('call_direction') == 'inbound' &&
!conversation.additional_attributes&.dig('agent_joined')
end
end
@@ -75,6 +75,43 @@ RSpec.describe Voice::StatusUpdateService do
expect(message.content_attributes.dig('data', 'status')).to eq('no-answer')
end
it 'does not overwrite no-answer with a later completed callback' do
conversation.update!(additional_attributes: { 'call_direction' => 'inbound', 'call_status' => 'no-answer' })
message.update!(content_attributes: { data: { call_sid: call_sid, status: 'no-answer' } })
described_class.new(
account: account,
call_sid: call_sid,
call_status: 'completed',
payload: { 'CallDuration' => '4' }
).perform
conversation.reload
message.reload
expect(conversation.additional_attributes['call_status']).to eq('no-answer')
expect(message.content_attributes.dig('data', 'status')).to eq('no-answer')
expect(message.content_attributes.dig('data', 'meta', 'duration')).to be_nil
end
it 'ignores inbound in-progress callbacks before any agent joins' do
conversation
message
described_class.new(
account: account,
call_sid: call_sid,
call_status: 'in-progress'
).perform
conversation.reload
message.reload
expect(conversation.additional_attributes['call_status']).to eq('ringing')
expect(conversation.additional_attributes['call_started_at']).to be_nil
expect(message.content_attributes.dig('data', 'status')).to eq('ringing')
end
it 'no-ops when conversation not found' do
expect do
described_class.new(account: account, call_sid: 'UNKNOWN', call_status: 'busy').perform