Overview

Namespaces

  • Docta
    • MercadoLibre
      • Exception
      • OAuth2
        • Client
          • Test
  • GuzzleHttp
    • Cookie
    • Exception
    • Handler
    • Promise
      • Test
      • Tests
    • Psr7
    • Test
      • Handler
    • Tests
      • CookieJar
      • Event
      • Exception
      • Handler
      • Promise
      • Psr7
  • League
    • OAuth2
      • Client
        • Grant
          • Exception
        • Provider
          • Exception
        • Test
          • Grant
          • Provider
            • Exception
            • Fake
          • Token
          • Tool
        • Token
        • Tool
  • None
  • Psr
    • Http
      • Message

Classes

  • Docta\MercadoLibre\Client
  • Docta\MercadoLibre\OAuth2\Client\Provider
  • Docta\MercadoLibre\OAuth2\Client\ResourceGeneric
  • Docta\MercadoLibre\OAuth2\Client\ResourceOwner
  • Docta\MercadoLibre\OAuth2\Client\Test\ProviderTest

Exceptions

  • Docta\MercadoLibre\Exception\ClientException
  • Overview
  • Namespace
  • Class
  • Download
  1:   2:   3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  14:  15:  16:  17:  18:  19:  20:  21:  22:  23:  24:  25:  26:  27:  28:  29:  30:  31:  32:  33:  34:  35:  36:  37:  38:  39:  40:  41:  42:  43:  44:  45:  46:  47:  48:  49:  50:  51:  52:  53:  54:  55:  56:  57:  58:  59:  60:  61:  62:  63:  64:  65:  66:  67:  68:  69:  70:  71:  72:  73:  74:  75:  76:  77:  78:  79:  80:  81:  82:  83:  84:  85:  86:  87:  88:  89:  90:  91:  92:  93:  94:  95:  96:  97:  98:  99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363: 364: 365: 366: 367: 368: 369: 370: 371: 372: 373: 374: 375: 376: 377: 378: 379: 380: 381: 382: 383: 384: 385: 386: 387: 388: 389: 390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402: 403: 404: 405: 406: 407: 408: 409: 410: 411: 412: 413: 414: 415: 416: 417: 418: 419: 420: 421: 422: 423: 424: 425: 426: 427: 428: 429: 430: 431: 432: 433: 434: 435: 436: 437: 438: 439: 440: 441: 442: 443: 444: 445: 446: 447: 448: 449: 450: 451: 452: 453: 454: 455: 456: 457: 458: 459: 460: 461: 462: 463: 464: 465: 466: 467: 468: 469: 470: 471: 472: 473: 474: 475: 476: 477: 478: 479: 480: 481: 482: 483: 484: 485: 486: 487: 488: 489: 490: 491: 492: 493: 494: 495: 496: 497: 498: 499: 500: 501: 502: 503: 504: 505: 506: 507: 508: 509: 510: 511: 512: 513: 514: 515: 516: 517: 518: 519: 520: 521: 522: 523: 524: 525: 526: 527: 528: 529: 530: 531: 532: 533: 534: 535: 536: 537: 538: 539: 540: 541: 542: 543: 544: 545: 546: 547: 548: 549: 550: 551: 552: 553: 554: 555: 556: 557: 558: 559: 560: 561: 562: 563: 564: 565: 566: 567: 568: 569: 570: 571: 572: 573: 574: 575: 576: 577: 578: 579: 580: 581: 582: 583: 584: 585: 586: 587: 588: 589: 590: 591: 592: 593: 594: 595: 596: 597: 598: 599: 600: 601: 602: 603: 604: 605: 606: 607: 608: 609: 610: 611: 612: 613: 614: 615: 616: 617: 618: 619: 620: 621: 622: 623: 624: 625: 626: 627: 628: 629: 630: 631: 632: 633: 634: 635: 636: 637: 638: 639: 640: 641: 642: 643: 644: 645: 646: 647: 648: 649: 650: 651: 652: 653: 654: 655: 656: 657: 658: 659: 660: 661: 662: 663: 664: 665: 666: 667: 668: 669: 670: 671: 672: 673: 674: 675: 676: 677: 678: 679: 680: 681: 682: 683: 684: 685: 686: 687: 688: 689: 690: 691: 692: 693: 694: 695: 696: 697: 698: 699: 700: 701: 702: 703: 704: 705: 706: 
<?php

