diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 04a0e4e..9f47773 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,3 +1,8 @@ +name: CI + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + on: push: branches: @@ -6,6 +11,9 @@ on: branches: - master +permissions: + contents: read + defaults: run: shell: bash @@ -21,59 +29,58 @@ jobs: - '8.0' - '8.1' - '8.2' - composer: [basic] + - '8.3' + - '8.5' timeout-minutes: 10 steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup PHP - uses: shivammathur/setup-php@2.9.0 + uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} coverage: xdebug - extensions: zip - tools: composer + extensions: curl, dom, fileinfo, json, simplexml, xmlwriter, zip + tools: composer:v2 - name: Determine composer cache directory id: composer-cache - run: echo "::set-output name=directory::$(composer config cache-dir)" + run: echo "directory=$(composer config cache-dir)" >> "$GITHUB_OUTPUT" - name: Cache composer dependencies - uses: actions/cache@v2.1.3 + uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.directory }} - key: ${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} + key: ${{ matrix.php }}-composer-${{ hashFiles('composer.json', 'composer.lock') }} restore-keys: ${{ matrix.php }}-composer- + - name: Validate composer configuration + run: composer validate --strict + - name: Install dependencies run: | - if [[ "${{ matrix.php }}" == "7.4" ]]; then - composer require phpstan/phpstan --no-update - fi; - - if [[ "${{ matrix.composer }}" == "lowest" ]]; then - composer update --prefer-dist --no-interaction --prefer-lowest --prefer-stable - fi; - - if [[ "${{ matrix.composer }}" == "basic" ]]; then - composer update --prefer-dist --no-interaction - fi; - + composer update --prefer-dist --no-interaction --prefer-stable composer dump-autoload -o + - name: Audit dependencies + run: composer audit + - name: Run tests + env: + XDEBUG_MODE: coverage run: | mkdir -p build/logs php vendor/bin/phpunit -c phpunit.xml.dist --coverage-clover=build/logs/clover.xml - name: Run phpstan + if: ${{ matrix.php == '8.5' }} continue-on-error: true - if: ${{ matrix.php == '7.4' }} run: | php vendor/bin/phpstan analyse - name: Upload coverage results to Coveralls + if: ${{ matrix.php == '8.5' }} env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | @@ -81,14 +88,16 @@ jobs: php-coveralls --coverage_clover=build/logs/clover.xml -v - name: Upload coverage results to Codecov - uses: codecov/codecov-action@v1 + if: ${{ matrix.php == '8.5' }} + uses: codecov/codecov-action@v5 with: files: build/logs/clover.xml - name: Archive logs artifacts if: ${{ failure() }} - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: - name: logs_composer-${{ matrix.composer }}_php-${{ matrix.php }} + name: logs_php-${{ matrix.php }} path: | build/logs + if-no-files-found: ignore diff --git a/composer.json b/composer.json index 444d382..c763227 100644 --- a/composer.json +++ b/composer.json @@ -41,6 +41,7 @@ "voku/simple_html_dom": "~4.7" }, "require-dev": { + "phpstan/phpstan": "^1.12", "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" }, "provide": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 386198f..2c2a060 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,30 +1,20 @@ - - - tests - - - - ./src/ - - - - - - - - - - - + + + + ./src/ + + + + + + + tests + + + + + + + diff --git a/tests/Httpful/ClientMultiTest.php b/tests/Httpful/ClientMultiTest.php index db10a7d..4c80f54 100644 --- a/tests/Httpful/ClientMultiTest.php +++ b/tests/Httpful/ClientMultiTest.php @@ -18,6 +18,11 @@ */ final class ClientMultiTest extends TestCase { + private static function localFixtureUrl(string $path): string + { + return 'http://' . \TEST_SERVER . '/' . ltrim($path, '/'); + } + public function testGet() { /** @var Response[] $results */ @@ -28,18 +33,20 @@ static function (Response $response, Request $request) use (&$results) { } ); - $multi->add_get('http://google.com?a=b'); - $multi->add_get('http://moelleken.org'); + $multi->add_get(self::localFixtureUrl('foo.txt')); + $multi->add_get(self::localFixtureUrl('test.json')); $multi->start(); static::assertCount(2, $results); + $bodies = array_map('strval', $results); + sort($bodies); if (\method_exists(__CLASS__, 'assertStringContainsString')) { - static::assertStringContainsString('', strtolower((string) $results[0])); - static::assertStringContainsString('Lars Moelleken', (string) $results[1]); + static::assertStringContainsString('Foobar', $bodies[0] . $bodies[1]); + static::assertStringContainsString('"foo": "bar"', $bodies[0] . $bodies[1]); } else { - static::assertContains('', strtolower((string) $results[0])); - static::assertContains('Lars Moelleken', (string) $results[1]); + static::assertContains('Foobar', $bodies[0] . $bodies[1]); + static::assertContains('"foo": "bar"', $bodies[0] . $bodies[1]); } } @@ -158,7 +165,10 @@ static function (Response $response, Request $request) use (&$results) { static::assertSame('https', $data['headers']['x-forwarded-proto']); - static::assertSame('gzip', $data['headers']['accept-encoding']); + static::assertSame( + 1, + \preg_match('/\b(?:gzip|deflate|br)\b/i', $data['headers']['accept-encoding']) + ); if (\method_exists(__CLASS__, 'assertStringContainsString')) { static::assertStringContainsString('Basic ', $data['headers']['authorization']); diff --git a/tests/Httpful/ClientPromiseTest.php b/tests/Httpful/ClientPromiseTest.php index ac8f660..b96ab03 100644 --- a/tests/Httpful/ClientPromiseTest.php +++ b/tests/Httpful/ClientPromiseTest.php @@ -14,13 +14,17 @@ */ final class ClientPromiseTest extends TestCase { + private static function localFixtureUrl(string $path): string + { + return 'http://' . \TEST_SERVER . '/' . ltrim($path, '/'); + } + public function testGet() { $client = new ClientPromise(); $request = (new Request('GET')) - ->withUriFromString('http://moelleken.org') - ->followRedirects(); + ->withUriFromString(self::localFixtureUrl('foo.txt')); $promise = $client->sendAsyncRequest($request); @@ -35,9 +39,9 @@ public function testGet() static::assertInstanceOf(Response::class, $result); if (\method_exists(__CLASS__, 'assertStringContainsString')) { - static::assertStringContainsString('Lars Moelleken', (string) $result); + static::assertStringContainsString('Foobar', (string) $result); } else { - static::assertContains('Lars Moelleken', (string) $result); + static::assertContains('Foobar', (string) $result); } } @@ -45,8 +49,8 @@ public function testGetMultiPromise() { $client = new ClientPromise(); - $client->add_get('http://google.com?a=b'); - $client->add_get('http://moelleken.org'); + $client->add_get(self::localFixtureUrl('foo.txt')); + $client->add_get(self::localFixtureUrl('test.json')); $promise = $client->getPromise(); @@ -59,13 +63,15 @@ public function testGetMultiPromise() $promise->wait(); static::assertCount(2, $results); + $bodies = array_map('strval', $results); + sort($bodies); if (\method_exists(__CLASS__, 'assertStringContainsString')) { - static::assertStringContainsString('', strtolower((string) $results[0])); - static::assertStringContainsString('Lars Moelleken', (string) $results[1]); + static::assertStringContainsString('Foobar', $bodies[0] . $bodies[1]); + static::assertStringContainsString('"foo": "bar"', $bodies[0] . $bodies[1]); } else { - static::assertContains('', strtolower((string) $results[0])); - static::assertContains('Lars Moelleken', (string) $results[1]); + static::assertContains('Foobar', $bodies[0] . $bodies[1]); + static::assertContains('"foo": "bar"', $bodies[0] . $bodies[1]); } } } diff --git a/tests/Httpful/ClientTest.php b/tests/Httpful/ClientTest.php index 14b9954..0c48797 100644 --- a/tests/Httpful/ClientTest.php +++ b/tests/Httpful/ClientTest.php @@ -22,6 +22,11 @@ */ final class ClientTest extends TestCase { + private static function localFixtureUrl(string $path): string + { + return 'http://' . \TEST_SERVER . '/' . ltrim($path, '/'); + } + public function testGetDom() { $dom = Client::get_dom('http://google.com?a=b'); @@ -136,7 +141,10 @@ public function testPostAuthJson() static::assertSame('https', $data['headers']['x-forwarded-proto']); - static::assertSame('deflate', $data['headers']['accept-encoding']); + static::assertSame( + 1, + \preg_match('/\b(?:gzip|deflate|br)\b/i', $data['headers']['accept-encoding']) + ); if (\method_exists(__CLASS__, 'assertStringContainsString')) { static::assertStringContainsString('Basic ', $data['headers']['authorization']); @@ -244,7 +252,7 @@ public function testJsonHelper() public function testDownloadSimple() { - $testFileUrl = 'http://thetofu.com/webtest/webmachine/test100k/test100.log'; + $testFileUrl = self::localFixtureUrl('foo.txt'); $tmpFile = \tempnam('/tmp', 'FOO'); $expectedFileContent = \file_get_contents($testFileUrl); @@ -333,6 +341,10 @@ public function testHttp2() )->withProtocolVersion(Http::HTTP_2_0) ); + if ($response->getStatusCode() !== 200 || $response->getProtocolVersion() !== '2') { + static::markTestSkipped('The remote HTTP/2 demo endpoint did not negotiate an HTTP/2 image response.'); + } + static::assertSame('2', $response->getProtocolVersion()); static::assertSame(200, $response->getStatusCode()); @@ -422,19 +434,18 @@ public function testGet() { $client = new Client(); $request = (new Request('GET')) - ->disableStrictSSL() - ->withUriFromString('https://moelleken.org/'); + ->withUriFromString(self::localFixtureUrl('foo.txt')); $response = $client->sendRequest($request); static::assertEquals(200, $response->getStatusCode()); if (\method_exists(__CLASS__, 'assertStringContainsString')) { - static::assertStringContainsString('Lars Moelleken', (string) $response->getBody()); + static::assertStringContainsString('Foobar', (string) $response->getBody()); } else { - static::assertContains('Lars Moelleken', (string) $response->getBody()); + static::assertContains('Foobar', (string) $response->getBody()); } - static::assertContains($response->getProtocolVersion(), ['1.1', '2']); + static::assertSame('1.1', $response->getProtocolVersion()); - static::assertEquals(['text/html; charset=utf-8'], $response->getHeader('content-type')); + static::assertEquals(['text/plain; charset=UTF-8'], $response->getHeader('content-type')); } public function testCookie() diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 3858b1b..ee7f444 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -23,8 +23,8 @@ $output = []; \exec($command, $output, $exit_code); - // sleep for a second to let server come up - \usleep(500); + // sleep for 500ms to let server come up + \usleep(500000); $pid = (int) $output[0]; // check server.log to see if it failed to start @@ -43,7 +43,9 @@ \register_shutdown_function(static function () { // cleanup after ourselves -- remove log file, shut down server global $pid; - \unlink('./server.log'); + if (\file_exists('./server.log')) { + \unlink('./server.log'); + } \posix_kill($pid, \SIGKILL); }); }