1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
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');
}
}
|