summaryrefslogtreecommitdiffstats
path: root/tests/LookupTest.php
diff options
context:
space:
mode:
authorMurilo Ijanc <[email protected]>2026-07-27 20:31:30 -0300
committerMurilo Ijanc <[email protected]>2026-07-27 20:31:30 -0300
commit67276c6444a70b61030ab3ee52b84dc5014af0e8 (patch)
treeac0246ed287c6aec57e335403b20b23214ec4eb0 /tests/LookupTest.php
parent64764fb9fac0227f8e0443e7c7519d73aec275b9 (diff)
downloadphp-main.tar.gz
Carry the municipality of a not-found CEP on the exceptionHEADv0.2.0main
The API now sends an optional faixa block on the 404 of GET /{cep}, naming the municipality the CEP would have belonged to. That failing CEP is rarely random: it is usually someone who knows the city and guessed the number, most often the -000 that 957 municipalities never received. Knowing the lookup failed "in Areal/RJ" is the difference between a message a user can act on and a bare "CEP not found". It stays an error. Faixa hangs off CepNotFoundException, never off Address, and parsing is deliberately forgiving: a missing, empty or malformed block yields null rather than turning a clean not-found into a parse failure. Its cep_ini/cep_fim are the CEPs that exist, not the bounds the Correios publish, and the interval between them is sparse -- hence ceps, and hence the docblocks insisting the pair is not a list of valid CEPs.
Diffstat (limited to 'tests/LookupTest.php')
-rw-r--r--tests/LookupTest.php139
1 files changed, 139 insertions, 0 deletions
diff --git a/tests/LookupTest.php b/tests/LookupTest.php
index 9483948..03dee57 100644
--- a/tests/LookupTest.php
+++ b/tests/LookupTest.php
@@ -10,6 +10,23 @@ use Cepis\InvalidResponseException;
final class LookupTest extends ServerTestCase
{
+ /**
+ * The `faixa` the API sends on the 404 of a CEP that does not exist but
+ * falls inside a municipality's range. `25845000` is the textbook case: it
+ * is the DNE's own start for Areal, it is the number everyone guesses, and
+ * it was never issued — which is why `cep_ini` is `25845001`.
+ */
+ private const AREAL_FAIXA = [
+ 'localidade' => 'Areal',
+ 'uf' => 'RJ',
+ 'estado' => 'Rio de Janeiro',
+ 'regiao' => 'Sudeste',
+ 'ibge' => '3300225',
+ 'cep_ini' => '25845001',
+ 'cep_fim' => '25849899',
+ 'ceps' => 118,
+ ];
+
public function testSuccessfulLookup(): void
{
$this->server->respond('/01001000', [self::json(self::SE_ADDRESS)]);
@@ -94,6 +111,128 @@ final class LookupTest extends ServerTestCase
self::fail('expected CepNotFoundException');
} catch (CepNotFoundException $exception) {
self::assertStringContainsString('99999999', $exception->getMessage());
+ self::assertNull($exception->faixa);
+ }
+ }
+
+ public function testNotFoundCarriesTheFaixaWhenTheApiSendsOne(): void
+ {
+ $this->server->respond('/25845000', [self::json(
+ [
+ 'erro' => 'cep_nao_encontrado',
+ 'mensagem' => 'CEP não existe na base do DNE',
+ 'faixa' => self::AREAL_FAIXA,
+ ],
+ 404,
+ )]);
+
+ try {
+ $this->client->lookup('25845-000');
+ self::fail('expected CepNotFoundException');
+ } catch (CepNotFoundException $exception) {
+ $faixa = $exception->faixa;
+ self::assertNotNull($faixa);
+ self::assertSame('Areal', $faixa->localidade);
+ self::assertSame('RJ', $faixa->uf);
+ self::assertSame('Rio de Janeiro', $faixa->estado);
+ self::assertSame('Sudeste', $faixa->regiao);
+ self::assertSame('3300225', $faixa->ibge);
+ self::assertSame(118, $faixa->ceps);
+ self::assertSame('Areal/RJ', $faixa->label());
+
+ // The bounds are the CEPs that exist, not the ones the Correios
+ // publish: the CEP that just failed is the DNE's own start for
+ // Areal and is below cep_ini for exactly that reason.
+ self::assertSame('25845001', $faixa->cep_ini);
+ self::assertSame('25849899', $faixa->cep_fim);
+ self::assertTrue($faixa->cep_ini > '25845000');
+
+ // 118 CEPs over ~4,900 numbers: the pair is a range, never a list.
+ self::assertLessThan(
+ (int) $faixa->cep_fim - (int) $faixa->cep_ini,
+ $faixa->ceps,
+ );
+
+ self::assertStringContainsString('Areal/RJ', $exception->getMessage());
+ self::assertStringContainsString('25845000', $exception->getMessage());
+ }
+ }
+
+ public function testFaixaIsStillANotFoundAndNotAnAddress(): void
+ {
+ $this->server->respond('/25845000', [self::json(
+ ['erro' => 'cep_nao_encontrado', 'faixa' => self::AREAL_FAIXA],
+ 404,
+ )]);
+
+ $this->expectException(CepNotFoundException::class);
+ $this->client->lookup('25845000');
+ }
+
+ /**
+ * A `faixa` this client cannot read costs nothing: the answer is the same
+ * not-found it would have been without one. Anything else would trade a
+ * useful exception for an unhelpful parse failure.
+ */
+ public function testUnreadableFaixaStillGivesACleanNotFound(): void
+ {
+ $shapes = [
+ 'null' => null,
+ 'empty object' => [],
+ 'a string' => 'Areal',
+ 'a number' => 42,
+ 'a list' => ['Areal', 'RJ'],
+ 'no municipality' => ['uf' => 'RJ', 'cep_ini' => '25845001'],
+ 'empty municipality' => ['localidade' => '', 'uf' => 'RJ'],
+ 'nested junk' => ['localidade' => ['Areal'], 'ceps' => 'many'],
+ ];
+
+ $specs = [];
+ foreach ($shapes as $faixa) {
+ $specs[] = self::json(['erro' => 'cep_nao_encontrado', 'faixa' => $faixa], 404);
+ }
+ $this->server->respond('/25845000', $specs);
+
+ foreach (array_keys($shapes) as $name) {
+ try {
+ $this->client->lookup('25845000');
+ self::fail('expected CepNotFoundException for ' . $name);
+ } catch (CepNotFoundException $exception) {
+ self::assertNull($exception->faixa, $name);
+ self::assertStringContainsString('25845000', $exception->getMessage(), $name);
+ }
+ }
+ }
+
+ public function testPartialFaixaKeepsWhatItCanRead(): void
+ {
+ $this->server->respond('/25845000', [self::json(
+ ['erro' => 'cep_nao_encontrado', 'faixa' => ['localidade' => 'Areal', 'ceps' => 'many']],
+ 404,
+ )]);
+
+ try {
+ $this->client->lookup('25845000');
+ self::fail('expected CepNotFoundException');
+ } catch (CepNotFoundException $exception) {
+ self::assertNotNull($exception->faixa);
+ self::assertSame('Areal', $exception->faixa->localidade);
+ self::assertSame('', $exception->faixa->uf);
+ self::assertSame('', $exception->faixa->cep_ini);
+ self::assertSame(0, $exception->faixa->ceps);
+ self::assertSame('Areal', $exception->faixa->label());
+ }
+ }
+
+ public function testUnreadableNotFoundBodyStillGivesACleanNotFound(): void
+ {
+ $this->server->respond('/25845000', [self::raw(404, '<html>proxy says no</html>')]);
+
+ try {
+ $this->client->lookup('25845000');
+ self::fail('expected CepNotFoundException');
+ } catch (CepNotFoundException $exception) {
+ self::assertNull($exception->faixa);
}
}