namespace League\OAuth2\Client\Test\Provider;

use Eloquent\Liberator\Liberator;
use Eloquent\Phony\Phpunit\Phony;
use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\ClientInterface;
use League\OAuth2\Client\Provider\AbstractProvider;
use League\OAuth2\Client\Test\Provider\Fake as MockProvider;
use League\OAuth2\Client\Grant\AbstractGrant;
use League\OAuth2\Client\Grant\GrantFactory;
use League\OAuth2\Client\Token\AccessToken;
use League\OAuth2\Client\Tool\RequestFactory;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\StreamInterface;

class AbstractProviderTest extends TestCase
{
    /**
     * @var AbstractProvider
     */
    protected $provider;

    protected function setUp()
    {
        $this->provider = new MockProvider([
            'clientId' => 'mock_client_id',
            'clientSecret' => 'mock_secret',
            'redirectUri' => 'none',
        ]);
    }

    /**
     * @expectedException League\OAuth2\Client\Grant\Exception\InvalidGrantException
     */
    public function testInvalidGrantString()
    {
        $this->provider->getAccessToken('invalid_grant', ['invalid_parameter' => 'none']);
    }

    /**
     * @expectedException League\OAuth2\Client\Grant\Exception\InvalidGrantException
     */
    public function testInvalidGrantObject()
    {
        $grant = new \StdClass();
        $this->provider->getAccessToken($grant, ['invalid_parameter' => 'none']);
    }

    public function testAuthorizationUrlStateParam()
    {
        $this->assertContains('state=XXX', $this->provider->getAuthorizationUrl([
            'state' => 'XXX'
        ]));
    }

    /**
     * Tests https://github.com/thephpleague/oauth2-client/pull/485
     */
    public function testCustomAuthorizationUrlOptions()
    {
        $url = $this->provider->getAuthorizationUrl([
            'foo' => 'BAR'
        ]);
        $query = parse_url($url, PHP_URL_QUERY);
        $this->assertNotEmpty($query);

        parse_str($query, $params);
        $this->assertArrayHasKey('foo', $params);
        $this->assertSame('BAR', $params['foo']);
    }

    /**
     * Tests https://github.com/thephpleague/oauth2-client/issues/134
     */
    public function testConstructorSetsProperties()
    {
        $options = [
            'clientId' => '1234',
            'clientSecret' => '4567',
            'redirectUri' => 'http://example.org/redirect'
        ];

        $mockProvider = new MockProvider($options);

        foreach ($options as $key => $value) {
            $this->assertAttributeEquals($value, $key, $mockProvider);
        }
    }

    public function testConstructorSetsClientOptions()
    {
        $timeout = rand(100, 900);

        $mockProvider = new MockProvider(compact('timeout'));

        $config = $mockProvider->getHttpClient()->getConfig();

        $this->assertContains('timeout', $config);
        $this->assertEquals($timeout, $config['timeout']);
    }

    public function testCanSetAProxy()
    {
        $proxy = '192.168.0.1:8888';

        $mockProvider = new MockProvider(['proxy' => $proxy]);

        $config = $mockProvider->getHttpClient()->getConfig();

        $this->assertContains('proxy', $config);
        $this->assertEquals($proxy, $config['proxy']);
    }

    public function testCannotDisableVerifyIfNoProxy()
    {
        $mockProvider = new MockProvider(['verify' => false]);

        $config = $mockProvider->getHttpClient()->getConfig();

        $this->assertContains('verify', $config);
        $this->assertTrue($config['verify']);
    }

    public function testCanDisableVerificationIfThereIsAProxy()
    {
        $mockProvider = new MockProvider(['proxy' => '192.168.0.1:8888', 'verify' => false]);

        $config = $mockProvider->getHttpClient()->getConfig();

        $this->assertContains('verify', $config);
        $this->assertFalse($config['verify']);
    }

    public function testConstructorSetsGrantFactory()
    {
        $mockAdapter = Phony::mock(GrantFactory::class)->get();

        $mockProvider = new MockProvider([], ['grantFactory' => $mockAdapter]);
        $this->assertSame($mockAdapter, $mockProvider->getGrantFactory());
    }

    public function testConstructorSetsHttpAdapter()
    {
        $mockAdapter = Phony::mock(ClientInterface::class)->get();

        $mockProvider = new MockProvider([], ['httpClient' => $mockAdapter]);
        $this->assertSame($mockAdapter, $mockProvider->getHttpClient());
    }

