diff --git a/CHANGELOG.md b/CHANGELOG.md index 4aa27bb..ea094f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,40 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. +## 1.0.0alpha3 - 2018-02-27 + +### Added + +- Nothing. + +### Changed + +- [#17](https://github.com/zendframework/zend-expressive-authentication-oauth2/pull/17) + changes the constructor of each of the `Zend\Expressive\Authentication\OAuth2\OAuth2Adapter` + and `Zend\Expressive\Authentication\OAuth2\OAuth2Middleware` classes to accept + a callable `$responseFactory` instead of a `Psr\Http\Message\ResponseInterface` + response prototype. The `$responseFactory` should produce a + `ResponseInterface` implementation when invoked. + +- [#17](https://github.com/zendframework/zend-expressive-authentication-oauth2/pull/17) + updates the `OAuth2AdapterFactory` and `OAuth2MiddlewareFactory` classes to no + longer use `Zend\Expressive\Authentication\ResponsePrototypeTrait`, and + instead always depend on the `Psr\Http\Message\ResponseInterface` service to + correctly return a PHP callable capable of producing a `ResponseInterface` + instance. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. + ## 1.0.0alpha2 - 2018-02-26 ### Added diff --git a/src/OAuth2Adapter.php b/src/OAuth2Adapter.php index 32e1047..f1278f1 100644 --- a/src/OAuth2Adapter.php +++ b/src/OAuth2Adapter.php @@ -28,20 +28,16 @@ class OAuth2Adapter implements AuthenticationInterface protected $resourceServer; /** - * @var ResponseInterface + * @var callable */ - protected $responsePrototype; + protected $responseFactory; - /** - * Constructor - * - * @param ResourceServer $resourceServer - * @param ResponseInterface $responsePrototype - */ - public function __construct(ResourceServer $resourceServer, ResponseInterface $responsePrototype) + public function __construct(ResourceServer $resourceServer, callable $responseFactory) { $this->resourceServer = $resourceServer; - $this->responsePrototype = $responsePrototype; + $this->responseFactory = function () use ($responseFactory) : ResponseInterface { + return $responseFactory(); + }; } /** @@ -66,7 +62,7 @@ public function authenticate(ServerRequestInterface $request) : ?UserInterface */ public function unauthorizedResponse(ServerRequestInterface $request) : ResponseInterface { - return $this->responsePrototype + return ($this->responseFactory)() ->withHeader( 'WWW-Authenticate', 'Bearer token-example' diff --git a/src/OAuth2AdapterFactory.php b/src/OAuth2AdapterFactory.php index e64ac96..733cb4d 100644 --- a/src/OAuth2AdapterFactory.php +++ b/src/OAuth2AdapterFactory.php @@ -12,13 +12,11 @@ use League\OAuth2\Server\ResourceServer; use Psr\Container\ContainerInterface; +use Psr\Http\Message\ResponseInterface; use Zend\Expressive\Authentication\OAuth2\Exception; -use Zend\Expressive\Authentication\ResponsePrototypeTrait; class OAuth2AdapterFactory { - use ResponsePrototypeTrait; - public function __invoke(ContainerInterface $container) : OAuth2Adapter { $resourceServer = $container->has(ResourceServer::class) @@ -33,7 +31,7 @@ public function __invoke(ContainerInterface $container) : OAuth2Adapter return new OAuth2Adapter( $resourceServer, - $this->getResponsePrototype($container) + $container->get(ResponseInterface::class) ); } } diff --git a/src/OAuth2Middleware.php b/src/OAuth2Middleware.php index 346ad58..e2219a6 100644 --- a/src/OAuth2Middleware.php +++ b/src/OAuth2Middleware.php @@ -26,20 +26,16 @@ class OAuth2Middleware implements MiddlewareInterface protected $server; /** - * @var ResponseInterface + * @var callable */ - protected $responsePrototype; + protected $responseFactory; - /** - * Constructor - * - * @param AuthorizationServer $server - * @param ResponseInterface $responsePrototype - */ - public function __construct(AuthorizationServer $server, ResponseInterface $responsePrototype) + public function __construct(AuthorizationServer $server, callable $responseFactory) { $this->server = $server; - $this->responsePrototype = $responsePrototype; + $this->responseFactory = function () use ($responseFactory) : ResponseInterface { + return $responseFactory(); + }; } /** @@ -54,7 +50,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface case 'POST': return $this->accessTokenRequest($request); } - return $this->responsePrototype->withStatus(501); // Method not implemented + return ($this->responseFactory)()->withStatus(501); // Method not implemented } /** @@ -69,6 +65,9 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface */ protected function authorizationRequest(ServerRequestInterface $request) : ResponseInterface { + // Create a new response for the request + $response = ($this->responseFactory)(); + try { // Validate the HTTP request and return an AuthorizationRequest object. $authRequest = $this->server->validateAuthorizationRequest($request); @@ -87,12 +86,12 @@ protected function authorizationRequest(ServerRequestInterface $request) : Respo $authRequest->setAuthorizationApproved(true); // Return the HTTP redirect response - return $this->server->completeAuthorizationRequest($authRequest, $this->responsePrototype); + return $this->server->completeAuthorizationRequest($authRequest, $response); } catch (OAuthServerException $exception) { - return $exception->generateHttpResponse($this->responsePrototype); + return $exception->generateHttpResponse($response); } catch (\Exception $exception) { return (new OAuthServerException($exception->getMessage(), 0, 'unknown_error', 500)) - ->generateHttpResponse($this->responsePrototype); + ->generateHttpResponse($response); } } @@ -109,13 +108,16 @@ protected function authorizationRequest(ServerRequestInterface $request) : Respo */ protected function accessTokenRequest(ServerRequestInterface $request) : ResponseInterface { + // Create a new response for the request + $response = ($this->responseFactory)(); + try { - return $this->server->respondToAccessTokenRequest($request, $this->responsePrototype); + return $this->server->respondToAccessTokenRequest($request, $response); } catch (OAuthServerException $exception) { - return $exception->generateHttpResponse($this->responsePrototype); + return $exception->generateHttpResponse($response); } catch (\Exception $exception) { return (new OAuthServerException($exception->getMessage(), 0, 'unknown_error', 500)) - ->generateHttpResponse($this->responsePrototype); + ->generateHttpResponse($response); } } } diff --git a/src/OAuth2MiddlewareFactory.php b/src/OAuth2MiddlewareFactory.php index 643f3d4..0a980ae 100644 --- a/src/OAuth2MiddlewareFactory.php +++ b/src/OAuth2MiddlewareFactory.php @@ -12,12 +12,10 @@ use League\OAuth2\Server\AuthorizationServer; use Psr\Container\ContainerInterface; -use Zend\Expressive\Authentication\ResponsePrototypeTrait; +use Psr\Http\Message\ResponseInterface; class OAuth2MiddlewareFactory { - use ResponsePrototypeTrait; - public function __invoke(ContainerInterface $container) : OAuth2Middleware { $authServer = $container->has(AuthorizationServer::class) @@ -33,7 +31,7 @@ public function __invoke(ContainerInterface $container) : OAuth2Middleware return new OAuth2Middleware( $authServer, - $this->getResponsePrototype($container) + $container->get(ResponseInterface::class) ); } } diff --git a/test/OAuth2AdapterFactoryTest.php b/test/OAuth2AdapterFactoryTest.php index 3df5cb3..aa08059 100644 --- a/test/OAuth2AdapterFactoryTest.php +++ b/test/OAuth2AdapterFactoryTest.php @@ -12,19 +12,37 @@ use League\OAuth2\Server\ResourceServer; use PHPUnit\Framework\TestCase; +use Prophecy\Prophecy\ObjectProphecy; use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseInterface; +use stdClass; +use TypeError; use Zend\Expressive\Authentication\AuthenticationInterface; use Zend\Expressive\Authentication\OAuth2\OAuth2Adapter; use Zend\Expressive\Authentication\OAuth2\OAuth2AdapterFactory; class OAuth2AdapterFactoryTest extends TestCase { + /** @var ContainerInterface|ObjectProphecy */ + private $container; + + /** @var ResourceServer|ObjectProphecy */ + private $resourceServer; + + /** @var ResponseInterface|ObjectProphecy */ + private $response; + + /** @var callable */ + private $responseFactory; + public function setUp() { - $this->container = $this->prophesize(ContainerInterface::class); - $this->resourceServer = $this->prophesize(ResourceServer::class); - $this->response = $this->prophesize(ResponseInterface::class); + $this->container = $this->prophesize(ContainerInterface::class); + $this->resourceServer = $this->prophesize(ResourceServer::class); + $this->response = $this->prophesize(ResponseInterface::class); + $this->responseFactory = function () { + return $this->response->reveal(); + }; } public function testConstructor() @@ -42,7 +60,7 @@ public function testInvokeWithEmptyContainer() $oauth2Adapter = $factory($this->container->reveal()); } - public function testInvokeWithResourceServerEmptyResponse() + public function testFactoryRaisesTypeErrorForNonCallableResponseFactory() { $this->container ->has(ResourceServer::class) @@ -52,17 +70,35 @@ public function testInvokeWithResourceServerEmptyResponse() ->willReturn($this->resourceServer->reveal()); $this->container - ->has(ResponseInterface::class) - ->willReturn(false); + ->get(ResponseInterface::class) + ->willReturn(new stdClass()); $factory = new OAuth2AdapterFactory(); + + $this->expectException(TypeError::class); $adapter = $factory($this->container->reveal()); + } - $this->assertInstanceOf(OAuth2Adapter::class, $adapter); - $this->assertInstanceOf(AuthenticationInterface::class, $adapter); + public function testFactoryRaisesTypeErrorWhenResponseServiceProvidesResponseInstance() + { + $this->container + ->has(ResourceServer::class) + ->willReturn(true); + $this->container + ->get(ResourceServer::class) + ->willReturn($this->resourceServer->reveal()); + + $this->container + ->get(ResponseInterface::class) + ->will([$this->response, 'reveal']); + + $factory = new OAuth2AdapterFactory(); + + $this->expectException(TypeError::class); + $adapter = $factory($this->container->reveal()); } - public function testInvokeResourceServerAndResponse() + public function testFactoryReturnsInstanceWhenAppropriateDependenciesArePresentInContainer() { $this->container ->has(ResourceServer::class) @@ -76,9 +112,7 @@ public function testInvokeResourceServerAndResponse() ->willReturn(true); $this->container ->get(ResponseInterface::class) - ->willReturn(function () { - return $this->response->reveal(); - }); + ->willReturn($this->responseFactory); $factory = new OAuth2AdapterFactory(); $adapter = $factory($this->container->reveal()); diff --git a/test/OAuth2AdapterTest.php b/test/OAuth2AdapterTest.php index 72eea1b..bed3ce8 100644 --- a/test/OAuth2AdapterTest.php +++ b/test/OAuth2AdapterTest.php @@ -22,17 +22,29 @@ class OAuth2AdapterTest extends TestCase { + /** @var ResourceServer|ObjectProphecy */ + private $resourceServer; + + /** @var ResponseInterface|ObjectProphecy */ + private $response; + + /** @var callable */ + private $responseFactory; + public function setUp() { - $this->resourceServer = $this->prophesize(ResourceServer::class); - $this->response = $this->prophesize(ResponseInterface::class); + $this->resourceServer = $this->prophesize(ResourceServer::class); + $this->response = $this->prophesize(ResponseInterface::class); + $this->responseFactory = function () { + return $this->response->reveal(); + }; } public function testConstructor() { $adapter = new OAuth2Adapter( $this->resourceServer->reveal(), - $this->response->reveal() + $this->responseFactory ); $this->assertInstanceOf(OAuth2Adapter::class, $adapter); $this->assertInstanceOf(AuthenticationInterface::class, $adapter); @@ -50,7 +62,7 @@ public function testOAuthServerExceptionRaisedDuringAuthenticateResultsInInvalid $adapter = new OAuth2Adapter( $this->resourceServer->reveal(), - $this->response->reveal() + $this->responseFactory ); $this->assertNull($adapter->authenticate($request->reveal())); @@ -67,7 +79,7 @@ public function testAuthenticateReturnsNullIfResourceServerDoesNotProduceAUserId $adapter = new OAuth2Adapter( $this->resourceServer->reveal(), - $this->response->reveal() + $this->responseFactory ); $this->assertNull($adapter->authenticate($request->reveal())); @@ -84,7 +96,7 @@ public function testAuthenticateReturnsAUserIfTheResourceServerProducesAUserId() $adapter = new OAuth2Adapter( $this->resourceServer->reveal(), - $this->response->reveal() + $this->responseFactory ); $user = $adapter->authenticate($request->reveal()); @@ -107,7 +119,7 @@ public function testUnauthorizedResponseProducesAResponseWithAWwwAuthenticateHea $adapter = new OAuth2Adapter( $this->resourceServer->reveal(), - $this->response->reveal() + $this->responseFactory ); $this->assertSame( diff --git a/test/OAuth2MiddlewareFactoryTest.php b/test/OAuth2MiddlewareFactoryTest.php index 1a80c7f..021938a 100644 --- a/test/OAuth2MiddlewareFactoryTest.php +++ b/test/OAuth2MiddlewareFactoryTest.php @@ -12,8 +12,11 @@ use League\OAuth2\Server\AuthorizationServer; use PHPUnit\Framework\TestCase; +use Prophecy\Prophecy\ObjectProphecy; use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseInterface; +use stdClass; +use TypeError; use Zend\Expressive\Authentication\OAuth2\Exception\InvalidConfigException; use Zend\Expressive\Authentication\OAuth2\OAuth2Middleware; use Zend\Expressive\Authentication\OAuth2\OAuth2MiddlewareFactory; @@ -23,6 +26,15 @@ */ class OAuth2MiddlewareFactoryTest extends TestCase { + /** @var AuthorizationServer|ObjectProphecy */ + private $authServer; + + /** @var AuthServer|ObjectProphecy */ + private $container; + + /** @var ResponseInterface|ObjectProphecy */ + private $response; + public function setUp() { $this->container = $this->prophesize(ContainerInterface::class); @@ -44,7 +56,7 @@ public function testInvokeWithEmptyContainer() $middleware = $factory($this->container->reveal()); } - public function testInvokeWithAuthServerWithoutResponseInterface() + public function testFactoryRaisesTypeErrorForNonCallableResponseFactory() { $this->container ->has(AuthorizationServer::class) @@ -53,21 +65,37 @@ public function testInvokeWithAuthServerWithoutResponseInterface() ->get(AuthorizationServer::class) ->willReturn($this->authServer->reveal()); $this->container - ->has(ResponseInterface::class) - ->willReturn(false); + ->get(ResponseInterface::class) + ->willReturn(new stdClass()); $factory = new OAuth2MiddlewareFactory(); - $middleware = $factory($this->container->reveal()); - $this->assertInstanceOf(OAuth2Middleware::class, $middleware); + + $this->expectException(TypeError::class); + $factory($this->container->reveal()); } - public function testInvokeWithAuthServerWithResponseInterface() + public function testFactoryRaisesTypeErrorWhenResponseServiceProvidesResponseInstance() { $this->container ->has(AuthorizationServer::class) ->willReturn(true); $this->container - ->has(ResponseInterface::class) + ->get(AuthorizationServer::class) + ->willReturn($this->authServer->reveal()); + $this->container + ->get(ResponseInterface::class) + ->will([$this->response, 'reveal']); + + $factory = new OAuth2MiddlewareFactory(); + + $this->expectException(TypeError::class); + $factory($this->container->reveal()); + } + + public function testFactoryReturnsInstanceWhenAppropriateDependenciesArePresentInContainer() + { + $this->container + ->has(AuthorizationServer::class) ->willReturn(true); $this->container ->get(AuthorizationServer::class) diff --git a/test/OAuth2MiddlewareTest.php b/test/OAuth2MiddlewareTest.php index 5dfe8f5..b84a213 100644 --- a/test/OAuth2MiddlewareTest.php +++ b/test/OAuth2MiddlewareTest.php @@ -15,6 +15,7 @@ use League\OAuth2\Server\RequestTypes\AuthorizationRequest; use PHPUnit\Framework\TestCase; use Prophecy\Argument; +use Prophecy\Prophecy\ObjectProphecy; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\StreamInterface; @@ -25,20 +26,41 @@ class OAuth2MiddlewareTest extends TestCase { + /** @var AuthorizationRequest|ObjectProphecy */ + private $authRequest; + + /** @var AuthorizationServer|ObjectProphecy */ + private $authServer; + + /** @var RequestHandlerInterface|ObjectProphecy */ + private $handler; + + /** @var ResponseInterface|ObjectProphecy */ + private $response; + + /** @var callable */ + private $responseFactory; + + /** @var ServerRequestInterface|ObjectProphecy */ + private $serverRequest; + public function setUp() { - $this->authServer = $this->prophesize(AuthorizationServer::class); - $this->response = $this->prophesize(ResponseInterface::class); - $this->serverRequest = $this->prophesize(ServerRequestInterface::class); - $this->authRequest = $this->prophesize(AuthorizationRequest::class); - $this->handler = $this->prophesize(RequestHandlerInterface::class); + $this->authServer = $this->prophesize(AuthorizationServer::class); + $this->response = $this->prophesize(ResponseInterface::class); + $this->serverRequest = $this->prophesize(ServerRequestInterface::class); + $this->authRequest = $this->prophesize(AuthorizationRequest::class); + $this->handler = $this->prophesize(RequestHandlerInterface::class); + $this->responseFactory = function () { + return $this->response->reveal(); + }; } public function testConstructor() { $middleware = new OAuth2Middleware( $this->authServer->reveal(), - $this->response->reveal() + $this->responseFactory ); $this->assertInstanceOf(OAuth2Middleware::class, $middleware); $this->assertInstanceOf(MiddlewareInterface::class, $middleware); @@ -69,7 +91,7 @@ public function testProcessWithGet() $middleware = new OAuth2Middleware( $this->authServer->reveal(), - $this->response->reveal() + $this->responseFactory ); $response = $middleware->process( $this->serverRequest->reveal(), @@ -92,7 +114,7 @@ public function testProcessWithPost() $middleware = new OAuth2Middleware( $this->authServer->reveal(), - $this->response->reveal() + $this->responseFactory ); $response = $middleware->process( $this->serverRequest->reveal(), @@ -117,7 +139,7 @@ public function testAuthorizationRequestRaisingOAuthServerExceptionGeneratesResp $middleware = new OAuth2Middleware( $this->authServer->reveal(), - $this->response->reveal() + $this->responseFactory ); $this->serverRequest->getMethod()->willReturn('GET'); @@ -157,7 +179,7 @@ public function testAuthorizationRequestRaisingUnknownExceptionGeneratesResponse $middleware = new OAuth2Middleware( $this->authServer->reveal(), - $this->response->reveal() + $this->responseFactory ); $this->serverRequest->getMethod()->willReturn('GET'); @@ -177,7 +199,7 @@ public function testReturns501ResponseForInvalidMethods() $middleware = new OAuth2Middleware( $this->authServer->reveal(), - $this->response->reveal() + $this->responseFactory ); $response = $middleware->process( @@ -206,7 +228,7 @@ public function testPostRequestResultingInOAuthServerExceptionUsesExceptionToGen $middleware = new OAuth2Middleware( $this->authServer->reveal(), - $this->response->reveal() + $this->responseFactory ); $response = $middleware->process( @@ -247,7 +269,7 @@ public function testPostRequestResultingInGenericExceptionCastsExceptionToOauthS $middleware = new OAuth2Middleware( $this->authServer->reveal(), - $this->response->reveal() + $this->responseFactory ); $response = $middleware->process( diff --git a/test/Pdo/OAuth2PdoMiddlewareTest.php b/test/Pdo/OAuth2PdoMiddlewareTest.php index ac2829b..344c78b 100644 --- a/test/Pdo/OAuth2PdoMiddlewareTest.php +++ b/test/Pdo/OAuth2PdoMiddlewareTest.php @@ -19,6 +19,7 @@ use League\OAuth2\Server\Grant\RefreshTokenGrant; use PDO; use PHPUnit\Framework\TestCase; +use Prophecy\Prophecy\ObjectProphecy; use Psr\Http\Server\RequestHandlerInterface; use Zend\Diactoros\Response; use Zend\Diactoros\ServerRequest; @@ -40,6 +41,39 @@ class OAuth2PdoMiddlewareTest extends TestCase const PRIVATE_KEY = __DIR__ .'/../TestAsset/private.key'; const ENCRYPTION_KEY = 'T2x2+1OGrEzfS+01OUmwhOcJiGmE58UD1fllNn6CGcQ='; + /** @var AccessTokenRepository */ + private $accessTokenRepository; + + /** @var AuthCodeRepository */ + private $authCodeRepository; + + /** @var AuthServer */ + private $authServer; + + /** @var ClientRepository */ + private $clientRepository; + + /** @var RequestHandlerInterface|ObjectProphecy */ + private $handler; + + /** @var PdoService */ + private $pdoService; + + /** @var RefreshTokenRepository */ + private $refreshTokenRepository; + + /** @var Response */ + private $response; + + /** @var callable */ + private $responseFactory; + + /** @var ScopeRepository */ + private $scopeRepository; + + /** @var UserRepository */ + private $userRepository; + public static function setUpBeforeClass() { self::tearDownAfterClass(); @@ -88,11 +122,17 @@ public function setUp() ); $this->handler = $this->prophesize(RequestHandlerInterface::class); + $this->responseFactory = function () { + return $this->response; + }; } public function testConstructor() { - $authMiddleware = new OAuth2Middleware($this->authServer, $this->response); + $authMiddleware = new OAuth2Middleware( + $this->authServer, + $this->responseFactory + ); $this->assertInstanceOf(OAuth2Middleware::class, $authMiddleware); } @@ -123,7 +163,12 @@ public function testProcessClientCredentialGrant() $params, [ 'Content-Type' => 'application/x-www-form-urlencoded' ] ); - $authMiddleware = new OAuth2Middleware($this->authServer, $this->response); + + $authMiddleware = new OAuth2Middleware( + $this->authServer, + $this->responseFactory + ); + $response = $authMiddleware->process($request, $this->handler->reveal()); $this->assertEquals(200, $response->getStatusCode()); @@ -166,7 +211,12 @@ public function testProcessPasswordGrant() $params, [ 'Content-Type' => 'application/x-www-form-urlencoded' ] ); - $authMiddleware = new OAuth2Middleware($this->authServer, $this->response); + + $authMiddleware = new OAuth2Middleware( + $this->authServer, + $this->responseFactory + ); + $response = $authMiddleware->process($request, $this->handler->reveal()); $this->assertEquals(200, $response->getStatusCode()); @@ -214,7 +264,11 @@ public function testProcessGetAuthorizationCode() $params ); - $authMiddleware = new OAuth2Middleware($this->authServer, $this->response); + $authMiddleware = new OAuth2Middleware( + $this->authServer, + $this->responseFactory + ); + $response = $authMiddleware->process($request, $this->handler->reveal()); $this->assertEquals(302, $response->getStatusCode()); @@ -265,7 +319,12 @@ public function testProcessFromAuthorizationCode(string $code) $params, [ 'Content-Type' => 'application/x-www-form-urlencoded' ] ); - $authMiddleware = new OAuth2Middleware($this->authServer, $this->response); + + $authMiddleware = new OAuth2Middleware( + $this->authServer, + $this->responseFactory + ); + $response = $authMiddleware->process($request, $this->handler->reveal()); $this->assertEquals(200, $response->getStatusCode()); @@ -307,7 +366,12 @@ public function testProcessImplicitGrant() [], $params ); - $authMiddleware = new OAuth2Middleware($this->authServer, $this->response); + + $authMiddleware = new OAuth2Middleware( + $this->authServer, + $this->responseFactory + ); + $response = $authMiddleware->process($request, $this->handler->reveal()); $this->assertEquals(302, $response->getStatusCode()); @@ -356,7 +420,11 @@ public function testProcessRefreshTokenGrant(string $refreshToken) [ 'Content-Type' => 'application/x-www-form-urlencoded' ] ); - $authMiddleware = new OAuth2Middleware($this->authServer, $this->response); + $authMiddleware = new OAuth2Middleware( + $this->authServer, + $this->responseFactory + ); + $response = $authMiddleware->process($request, $this->handler->reveal()); $this->assertEquals(200, $response->getStatusCode());