Refactor hangup cause handling in Bird class and add new unit tests for call flow validation.

This commit is contained in:
Jeppe Bundgaard
2026-03-26 13:22:35 +01:00
parent 95063d2a70
commit b4ef513ff4
2 changed files with 53 additions and 1 deletions
+1 -1
View File
@@ -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'];
@@ -18,8 +18,12 @@ class BirdGateCallClientFake extends bird
{
/** @var string[] */
public array $statusQueue = [];
/** @var string[] */
public array $statusesSeen = [];
public int $hangupCalls = 0;
/** @var array<int,array<string,mixed>> */
public array $hangupPayloads = [];
/** @var array<int,array<string,mixed>> */
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([]);
});