    public function testConstructorSetsRequestFactory()
    {
        $mockAdapter = Phony::mock(RequestFactory::class)->get();

        $mockProvider = new MockProvider([], ['requestFactory' => $mockAdapter]);
        $this->assertSame($mockAdapter, $mockProvider->getRequestFactory());
    }

    public function testSetRedirectHandler()
    {
        $this->testFunction = false;
        $this->state = false;

        $callback = function ($url, $provider) {
            $this->testFunction = $url;
            $this->state = $provider->getState();
        };

        $this->provider->authorize([], $callback);

        $this->assertNotFalse($this->testFunction);
        $this->assertAttributeEquals($this->state, 'state', $this->provider);
    }

    /**
     * @dataProvider userPropertyProvider
     */
    public function testGetUserProperties($response, $name = null, $email = null, $id = null)
    {
        // Mock
        $provider = new MockProvider([
          'clientId' => 'mock_client_id',
          'clientSecret' => 'mock_secret',
          'redirectUri' => 'none',
        ]);

        $token = new AccessToken(['access_token' => 'abc', 'expires_in' => 3600]);

        $stream = Phony::mock(StreamInterface::class);
        $stream->__toString->returns(json_encode(compact('id', 'name', 'email')));

        $response = Phony::mock(ResponseInterface::class);
        $response->getBody->returns($stream->get());
        $response->getHeader->with('content-type')->returns('application/json');

        $client = Phony::mock(ClientInterface::class);
        $client->send->returns($response->get());

        // Run
        $provider->setHttpClient($client->get());
        $user = $provider->getResourceOwner($token);
        $url = $provider->getResourceOwnerDetailsUrl($token);

        // Verify
        $this->assertEquals($id, $user->getId());
        $this->assertEquals($name, $user->getUserScreenName());
        $this->assertEquals($email, $user->getUserEmail());

        $this->assertArrayHasKey('name', $user->toArray());
        $this->assertArrayHasKey('email', $user->toArray());

        Phony::inOrder(
            $client->send->calledWith(
                $this->callback(function ($request) use ($url) {
                    return $request->getMethod() === 'GET'
                        && $request->hasHeader('Authorization')
                        && (string) $request->getUri() === $url;
                })
            ),
            $response->getBody->called(),
            $stream->__toString->called(),
            $response->getHeader->called()
        );
    }

    /**
     * @dataProvider userPropertyProvider
     */
    public function testGetUserPropertiesThrowsExceptionWhenNonJsonResponseIsReceived()
    {
        $this->expectException(\UnexpectedValueException::class);
// Mock
        $provider = new MockProvider([
            'clientId' => 'mock_client_id',
            'clientSecret' => 'mock_secret',
            'redirectUri' => 'none',
        ]);

        $token = new AccessToken(['access_token' => 'abc', 'expires_in' => 3600]);

        $stream = Phony::mock(StreamInterface::class);
        $stream->__toString->returns("<html><body>some unexpected response.</body></html>");

        $response = Phony::mock(ResponseInterface::class);
        $response->getBody->returns($stream->get());
        $response->getHeader->with('content-type')->returns('text/html');

        $client = Phony::mock(ClientInterface::class);
        $client->send->returns($response->get());

        // Run
        $provider->setHttpClient($client->get());

        $user = $provider->getResourceOwner($token);
    }

    public function userPropertyProvider()
    {
        $response = [
            'id'    => 1,
            'email' => 'test@example.com',
            'name'  => 'test',
        ];

        $response2 = [
            'id'    => null,
            'email' => null,
            'name'  => null,
        ];

        $response3 = [];

        return [
            'full response'  => [$response, 'test', 'test@example.com', 1],
            'empty response' => [$response2],
            'no response'    => [$response3],
        ];
    }

    public function getHeadersTest()
    {
        $provider = $this->getMockForAbstractClass(
            '\League\OAuth2\Client\Provider\AbstractProvider',
            [
              [
                  'clientId'     => 'mock_client_id',
                  'clientSecret' => 'mock_secret',
                  'redirectUri'  => 'none',
              ]
            ]
        );

        /**
         * @var $provider AbstractProvider
         */
        $this->assertEquals([], $provider->getHeaders());
        $this->assertEquals([], $provider->getHeaders('mock_token'));

        $provider->authorizationHeader = 'Bearer';
        $this->assertEquals(['Authorization' => 'Bearer abc'], $provider->getHeaders('abc'));

        $token = new AccessToken(['access_token' => 'xyz', 'expires_in' => 3600]);
        $this->assertEquals(['Authorization' => 'Bearer xyz'], $provider->getHeaders($token));
    }

