From b4ef513ff4c737f6d677ade684fc957067016236 Mon Sep 17 00:00:00 2001 From: Jeppe Bundgaard Date: Thu, 26 Mar 2026 13:22:35 +0100 Subject: [PATCH] Refactor hangup cause handling in `Bird` class and add new unit tests for call flow validation. --- services/nginx/app/classes/bird.php | 2 +- .../tests/Unit/Bird/BirdGateCallFlowTest.php | 52 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/services/nginx/app/classes/bird.php b/services/nginx/app/classes/bird.php index 19a65cca..49609cff 100644 --- a/services/nginx/app/classes/bird.php +++ b/services/nginx/app/classes/bird.php @@ -11,7 +11,7 @@ class bird { public const TEST_OUTBOUND_NUMBER_RAW = '+45 42 33 11 28'; public const TEST_OUTBOUND_NUMBER_E164 = '+4542331128'; - private const ALLOWED_HANGUP_CAUSES = ['rejected', 'busy', 'completed']; + private const ALLOWED_HANGUP_CAUSES = ['rejected', 'busy']; private const ACCEPTED_CALL_STATUSES = ['accepted', 'ongoing']; private const TERMINAL_GATE_FAILURE_STATUSES = ['rejected', 'busy', 'failed', 'cancelled', 'no-answer', 'completed']; diff --git a/services/nginx/app/tests/Unit/Bird/BirdGateCallFlowTest.php b/services/nginx/app/tests/Unit/Bird/BirdGateCallFlowTest.php index 0c4717c6..36cb169f 100644 --- a/services/nginx/app/tests/Unit/Bird/BirdGateCallFlowTest.php +++ b/services/nginx/app/tests/Unit/Bird/BirdGateCallFlowTest.php @@ -18,8 +18,12 @@ class BirdGateCallClientFake extends bird { /** @var string[] */ public array $statusQueue = []; + /** @var string[] */ + public array $statusesSeen = []; public int $hangupCalls = 0; /** @var array> */ + public array $hangupPayloads = []; + /** @var array> */ public array $createPayloads = []; /** @@ -51,6 +55,7 @@ class BirdGateCallClientFake extends bird if (!is_string($status) || trim($status) === '') { $status = 'ringing'; } + $this->statusesSeen[] = $status; return [ 'id' => $callId, @@ -61,6 +66,7 @@ class BirdGateCallClientFake extends bird public function hangupVoiceCall(string $workspaceId, string $channelId, string $callId, array $payload = []): array|object|null { $this->hangupCalls++; + $this->hangupPayloads[] = $payload; return ['status' => 'completed']; } @@ -120,6 +126,20 @@ it('hangs up when call reaches accepted state', function (): void { expect($client->createPayloads[0]['payload']['to'])->toBe('+4512345678'); expect($client->createPayloads[0]['payload']['ringTimeout'])->toBe(10); expect(array_key_exists('timeout', $client->createPayloads[0]['payload']))->toBeFalse(); + expect($client->hangupPayloads[0])->toBe([]); +}); + +it('hangs up immediately when status transitions to accepted', function (): void { + $client = new BirdGateCallClientFake( + workspaceId: 'workspace_1', + channelId: 'channel_1', + statusQueue: ['ringing', 'accepted', 'ongoing'] + ); + + $client->callGateAndHangupWhenAccepted(45, 12345678, 10); + + expect($client->hangupCalls)->toBe(1); + expect($client->statusesSeen)->toBe(['ringing', 'accepted']); }); it('maps legacy timeout option to documented ringTimeout payload field', function (): void { @@ -152,3 +172,35 @@ it('clamps derived ringTimeout to Bird documented max when gate timeout is high' expect($client->createPayloads[0]['payload']['ringTimeout'])->toBe(120); expect(array_key_exists('timeout', $client->createPayloads[0]['payload']))->toBeFalse(); }); + +it('passes documented hangup cause when provided', function (): void { + $client = new BirdGateCallClientFake( + workspaceId: 'workspace_1', + channelId: 'channel_1', + statusQueue: ['accepted'] + ); + + $client->createOutboundTestCallAndHangupWhenAccepted('workspace_1', 'channel_1', [ + 'to' => '+4512345678', + 'hangupCause' => 'rejected', + ]); + + expect($client->hangupCalls)->toBe(1); + expect($client->hangupPayloads[0]['cause'])->toBe('rejected'); +}); + +it('drops unsupported hangup cause values from request payload', function (): void { + $client = new BirdGateCallClientFake( + workspaceId: 'workspace_1', + channelId: 'channel_1', + statusQueue: ['accepted'] + ); + + $client->createOutboundTestCallAndHangupWhenAccepted('workspace_1', 'channel_1', [ + 'to' => '+4512345678', + 'hangupCause' => 'completed', + ]); + + expect($client->hangupCalls)->toBe(1); + expect($client->hangupPayloads[0])->toBe([]); +});