Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Tests

on: [ push, pull_request ]

jobs:
test:
name: P${{ matrix.php }} - ${{ matrix.dependency-version }} - ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
matrix:
os: [ ubuntu-latest ]
php: [ 8.4, 8.5 ]
dependency-version: [ prefer-lowest, prefer-stable ]

steps:
- name: Checkout code
uses: actions/checkout@v6

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick
coverage: none

- name: Install dependencies
run: composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction

- name: Run tests
run: vendor/bin/pest --colors=always
4 changes: 2 additions & 2 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

declare(strict_types=1);

$finder = (new PhpCsFixer\Finder())
$finder = new PhpCsFixer\Finder()
->in([
__DIR__ . '/src',
__DIR__ . '/tests',
]);

return (new PhpCsFixer\Config())
return new PhpCsFixer\Config()
->setRiskyAllowed(true)
->setRules([
'@PER-CS2.0' => true,
Expand Down
15 changes: 10 additions & 5 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
>
cacheDirectory=".phpunit.cache">
<testsuites>
<testsuite name="Test Suite">
<directory suffix="Test.php">./tests</directory>
<testsuite name="Arch">
<directory>tests/Arch</directory>
</testsuite>
<testsuite name="Unit">
<directory>tests/Unit</directory>
</testsuite>
<testsuite name="Feature">
<directory>tests/Feature</directory>
</testsuite>
</testsuites>
<source>
<include>
<directory>app</directory>
<directory>src</directory>
</include>
</source>
Expand Down
22 changes: 0 additions & 22 deletions phpunit.xml.dist

This file was deleted.

5 changes: 5 additions & 0 deletions tests/Pest.php
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
<?php

declare(strict_types=1);

use Shoxcie\BatchHttpClient\Tests\TestCase;

pest()->use(TestCase::class)
->in('Unit', 'Feature', 'Arch');
45 changes: 45 additions & 0 deletions tests/Support/CapturingHttpClient.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace Shoxcie\BatchHttpClient\Tests\Support;

use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;
use Symfony\Contracts\HttpClient\ResponseStreamInterface;

class CapturingHttpClient implements HttpClientInterface
{
/** @var array<string, ResponseInterface> */
private array $responses = [];

public function __construct(
private readonly HttpClientInterface $inner,
) {}


public function request(string $method, string $url, array $options = []): ResponseInterface
{
return $this->responses[$options["user_data"][0]] = $this->inner->request($method, $url, $options);
}

public function stream(iterable|ResponseInterface $responses, ?float $timeout = null): ResponseStreamInterface
{
return $this->inner->stream($responses, $timeout);
}

public function withOptions(array $options): static
{
return $this;
}

public function getResponse(string $key): ?ResponseInterface
{
return $this->responses[$key] ?? null;
}

public function getResponses(): array
{
return $this->responses;
}
}
2 changes: 1 addition & 1 deletion tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace Tests;
namespace Shoxcie\BatchHttpClient\Tests;

use PHPUnit\Framework\TestCase as BaseTestCase;

Expand Down
65 changes: 42 additions & 23 deletions tests/Unit/BatchHttpClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use function Shoxcie\BatchHttpClient\getUserData;

use Shoxcie\BatchHttpClient\RequestConfig;
use Shoxcie\BatchHttpClient\Tests\Support\CapturingHttpClient;
use Symfony\Component\HttpClient\Exception\ServerException;
use Symfony\Component\HttpClient\Exception\TransportException;
use Symfony\Component\HttpClient\MockHttpClient;
Expand All @@ -31,7 +32,7 @@
->toHaveCount(1)
->toHaveKey('users')
->and($results['users'])
->toBe(['id' => 1, 'name' => 'Alice']);
->toBe(['id' => 1, 'name' => 'Alice']);
});

test('multiple requests return results with matching keys', function (): void {
Expand Down Expand Up @@ -260,13 +261,13 @@ function (string $method, string $url) use (&$callCounts): JsonMockResponse {

expect(
fn(): array => new BatchHttpClient($mockClient)
->request([
'api' => new RequestConfig('GET', 'https://api.example.com/api', maxRetries: 2),
])
->fetch(),
)->toThrow(ServerException::class);
->request([
'api' => new RequestConfig('GET', 'https://api.example.com/api', maxRetries: 2),
])
->fetch(),
)->toThrow(ServerException::class)
->and($mockClient->getRequestsCount())->toBe(3);

expect($mockClient->getRequestsCount())->toBe(3);
});

test('throws exception immediately with no retries', function (): void {
Expand All @@ -276,16 +277,34 @@ function (string $method, string $url) use (&$callCounts): JsonMockResponse {

expect(
fn(): array => new BatchHttpClient($mockClient)
->request([
'api' => new RequestConfig('GET', 'https://api.example.com/api'),
])
->fetch(),
)->toThrow(ServerException::class);

expect($mockClient->getRequestsCount())->toBe(1);
->request([
'api' => new RequestConfig('GET', 'https://api.example.com/api'),
])
->fetch(),
)->toThrow(ServerException::class)
->and($mockClient->getRequestsCount())->toBe(1);
});

test('cancels all in-flight requests on failure', function (): void {})->todo();
test('cancels all in-flight requests on failure', function (): void {
$capturing = new CapturingHttpClient(new MockHttpClient(
fn(string $method, string $url): JsonMockResponse => str_contains($url, '/failing')
? new JsonMockResponse([], ['http_code' => 500])
: new JsonMockResponse(['ok' => true]),
));

expect(
fn(): array => new BatchHttpClient($capturing)
->request([
'failing' => new RequestConfig('GET', 'https://api.example.com/failing'),
'alpha' => new RequestConfig('GET', 'https://api.example.com/alpha'),
'beta' => new RequestConfig('GET', 'https://api.example.com/beta'),
])
->fetch(),
)->toThrow(ServerException::class)
->and($capturing->getResponses())->toHaveCount(3)
->and($capturing->getResponse("alpha")->getInfo('canceled'))->toBeTrue()
->and($capturing->getResponse("beta")->getInfo('canceled'))->toBeTrue();
});
});

describe('throwOnError: false', function (): void {
Expand Down Expand Up @@ -394,10 +413,10 @@ function (string $method, string $url): JsonMockResponse {

expect(
fn(): array => new BatchHttpClient($mockClient)
->request([
'api' => new RequestConfig('GET', 'https://api.example.com/api'),
])
->fetch(),
->request([
'api' => new RequestConfig('GET', 'https://api.example.com/api'),
])
->fetch(),
)->toThrow(TransportException::class);
});

Expand Down Expand Up @@ -922,10 +941,10 @@ function () use (&$callCount): JsonMockResponse {

expect(
fn(): array => new BatchHttpClient($mockClient)
->request([
'api' => new RequestConfig('GET', 'https://api.example.com/api'),
])
->fetch(),
->request([
'api' => new RequestConfig('GET', 'https://api.example.com/api'),
])
->fetch(),
)->toThrow(\JsonException::class);
});

Expand Down