diff --git a/tests/CanAccessTokenStub.php b/tests/CanAccessTokenStub.php new file mode 100644 index 0000000..c6cb3ae --- /dev/null +++ b/tests/CanAccessTokenStub.php @@ -0,0 +1,21 @@ + bin2hex(random_bytes(128)), + 'expires_in' => 3600, + ]; + + $values = array_replace($default, ...$args); + + return new AccessToken($values); + } +} diff --git a/tests/CanMockHttp.php b/tests/CanMockHttp.php new file mode 100644 index 0000000..c8b1625 --- /dev/null +++ b/tests/CanMockHttp.php @@ -0,0 +1,29 @@ +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; + } +} diff --git a/tests/StackExchangeTest.php b/tests/StackExchangeTest.php index aec4178..8b1d82a 100644 --- a/tests/StackExchangeTest.php +++ b/tests/StackExchangeTest.php @@ -1,187 +1,172 @@ 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); +}