|
| 1 | +<?php |
| 2 | + |
| 3 | +/* |
| 4 | + * (c) Packagist Conductors GmbH <[email protected]> |
| 5 | + * |
| 6 | + * For the full copyright and license information, please view the LICENSE |
| 7 | + * file that was distributed with this source code. |
| 8 | + */ |
| 9 | + |
| 10 | +namespace PrivatePackagist\ApiClient\HttpClient\Plugin; |
| 11 | + |
| 12 | +use GuzzleHttp\Psr7\HttpFactory; |
| 13 | +use GuzzleHttp\Psr7\Response; |
| 14 | +use Http\Client\Common\HttpMethodsClientInterface; |
| 15 | +use Http\Message\MessageFactory\GuzzleMessageFactory; |
| 16 | +use Http\Message\RequestMatcher as RequestMatcherInterface; |
| 17 | +use Http\Mock\Client as MockClient; |
| 18 | +use PHPUnit\Framework\TestCase; |
| 19 | +use PrivatePackagist\ApiClient\HttpClient\HttpPluginClientBuilder; |
| 20 | +use Psr\Http\Message\RequestInterface; |
| 21 | +use Psr\Http\Message\ResponseInterface; |
| 22 | + |
| 23 | +class HttpPluginClientBuilderTest extends TestCase |
| 24 | +{ |
| 25 | + /** @dataProvider provideRequestFactories */ |
| 26 | + public function testRequestFactory(?object $factory, ?string $expectedException): void |
| 27 | + { |
| 28 | + if ($expectedException !== null) { |
| 29 | + $this->expectException($expectedException); |
| 30 | + } |
| 31 | + |
| 32 | + $mockHttp = new MockClient; |
| 33 | + $mockHttp->setDefaultException(new \Exception('Mock HTTP client did not match request.')); |
| 34 | + $mockHttp->on($this->matchRequestIncludingHeaders(), new Response(307, ['Location' => '/kittens.jpg'])); |
| 35 | + |
| 36 | + $builder = new HttpPluginClientBuilder($mockHttp, $factory); |
| 37 | + // Make sure that the RequestFactory passed is acceptable for the client. |
| 38 | + $client = $builder->getHttpClient(); |
| 39 | + $this->assertInstanceOf(HttpMethodsClientInterface::class, $client); |
| 40 | + |
| 41 | + // Ensure that the Request Factory correctly generates a request object (including headers |
| 42 | + // as RequestFactory and RequestFactoryInterface set headers differently). |
| 43 | + $response = $client->get('https://example.com/puppies.jpg', ['Accept' => 'image/vnd.cute+jpeg']); |
| 44 | + $this->assertInstanceOf(ResponseInterface::class, $response); |
| 45 | + $this->assertSame(307, $response->getStatusCode()); |
| 46 | + $locationHeaders = $response->getHeader('Location'); |
| 47 | + $this->assertCount(1, $locationHeaders); |
| 48 | + $this->assertSame('/kittens.jpg', reset($locationHeaders)); |
| 49 | + } |
| 50 | + |
| 51 | + /** |
| 52 | + * The concrete implementation of the RequestMatcher interface does not allow matching on |
| 53 | + * headers, which we need to test to ensure both legacy and PSR17 implementations work. |
| 54 | + */ |
| 55 | + protected function matchRequestIncludingHeaders(): RequestMatcherInterface |
| 56 | + { |
| 57 | + return new class implements RequestMatcherInterface { |
| 58 | + public function matches(RequestInterface $request): bool |
| 59 | + { |
| 60 | + $acceptHeaders = $request->getHeader('Accept'); |
| 61 | + return $request->getUri()->getPath() === '/puppies.jpg' |
| 62 | + && count($acceptHeaders) === 1 |
| 63 | + && reset($acceptHeaders) === 'image/vnd.cute+jpeg'; |
| 64 | + } |
| 65 | + }; |
| 66 | + } |
| 67 | + |
| 68 | + /** @return iterable{object|null, class-string|null} */ |
| 69 | + private static function provideRequestFactories(): iterable |
| 70 | + { |
| 71 | + // Fallback |
| 72 | + yield [null, null]; |
| 73 | + // Legacy |
| 74 | + yield [new GuzzleMessageFactory, null]; |
| 75 | + // PSR17 |
| 76 | + yield [new HttpFactory, null]; |
| 77 | + // Invalid |
| 78 | + yield [new \stdClass, \TypeError::class]; |
| 79 | + } |
| 80 | +} |
0 commit comments