summaryrefslogtreecommitdiffstats
path: root/tests/TransportTest.php
diff options
context:
space:
mode:
authorMurilo Ijanc <[email protected]>2026-07-26 14:58:55 -0300
committerMurilo Ijanc <[email protected]>2026-07-26 14:58:55 -0300
commit64764fb9fac0227f8e0443e7c7519d73aec275b9 (patch)
treecb4fc56eeb6ecce53854f9c530609cf01ab3349f /tests/TransportTest.php
downloadphp-0.1.0.tar.gz
Initial importv0.1.0
Diffstat (limited to 'tests/TransportTest.php')
-rw-r--r--tests/TransportTest.php141
1 files changed, 141 insertions, 0 deletions
diff --git a/tests/TransportTest.php b/tests/TransportTest.php
new file mode 100644
index 0000000..da467a1
--- /dev/null
+++ b/tests/TransportTest.php
@@ -0,0 +1,141 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Cepis\Tests;
+
+use Cepis\CepNotFoundException;
+use Cepis\Client;
+use Cepis\CurlTransport;
+use Cepis\NetworkException;
+use Cepis\RateLimitedException;
+use Cepis\Response;
+use Cepis\ServerErrorException;
+
+/**
+ * The transport seam is the extension point a WordPress plugin uses to route
+ * requests through `wp_remote_get`, so it gets tested the same way the rest
+ * does: a real implementation against the real test server.
+ */
+final class TransportTest extends ServerTestCase
+{
+ public function testDefaultTransportIsCurl(): void
+ {
+ self::assertInstanceOf(CurlTransport::class, (new Client())->transport);
+ }
+
+ public function testAnAlternativeTransportIsUsedEndToEnd(): void
+ {
+ $client = new Client(baseUrl: $this->server->url, transport: new StreamTransport());
+ $this->server->respond('/01001000', [self::json(self::SE_ADDRESS)]);
+
+ $address = $client->lookup('01001000');
+
+ self::assertSame('São Paulo', $address->localidade);
+ self::assertSame(1, $this->server->countCalls('/01001000'));
+ }
+
+ public function testTheClientSendsItsIdentifyingHeaders(): void
+ {
+ $transport = new RecordingTransport([new Response(200, [], '{"cep":"01001000"}')]);
+ $client = new Client(baseUrl: 'https://example.invalid', timeout: 3.5, transport: $transport);
+
+ $client->lookup('01001000');
+
+ self::assertSame('https://example.invalid/01001000', $transport->calls[0]['url']);
+ self::assertStringContainsString('cepis-php/', $transport->calls[0]['headers']['User-Agent']);
+ self::assertArrayHasKey('Accept', $transport->calls[0]['headers']);
+ self::assertSame(3.5, $transport->calls[0]['timeout']);
+ }
+
+ public function testATransportThrowingNetworkExceptionIsRetried(): void
+ {
+ $transport = new RecordingTransport([]);
+ $client = new Client(
+ baseUrl: 'https://example.invalid',
+ maxAttempts: 3,
+ backoffBase: 0.001,
+ backoffMax: 0.01,
+ transport: $transport,
+ );
+
+ try {
+ $client->lookup('01001000');
+ self::fail('expected NetworkException');
+ } catch (NetworkException) {
+ self::assertCount(3, $transport->calls);
+ }
+ }
+
+ public function testATransportReturningA503IsRetriedNotThrown(): void
+ {
+ $transport = new RecordingTransport([
+ new Response(503, [], '{}'),
+ new Response(503, [], '{}'),
+ new Response(200, [], json_encode(self::SE_ADDRESS, JSON_THROW_ON_ERROR)),
+ ]);
+ $client = new Client(
+ baseUrl: 'https://example.invalid',
+ backoffBase: 0.001,
+ backoffMax: 0.01,
+ transport: $transport,
+ );
+
+ self::assertSame('São Paulo', $client->lookup('01001000')->localidade);
+ self::assertCount(3, $transport->calls);
+ }
+
+ public function testA404FromATransportIsNotRetried(): void
+ {
+ $transport = new RecordingTransport([new Response(404, [], '{}')]);
+ $client = new Client(baseUrl: 'https://example.invalid', transport: $transport);
+
+ try {
+ $client->lookup('99999999');
+ self::fail('expected CepNotFoundException');
+ } catch (CepNotFoundException) {
+ self::assertCount(1, $transport->calls);
+ }
+ }
+
+ public function testRetryAfterIsReadFromATransportSuppliedHeader(): void
+ {
+ $transport = new RecordingTransport([new Response(429, ['retry-after' => ['5']], '{}')]);
+ $client = new Client(
+ baseUrl: 'https://example.invalid',
+ maxAttempts: 1,
+ transport: $transport,
+ );
+
+ try {
+ $client->lookup('01001000');
+ self::fail('expected RateLimitedException');
+ } catch (RateLimitedException $exception) {
+ self::assertEqualsWithDelta(5.0, $exception->retryAfter, 1e-9);
+ }
+ }
+
+ public function testResponseHeaderLookupIsCaseInsensitiveAndTakesListValues(): void
+ {
+ $response = new Response(200, ['Retry-After' => '7', 'X-Multi' => ['a', 'b']], '');
+
+ self::assertSame('7', $response->header('retry-after'));
+ self::assertSame('7', $response->header('RETRY-AFTER'));
+ self::assertSame('a', $response->header('x-multi'));
+ self::assertNull($response->header('absent'));
+ }
+
+ public function testServerErrorStillSurfacesThroughAnAlternativeTransport(): void
+ {
+ $client = new Client(
+ baseUrl: $this->server->url,
+ backoffBase: 0.001,
+ backoffMax: 0.01,
+ transport: new StreamTransport(),
+ );
+ $this->server->respond('/01001000', [self::raw(500, '{}')]);
+
+ $this->expectException(ServerErrorException::class);
+ $client->lookup('01001000');
+ }
+}