Skip to content

Commit

Permalink
Improve tests
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexMasterov committed Jun 24, 2017
1 parent e53c471 commit c9b642c
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 128 deletions.
21 changes: 21 additions & 0 deletions tests/CanAccessTokenStub.php
@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);

namespace AlexMasterov\OAuth2\Client\Provider\Tests;

use League\OAuth2\Client\Token\AccessToken;

trait CanAccessTokenStub
{
protected function accessToken(...$args): AccessToken
{
$default = [
'access_token' => bin2hex(random_bytes(128)),
'expires_in' => 3600,
];

$values = array_replace($default, ...$args);

return new AccessToken($values);
}
}
29 changes: 29 additions & 0 deletions tests/CanMockHttp.php
@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);

namespace AlexMasterov\OAuth2\Client\Provider\Tests;

use Psr\Http\Message\ResponseInterface;

trait CanMockHttp
{
protected function mockResponse(
string $body = '',
string $type = 'application/json',
int $code = 200
): ResponseInterface {
$response = self::createMock(ResponseInterface::class);
$response->expects(self::any())
->method('getHeader')
->with(self::stringContains('content-type'))
->willReturn($type);
$response->expects(self::any())
->method('getBody')
->willReturn($body);
$response->expects(self::any())
->method('getStatusCode')
->willReturn($code);

return $response;
}
}
241 changes: 113 additions & 128 deletions tests/StackExchangeTest.php
@@ -1,187 +1,172 @@
<?php
declare(strict_types=1);

namespace AlexMasterov\OAuth2\Client\Tests\Provider;
namespace AlexMasterov\OAuth2\Client\Provider\Tests;

use AlexMasterov\OAuth2\Client\Provider\Exception\StackExchangeException;
use AlexMasterov\OAuth2\Client\Provider\StackExchange;
use Eloquent\Phony\Phpunit\Phony;
use GuzzleHttp\ClientInterface;
use League\OAuth2\Client\Token\AccessToken;
use AlexMasterov\OAuth2\Client\Provider\{
StackExchange,
StackExchangeException,
StackExchangeResourceOwner,
Tests\CanAccessTokenStub,
Tests\CanMockHttp
};
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ResponseInterface;

