diff options
| author | Murilo Ijanc <[email protected]> | 2026-07-27 20:42:18 -0300 |
|---|---|---|
| committer | Murilo Ijanc <[email protected]> | 2026-07-27 20:42:18 -0300 |
| commit | 218bc8fbfbbdc2520130ef43652a6cd22a6e67fb (patch) | |
| tree | 4291a5bb3a235a21b44cfbc51bf1d0c6b9af0c3e /test | |
| parent | 5598ea80735a51cb7b3690837b2b7d4240331b2d (diff) | |
| download | ocaml-1e24c8acae5286ca3d6a5aa35efaf61508b57b0f.tar.gz | |
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 answers
those 404s with a faixa naming the municipality whose band the number
falls into, so a caller can ask "did you mean somewhere in Areal/RJ?"
instead of returning a bare "not found".
Cep_not_found becomes an inline record so the context rides along with
the message, matching how Rate_limited and Server_error already carry
theirs. It is still a 404: faixa is context on the failure and never
promotes it to an address, and its bounds are the smallest and largest
CEP that actually exist there — sparse across the interval, which is
what the ceps count is for — not the bounds the Correios publish.
Parsing is deliberately lenient. A missing, null, malformed or
municipality-less faixa yields None rather than an error: a 404 is a
clean answer, and no shape of this field should turn it into an
Invalid_response.
Diffstat (limited to 'test')
| -rw-r--r-- | test/test_cepis.ml | 67 |
1 files changed, 66 insertions, 1 deletions
diff --git a/test/test_cepis.ml b/test/test_cepis.ml index cc98c4a..a1c575f 100644 --- a/test/test_cepis.ml +++ b/test/test_cepis.ml @@ -180,6 +180,15 @@ let is_error name predicate = function | Ok _ -> check name false | Error e -> check name (predicate e) +let contains haystack needle = + let n = String.length haystack and m = String.length needle in + let rec go i = + if i + m > n then false + else if String.sub haystack i m = needle then true + else go (i + 1) + in + go 0 + let test_normalisation () = List.iter (fun value -> @@ -262,11 +271,65 @@ let test_not_found () = with_server (fun srv client -> respond srv "/99999999" [ reply 404 {|{"erro":"cep_nao_encontrado","mensagem":"gone"}|} ]; + let result = Cepis.Client.lookup client "99999999" in is_error "404 becomes Cep_not_found" (function Cepis.Cep_not_found _ -> true | _ -> false) - (Cepis.Client.lookup client "99999999"); + result; + is_error "a 404 without faixa yields None" + (function + | Cepis.Cep_not_found { faixa = None; _ } -> true | _ -> false) + result; check "404 is not retried" (count srv "/99999999" = 1)) +let areal_404 = + {|{"erro":"cep_nao_encontrado","mensagem":"CEP não existe na base do DNE","faixa":{"localidade":"Areal","uf":"RJ","estado":"Rio de Janeiro","regiao":"Sudeste","ibge":"3300225","cep_ini":"25845001","cep_fim":"25849899","ceps":118}}|} + +let test_not_found_with_faixa () = + with_server (fun srv client -> + respond srv "/25845000" [ reply 404 areal_404 ]; + match Cepis.Client.lookup client "25845000" with + | Ok _ -> check "a faixa does not turn a 404 into an address" false + | Error (Cepis.Cep_not_found { faixa = Some f; _ } as e) -> + check "faixa names the municipality" (f.Cepis.localidade = "Areal"); + check "faixa carries the uf" (f.Cepis.uf = "RJ"); + check "faixa carries the ibge code" (f.Cepis.ibge = "3300225"); + check "faixa carries the region" (f.Cepis.regiao = "Sudeste"); + check "faixa bounds are the CEPs that exist, not the DNE's" + (f.Cepis.cep_ini = "25845001" && f.Cepis.cep_fim = "25849899"); + check "faixa counts how sparse the range is" (f.Cepis.ceps = 118); + check "the rendered error mentions the municipality" + (contains (Cepis.string_of_error e) "Areal"); + check "the rendered error still says not found" + (contains (Cepis.string_of_error e) "CEP not found") + | Error _ -> check "a 404 with faixa is still Cep_not_found" false) + +let test_not_found_faixa_is_defensive () = + with_server (fun srv client -> + List.iter + (fun (path, body, label) -> + respond srv path [ reply 404 body ]; + is_error + (Printf.sprintf "%s yields no faixa, never a parse failure" label) + (function + | Cepis.Cep_not_found { faixa = None; _ } -> true | _ -> false) + (Cepis.Client.lookup client (String.sub path 1 8))) + [ + ( "/25845002", + {|{"erro":"cep_nao_encontrado","faixa":null}|}, + "an explicit null" ); + ( "/25845003", + {|{"erro":"cep_nao_encontrado","faixa":{}}|}, + "an empty object" ); + ( "/25845004", + {|{"erro":"cep_nao_encontrado","faixa":"Areal"}|}, + "a bare string" ); + ( "/25845005", + {|{"erro":"cep_nao_encontrado","faixa":{"uf":"RJ","ceps":"muitos"}}|}, + "a faixa with no municipality" ); + ("/25845006", "<html>captive portal</html>", "a body that is not JSON"); + ("/25845007", "", "an empty body"); + ]) + let test_local_validation_makes_no_request () = with_server (fun srv client -> is_error "malformed CEP is rejected locally" @@ -544,6 +607,8 @@ let () = test_malformed_geo (); test_unknown_field (); test_not_found (); + test_not_found_with_faixa (); + test_not_found_faixa_is_defensive (); test_local_validation_makes_no_request (); test_api_400 (); test_not_json (); |