From bf7ed4e1b67d17573d6454cb0faabe924a9da594 Mon Sep 17 00:00:00 2001 From: Ondrej Machulda Date: Tue, 14 Nov 2017 12:46:08 +0100 Subject: [PATCH 1/2] Use custom TestCase base class to DRY with helper methods; unify codestyle --- src/Http/ResponseDecoderInterface.php | 3 +-- tests/Exception/AuthorizationExceptionTest.php | 5 ++--- tests/Exception/RequestExceptionTest.php | 2 +- tests/Http/HmacAuthenticationTest.php | 2 +- tests/Http/Plugin/ExceptionPluginTest.php | 3 +-- tests/Http/ResponseDecoderTest.php | 14 ++------------ tests/Model/CommandResponseTest.php | 5 ++--- tests/Model/ResponseTest.php | 5 ++--- tests/TestCase.php | 18 ++++++++++++++++++ 9 files changed, 30 insertions(+), 27 deletions(-) create mode 100644 tests/TestCase.php diff --git a/src/Http/ResponseDecoderInterface.php b/src/Http/ResponseDecoderInterface.php index a78e34c..bea698b 100644 --- a/src/Http/ResponseDecoderInterface.php +++ b/src/Http/ResponseDecoderInterface.php @@ -1,5 +1,4 @@ -expectExceptionMessage('"invalid": [],'); $this->decoder->decode($response); } - - private function createJsonResponseFromFile(string $fileName): ResponseInterface - { - $jsonData = file_get_contents($fileName); - $response = new Response(StatusCodeInterface::STATUS_OK, ['Content-Type' => 'application/json'], $jsonData); - - return $response; - } } diff --git a/tests/Model/CommandResponseTest.php b/tests/Model/CommandResponseTest.php index bcea453..84663cc 100644 --- a/tests/Model/CommandResponseTest.php +++ b/tests/Model/CommandResponseTest.php @@ -1,10 +1,9 @@ - 'application/json'], $jsonData); + + return $response; + } +} From 560fa87b7fc718db40bd9d98ec6636941727aa45 Mon Sep 17 00:00:00 2001 From: Ondrej Machulda Date: Tue, 14 Nov 2017 13:22:05 +0100 Subject: [PATCH 2/2] RequestManager to make abstraction over HTTP layer and handle request and responses processing --- src/Http/RequestManager.php | 126 ++++++++++++++++++++++++++++++ src/Model/Request.php | 38 +++++++++ tests/Http/RequestManagerTest.php | 57 ++++++++++++++ tests/Model/RequestTest.php | 24 ++++++ 4 files changed, 245 insertions(+) create mode 100644 src/Http/RequestManager.php create mode 100644 src/Model/Request.php create mode 100644 tests/Http/RequestManagerTest.php create mode 100644 tests/Model/RequestTest.php diff --git a/src/Http/RequestManager.php b/src/Http/RequestManager.php new file mode 100644 index 0000000..959e78a --- /dev/null +++ b/src/Http/RequestManager.php @@ -0,0 +1,126 @@ +accountId = $accountId; + $this->apiKey = $apiKey; + } + + public function sendRequest(Request $request): Response + { + $httpRequest = $this->createHttpRequestFromMatejRequest($request); + + $client = $this->createConfiguredHttpClient(); + + $httpResponse = $client->sendRequest($httpRequest); + + return $this->getResponseDecoder()->decode($httpResponse); + } + + /** @codeCoverageIgnore */ + public function setHttpClient(HttpClient $httpClient): void + { + $this->httpClient = $httpClient; + } + + /** @codeCoverageIgnore */ + public function setMessageFactory(MessageFactory $messageFactory): void + { + $this->messageFactory = $messageFactory; + } + + /** @codeCoverageIgnore */ + public function setResponseDecoder(ResponseDecoderInterface $responseDecoder): void + { + $this->responseDecoder = $responseDecoder; + } + + protected function getHttpClient(): HttpClient + { + if ($this->httpClient === null) { + // @codeCoverageIgnoreStart + $this->httpClient = HttpClientDiscovery::find(); + // @codeCoverageIgnoreEnd + } + + return $this->httpClient; + } + + protected function getMessageFactory(): MessageFactory + { + if ($this->messageFactory === null) { + $this->messageFactory = MessageFactoryDiscovery::find(); + } + + return $this->messageFactory; + } + + protected function getResponseDecoder(): ResponseDecoderInterface + { + if ($this->responseDecoder === null) { + $this->responseDecoder = new ResponseDecoder(); + } + + return $this->responseDecoder; + } + + protected function createConfiguredHttpClient(): HttpClient + { + return new PluginClient( + $this->getHttpClient(), + [ + new AuthenticationPlugin(new HmacAuthentication($this->apiKey)), + new ExceptionPlugin(), + ] + ); + } + + protected function createHttpRequestFromMatejRequest(Request $request): RequestInterface + { + $requestBody = json_encode($request->getData()); + $uri = $this->buildBaseUrl() . $request->getPath(); + + return $this->getMessageFactory() + ->createRequest( + $request->getMethod(), + $uri, + ['Content-Type' => 'application/json'], + $requestBody + ); + } + + protected function buildBaseUrl(): string + { + return sprintf('https://%s.matej.lmc.cz', $this->accountId); + } +} diff --git a/src/Model/Request.php b/src/Model/Request.php new file mode 100644 index 0000000..51c311d --- /dev/null +++ b/src/Model/Request.php @@ -0,0 +1,38 @@ +path = $path; + $this->method = $method; + $this->data = $data; + } + + public function getPath(): string + { + return $this->path; + } + + public function getMethod(): string + { + return $this->method; + } + + public function getData(): array + { + return $this->data; + } +} diff --git a/tests/Http/RequestManagerTest.php b/tests/Http/RequestManagerTest.php new file mode 100644 index 0000000..e332586 --- /dev/null +++ b/tests/Http/RequestManagerTest.php @@ -0,0 +1,57 @@ +createJsonResponseFromFile(__DIR__ . '/Fixtures/response-one-successful-command.json'); + + $mockClient = new Client(); + $mockClient->addResponse($dummyHttpResponse); + + $requestManager = new RequestManager('account-id', 'api-key'); + $requestManager->setHttpClient($mockClient); + + $request = new Request( + '/foo/endpoint', + RequestMethodInterface::METHOD_PUT, + ['foo' => 'bar', 'list' => ['lorem' => 'ipsum', 'dolor' => 333]] + ); + + $matejResponse = $requestManager->sendRequest($request); + + // Request should be decoded to Matej Response; decoding itself is comprehensively tested in ResponseDecoderTest + $this->assertInstanceOf(Response::class, $matejResponse); + + // Assert properties of the send request + $recordedRequests = $mockClient->getRequests(); + $this->assertCount(1, $recordedRequests); + $this->assertRegExp( + '~https\://account\-id\.matej\.lmc\.cz/foo/endpoint\?hmac_timestamp\=[0-9]+&hmac_sign\=[[:alnum:]]~', + $recordedRequests[0]->getUri()->__toString() + ); + $this->assertSame(RequestMethodInterface::METHOD_PUT, $recordedRequests[0]->getMethod()); + $this->assertJsonStringEqualsJsonString( + '{"foo":"bar","list":{"lorem":"ipsum","dolor":333}}', + $recordedRequests[0]->getBody()->__toString() + ); + $this->assertSame(['application/json'], $recordedRequests[0]->getHeader('Content-Type')); + } +} diff --git a/tests/Model/RequestTest.php b/tests/Model/RequestTest.php new file mode 100644 index 0000000..a31ca72 --- /dev/null +++ b/tests/Model/RequestTest.php @@ -0,0 +1,24 @@ + 'bar', ['lorem' => 'ipsum']]; + + $request = new Request($path, $method, $data); + + $this->assertInstanceOf(Request::class, $request); + $this->assertSame($path, $request->getPath()); + $this->assertSame($method, $request->getMethod()); + $this->assertSame($data, $request->getData()); + } +}