    public function testScopesOverloadedDuringAuthorize()
    {
        $url = $this->provider->getAuthorizationUrl();

        parse_str(parse_url($url, PHP_URL_QUERY), $qs);

        $this->assertArrayHasKey('scope', $qs);
        $this->assertSame('test', $qs['scope']);

        $url = $this->provider->getAuthorizationUrl(['scope' => ['foo', 'bar']]);

        parse_str(parse_url($url, PHP_URL_QUERY), $qs);

        $this->assertArrayHasKey('scope', $qs);
        $this->assertSame('foo,bar', $qs['scope']);
    }

    public function testAuthorizationStateIsRandom()
    {
        $last = null;

        for ($i = 0; $i < 100; $i++) {
            // Repeat the test multiple times to verify state changes
            $url = $this->provider->getAuthorizationUrl();

            parse_str(parse_url($url, PHP_URL_QUERY), $qs);

            $this->assertRegExp('/^[a-zA-Z0-9\/+]{32}$/', $qs['state']);
            $this->assertNotSame($qs['state'], $last);

            $last = $qs['state'];
        }
    }

    public function testErrorResponsesCanBeCustomizedAtTheProvider()
    {
        $provider = new MockProvider([
          'clientId' => 'mock_client_id',
          'clientSecret' => 'mock_secret',
          'redirectUri' => 'none',
        ]);

        $error = ["error" => "Foo error", "code" => 1337];
        $errorJson = json_encode($error);

        $stream = Phony::mock(StreamInterface::class);
        $stream->__toString->returns($errorJson);

        $response = Phony::mock(ResponseInterface::class);
        $response->getBody->returns($stream->get());
        $response->getHeader->with('content-type')->returns('application/json');

        $client = Phony::mock(ClientInterface::class);
        $client->send->returns($response->get());

        // Run
        $provider->setHttpClient($client->get());

        $errorMessage = '';
        $errorCode = 0;

        try {
            $provider->getAccessToken('authorization_code', ['code' => 'mock_authorization_code']);
        } catch (IdentityProviderException $e) {
            $errorMessage = $e->getMessage();
            $errorCode = $e->getCode();
            $errorBody = $e->getResponseBody();
        }

        $method = $provider->getAccessTokenMethod();
        $url = $provider->getBaseAccessTokenUrl([]);

        // Verify
        $this->assertEquals($error['error'], $errorMessage);
        $this->assertEquals($error['code'], $errorCode);
        $this->assertEquals($error, $errorBody);

        Phony::inOrder(
            $client->send->calledWith(
                $this->callback(function ($request) use ($method, $url) {
                    return $request->getMethod() === $method
                        && (string) $request->getUri() === $url;
                })
            ),
            $response->getBody->called(),
            $stream->__toString->called(),
            $response->getHeader->called()
        );
    }

    /**
     * @expectedException \League\OAuth2\Client\Provider\Exception\IdentityProviderException
     */
    public function testClientErrorTriggersProviderException()
    {
        $provider = new MockProvider([
          'clientId' => 'mock_client_id',
          'clientSecret' => 'mock_secret',
          'redirectUri' => 'none',
        ]);

        $stream = Phony::mock(StreamInterface::class);
        $stream->__toString->returns('{"error":"Foo error","code":1337}');

        $request = Phony::mock(RequestInterface::class);

        $response = Phony::mock(ResponseInterface::class);
        $response->getStatusCode->returns(400);
        $response->getBody->returns($stream->get());
        $response->getHeader->with('content-type')->returns('application/json');

        $client = Phony::mock(ClientInterface::class);
        $client->send->throws(new BadResponseException(
            'test exception',
            $request->get(),
            $response->get()
        ));

        // Run
        $provider->setHttpClient($client->get());
        $provider->getAccessToken('authorization_code', ['code' => 'mock_authorization_code']);
    }

    public function testGetResponse()
    {
        $provider = new MockProvider();

        $request = Phony::mock(RequestInterface::class)->get();
        $response = Phony::mock(ResponseInterface::class)->get();

        $client = Phony::mock(ClientInterface::class);
        $client->send->with($request)->returns($response);

        // Run
        $provider->setHttpClient($client->get());
        $output = $provider->getResponse($request);

        // Verify
        $this->assertSame($output, $response);
    }

