summaryrefslogtreecommitdiffstats
path: root/t/09-faixa.t
diff options
context:
space:
mode:
Diffstat (limited to 't/09-faixa.t')
-rw-r--r--t/09-faixa.t178
1 files changed, 178 insertions, 0 deletions
diff --git a/t/09-faixa.t b/t/09-faixa.t
new file mode 100644
index 0000000..ea3f76a
--- /dev/null
+++ b/t/09-faixa.t
@@ -0,0 +1,178 @@
+use strict;
+use warnings;
+use utf8;
+
+use FindBin ();
+use lib "$FindBin::Bin/lib";
+
+use Test::More;
+use TestServer qw(json new_client);
+
+use Cepis;
+
+sub areal {
+ return {
+ localidade => 'Areal',
+ uf => 'RJ',
+ estado => 'Rio de Janeiro',
+ regiao => 'Sudeste',
+ ibge => '3300225',
+ cep_ini => '25845001',
+ cep_fim => '25849899',
+ ceps => 118,
+ };
+}
+
+sub not_found {
+ my (%extra) = @_;
+ return json(
+ { erro => 'cep_nao_encontrado',
+ mensagem => 'CEP não existe na base do DNE',
+ %extra,
+ }
+ );
+}
+
+# The 404 that knows where the CEP would have been.
+{
+ my $server = TestServer->new(
+ routes => { '/25845000' => [ 404, not_found( faixa => areal() ), {} ] } );
+ my $client = new_client($server);
+
+ my $ok = eval { $client->lookup('25845000'); 1 };
+ my $err = $@;
+
+ ok( !$ok, 'a CEP with a faixa still throws' );
+ isa_ok( $err, 'Cepis::Error::CEPNotFound' );
+
+ my $faixa = $err->faixa;
+ isa_ok( $faixa, 'Cepis::Faixa' );
+ is( $faixa->localidade, 'Areal', 'localidade' );
+ is( $faixa->uf, 'RJ', 'uf' );
+ is( $faixa->estado, 'Rio de Janeiro', 'estado' );
+ is( $faixa->regiao, 'Sudeste', 'regiao' );
+ is( $faixa->ibge, '3300225', 'ibge' );
+ is( $faixa->cep_ini, '25845001', 'cep_ini' );
+ is( $faixa->cep_fim, '25849899', 'cep_fim' );
+ is( $faixa->ceps, 118, 'ceps counts what actually exists' );
+ is( $faixa->label, 'Areal/RJ', 'label is ready to print' );
+
+ like( "$err", qr/Areal/, 'the message names the municipality' );
+ like( "$err", qr/25845000/, 'and still names the CEP' );
+ is( $server->count('/25845000'), 1, 'a 404 with a faixa is not retried' );
+
+ is_deeply( $faixa->to_hash, areal(), 'to_hash gives the eight fields back' );
+
+ # The DNE's own start for Areal is 25845000 — the CEP that just failed. What
+ # comes back is the smallest CEP that exists, which is a different number.
+ isnt( $faixa->cep_ini, '25845000',
+ 'cep_ini is not the published start of the band' );
+
+ # 118 CEPs across ~4,900 numbers: the range is sparse, and ceps is what says
+ # so. Nothing here may be read as a list of valid CEPs.
+ cmp_ok( $faixa->ceps, '<', $faixa->cep_fim - $faixa->cep_ini,
+ 'the range is far sparser than its span' );
+
+ $server->stop;
+}
+
+# The 404 that does not: a band no municipality owns. The API omits the field.
+{
+ my $server
+ = TestServer->new( routes => { '/99999999' => [ 404, not_found(), {} ] } );
+ my $client = new_client($server);
+
+ my $ok = eval { $client->lookup('99999999'); 1 };
+ my $err = $@;
+
+ ok( !$ok, 'a CEP with no faixa throws the same error' );
+ isa_ok( $err, 'Cepis::Error::CEPNotFound' );
+ is( $err->faixa, undef, 'and faixa is undef, because the API omitted it' );
+ like( "$err", qr/99999999/, 'the message names the CEP' );
+ unlike( "$err", qr/would fall in/, 'and promises nothing about where it was' );
+
+ $server->stop;
+}
+
+# A faixa that cannot be trusted must never cost the caller its clean 404.
+{
+ my @broken = (
+ [ 'null' => undef ],
+ [ 'an empty hash' => {} ],
+ [ 'a scalar' => 'Areal' ],
+ [ 'an array' => [ 'Areal', 'RJ' ] ],
+ [ 'no localidade' => { %{ areal() }, localidade => '' } ],
+ [ 'no ceps' => { map { $_ => areal()->{$_} } qw(localidade uf cep_ini cep_fim) } ],
+ [ 'a non-numeric ceps' => { %{ areal() }, ceps => 'many' } ],
+ [ 'a malformed cep_ini' => { %{ areal() }, cep_ini => '2584500' } ],
+ [ 'a masked cep_fim' => { %{ areal() }, cep_fim => '25849-899' } ],
+ [ 'a nested localidade' => { %{ areal() }, localidade => { name => 'Areal' } } ],
+ );
+
+ for my $case (@broken) {
+ my ( $label, $faixa ) = @$case;
+
+ my $server = TestServer->new(
+ routes => { '/25845000' => [ 404, not_found( faixa => $faixa ), {} ] } );
+ my $client = new_client($server);
+
+ my $ok = eval { $client->lookup('25845000'); 1 };
+ my $err = $@;
+
+ ok( !$ok, "a faixa that is $label still throws" );
+ isa_ok( $err, 'Cepis::Error::CEPNotFound' );
+ is( $err->faixa, undef, "a faixa that is $label is dropped" );
+
+ $server->stop;
+ }
+}
+
+# A 404 body that is not JSON at all — a proxy, an HTML page — is still a 404.
+{
+ my $server = TestServer->new(
+ routes => {
+ '/25845000' => [ 404, '<html>404 Not Found</html>', {} ],
+ '/25845002' => [ 404, '', {} ],
+ '/25845003' => [ 404, '[1,2,3]', {} ],
+ }
+ );
+ my $client = new_client($server);
+
+ for my $cep (qw(25845000 25845002 25845003)) {
+ my $ok = eval { $client->lookup($cep); 1 };
+ ok( !$ok, "an unparseable 404 body still throws for $cep" );
+ isa_ok( $@, 'Cepis::Error::CEPNotFound' );
+ is( $@->faixa, undef, 'with no faixa' );
+ }
+
+ $server->stop;
+}
+
+# from_json on its own, without a request in the way.
+{
+ isa_ok( Cepis::Faixa->from_json( areal() ), 'Cepis::Faixa' );
+ is( Cepis::Faixa->from_json(undef), undef, 'from_json(undef) is undef' );
+ is( Cepis::Faixa->from_json( [] ), undef, 'from_json on an array is undef' );
+ is( Cepis::Faixa->from_json('Areal'), undef, 'from_json on a string is undef' );
+
+ my $partial = Cepis::Faixa->from_json(
+ { localidade => 'Areal', cep_ini => '25845001', cep_fim => '25849899', ceps => 118 } );
+ isa_ok( $partial, 'Cepis::Faixa' );
+ is( $partial->uf, '', 'a missing uf is an empty string, as elsewhere' );
+ is( $partial->estado, '', 'and so is a missing estado' );
+ is( $partial->label, 'Areal', 'label falls back to the municipality alone' );
+
+ my $numeric = Cepis::Faixa->from_json( { %{ areal() }, ceps => '118' } );
+ is( $numeric->ceps, 118, 'ceps comes back as a number' );
+}
+
+# An unknown CEP with a faixa is still undef in a batch: the CSV path is
+# untouched by any of this.
+{
+ my $address = Cepis::Address->from_json( { %{ areal() }, cep => '25845000' } );
+ is( $address->localidade, 'Areal', 'Cepis::Address is unaffected' );
+ is( $address->geo, undef, 'and grows no faixa of its own' );
+ is( $address->field('cep_ini'), '25845001', 'stray fields land in raw as ever' );
+}
+
+done_testing();