From 589d6d3c5413e95562a0c0c7d76c3d7aea0ba7e8 Mon Sep 17 00:00:00 2001 From: OlivierLC <39598345+OlivierLamyCanuel@users.noreply.github.com> Date: Thu, 21 Nov 2024 09:40:17 -0500 Subject: [PATCH] Upgrade to php 8.1 (#36) * upgrade to php 8.1 * wip * fix tests * fix phpunit.xml * debug CI * fix test * remove mate * fix getBody * debug CI * fix CI? * add waitime --- .github/workflows/ci.yml | 59 ++++---- composer.json | 4 +- src/HttpResponse.php | 242 ++++++++++++++++++++------------- src/Mocks/MockHttpClient.php | 12 +- tests/Fixtures/EchoHandler.php | 23 ++-- tests/HttpClientTest.php | 141 +++++++++++-------- tests/HttpHandlerTest.php | 26 ++-- tests/MiddlewareTest.php | 66 +++++---- tests/ReadmeTest.php | 55 +++++--- tests/phpunit.xml.dist | 7 - 10 files changed, 381 insertions(+), 254 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6d9a9bf..4f62e50 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,40 +1,37 @@ name: CI Backend on: - push: - branches: - - master - pull_request: + push: + branches: + - master + pull_request: concurrency: - # Concurrency is only limited on pull requests. head_ref is only defined on PR triggers so otherwise it will use the random run id and always build all pushes. - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true + # Concurrency is only limited on pull requests. head_ref is only defined on PR triggers so otherwise it will use the random run id and always build all pushes. + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true permissions: - contents: read + contents: read jobs: - phpunit-tests: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - php-version: [ "7.4", "8.0", "8.1", "8.2" ] - steps: - - - uses: actions/checkout@v3 - - - name: Installing PHP ${{ matrix.php-version }} - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-version }} - - - name: Composer Install - run: composer install -o - - - name: Start test server - run: php -S 0.0.0.0:8091 ./tests/test-server.php > /dev/null 2>&1 & - - - name: PHPUnit - run: ./vendor/bin/phpunit -c ./tests/phpunit.xml.dist + phpunit-tests: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php-version: ["8.0", "8.1", "8.2"] + steps: + - uses: actions/checkout@v3 + - name: Installing PHP ${{ matrix.php-version }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + - name: Composer Install + run: composer install -o + - name: Start test server + run: php -S 0.0.0.0:8091 ./tests/test-server.php > /dev/null 2>&1 & + - name: Waiting for server to start + run: sleep 10s + - name: PHPUnit + run: ./vendor/bin/phpunit -c ./tests/phpunit.xml.dist diff --git a/composer.json b/composer.json index 2749f07..cdc092b 100644 --- a/composer.json +++ b/composer.json @@ -9,11 +9,11 @@ } ], "require": { - "php": ">=7.4", + "php": ">=8.0", "ext-curl": "*", "ext-json": "*", "vanilla/garden-utils": "^1.1", - "psr/http-message": ">=1.0", + "psr/http-message": "^1.0", "slim/psr7": "^1.6" }, "require-dev": { diff --git a/src/HttpResponse.php b/src/HttpResponse.php index efae568..17ad457 100644 --- a/src/HttpResponse.php +++ b/src/HttpResponse.php @@ -7,13 +7,16 @@ namespace Garden\Http; - use Psr\Http\Message\ResponseInterface; /** * Representation of an outgoing, server-side response. */ -class HttpResponse extends HttpMessage implements \ArrayAccess, \JsonSerializable, ResponseInterface { +class HttpResponse extends HttpMessage implements + \ArrayAccess, + \JsonSerializable, + ResponseInterface +{ /// Properties /// /** @@ -39,59 +42,59 @@ class HttpResponse extends HttpMessage implements \ArrayAccess, \JsonSerializabl /** * @var array HTTP response codes and messages. */ - protected static $reasonPhrases = array( + protected static $reasonPhrases = [ // Could not resolve host. - 0 => 'Could not resolve host', + 0 => "Could not resolve host", // Informational 1xx - 100 => 'Continue', - 101 => 'Switching Protocols', + 100 => "Continue", + 101 => "Switching Protocols", // Successful 2xx - 200 => 'OK', - 201 => 'Created', - 202 => 'Accepted', - 203 => 'Non-Authoritative Information', - 204 => 'No Content', - 205 => 'Reset Content', - 206 => 'Partial Content', + 200 => "OK", + 201 => "Created", + 202 => "Accepted", + 203 => "Non-Authoritative Information", + 204 => "No Content", + 205 => "Reset Content", + 206 => "Partial Content", // Redirection 3xx - 300 => 'Multiple Choices', - 301 => 'Moved Permanently', - 302 => 'Found', - 303 => 'See Other', - 304 => 'Not Modified', - 305 => 'Use Proxy', - 306 => '(Unused)', - 307 => 'Temporary Redirect', + 300 => "Multiple Choices", + 301 => "Moved Permanently", + 302 => "Found", + 303 => "See Other", + 304 => "Not Modified", + 305 => "Use Proxy", + 306 => "(Unused)", + 307 => "Temporary Redirect", // Client Error 4xx - 400 => 'Bad Request', - 401 => 'Unauthorized', - 402 => 'Payment Required', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Timeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Long', - 415 => 'Unsupported Media Type', - 416 => 'Requested Range Not Satisfiable', - 417 => 'Expectation Failed', + 400 => "Bad Request", + 401 => "Unauthorized", + 402 => "Payment Required", + 403 => "Forbidden", + 404 => "Not Found", + 405 => "Method Not Allowed", + 406 => "Not Acceptable", + 407 => "Proxy Authentication Required", + 408 => "Request Timeout", + 409 => "Conflict", + 410 => "Gone", + 411 => "Length Required", + 412 => "Precondition Failed", + 413 => "Request Entity Too Large", + 414 => "Request-URI Too Long", + 415 => "Unsupported Media Type", + 416 => "Requested Range Not Satisfiable", + 417 => "Expectation Failed", 418 => 'I\'m a teapot', - 422 => 'Unprocessable Entity', - 423 => 'Locked', + 422 => "Unprocessable Entity", + 423 => "Locked", // Server Error 5xx - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version Not Supported' - ); + 500 => "Internal Server Error", + 501 => "Not Implemented", + 502 => "Bad Gateway", + 503 => "Service Unavailable", + 504 => "Gateway Timeout", + 505 => "HTTP Version Not Supported", + ]; /// Methods /// @@ -102,7 +105,11 @@ class HttpResponse extends HttpMessage implements \ArrayAccess, \JsonSerializabl * @param array|string $headers An array of response headers or a header string. * @param string $rawBody The raw body of the response. */ - public function __construct($status = null, $headers = '', string $rawBody = '') { + public function __construct( + $status = null, + $headers = "", + string $rawBody = "" + ) { $this->setHeaders($headers); if (isset($status)) { $this->setStatus($status); @@ -117,11 +124,15 @@ public function __construct($status = null, $headers = '', string $rawBody = '') * * @return mixed Returns the http response body, decoded according to its content type. */ - public function getBody() { + public function getBody() + { if (!isset($this->body)) { - $contentType = $this->getHeader('Content-Type'); + $contentType = $this->getHeader("Content-Type"); - if (stripos($contentType, 'application/json') !== false) { + if ( + !is_null($this->rawBody) && + stripos($contentType, "application/json") !== false + ) { $this->body = json_decode($this->rawBody, true); } else { $this->body = $this->rawBody; @@ -138,14 +149,23 @@ public function getBody() { * @param mixed $body The new body. * @return $this */ - public function setBody($body) { + public function setBody($body) + { if (is_string($body) || is_null($body)) { $this->rawBody = $this->body = $body; - } elseif (is_array($body) || is_bool($body) || is_numeric($body) || $body instanceof \JsonSerializable) { - $this->rawBody = json_encode($body, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + } elseif ( + is_array($body) || + is_bool($body) || + is_numeric($body) || + $body instanceof \JsonSerializable + ) { + $this->rawBody = json_encode( + $body, + JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES + ); $this->body = $body; } else { - $this->rawBody = ''; + $this->rawBody = ""; $this->body = $body; } @@ -168,7 +188,8 @@ public function setBody($body) { * * @return HttpResponse Returns `$this` for fluent calls. */ - public function setHeaders($headers) { + public function setHeaders($headers) + { parent::setHeaders($headers); if ($statusLine = $this->parseStatusLine($headers)) { @@ -189,8 +210,10 @@ public function setHeaders($headers) { * @param string $class A string representation of the HTTP status code, with 'x' used as a wildcard. * @return boolean Returns `true` if the response code matches the {@link $class}, `false` otherwise. */ - public function isResponseClass(string $class): bool { - $pattern = '`^'.str_ireplace('x', '\d', preg_quote($class, '`')).'$`'; + public function isResponseClass(string $class): bool + { + $pattern = + "`^" . str_ireplace("x", "\d", preg_quote($class, "`")) . '$`'; $result = preg_match($pattern, $this->statusCode); return $result === 1; @@ -201,8 +224,9 @@ public function isResponseClass(string $class): bool { * * @return bool Returns `true` if the response was a successful 2xx code. */ - public function isSuccessful(): bool { - return $this->isResponseClass('2xx'); + public function isSuccessful(): bool + { + return $this->isResponseClass("2xx"); } /** @@ -210,7 +234,8 @@ public function isSuccessful(): bool { * * @return string The raw body of the response. */ - public function getRawBody(): string { + public function getRawBody(): string + { return $this->rawBody; } @@ -219,7 +244,8 @@ public function getRawBody(): string { * * @param string $body The new raw body. */ - public function setRawBody(string $body) { + public function setRawBody(string $body) + { $this->rawBody = $body; $this->body = null; return $this; @@ -230,7 +256,8 @@ public function setRawBody(string $body) { * * @return string Returns the raw body of the response. */ - public function __toString(): string { + public function __toString(): string + { return $this->rawBody; } @@ -239,7 +266,8 @@ public function __toString(): string { * * @return string Returns the status code and reason phrase separated by a space. */ - public function getStatus(): string { + public function getStatus(): string + { return trim("{$this->statusCode} {$this->reasonPhrase}"); } @@ -251,10 +279,17 @@ public function getStatus(): string { * If no reason is given then one will be determined from the status code. * @return $this */ - public function setStatus($code, $reasonPhrase = null) { - if (preg_match('`(?:HTTP/([\d.]+)\s+)?(\d{3})\s*(.*)`i', $code, $matches)) { + public function setStatus($code, $reasonPhrase = null) + { + if ( + preg_match( + "`(?:HTTP/([\d.]+)\s+)?(\d{3})\s*(.*)`i", + $code, + $matches + ) + ) { $this->protocolVersion = $matches[1] ?: $this->protocolVersion; - $code = (int)$matches[2]; + $code = (int) $matches[2]; $reasonPhrase = $reasonPhrase ?: $matches[3]; } @@ -262,9 +297,9 @@ public function setStatus($code, $reasonPhrase = null) { $reasonPhrase = static::$reasonPhrases[$code]; } if (is_numeric($code)) { - $this->setStatusCode((int)$code); + $this->setStatusCode((int) $code); } - $this->setReasonPhrase((string)$reasonPhrase); + $this->setReasonPhrase((string) $reasonPhrase); return $this; } @@ -273,7 +308,8 @@ public function setStatus($code, $reasonPhrase = null) { * * @return int Returns the code. */ - public function getStatusCode(): int { + public function getStatusCode(): int + { return $this->statusCode ?? 200; } @@ -283,7 +319,8 @@ public function getStatusCode(): int { * @param int $statusCode The new status code of the response. * @return HttpResponse Returns `$this` for fluent calls. */ - public function setStatusCode(int $statusCode) { + public function setStatusCode(int $statusCode) + { $this->statusCode = $statusCode; return $this; } @@ -293,7 +330,8 @@ public function setStatusCode(int $statusCode) { * * @return string Returns the reason phrase. */ - public function getReasonPhrase(): string { + public function getReasonPhrase(): string + { if ($this->statusCode === 0 && !empty($this->rawBody)) { // CURL often returns a 0 error code if it failed to connect. // This could be for multiple reasons. We need the actual message provided to differentiate between @@ -310,7 +348,8 @@ public function getReasonPhrase(): string { * @param string $reasonPhrase The new reason phrase. * @return HttpResponse Returns `$this` for fluent calls. */ - public function setReasonPhrase(string $reasonPhrase) { + public function setReasonPhrase(string $reasonPhrase) + { $this->reasonPhrase = $reasonPhrase; return $this; } @@ -321,7 +360,8 @@ public function setReasonPhrase(string $reasonPhrase) { * @param int $status The status to test. * @return string|null Returns a reason phrase or null for an invalid status. */ - public static function reasonPhrase(int $status): ?string { + public static function reasonPhrase(int $status): ?string + { return self::$reasonPhrases[$status] ?? null; } @@ -331,26 +371,33 @@ public static function reasonPhrase(int $status): ?string { * @param string|array $headers Either a header string or a header array. * @return string Returns the status line or an empty string if the first line is not an HTTP status. */ - private function parseStatusLine($headers): string { + private function parseStatusLine($headers): string + { if (empty($headers)) { - return ''; + return ""; } if (is_string($headers)) { - if (preg_match_all('`(?:^|\n)(HTTP/[^\r]+)\r\n`', $headers, $matches)) { + if ( + preg_match_all( + '`(?:^|\n)(HTTP/[^\r]+)\r\n`', + $headers, + $matches + ) + ) { $firstLine = end($matches[1]); } else { $firstLine = trim(strstr($headers, "\r\n", true)); } } else { - $firstLine = (string)reset($headers); + $firstLine = (string) reset($headers); } // Test the status line. - if (strpos($firstLine, 'HTTP/') === 0) { + if (strpos($firstLine, "HTTP/") === 0) { return $firstLine; } - return ''; + return ""; } /** @@ -365,7 +412,8 @@ private function parseStatusLine($headers): string { * The return value will be casted to boolean if non-boolean was returned. * @link http://php.net/manual/en/arrayaccess.offsetexists.php */ - public function offsetExists($offset): bool { + public function offsetExists($offset): bool + { $body = $this->getBody(); return isset($body[$offset]); } @@ -405,7 +453,6 @@ public function offsetSet($offset, $value) { } else { $this->body[$offset] = $value; } - } /** @@ -428,7 +475,8 @@ public function offsetUnset($offset) { * * @return HttpRequest|null Returns the request. */ - public function getRequest() { + public function getRequest() + { return $this->request; } @@ -438,7 +486,8 @@ public function getRequest() { * @param HttpRequest $request The request that generated this response. * @return $this */ - public function setRequest(HttpRequest $request = null) { + public function setRequest(HttpRequest $request = null) + { $this->request = $request; return $this; } @@ -448,7 +497,8 @@ public function setRequest(HttpRequest $request = null) { * * @return HttpResponseException */ - public function asException(): HttpResponseException { + public function asException(): HttpResponseException + { $request = $this->getRequest(); if ($request !== null) { $requestID = "Request \"{$request->getMethod()} {$request->getUrl()}\""; @@ -463,13 +513,21 @@ public function asException(): HttpResponseException { } $body = $this->getBody(); - if (is_array($body) && isset($body['message']) && is_string($body['message'])) { - $responseMessage = "and a custom message of \"{$body['message']}\""; + if ( + is_array($body) && + isset($body["message"]) && + is_string($body["message"]) + ) { + $responseMessage = "and a custom message of \"{$body["message"]}\""; } else { $responseMessage = "and a standard message of \"{$this->getReasonPhrase()}\""; } - $message = implode(" ", [$requestID, $responseAction, $responseMessage]); + $message = implode(" ", [ + $requestID, + $responseAction, + $responseMessage, + ]); return new HttpResponseException($this, $message); } @@ -479,7 +537,8 @@ public function asException(): HttpResponseException { * * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { return [ "statusCode" => $this->getStatusCode(), "content-type" => $this->getHeader("content-type") ?: null, @@ -493,7 +552,8 @@ public function jsonSerialize(): array { /** * @inheritDoc */ - public function withStatus($code, $reasonPhrase = '') { + public function withStatus($code, $reasonPhrase = "") + { $cloned = clone $this; $cloned->setStatus($code); return $cloned; diff --git a/src/Mocks/MockHttpClient.php b/src/Mocks/MockHttpClient.php index 116a606..4bc0128 100644 --- a/src/Mocks/MockHttpClient.php +++ b/src/Mocks/MockHttpClient.php @@ -14,15 +14,16 @@ /** * Mock HTTP client for testing. Does send actual HTTP requests. */ -class MockHttpClient extends HttpClient { - +class MockHttpClient extends HttpClient +{ /** @var MockHttpHandler */ private $mockHandler; /** * @inheritdoc */ - public function __construct(string $baseUrl = '') { + public function __construct(string $baseUrl = "") + { parent::__construct($baseUrl); $this->mockHandler = new MockHttpHandler(); $this->setHandler($this->mockHandler); @@ -36,7 +37,8 @@ public function __construct(string $baseUrl = '') { * * @return $this */ - public function addMockRequest(HttpRequest $request, HttpResponse $response) { + public function addMockRequest(HttpRequest $request, HttpResponse $response) + { $this->mockHandler->addMockRequest($request, $response); return $this; } @@ -55,7 +57,7 @@ public function addMockResponse( string $uri, HttpResponse $response, string $method = HttpRequest::METHOD_GET - ) { + ): static { $this->mockHandler->addMockResponse($uri, $response, $method); return $this; } diff --git a/tests/Fixtures/EchoHandler.php b/tests/Fixtures/EchoHandler.php index de2991e..93f7b5f 100644 --- a/tests/Fixtures/EchoHandler.php +++ b/tests/Fixtures/EchoHandler.php @@ -11,17 +11,24 @@ use Garden\Http\HttpRequest; use Garden\Http\HttpResponse; -class EchoHandler implements HttpHandlerInterface { - public function send(HttpRequest $request): HttpResponse { - parse_str(parse_url($request->getUrl(), PHP_URL_QUERY), $get); +class EchoHandler implements HttpHandlerInterface +{ + public function send(HttpRequest $request): HttpResponse + { + $url = parse_url($request->getUrl(), PHP_URL_QUERY); + if ($url) { + parse_str($url, $get); + } else { + $get = []; + } $response = new HttpResponse(); $response->setBody([ - 'method' => $request->getMethod(), - 'url' => $request->getUrl(), - 'headers' => $request->getHeaders(), - 'get' => $get, - 'body' => $request->getBody(), + "method" => $request->getMethod(), + "url" => $request->getUrl(), + "headers" => $request->getHeaders(), + "get" => $get, + "body" => $request->getBody(), ]); $response->setRequest($request); diff --git a/tests/HttpClientTest.php b/tests/HttpClientTest.php index 0962bf7..efd7132 100644 --- a/tests/HttpClientTest.php +++ b/tests/HttpClientTest.php @@ -15,24 +15,28 @@ /** * Contains tests against the {@link HttpClient} class. - * + * * Run this in a separate process before executing this test suite: - * + * * php -S 0.0.0.0:8091 ./tests/test-server.php */ -class HttpClientTest extends TestCase { - +class HttpClientTest extends TestCase +{ /** * Get the API that will be used to make test calls. * * @return HttpClient Returns the test {@link HttpClient}. */ - public function getApi() { + public function getApi() + { $api = new HttpClient(); - $api->setBaseUrl('http://0.0.0.0:8091/') - ->setDefaultHeader('Referer', basename(str_replace('\\', '/', __CLASS__))) - ->setDefaultHeader('Content-Type', 'application/json') - ->setDefaultHeader('Accept', 'application/json') + $api->setBaseUrl("http://0.0.0.0:8091/") + ->setDefaultHeader( + "Referer", + basename(str_replace("\\", "/", __CLASS__)) + ) + ->setDefaultHeader("Content-Type", "application/json") + ->setDefaultHeader("Accept", "application/json") ->setThrowExceptions(true); return $api; } @@ -40,14 +44,15 @@ public function getApi() { /** * A simple test to see if we have access to the server. */ - public function testAccess() { + public function testAccess() + { $api = $this->getApi(); - $response = $api->get('/echo'); + $response = $api->get("/echo"); $data = $response->getBody(); $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals('bar', $data['foo']); + $this->assertEquals("bar", $data["foo"]); } /** @@ -57,12 +62,13 @@ public function testAccess() { * @dataProvider provideMethods * @throws \Exception Throws an exception when the returned data is a string. */ - public function testHttpMethodNames($method) { + public function testHttpMethodNames($method) + { $api = $this->getApi()->setThrowExceptions(false); $methodName = strtolower($method); /* @var HttpResponse $r */ - $r = $api->$methodName('/echo', ['foo' => 'bar']); + $r = $api->$methodName("/echo", ["foo" => "bar"]); $data = $r->getBody(); if (is_string($data)) { @@ -73,63 +79,66 @@ public function testHttpMethodNames($method) { if ($method === HttpRequest::METHOD_HEAD) { $this->assertNull($data); } else { - $this->assertEquals($method, $data['method']); + $this->assertEquals($method, $data["method"]); } } /** * Test basic HTTP authorization. */ - public function testBasicAuth() { + public function testBasicAuth() + { $api = $this->getApi(); - $api->setDefaultOption('auth', ['foo', 'bar']); + $api->setDefaultOption("auth", ["foo", "bar"]); - $response = $api->get('/basic-protected/foo/bar'); + $response = $api->get("/basic-protected/foo/bar"); $data = $response->getBody(); $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals('You are in.', $data['message']); + $this->assertEquals("You are in.", $data["message"]); } /** * Test basic authentication when the wrong username is supplied. */ - public function testBasicAuthWrongUsername() { + public function testBasicAuthWrongUsername() + { $this->expectException(HttpResponseException::class); $this->expectExceptionCode(401); $this->expectExceptionMessage("Invalid username."); $api = $this->getApi(); - $api->setDefaultOption('auth', ['foo', 'bar']); + $api->setDefaultOption("auth", ["foo", "bar"]); - $response = $api->get('/basic-protected/fooz/bar'); + $response = $api->get("/basic-protected/fooz/bar"); $data = $response->getBody(); } /** * Test that the basic getters and setters work. */ - public function testBasicPropertyAccess() { + public function testBasicPropertyAccess() + { $api = $this->getApi(); - $baseUrl = 'https://localhost'; + $baseUrl = "https://localhost"; $this->assertNotSame($baseUrl, $api->getBaseUrl()); $api->setBaseUrl($baseUrl); $this->assertSame($baseUrl, $api->getBaseUrl()); - $this->assertNotSame('B', $api->getDefaultHeader('A')); - $api->setDefaultHeader('A', 'B'); - $this->assertSame('B', $api->getDefaultHeader('A')); + $this->assertNotSame("B", $api->getDefaultHeader("A")); + $api->setDefaultHeader("A", "B"); + $this->assertSame("B", $api->getDefaultHeader("A")); - $headers = ['Foo' => 'bar', 'Boo' => 'baz', 'a' => 'c']; + $headers = ["Foo" => "bar", "Boo" => "baz", "a" => "c"]; $this->assertNotSame($headers, $api->getDefaultHeaders()); $api->setDefaultHeaders($headers); $this->assertSame($headers, $api->getDefaultHeaders()); - $this->assertNotSame('B', $api->getDefaultOption('A')); - $api->setDefaultOption('A', 'B'); - $this->assertSame('B', $api->getDefaultOption('A')); + $this->assertNotSame("B", $api->getDefaultOption("A")); + $api->setDefaultOption("A", "B"); + $this->assertSame("B", $api->getDefaultOption("A")); - $options = ['Foo' => 'bar', 'Boo' => 'baz', 'a' => 'c']; + $options = ["Foo" => "bar", "Boo" => "baz", "a" => "c"]; $this->assertNotSame($options, $api->getDefaultOptions()); $api->setDefaultOptions($options); $this->assertSame($options, $api->getDefaultOptions()); @@ -143,49 +152,58 @@ public function testBasicPropertyAccess() { /** * Test basic authentication when the correct username is supplied. */ - public function testBasicWrongPassword() { + public function testBasicWrongPassword() + { $this->expectException(HttpResponseException::class); $this->expectExceptionCode(401); $this->expectExceptionMessage("Invalid password."); $api = $this->getApi(); - $api->setDefaultOption('auth', ['foo', 'bar']); + $api->setDefaultOption("auth", ["foo", "bar"]); - $response = $api->get('/basic-protected/foo/baz'); + $response = $api->get("/basic-protected/foo/baz"); $data = $response->getBody(); } /** * Test an API call that returns an error response rather than throw an exception. */ - public function testErrorResponse() { + public function testErrorResponse() + { $api = $this->getApi()->setThrowExceptions(false); - $api->setDefaultOption('auth', ['foo', 'bar']); + $api->setDefaultOption("auth", ["foo", "bar"]); - $response = $api->get('/basic-protected/fooz/bar'); + $response = $api->get("/basic-protected/fooz/bar"); $this->assertSame(401, $response->getStatusCode()); } - public function testResponseInException() { + public function testResponseInException() + { $api = $this->getApi(); - $api->setDefaultOption('auth', ['foo', 'bar']); + $api->setDefaultOption("auth", ["foo", "bar"]); try { - $response = $api->get('/basic-protected/fooz/bar'); + $response = $api->get("/basic-protected/fooz/bar"); } catch (HttpResponseException $ex) { $this->assertInstanceOf(HttpResponse::class, $ex->getResponse()); $this->assertInstanceOf(HttpRequest::class, $ex->getRequest()); - $this->assertSame($ex->getResponse()->getRequest(), $ex->getRequest()); - $this->assertSame($ex->getCode(), $ex->getResponse()->getStatusCode()); + $this->assertSame( + $ex->getResponse()->getRequest(), + $ex->getRequest() + ); + $this->assertSame( + $ex->getCode(), + $ex->getResponse()->getStatusCode() + ); } } - /** * Provide all of the default HTTP methods. * * @return array Returns a data provider array of HTTP methods. */ - public function provideMethods() { + public function provideMethods() + { $arr = [ HttpRequest::METHOD_GET => [HttpRequest::METHOD_GET], HttpRequest::METHOD_HEAD => [HttpRequest::METHOD_HEAD], @@ -202,7 +220,8 @@ public function provideMethods() { /** * Tests the default behavior where a cURL handle should not be reused between requests by default */ - public function testConnectionIsNotReusedByDefault() { + public function testConnectionIsNotReusedByDefault() + { $api = $this->getApi(); $this->assertNotEquals( @@ -214,10 +233,13 @@ public function testConnectionIsNotReusedByDefault() { /** * Tests that when Keep-Alive is requested through header, the cURL handle is being reused */ - public function testConnectionIsReusedWhenKeepAliveRequested() { - $this->markTestSkipped("We don't have a local way of running a keepalive server currently"); - $api = new $this->getApi(); - $headers = ['Connection' => 'keep-alive']; + public function testConnectionIsReusedWhenKeepAliveRequested() + { + $this->markTestSkipped( + "We don't have a local way of running a keepalive server currently" + ); + $api = new $this->getApi(); + $headers = ["Connection" => "keep-alive"]; $this->assertEquals( $this->getClientPort($api, $headers), @@ -228,11 +250,12 @@ public function testConnectionIsReusedWhenKeepAliveRequested() { /** * Tests that when Keep-Alive is used, if a subsequent request doesn't request it, then the connection is reset */ - public function testConnectionIsReusedCorrectlyInMixedRequestsWithNoExplicitHeader() { + public function testConnectionIsReusedCorrectlyInMixedRequestsWithNoExplicitHeader() + { $api = $this->getApi(); $this->assertNotEquals( - $this->getClientPort($api, ['Connection' => 'keep-alive']), + $this->getClientPort($api, ["Connection" => "keep-alive"]), $this->getClientPort($api) ); } @@ -241,12 +264,13 @@ public function testConnectionIsReusedCorrectlyInMixedRequestsWithNoExplicitHead * Tests that when Keep-Alive is used, if a subsequent request explicitly requests to close the connection, then * the connection is reset */ - public function testConnectionIsReusedCorrectlyInMixedRequestsWithExplicitClose() { + public function testConnectionIsReusedCorrectlyInMixedRequestsWithExplicitClose() + { $api = $this->getApi(); $this->assertNotEquals( - $this->getClientPort($api, ['Connection' => 'keep-alive']), - $this->getClientPort($api, ['Connection' => 'close']) + $this->getClientPort($api, ["Connection" => "keep-alive"]), + $this->getClientPort($api, ["Connection" => "close"]) ); } @@ -258,9 +282,10 @@ public function testConnectionIsReusedCorrectlyInMixedRequestsWithExplicitClose( * @param array $headers additional headers to add to the request * @return string of the port number as reported by nginx */ - protected function getClientPort(HttpClient $client, array $headers = []) { - $response = $client->request('GET', 'echo', null, $headers); + protected function getClientPort(HttpClient $client, array $headers = []) + { + $response = $client->request("GET", "echo", null, $headers); $body = $response->getBody(); - return $body['phpServer']['REMOTE_PORT']; + return $body["phpServer"]["REMOTE_PORT"]; } } diff --git a/tests/HttpHandlerTest.php b/tests/HttpHandlerTest.php index f24fa53..e8006df 100644 --- a/tests/HttpHandlerTest.php +++ b/tests/HttpHandlerTest.php @@ -7,32 +7,42 @@ namespace Garden\Http\Tests; +require_once __DIR__ . "/Fixtures/EchoHandler.php"; + use Garden\Http\HttpClient; use Garden\Http\HttpRequest; use Garden\Http\Tests\Fixtures\EchoHandler; use PHPUnit\Framework\TestCase; -class HttpHandlerTest extends TestCase { +class HttpHandlerTest extends TestCase +{ /** * Test sending a request through a handler. */ - public function testRequestWithHandler() { - $request = new HttpRequest('GET', 'http://example.com', ['foo' => 'bar']); + public function testRequestWithHandler() + { + $request = new HttpRequest("GET", "http://example.com", [ + "foo" => "bar", + ]); $handler = new EchoHandler(); $response = $handler->send($request); - $this->assertSame($request->getBody(), $response->getBody()['body']); + $this->assertSame($request->getBody(), $response->getBody()["body"]); } /** * Test a handler with an HTTP client. */ - public function testClientWithHandler() { - $api = new HttpClient('https://example.com', new EchoHandler()); + public function testClientWithHandler() + { + $api = new HttpClient("https://example.com", new EchoHandler()); - $response = $api->post('', ['foo' => 'bar']); + $response = $api->post("", ["foo" => "bar"]); - $this->assertSame($response->getRequest()->getBody(), $response->getBody()['body']); + $this->assertSame( + $response->getRequest()->getBody(), + $response->getBody()["body"] + ); } } diff --git a/tests/MiddlewareTest.php b/tests/MiddlewareTest.php index e2d1db0..8b750e3 100644 --- a/tests/MiddlewareTest.php +++ b/tests/MiddlewareTest.php @@ -7,6 +7,8 @@ namespace Garden\Http\Tests; +require_once __DIR__ . "/Fixtures/MockRequest.php"; + use Garden\Http\HttpClient; use Garden\Http\HttpRequest; use Garden\Http\HttpResponse; @@ -16,42 +18,56 @@ /** * Tests for HTTP client middleware. */ -class MiddlewareTest extends TestCase { +class MiddlewareTest extends TestCase +{ /** * Middleware should pass its call off to the inner request sending. */ - public function testInnerSend() { - $api = new HttpClient('http://example.com/api'); - $api->addMiddleware([MockRequest::class, 'middleware']); + public function testInnerSend() + { + $api = new HttpClient("http://example.com/api"); + $requester = new MockRequest(); + $api->addMiddleware([$requester, "middleware"]); - $response = $api->post('/stuff?c=123', ['foo' => 'bar'], ['X-Baz' => 'bam']); - $this->assertEquals(MockRequest::class, $response->getBody()['class']); + $response = $api->post( + "/stuff?c=123", + ["foo" => "bar"], + ["X-Baz" => "bam"] + ); + $this->assertEquals(MockRequest::class, $response->getBody()["class"]); } /** * Middleware should be able to short-circuit the response. */ - public function testOverwriteResponse() { - $api = new HttpClient('http://example.com/api'); - $api->addMiddleware([MockRequest::class, 'echoRequest']); + public function testOverwriteResponse() + { + $api = new HttpClient("http://example.com/api"); + $requester = new MockRequest(); + $api->addMiddleware([$requester, "echoRequest"]); - $response = $api->post('/stuff?c=123', ['foo' => 'bar'], ['X-Baz' => 'bam']); - $this->assertEquals(HttpRequest::class, $response->getBody()['class']); + $response = $api->post( + "/stuff?c=123", + ["foo" => "bar"], + ["X-Baz" => "bam"] + ); + $this->assertEquals(HttpRequest::class, $response->getBody()["class"]); } /** * Multiple middleware should chain, calling the last middleware first. */ - public function testMiddlewareChaining() { - $api = new HttpClient('http://example.com/api'); - $api->addMiddleware($this->makeChainMiddleware('a')) - ->addMiddleware($this->makeChainMiddleware('b')) - ->addMiddleware($this->makeChainMiddleware('c')); + public function testMiddlewareChaining() + { + $api = new HttpClient("http://example.com/api"); + $api->addMiddleware($this->makeChainMiddleware("a")) + ->addMiddleware($this->makeChainMiddleware("b")) + ->addMiddleware($this->makeChainMiddleware("c")); - $response = $api->post('/'); + $response = $api->post("/"); - $this->assertEquals('abc', $response->getHeader('X-Foo')); - $this->assertEquals('cba', $response->getRequest()->getHeader('X-Foo')); + $this->assertEquals("abc", $response->getHeader("X-Foo")); + $this->assertEquals("cba", $response->getRequest()->getHeader("X-Foo")); } /** @@ -60,12 +76,16 @@ public function testMiddlewareChaining() { * @param string $val The value to append. * @return \Closure Returns the middleware. */ - protected function makeChainMiddleware(string $val) { - return function (HttpRequest $request, callable $next) use ($val): HttpResponse { - $request->setHeader('X-Foo', $request->getHeader('X-Foo').$val); + protected function makeChainMiddleware(string $val) + { + return function ( + HttpRequest $request, + callable $next + ) use ($val): HttpResponse { + $request->setHeader("X-Foo", $request->getHeader("X-Foo") . $val); $response = $next($request); - $response->setHeader('X-Foo', $response->getHeader('X-Foo').$val); + $response->setHeader("X-Foo", $response->getHeader("X-Foo") . $val); return $response; }; diff --git a/tests/ReadmeTest.php b/tests/ReadmeTest.php index 4aa97e9..c74aee6 100644 --- a/tests/ReadmeTest.php +++ b/tests/ReadmeTest.php @@ -7,6 +7,8 @@ namespace Garden\Http\Tests; +require_once __DIR__ . "/Fixtures/HmacMiddleware.php"; + use Garden\Http\HttpClient; use Garden\Http\HttpResponseException; use Garden\Http\Tests\Fixtures\HmacMiddleware; @@ -15,64 +17,75 @@ /** * Test cases for the README. */ -class ReadmeTest extends TestCase { - public function testBasicExample() { - $api = new HttpClient('http://httpbin.org'); +class ReadmeTest extends TestCase +{ + public function testBasicExample() + { + $api = new HttpClient("http://httpbin.org"); $api->setThrowExceptions(true); - $api->setDefaultHeader('Content-Type', 'application/json'); + $api->setDefaultHeader("Content-Type", "application/json"); // Get some data from the API. - $response = $api->get('/get'); // requests off of base url + $response = $api->get("/get"); // requests off of base url $data = $response->getBody(); // returns array of json decoded data - $response = $api->post('https://httpbin.org/post', ['foo' => 'bar']); + $response = $api->post("https://httpbin.org/post", ["foo" => "bar"]); // Access the response like an array. - $posted = $response['json']; // should be ['foo' => 'bar'] + $posted = $response["json"]; // should be ['foo' => 'bar'] if (!$response->isSuccessful()) { $this->markTestSkipped(); } $this->assertIsArray($data); - $this->assertSame(['foo' => 'bar'], $posted); + $this->assertSame(["foo" => "bar"], $posted); } /** * Test that exceptions can be thrown. */ - public function testExceptionsExample() { + public function testExceptionsExample() + { $this->expectException(HttpResponseException::class); $this->expectExceptionCode(404); - $api = new HttpClient('https://httpbin.org'); + $api = new HttpClient("https://httpbin.org"); $api->setThrowExceptions(true); try { - $api->get('/status/404'); + $api->get("/status/404"); } catch (\Exception $ex) { $code = $ex->getCode(); // should be 404 throw $ex; } } - public function testBasicAuthentication() { - $api = new HttpClient('https://httpbin.org'); - $api->setDefaultOption('auth', ['username', 'password123']); + public function testBasicAuthentication() + { + $api = new HttpClient("https://httpbin.org"); + $api->setDefaultOption("auth", ["username", "password123"]); // This request is made with the default authentication set above. - $r1 = $api->get('/basic-auth/username/password123'); + $r1 = $api->get("/basic-auth/username/password123"); // This request overrides the basic authentication. - $r2 = $api->get('/basic-auth/username/password', [], [], ['auth' => ['username', 'password']]); + $r2 = $api->get( + "/basic-auth/username/password", + [], + [], + ["auth" => ["username", "password"]] + ); $this->assertEquals(200, $r1->getStatusCode()); $this->assertEquals(200, $r2->getStatusCode()); } - public function testRequestMiddleware() { - $api = new HttpClient('https://httpbin.org'); - $api->addMiddleware(new HmacMiddleware('key', 'password')); + public function testRequestMiddleware() + { + $api = new HttpClient("https://httpbin.org"); + $middleware = new HmacMiddleware("key", "password"); + $api->addMiddleware($middleware); - $r = $api->get('/get'); + $r = $api->get("/get"); - $this->assertNotEmpty($r['headers']['Authorization']); + $this->assertNotEmpty($r["headers"]["Authorization"]); } } diff --git a/tests/phpunit.xml.dist b/tests/phpunit.xml.dist index 25d1283..45b6eea 100644 --- a/tests/phpunit.xml.dist +++ b/tests/phpunit.xml.dist @@ -8,7 +8,6 @@ convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" - syntaxCheck="false" bootstrap="../vendor/autoload.php" > @@ -23,10 +22,4 @@ . - - - - ../src - -