    public function testAuthenticatedRequestAndResponse()
    {
        $provider = new MockProvider();

        $token = new AccessToken(['access_token' => 'abc', 'expires_in' => 3600]);
        $request = $provider->getAuthenticatedRequest('get', 'https://api.example.com/v1/test', $token);

        $stream = Phony::mock(StreamInterface::class);
        $stream->__toString->returns('{"example":"response"}');

        $response = Phony::mock(ResponseInterface::class);
        $response->getBody->returns($stream->get());
        $response->getHeader->with('content-type')->returns('application/json');

        $client = Phony::mock(ClientInterface::class);
        $client->send->with($request)->returns($response->get());

        // Run
        $provider->setHttpClient($client->get());
        $result = $provider->getParsedResponse($request);

        // Verify
        $this->assertSame(['example' => 'response'], $result);

        $this->assertInstanceOf(RequestInterface::class, $request);

        // Authorization header should contain the token
        $header = $request->getHeader('Authorization');
        $this->assertContains('Bearer abc', $header);

        Phony::inOrder(
            $client->send->called(),
            $response->getBody->called(),
            $stream->__toString->called(),
            $response->getHeader->called()
        );
    }

    public function getAccessTokenMethodProvider()
    {
        return [
            ['GET'],
            ['POST'],
        ];
    }

    /**
     * @dataProvider getAccessTokenMethodProvider
     */
    public function testGetAccessToken($method)
    {
        $provider = new MockProvider([
          'clientId' => 'mock_client_id',
          'clientSecret' => 'mock_secret',
          'redirectUri' => 'none',
        ]);

        $provider->setAccessTokenMethod($method);

        $grant_name = 'mock';
        $raw_response = ['access_token' => 'okay', 'expires' => time() + 3600, 'resource_owner_id' => 3];

        $grant = Phony::mock(AbstractGrant::class);
        $grant->prepareRequestParameters->returns([]);

        $stream = Phony::mock(StreamInterface::class);
        $stream->__toString->returns(json_encode($raw_response));

        $response = Phony::mock(ResponseInterface::class);
        $response->getBody->returns($stream->get());
        $response->getHeader->with('content-type')->returns('application/json');

        $client = Phony::mock(ClientInterface::class);
        $client->send->returns($response->get());

        // Run
        $provider->setHttpClient($client->get());
        $token = $provider->getAccessToken($grant->get(), ['code' => 'mock_authorization_code']);

        // Verify
        $this->assertInstanceOf(AccessToken::class, $token);

        $this->assertSame($raw_response['resource_owner_id'], $token->getResourceOwnerId());
        $this->assertSame($raw_response['access_token'], $token->getToken());
        $this->assertSame($raw_response['expires'], $token->getExpires());

        Phony::inOrder(
            $grant->prepareRequestParameters->calledWith('~', '~'),
            $client->send->calledWith(
                $this->callback(function ($request) use ($provider) {
                    return $request->getMethod() === $provider->getAccessTokenMethod()
                        && (string) $request->getUri() === $provider->getBaseAccessTokenUrl([]);
                })
            ),
            $response->getBody->called(),
            $stream->__toString->called(),
            $response->getHeader->called()
        );
    }

    private function getMethod($class, $name)
    {
        $class = new \ReflectionClass($class);
        $method = $class->getMethod($name);

        $method->setAccessible(true);
        return $method;
    }

    public function parseResponseProvider()
    {
        return [
            [
                'body'    => '{"a": 1}',
                'type'    => 'application/json',
                'parsed'  => ['a' => 1]
            ],
            [
                'body'    => 'string',
                'type'    => 'unknown',
                'parsed'  => 'string'
            ],
            [
                'body'    => 'a=1&b=2',
                'type'    => 'application/x-www-form-urlencoded',
                'parsed'  => ['a' => 1, 'b' => 2]
            ],
        ];
    }

    /**
     * @dataProvider parseResponseProvider
     */
    public function testParseResponse($body, $type, $parsed)
    {
        $stream = Phony::mock(StreamInterface::class);
        $stream->__toString->returns($body);

        $response = Phony::mock(ResponseInterface::class);
        $response->getBody->returns($stream->get());
        $response->getHeader->with('content-type')->returns($type);

        $method = $this->getMethod(AbstractProvider::class, 'parseResponse');
        $result = $method->invoke($this->provider, $response->get());

        $this->assertEquals($parsed, $result);
    }