class StackExchangeTest extends TestCase
{
/**
* @var StackExchange
*/
private $provider;
use CanAccessTokenStub;
use CanMockHttp;

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

protected function tearDown()
{
parent::tearDown();
}

protected function mockResponse($body)
{
$response = Phony::mock(ResponseInterface::class);
$response->getHeader->with('content-type')->returns('application/json');
$response->getBody->returns(json_encode($body));

return $response;
}

protected function mockClient(ResponseInterface $response)
public function testAuthorizationUrl()
{
$client = Phony::mock(ClientInterface::class);
$client->send->returns($response);
// Execute
$url = $this->provider()
->getAuthorizationUrl();

return $client;
// Verify
self::assertSame('/oauth', path($url));
}

protected function getMethod($class, $name)
public function testBaseAccessTokenUrl()
{
$class = new \ReflectionClass($class);
$method = $class->getMethod($name);
$method->setAccessible(true);
static $params = [];

return $method;
}

public function testAuthorizationUrl()
{
// Run
$url = $this->provider->getAuthorizationUrl();
$path = \parse_url($url, PHP_URL_PATH);
// Execute
$url = $this->provider()
->getBaseAccessTokenUrl($params);

// Verify
$this->assertSame('/oauth', $path);
self::assertSame('/oauth/access_token', path($url));
}

public function testBaseAccessTokenUrl()
public function testResourceOwnerDetailsUrl()
{
$params = [];
// Stub
$apiUrl = $this->apiUrl();
$tokenParams = [
'access_token' => 'mock_access_token',
'site' => 'stackoverflow',
];

// Run
$url = $this->provider->getBaseAccessTokenUrl($params);
$path = \parse_url($url, PHP_URL_PATH);
list($accessToken, $site) = array_values($tokenParams);

// Execute
$detailUrl = $this->provider()
->getResourceOwnerDetailsUrl($this->accessToken($tokenParams));

// Verify
$this->assertSame('/oauth/access_token', $path);
self::assertSame(
"{$apiUrl}me?access_token={$accessToken}&site={$site}",
$detailUrl
);
}

public function testDefaultScopes()
{
// Run
$method = $this->getMethod(get_class($this->provider), 'getDefaultScopes');
$result = $method->invoke($this->provider);
$getDefaultScopes = function () {
return $this->getDefaultScopes();
};

// Execute
$defaultScopes = $getDefaultScopes->call($this->provider());

// Verify
$this->assertEquals([], $result);
self::assertSame([], $defaultScopes);
}

public function testGetAccessToken()
public function testParseResponse()
{
// https://api.stackexchange.com/docs/authentication
$body = [
'access_token' => 'mock_access_token',
'token_type' => 'bearer',
'expires_in' => \time() * 3600,
'refresh_token' => 'mock_refresh_token',
];
$getParseResponse = function ($response) {
return $this->parseResponse($response);
};

$response = $this->mockResponse($body);
$client = $this->mockClient($response->get());
// Mock
$plain = $this->mockResponse('mock_body=test', 'text/plain');
$default = $this->mockResponse(json_encode(['mock_body' => 'test']));

// Run
$this->provider->setHttpClient($client->get());
$token = $this->provider->getAccessToken('authorization_code', ['code' => 'mock_authorization_code']);
// Execute
$parsedPlain = $getParseResponse->call($this->provider(), $plain);
$parsedDefault = $getParseResponse->call($this->provider(), $default);

// Verify
$this->assertNull($token->getResourceOwnerId());
$this->assertEquals($body['access_token'], $token->getToken());
$this->assertEquals($body['refresh_token'], $token->getRefreshToken());
$this->assertGreaterThanOrEqual($body['expires_in'], $token->getExpires());
self::assertSame(['mock_body' => 'test'], $parsedPlain);
self::assertSame(['mock_body' => 'test'], $parsedDefault);
}

public function testUserProperty()
public function testCheckResponse()
{
$body = [
'items' => [
0 => [
'user_id' => 12345678,
],
],
];
$getParseResponse = function () use (&$response, &$data) {
return $this->checkResponse($response, $data);
};

$tokenOptions = [
'access_token' => 'mock_access_token',
'expires_in' => 3600,
];

$token = new AccessToken($tokenOptions);
$response = $this->mockResponse($body);
$client = $this->mockClient($response->get());
// Stub
$code = 400;
$data = ['error' => [
'type' => 'Foo error',
'message' => 'Error message',
]];

// Run
$this->provider->setHttpClient($client->get());
$user = $this->provider->getResourceOwner($token);
// Mock
$response = $this->mockResponse('', '', $code);

// Verify
$this->assertSame([$body['items'][0]['user_id']], $user->getId());
self::expectException(StackExchangeException::class);
self::expectExceptionCode($code);
self::expectExceptionMessage(implode(': ', $data['error']));

foreach ($user->toArray() as $user) {
$this->assertArrayHasKey('user_id', $user);
}
// Execute
$getParseResponse->call($this->provider());
}

public function testParseResponse()
public function testCreateResourceOwner()
{
$body = 'access_token=mock_access_token&expires=3600';
parse_str($body, $parsed);
$getCreateResourceOwner = function () use (&$response, &$token) {
return $this->createResourceOwner($response, $token);
};

$response = Phony::mock(ResponseInterface::class);
$response->getHeader->with('content-type')->returns('text/plain');
$response->getBody->returns($body);
$client = $this->mockClient($response->get());
// Stub
$token = $this->accessToken();
$response = ['items' => [
0 => ['user_id' => random_int(1, 1000)],
]];

// Run
$method = $this->getMethod(get_class($this->provider), 'parseResponse');
$result = $method->invoke($this->provider, $response->get());
// Execute
$resourceOwner = $getCreateResourceOwner->call($this->provider());

// Verify
$this->assertEquals($parsed, $result);
self::assertInstanceOf(StackExchangeResourceOwner::class, $resourceOwner);

$items = $response['items'];
$ids = array_values($items[0]);

self::assertEquals($ids, $resourceOwner->getId());
self::assertSame($items, $resourceOwner->toArray());
}

public function testErrorResponses()
private function provider(...$args): StackExchange
{
$code = 400;
$body = [
'error' => [
'type' => 'Foo error',
'message' => 'Error message',
],
static $default = [
'clientId' => 'mock_client_id',
'clientSecret' => 'mock_secret',
'redirectUri' => 'mock_redirect_uri',
];

$response = $this->mockResponse($body);
$response->getStatusCode->returns($code);
$client = $this->mockClient($response->get());
$values = array_replace($default, ...$args);

$this->expectException(StackExchangeException::class);
$this->expectExceptionCode($code);
$this->expectExceptionMessage(implode(': ', $body['error']));
return new StackExchange($values);
}

// Run
$this->provider->setHttpClient($client->get());
$this->provider->getAccessToken('authorization_code', ['code' => 'mock_authorization_code']);
private function apiUrl(): string
{
$getApiUrl = function () {
return $this->urlApi;
};

return $getApiUrl->call($this->provider());
}
}

function path(string $url): string
{
return parse_url($url, PHP_URL_PATH);
}

0 comments on commit c9b642c

Please sign in to comment.