'hello', 'loop' => '2', 'timeout' => '4', ])->toArray(); $withHangup = bird_voice_say_payload::fromArray([ 'text' => 'hello', 'hangup' => 'false', 'unknown' => 'drop-me', ])->toArray(); expect($withoutHangup)->toMatchArray([ 'text' => 'hello', 'loop' => 2, 'timeout' => 4, 'hangup' => true, ]); expect($withHangup)->toMatchArray([ 'text' => 'hello', 'hangup' => false, ]); expect($withHangup)->not->toHaveKey('unknown'); }); it('normalizes log query csv and integer fields', function (): void { $query = bird_voice_calls_log_query_payload::fromArray([ 'limit' => '10', 'duration' => '60', 'channelId' => '2cbde4fd-8899-4f2d-95ea-2ab7cc6b8c97,5a6bd8c1-32b7-4a03-8b5a-b6a4a9d7a8b2', 'tag' => [' vip ', 'north', ''], 'unknown' => 'drop-me', ])->toArray(); expect($query['limit'])->toBe(10); expect($query['duration'])->toBe(60); expect($query['channelId'])->toBe([ '2cbde4fd-8899-4f2d-95ea-2ab7cc6b8c97', '5a6bd8c1-32b7-4a03-8b5a-b6a4a9d7a8b2', ]); expect($query['tag'])->toBe(['vip', 'north']); expect($query)->not->toHaveKey('unknown'); }); it('normalizes nested create-call payload sections and drops unknown keys', function (): void { $payload = bird_voice_create_call_payload::fromArray([ 'from' => '+4532330288', 'to' => '+4542331128', 'ringTimeout' => '12', 'maxDuration' => '120', 'record' => '1', 'stereo' => '0', 'tags' => [' vip ', ''], 'notification' => [ 'url' => 'https://example.com/webhook', 'ignored' => 'x', ], 'amdSettings' => [ 'enabled' => 'true', 'wordCount' => '2', 'ignored' => 'x', ], 'callFlow' => [ [ 'command' => 'say', 'conditions' => [ [ 'variable' => 'keys', 'operator' => 'eq', 'value' => '1', 'ignored' => 'x', ], ], 'options' => ['text' => 'hello'], 'ignored' => 'x', ], ], 'ignored' => 'x', ])->toArray(); expect($payload)->toMatchArray([ 'from' => '+4532330288', 'to' => '+4542331128', 'ringTimeout' => 12, 'maxDuration' => 120, 'record' => true, 'stereo' => false, 'tags' => ['vip'], 'notification' => ['url' => 'https://example.com/webhook'], 'amdSettings' => [ 'enabled' => true, 'wordCount' => 2, ], 'callFlow' => [ [ 'command' => 'say', 'conditions' => [ [ 'variable' => 'keys', 'operator' => 'eq', 'value' => '1', ], ], 'options' => ['text' => 'hello'], ], ], ]); expect($payload)->not->toHaveKey('ignored'); }); it('keeps gather nested say payload unchanged when hangup is omitted', function (): void { $payload = bird_voice_gather_payload::fromArray([ 'maxNumKeys' => '1', 'say' => [ 'text' => 'Press 1', 'loop' => '3', ], ])->toArray(); expect($payload['maxNumKeys'])->toBe(1); expect($payload['say'])->toMatchArray([ 'text' => 'Press 1', 'loop' => 3, ]); expect($payload['say'])->not->toHaveKey('hangup'); }); it('supports no-body and flash hangup payload normalization', function (): void { $empty = bird_no_body_payload::fromArray(['any' => 'value'])->toArray(); $flashHangup = bird_flash_hangup_payload::fromArray([ 'from' => '+4532330288', 'to' => '+4542331128', 'result' => 'verified', 'ignored' => 'x', ])->toArray(); expect($empty)->toBe([]); expect($flashHangup)->toBe([ 'from' => '+4532330288', 'to' => '+4542331128', 'result' => 'verified', ]); });