summaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorMurilo Ijanc <[email protected]>2026-07-27 20:25:04 -0300
committerMurilo Ijanc <[email protected]>2026-07-27 20:53:07 -0300
commit82d92ed4291663bae94014b4363064dbb26c6a51 (patch)
tree69d91a59c955f1e6ec6fc0007324b5d73b3f0d01 /test
parent8e719fc715eb7ae9aa1c842e633f15b5c6cd3688 (diff)
downloaddart-3f63360ff982ef2a932eca814fdd74d4cb4b1de9.tar.gz
Name the municipality behind a CEP that does not existHEADv0.2.0main
A CEP that fails a lookup is rarely a random number: it is usually someone who knows the city and guessed the digits, and the classic guess is the -000 that 957 municipalities never received. The API now attaches a faixa block to the 404 naming the municipality that band belongs to, so the caller can answer "no, but you were looking at Areal/RJ" instead of just "no". It stays an error. Faixa hangs off CepNotFoundException, never off Address, and cepIni/cepFim are the extremes that actually exist in the municipality rather than the range the Correios publish — echoing back the published start would return the very CEP that just failed. The interval is sparse, which is what ceps is for; it is not a list of alternatives. Parsing is deliberately forgiving: a missing, empty or malformed block is null. Losing a clean not-found to a parse error over the hint attached to it would be a bad trade.
Diffstat (limited to 'test')
-rw-r--r--test/cepis_test.dart166
1 files changed, 155 insertions, 11 deletions
diff --git a/test/cepis_test.dart b/test/cepis_test.dart
index 54df88f..e09601f 100644
--- a/test/cepis_test.dart
+++ b/test/cepis_test.dart
@@ -23,6 +23,23 @@ const Map<String, dynamic> seAddress = <String, dynamic>{
'siafi': '7107',
};
+const Map<String, dynamic> arealFaixa = <String, dynamic>{
+ 'localidade': 'Areal',
+ 'uf': 'RJ',
+ 'estado': 'Rio de Janeiro',
+ 'regiao': 'Sudeste',
+ 'ibge': '3300225',
+ 'cep_ini': '25845001',
+ 'cep_fim': '25849899',
+ 'ceps': 118,
+};
+
+String notFound({Map<String, dynamic>? faixa}) => jsonEncode(<String, dynamic>{
+ 'erro': 'cep_nao_encontrado',
+ 'mensagem': 'CEP não existe na base do DNE',
+ if (faixa != null) 'faixa': faixa,
+ });
+
const String csvHeader =
'cep,logradouro,complemento,unidade,bairro,localidade,uf,estado,regiao,'
'ibge,gia,ddd,siafi';
@@ -184,16 +201,7 @@ void main() {
});
test('404 becomes CepNotFoundException and is not retried', () async {
- server.routes.respond(
- '/99999999',
- Reply(
- 404,
- jsonEncode(<String, String>{
- 'erro': 'cep_nao_encontrado',
- 'mensagem': 'CEP não existe na base do DNE',
- }),
- ),
- );
+ server.routes.respond('/99999999', Reply(404, notFound()));
await expectLater(
client.lookup('99999999'),
@@ -208,6 +216,127 @@ void main() {
expect(server.routes.count('/99999999'), 1);
});
+ test('a 404 with no faixa leaves it null', () async {
+ server.routes.respond('/99999999', Reply(404, notFound()));
+
+ await expectLater(
+ client.lookup('99999999'),
+ throwsA(
+ isA<CepNotFoundException>().having(
+ (CepNotFoundException e) => e.faixa,
+ 'faixa',
+ isNull,
+ ),
+ ),
+ );
+ });
+
+ test('a 404 carries the municipality the CEP would belong to', () async {
+ server.routes.respond(
+ '/25845000',
+ Reply(404, notFound(faixa: arealFaixa)),
+ );
+
+ await expectLater(
+ client.lookup('25845000'),
+ throwsA(
+ isA<CepNotFoundException>()
+ .having((CepNotFoundException e) => e.cep, 'cep', '25845000')
+ .having(
+ (CepNotFoundException e) => e.faixa,
+ 'faixa',
+ const Faixa(
+ localidade: 'Areal',
+ uf: 'RJ',
+ estado: 'Rio de Janeiro',
+ regiao: 'Sudeste',
+ ibge: '3300225',
+ cepIni: '25845001',
+ cepFim: '25849899',
+ ceps: 118,
+ ),
+ ),
+ ),
+ );
+ });
+
+ test('the faixa does not turn a 404 into an address', () async {
+ server.routes.respond(
+ '/25845000',
+ Reply(404, notFound(faixa: arealFaixa)),
+ );
+
+ await expectLater(
+ client.lookup('25845000'),
+ throwsA(isA<CepNotFoundException>()),
+ );
+ expect(server.routes.count('/25845000'), 1);
+ });
+
+ test('the message names the municipality when there is one', () async {
+ server.routes.respond(
+ '/25845000',
+ Reply(404, notFound(faixa: arealFaixa)),
+ );
+
+ try {
+ await client.lookup('25845000');
+ fail('expected CepNotFoundException');
+ } on CepNotFoundException catch (error) {
+ expect(error.toString(), contains('Areal/RJ'));
+ expect(error.toString(), contains('118 CEPs'));
+ }
+ });
+
+ test('an unreadable faixa is null, not a failure', () async {
+ final bodies = <String>[
+ notFound(faixa: <String, dynamic>{}),
+ notFound(faixa: <String, dynamic>{'uf': 'RJ'}),
+ jsonEncode(<String, dynamic>{
+ 'erro': 'cep_nao_encontrado',
+ 'faixa': 'Areal',
+ }),
+ jsonEncode(<String, dynamic>{
+ 'erro': 'cep_nao_encontrado',
+ 'faixa': <String>[],
+ }),
+ '<html>portal</html>',
+ '',
+ ];
+
+ for (final String body in bodies) {
+ server.routes.respond('/99999999', Reply(404, body));
+
+ await expectLater(
+ client.lookup('99999999'),
+ throwsA(
+ isA<CepNotFoundException>().having(
+ (CepNotFoundException e) => e.faixa,
+ 'faixa',
+ isNull,
+ ),
+ ),
+ reason: body,
+ );
+ }
+ });
+
+ test('a faixa missing the counts still names the municipality', () async {
+ server.routes.respond(
+ '/25845000',
+ Reply(404, notFound(faixa: <String, dynamic>{'localidade': 'Areal'})),
+ );
+
+ try {
+ await client.lookup('25845000');
+ fail('expected CepNotFoundException');
+ } on CepNotFoundException catch (error) {
+ expect(error.faixa!.localidade, 'Areal');
+ expect(error.faixa!.uf, '');
+ expect(error.faixa!.ceps, 0);
+ }
+ });
+
test('an invalid CEP never leaves the machine', () async {
await expectLater(
client.lookup('123'),
@@ -265,7 +394,7 @@ void main() {
server.routes.respond('/01001000', Reply(200, jsonEncode(seAddress)));
await client.lookup('01001000');
- expect(server.routes.userAgents.single, startsWith('cepis-dart/0.1.0'));
+ expect(server.routes.userAgents.single, startsWith('cepis-dart/0.2.0'));
});
});
@@ -628,5 +757,20 @@ void main() {
expect(one, two);
expect(one.hashCode, two.hashCode);
});
+
+ test('two identical faixas are equal', () {
+ final one = Faixa.fromJson(Map<String, dynamic>.of(arealFaixa));
+ final two = Faixa.fromJson(Map<String, dynamic>.of(arealFaixa));
+ expect(one, two);
+ expect(one.hashCode, two.hashCode);
+ });
+
+ test('a faixa without a municipality is nothing to report', () {
+ expect(Faixa.fromJson(<String, dynamic>{}), isNull);
+ expect(
+ Faixa.fromJson(<String, dynamic>{'localidade': '', 'uf': 'RJ'}),
+ isNull,
+ );
+ });
});
}