<?php


namespace Twilio\Tests\Unit\Http;


use Twilio\Http\CurlClient;
use Twilio\Tests\Unit\UnitTest;

class CurlClientTest extends UnitTest {

    public function testPreemptiveAuthorization() {
        $client = new CurlClient();

        $options = $client->options(
            'GET',
            'http://api.twilio.com',
            array(),
            array(),
            array(),
            'test-user',
            'test-password'
        );

        $this->assertArrayHasKey(CURLOPT_HTTPHEADER, $options);

        $headers = $options[CURLOPT_HTTPHEADER];

        $authorization = null;
        foreach ($headers as $header) {
            $parse = \explode(':', $header);
            $headerKey = $parse[0];
            if ($headerKey == 'Authorization') {
                $authorization = $header;
                break;
            }
        }

        $this->assertNotNull($authorization);

        $authorizationPayload = \explode(' ', $authorization);
        $encodedAuthorization = \array_pop($authorizationPayload);
        $decodedAuthorization = \base64_decode($encodedAuthorization);

        $this->assertEquals('test-user:test-password', $decodedAuthorization);
    }


    /**
     * @param string $message Failure Message
     * @param mixed[] $params Params with which to build the query
     * @param string $expected Expected query string
     * @dataProvider buildQueryProvider
     */
    public function testBuildQuery($message, $params, $expected) {
        $client = new CurlClient();
        $actual = $client->buildQuery($params);
        $this->assertEquals($expected, $actual, $message);
    }

    public function buildQueryProvider() {
        return array(
            array(
                'Null Params',
                null,
                ''
            ),
            array(
                'Empty Params',
                array(),
                '',
            ),
            array(
                'Single Scalar',
                array('a' => 'z'),
                'a=z',
            ),
            array(
                'Multiple Scalars',
                array(
                    'a' => 'z',
                    'b' => 'y',
                ),
                'a=z&b=y',
            ),
            array(
                'Type Coercion: Booleans',
                array(
                    'a' => true,
                    'b' => false,
                ),
                'a=1&b=',
            ),
            array(
                'Type Coercion: Integers',
                array(
                    'a' => 7,
                    'b' => -14,
                    'c' => 0,
                ),
                'a=7&b=-14&c=0',
            ),
            array(
                'Nested Arrays',
                array(
                    'a' => array(1, 2, 3),
                    'b' => array('x', 'y', 'z'),
                ),
                'a=1&a=2&a=3&b=x&b=y&b=z',
            ),
            array(
                'URL Safety',
                array(
                    'a' => 'un$afe:// value!',
                ),
                'a=un%24afe%3A%2F%2F+value%21',
            ),
            array(
                'Encoded Key',
                array(
                    'StartTime>' => '2012-06-14',
                ),
                'StartTime%3E=2012-06-14',
            )
        );
    }

    /**
     * @param $method
     * @param $params
     * @param $expected
     * @dataProvider queryStringProvider
     * @throws \Twilio\Exceptions\EnvironmentException
     */
    public function testQueryString($method, $params, $expected) {
        $client = new CurlClient();

        $actual = $client->options($method, 'url', $params);

        $this->assertEquals($expected, $actual[CURLOPT_URL]);
    }

    public function queryStringProvider() {
        $methods = array('GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD', 'CUSTOM');
        $cases = array();

        foreach ($methods as $method) {
            $cases[] = array(
                $method,
                array(),
                'url',
            );

            $cases[] = array(
                $method,
                array(
                    'a' => '$z',
                    'b' => 7,
                    'c' => array(1, 'x', 2),
                ),
                'url?a=%24z&b=7&c=1&c=x&c=2',
            );
        }

        return $cases;
    }

    /**
     * @param mixed[] $params Parameters to post
     * @param mixed[] $data Data to post
     * @param string $expected Expected POSTFIELDS
     * @dataProvider postFieldsProvider
     * @throws \Twilio\Exceptions\EnvironmentException
     */
    public function testPostFields($params, $data, $expected) {
        $client = new CurlClient();

        $actual = $client->options('POST', 'url', $params, $data);

        $this->assertEquals($expected, $actual[CURLOPT_POSTFIELDS]);
    }

    public function postFieldsProvider() {
        return array(
            array(
                array(),
                array(),
                '',
            ),

            array(
                array(
                    'a' => 'x',
                ),
                array(
                    'a' => 'b',
                ),
                'a=b'
            ),

            array(
                array(
                    'a' => 'x',
                ),
                array(
                    'a' => 'x',
                ),
                'a=x'
            ),

            array(
                array(
                    'a' => 'x',
                ),
                array(
                    'a' => 'z',
                    'b' => 7,
                    'c' => array(1, 2, 3),
                ),
                'a=z&b=7&c=1&c=2&c=3',
            ),

            array(
                '',
                'a=x&b=z',
                'a=x&b=z',
            ),
        );
    }

    public function testPutFile() {
        $client = new CurlClient();
        $actual = $client->options('PUT', 'url', array(), array('a' => 1, 'b' => 2));
        $this->assertNotNull($actual[CURLOPT_INFILE]);
        $this->assertEquals('a=1&b=2', \fread($actual[CURLOPT_INFILE], $actual[CURLOPT_INFILESIZE]));
        $this->assertEquals(7, $actual[CURLOPT_INFILESIZE]);
    }

    /**
     * @param string $message Case message, displayed on assertion error
     * @param mixed[] $options Options to inject
     * @param mixed[] $expected Partial array to expect
     * @dataProvider userInjectedOptionsProvider
     */
    public function testUserInjectedOptions($message, $options, $expected) {
        $client = new CurlClient($options);
        $actual = $client->options(
            'GET',
            'url',
            array('param-key' => 'param-value'),
            array('data-key' => 'data-value'),
            array('header-key' => 'header-value'),
            'user',
            'password',
            20
        );
        foreach ($expected as $key => $value) {
            $this->assertEquals($value, $actual[$key], $message);
        }
    }

    public function userInjectedOptionsProvider() {
        return array(
            array(
                'No Conflict Options',
                array(
                    CURLOPT_VERBOSE => true,
                ),
                array(
                    CURLOPT_VERBOSE => true,
                ),
            ),
            array(
                'Options preferred over Defaults',
                array(
                    CURLOPT_TIMEOUT => 1000,
                ),
                array(
                    CURLOPT_TIMEOUT => 1000,
                ),
            ),
            array(
                'Required Options can not be injected',
                array(
                    CURLOPT_HTTPGET => false,
                ),
                array(
                    CURLOPT_HTTPGET => true,
                ),
            ),
            array(
                'Injected URL decorated with Query String',
                array(
                    CURLOPT_URL => 'user-provided-url',
                ),
                array(
                    CURLOPT_URL => 'user-provided-url?param-key=param-value',
                ),
            ),
            array(
                'Injected Headers are additive',
                array(
                    CURLOPT_HTTPHEADER => array(
                        'injected-key: injected-value',
                    ),
                ),
                array(
                    CURLOPT_HTTPHEADER => array(
                        'injected-key: injected-value',
                        'header-key: header-value',
                        'Authorization: Basic ' . \base64_encode('user:password'),
                    ),
                ),
            ),
        );
    }

}
