From c3b525537e944064869a2cd6495733388144ce64 Mon Sep 17 00:00:00 2001 From: Murilo Ijanc Date: Mon, 27 Jul 2026 20:42:22 -0300 Subject: Surface the faixa a 404 now carries A CEP that fails is almost never a random number. It is someone who knows the city and guessed the last digits, and the guess is usually the -000 that 957 municipalities never received. The API answers those 404s with a faixa object naming the municipality that owns the band, which turns "not found" from a dead end into something the caller can act on - and the caller cannot reach it through this library, because the 404 path never looked at the body at all. cepis_error is where a 404 already reports itself, so faixa goes there, as inline storage rather than a pointer: the struct is documented as owning nothing and declared on the caller's stack, and that stays true. It costs sizeof(cepis_error), 272 bytes to 384, so 0.2.0 needs a recompile. The parser needed one new primitive. cepis__json_field could only pull scalars out of a flat object; splitting it over cepis__json_find, which returns a pointer to a value of any shape, reaches one level down without building a document tree - the same depth cepis__geo_parse already needs for geo. Absence is the case that matters: no faixa, an empty one, or one that is not an object all leave the error byte-for-byte what it was before. cep_ini and cep_fim are deliberately not presented as a range. They are the lowest and highest CEP that exist, the band is sparse - Areal's 118 CEPs are scattered over ~4900 numbers - and ceps is the only honest count. --- tests/server.py | 43 +++++++++++++++++++-- tests/test_cepis.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 149 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/tests/server.py b/tests/server.py index c77d7f6..09410b2 100755 --- a/tests/server.py +++ b/tests/server.py @@ -64,10 +64,33 @@ for _n in range(600): "ibge": "3550308", "gia": "", "ddd": "11", "siafi": "7107", } +# Bands the API knows about. A 404 for a CEP inside one carries the owning +# municipality in "faixa"; a 404 outside every band omits the key entirely. +# cep_ini/cep_fim inside the object are the extreme CEPs that exist, which is +# why they differ from the band bounds: 25845000 is in the band, is the DNE's +# own start for Areal, and does not exist. +FAIXAS = [ + ("25845000", "25849999", { + "localidade": "Areal", "uf": "RJ", "estado": "Rio de Janeiro", + "regiao": "Sudeste", "ibge": "3300225", "cep_ini": "25845001", + "cep_fim": "25849899", "ceps": 118, + }), +] + HITS = {} LOCK = threading.Lock() +def not_found(cep=None): + body = {"erro": "cep_nao_encontrado", + "mensagem": "CEP não existe na base do DNE"} + for low, high, faixa in FAIXAS: + if cep is not None and low <= cep <= high: + body["faixa"] = faixa + break + return json.dumps(body, ensure_ascii=False) + + def bump(prefix): with LOCK: HITS[prefix] = HITS.get(prefix, 0) + 1 @@ -167,6 +190,20 @@ class Handler(BaseHTTPRequestHandler): if prefix == "escaped": self.send(200, json.dumps(DB["01001000"], ensure_ascii=True)) return + if prefix == "faixavazia": + self.send(404, json.dumps({ + "erro": "cep_nao_encontrado", + "mensagem": "CEP não existe na base do DNE", + "faixa": {}, + }, ensure_ascii=False)) + return + if prefix == "faixatexto": + self.send(404, json.dumps({ + "erro": "cep_nao_encontrado", + "mensagem": "CEP não existe na base do DNE", + "faixa": "Areal", + }, ensure_ascii=False)) + return if parts and parts[0] == "health": body = {"status": "ok", "dne": "26071", "ceps": 1596168, @@ -194,8 +231,7 @@ class Handler(BaseHTTPRequestHandler): return if not parts: - self.send(404, '{"erro":"cep_nao_encontrado",' - '"mensagem":"CEP não existe na base do DNE"}') + self.send(404, not_found()) return cep = parts[0] @@ -205,8 +241,7 @@ class Handler(BaseHTTPRequestHandler): return entry = DB.get(cep) if entry is None: - self.send(404, '{"erro":"cep_nao_encontrado",' - '"mensagem":"CEP não existe na base do DNE"}') + self.send(404, not_found(cep)) return self.send(200, json.dumps(entry, ensure_ascii=False)) diff --git a/tests/test_cepis.c b/tests/test_cepis.c index 3dd6dc6..0c3ec87 100644 --- a/tests/test_cepis.c +++ b/tests/test_cepis.c @@ -25,9 +25,16 @@ _Static_assert(offsetof(cepis_address, raw) == 112, "cepis_address layout"); _Static_assert(sizeof(struct cepis_health) == 48, "cepis_health layout"); _Static_assert(offsetof(struct cepis_health, base_gerada_em) == 32, "cepis_health layout"); -_Static_assert(sizeof(cepis_error) == 272, "cepis_error layout"); +_Static_assert(sizeof(cepis_faixa) == 112, "cepis_faixa layout"); +_Static_assert(offsetof(cepis_faixa, localidade) == 4, "cepis_faixa layout"); +_Static_assert(offsetof(cepis_faixa, uf) == 76, "cepis_faixa layout"); +_Static_assert(offsetof(cepis_faixa, cep_ini) == 79, "cepis_faixa layout"); +_Static_assert(offsetof(cepis_faixa, cep_fim) == 88, "cepis_faixa layout"); +_Static_assert(offsetof(cepis_faixa, ceps) == 104, "cepis_faixa layout"); +_Static_assert(sizeof(cepis_error) == 384, "cepis_error layout"); _Static_assert(offsetof(cepis_error, retry_after) == 8, "cepis_error layout"); _Static_assert(offsetof(cepis_error, message) == 16, "cepis_error layout"); +_Static_assert(offsetof(cepis_error, faixa) == 272, "cepis_error layout"); _Static_assert(sizeof(cepis_config) == 56, "cepis_config layout"); _Static_assert(offsetof(cepis_config, max_attempts) == 16, "cepis_config layout"); @@ -266,10 +273,106 @@ static void test_not_found_not_retried(void) CHECK(address == NULL); CHECK(err.status == CEPIS_ERR_NOT_FOUND); CHECK(strstr(err.message, "99999999") != NULL); + /* No band owns 99999999, so the API omits "faixa" and the message is + exactly the one this library produced before the field existed. */ + CHECK(err.faixa.present == 0); + CHECK_STR(err.faixa.localidade, ""); + CHECK_STR(err.faixa.uf, ""); + CHECK_STR(err.faixa.cep_ini, ""); + CHECK_STR(err.faixa.cep_fim, ""); + CHECK(err.faixa.ceps == 0); + CHECK_STR(err.message, "CEP 99999999 does not exist in the DNE database"); CHECK(hits("nf") == 1); cepis_client_free(client); } +static void test_not_found_faixa(void) +{ + cepis_client *client = client_at(urlf("%s/nff", base), 3, 0.001); + cepis_address *address = NULL; + cepis_error err; + + begin("404 carries the municipality that owns the band"); + CHECK(cepis_lookup(client, "25845-000", &address, &err) == + CEPIS_ERR_NOT_FOUND); + CHECK(address == NULL); + CHECK(err.status == CEPIS_ERR_NOT_FOUND); + CHECK(err.faixa.present == 1); + CHECK_STR(err.faixa.localidade, "Areal"); + CHECK_STR(err.faixa.uf, "RJ"); + /* cep_ini is 25845001, not the 25845000 that just failed: the band the + Correios publish starts one lower than the lowest CEP that exists. */ + CHECK_STR(err.faixa.cep_ini, "25845001"); + CHECK_STR(err.faixa.cep_fim, "25849899"); + CHECK(err.faixa.ceps == 118); + CHECK(strstr(err.message, "25845000") != NULL); + CHECK(strstr(err.message, "Areal") != NULL); + CHECK(strstr(err.message, "118") != NULL); + CHECK(hits("nff") == 1); + cepis_client_free(client); +} + +static void test_faixa_does_not_survive_reuse(void) +{ + cepis_client *found = client_at(urlf("%s/nfr1", base), 1, 0.001); + cepis_client *bare = client_at(urlf("%s/nfr2", base), 1, 0.001); + cepis_address *address = NULL; + cepis_error err; + + begin("a reused cepis_error does not keep the previous faixa"); + CHECK(cepis_lookup(found, "25845000", &address, &err) == + CEPIS_ERR_NOT_FOUND); + CHECK(err.faixa.present == 1); + CHECK(cepis_lookup(bare, "99999999", &address, &err) == + CEPIS_ERR_NOT_FOUND); + CHECK(err.faixa.present == 0); + CHECK_STR(err.faixa.localidade, ""); + CHECK(err.faixa.ceps == 0); + /* Any other call clears it too, not just another 404. */ + CHECK(cepis_lookup(found, "25845000", &address, &err) == + CEPIS_ERR_NOT_FOUND); + CHECK(err.faixa.present == 1); + CHECK(cepis_lookup(bare, "123", &address, &err) == CEPIS_ERR_INVALID_CEP); + CHECK(err.faixa.present == 0); + CHECK_STR(err.faixa.localidade, ""); + cepis_client_free(found); + cepis_client_free(bare); +} + +static void test_faixa_degenerate(void) +{ + cepis_client *empty = client_at(urlf("%s/faixavazia", base), 1, 0.001); + cepis_client *text = client_at(urlf("%s/faixatexto", base), 1, 0.001); + cepis_address *address = NULL; + cepis_error err; + + begin("an empty or non-object faixa is ignored"); + CHECK(cepis_lookup(empty, "25845000", &address, &err) == + CEPIS_ERR_NOT_FOUND); + CHECK(err.faixa.present == 0); + CHECK_STR(err.message, "CEP 25845000 does not exist in the DNE database"); + CHECK(cepis_lookup(text, "25845000", &address, &err) == + CEPIS_ERR_NOT_FOUND); + CHECK(err.faixa.present == 0); + CHECK_STR(err.faixa.localidade, ""); + CHECK_STR(err.message, "CEP 25845000 does not exist in the DNE database"); + CHECK(address == NULL); + cepis_client_free(empty); + cepis_client_free(text); +} + +static void test_faixa_without_error_struct(void) +{ + cepis_client *client = client_at(urlf("%s/nfnull", base), 1, 0.001); + cepis_address *address = NULL; + + begin("a 404 with a faixa and no cepis_error is still just a 404"); + CHECK(cepis_lookup(client, "25845000", &address, NULL) == + CEPIS_ERR_NOT_FOUND); + CHECK(address == NULL); + cepis_client_free(client); +} + static void test_bad_cep_from_api(void) { cepis_client *client = client_at(urlf("%s/always400", base), 3, 0.001); @@ -640,6 +743,8 @@ static void test_abi(void) NULL, NULL); probe("cepis_lookup(invalid)", (void *)cepis_lookup, client, (void *)"1", &address, &err); + probe("cepis_lookup(404 with faixa)", (void *)cepis_lookup, client, + (void *)"25845000", &address, &err); probe("cepis_lookup", (void *)cepis_lookup, client, (void *)"01001000", &address, &err); probe("cepis_address_free", (void *)cepis_address_free, address, NULL, @@ -691,6 +796,10 @@ int main(int argc, char **argv) test_surrogate_pair(); test_csv_bom(); test_not_found_not_retried(); + test_not_found_faixa(); + test_faixa_does_not_survive_reuse(); + test_faixa_degenerate(); + test_faixa_without_error_struct(); test_bad_cep_from_api(); test_local_validation_makes_no_request(); test_retry_then_success(); -- cgit v1.3