    /**
     * @expectedException UnexpectedValueException
     */
    public function testParseResponseJsonFailure()
    {
        $this->testParseResponse('{a: 1}', 'application/json', null);
    }

    public function getAppendQueryProvider()
    {
        return [
            ['test.com/?a=1', 'test.com/', '?a=1'],
            ['test.com/?a=1', 'test.com/', '&a=1'],
            ['test.com/?a=1', 'test.com/', 'a=1'],
            ['test.com/?a=1', 'test.com/?a=1', '?'],
            ['test.com/?a=1', 'test.com/?a=1', '&'],
            ['test.com/?a=1&b=2', 'test.com/?a=1', '&b=2'],
            ['test.com/?a=1&b=2', 'test.com/?a=1', 'b=2'],
            ['test.com/?a=1&b=2', 'test.com/?a=1', '?b=2'],
            ['test.com/?a=1&b=1&b=2', 'test.com/?a=1&b=1', 'b=2'],
            ['test.com/?a=1&b=2&b=2', 'test.com/?a=1&b=2', 'b=2'],
        ];
    }

    /**
     * @dataProvider getAppendQueryProvider
     */
    public function testAppendQuery($expected, $url, $query)
    {
        $method = $this->getMethod(AbstractProvider::class, 'appendQuery');
        $this->assertEquals($expected, $method->invoke($this->provider, $url, $query));
    }

    protected function getAbstractProviderMock()
    {
        $mock = Phony::partialMock(AbstractProvider::class);
        return Liberator::liberate($mock->get());
    }

    public function testDefaultAccessTokenMethod()
    {
        $provider = $this->getAbstractProviderMock();

        $method = $provider->getAccessTokenMethod();

        $expectedMethod = 'POST';
        $this->assertEquals($expectedMethod, $method);
    }

    public function testDefaultPrepareAccessTokenResponse()
    {
        $provider = Phony::partialMock(Fake\ProviderWithAccessTokenResourceOwnerId::class);
        $provider = Liberator::liberate($provider->get());

        $result = ['user_id' => uniqid()];
        $newResult = $provider->prepareAccessTokenResponse($result);

        $this->assertArrayHasKey('resource_owner_id', $newResult);
        $this->assertEquals($result['user_id'], $newResult['resource_owner_id']);
    }

    public function testPrepareAccessTokenResponseWithDotNotation()
    {
        $provider = Phony::partialMock(Fake\ProviderWithAccessTokenResourceOwnerId::class);
        $provider->getAccessTokenResourceOwnerId->returns('user.id');
        $provider = Liberator::liberate($provider->get());

        $result = ['user' => ['id' => uniqid()]];
        $newResult = $provider->prepareAccessTokenResponse($result);

        $this->assertArrayHasKey('resource_owner_id', $newResult);
        $this->assertEquals($result['user']['id'], $newResult['resource_owner_id']);
    }

    public function testPrepareAccessTokenResponseWithInvalidKeyType()
    {
        $provider = Phony::mock(Fake\ProviderWithAccessTokenResourceOwnerId::class);
        $provider->getAccessTokenResourceOwnerId->returns(new \stdClass);
        $provider = Liberator::liberate($provider->get());

        $result = ['user_id' => uniqid()];
        $newResult = $provider->prepareAccessTokenResponse($result);

        $this->assertFalse(isset($newResult['resource_owner_id']));
    }

    public function testPrepareAccessTokenResponseWithInvalidKeyPath()
    {
        $provider = Phony::mock(Fake\ProviderWithAccessTokenResourceOwnerId::class);
        $provider->getAccessTokenResourceOwnerId->returns('user.name');
        $provider = Liberator::liberate($provider->get());

        $result = ['user' => ['id' => uniqid()]];
        $newResult = $provider->prepareAccessTokenResponse($result);

        $this->assertFalse(isset($newResult['resource_owner_id']));
    }

    public function testDefaultAuthorizationHeaders()
    {
        $provider = $this->getAbstractProviderMock();

        $headers = $provider->getAuthorizationHeaders();

        $this->assertEquals([], $headers);
    }
}
MercadoLibre PHP SDK API documentation generated by ApiGen