diff options
| author | Murilo Ijanc <[email protected]> | 2026-07-27 20:31:30 -0300 |
|---|---|---|
| committer | Murilo Ijanc <[email protected]> | 2026-07-27 20:31:30 -0300 |
| commit | 67276c6444a70b61030ab3ee52b84dc5014af0e8 (patch) | |
| tree | ac0246ed287c6aec57e335403b20b23214ec4eb0 /tests/LookupTest.php | |
| parent | 64764fb9fac0227f8e0443e7c7519d73aec275b9 (diff) | |
| download | php-0674cf05ac9d9ff9698282742f01b2e88f536f03.tar.gz | |
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.php | 139 |
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); } } |