From 4ebb31f26efed74f8fcb049fe27ddf7cf5388a08 Mon Sep 17 00:00:00 2001 From: Murilo Ijanc Date: Mon, 27 Jul 2026 20:31:55 -0300 Subject: Carry the municipality of a CEP that does not exist A CEP that fails is rarely a random number: it is someone who knows the city and guessed the digits, and the classic guess is the -000 that 957 municipalities never received. The API now answers the 404 with the municipality that band belongs to, which is enough to tell the user "Areal/RJ, but not that number" instead of a bare failure. It stays an error. Faixa hangs off CEPNotFound rather than becoming a result, cep_ini/cep_fim are documented as the CEPs that exist there and not as a range to iterate, and an absent or unreadable block yields undefined so that context can never cost the caller the clean not-found it was already getting. --- test/cepis.test.ts | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 1 deletion(-) (limited to 'test/cepis.test.ts') diff --git a/test/cepis.test.ts b/test/cepis.test.ts index 911aa1d..67677fe 100644 --- a/test/cepis.test.ts +++ b/test/cepis.test.ts @@ -5,6 +5,7 @@ import { CEPNotFound, CepisError, Client, + Faixa, InvalidCEP, InvalidResponse, NetworkError, @@ -31,6 +32,17 @@ const SE_ADDRESS: Record = { siafi: "7107", }; +const AREAL_FAIXA: Record = { + localidade: "Areal", + uf: "RJ", + estado: "Rio de Janeiro", + regiao: "Sudeste", + ibge: "3300225", + cep_ini: "25845001", + cep_fim: "25849899", + ceps: 118, +}; + const CSV_HEADER = "cep,logradouro,complemento,unidade,bairro,localidade,uf,estado,regiao,ibge,gia,ddd,siafi"; @@ -217,7 +229,119 @@ describe("lookup", () => { } finally { probe.stop(true); } - expect(seen[0]).toContain("cepis-typescript/0.1.0"); + expect(seen[0]).toContain("cepis-typescript/0.2.0"); + }); +}); + +describe("faixa", () => { + async function notFound(cep: string): Promise { + try { + await client.lookup(cep); + } catch (error) { + expect(error).toBeInstanceOf(CEPNotFound); + return error as CEPNotFound; + } + throw new Error("expected CEPNotFound"); + } + + test("names the municipality the CEP would belong to", async () => { + server.routes.respond("/25845000", { + status: 404, + body: JSON.stringify({ + erro: "cep_nao_encontrado", + mensagem: "CEP não existe na base do DNE", + faixa: AREAL_FAIXA, + }), + }); + + const error = await notFound("25845000"); + expect(error.faixa).toBeInstanceOf(Faixa); + expect(error.faixa?.localidade).toBe("Areal"); + expect(error.faixa?.uf).toBe("RJ"); + expect(error.faixa?.estado).toBe("Rio de Janeiro"); + expect(error.faixa?.regiao).toBe("Sudeste"); + expect(error.faixa?.ibge).toBe("3300225"); + expect(error.faixa?.label).toBe("Areal/RJ"); + }); + + test("the range is what exists there, and it is sparse", async () => { + server.routes.respond("/25845000", { + status: 404, + body: JSON.stringify({ erro: "cep_nao_encontrado", faixa: AREAL_FAIXA }), + }); + + const error = await notFound("25845000"); + // The DNE's own start for Areal is 25845000 — the CEP that just failed. + expect(error.faixa?.cep_ini).toBe("25845001"); + expect(error.faixa?.cep_fim).toBe("25849899"); + // 118 CEPs across ~4,900 numbers: the pair is not a list of valid CEPs. + expect(error.faixa?.ceps).toBe(118); + }); + + test("a faixa is still a not-found, never an address", async () => { + server.routes.respond("/25845000", { + status: 404, + body: JSON.stringify({ erro: "cep_nao_encontrado", faixa: AREAL_FAIXA }), + }); + + const promise = client.lookup("25845000"); + await expect(promise).rejects.toBeInstanceOf(CEPNotFound); + await expect(promise).rejects.toThrow("25845000"); + await expect(promise).rejects.toThrow("Areal/RJ"); + expect(server.routes.count("/25845000")).toBe(1); + }); + + test("a band no municipality owns omits the field", async () => { + server.routes.respond("/99999999", { + status: 404, + body: JSON.stringify({ + erro: "cep_nao_encontrado", + mensagem: "CEP não existe na base do DNE", + }), + }); + + const error = await notFound("99999999"); + expect(error.faixa).toBeUndefined(); + expect(error.cep).toBe("99999999"); + expect(error.message).not.toContain("belongs to"); + }); + + test("a faixa that cannot be read costs nothing", async () => { + const useless: unknown[] = [null, "Areal", [], {}, { localidade: "" }, { uf: "RJ" }]; + for (const faixa of useless) { + server.routes.respond("/99999999", { + status: 404, + body: JSON.stringify({ erro: "cep_nao_encontrado", faixa }), + }); + const error = await notFound("99999999"); + expect(error.faixa).toBeUndefined(); + } + }); + + test("a 404 that is not JSON still yields a clean not-found", async () => { + server.routes.respond("/99999999", { + status: 404, + body: "captive portal", + }); + + const error = await notFound("99999999"); + expect(error.faixa).toBeUndefined(); + }); + + test("a partial faixa keeps what it can", async () => { + server.routes.respond("/99999999", { + status: 404, + body: JSON.stringify({ + erro: "cep_nao_encontrado", + faixa: { localidade: "Areal", ceps: "118" }, + }), + }); + + const error = await notFound("99999999"); + expect(error.faixa?.localidade).toBe("Areal"); + expect(error.faixa?.label).toBe("Areal"); + expect(error.faixa?.ceps).toBe(118); + expect(error.faixa?.cep_ini).toBe(""); }); }); -- cgit v1.3