using System.Globalization; namespace Cepis.Tests; /// A single lookup: the happy path, the coordinate, and the refusals. public sealed class LookupTests : HttpTestBase { [Fact] public async Task ReadsEveryField() { Server.Respond("/01001000", Reply.Of(200, Fixtures.SeAddress)); var address = await Client.LookupAsync("01001-000"); Assert.Equal("01001000", address.Cep); Assert.Equal("01001-000", address.FormattedCep); Assert.Equal("Praça da Sé", address.Logradouro); Assert.Equal("lado ímpar", address.Complemento); Assert.Equal("", address.Unidade); Assert.Equal("Sé", address.Bairro); Assert.Equal("São Paulo", address.Localidade); Assert.Equal("SP", address.UF); Assert.Equal("São Paulo", address.Estado); Assert.Equal("Sudeste", address.Regiao); Assert.Equal("3550308", address.Ibge); Assert.Equal("1004", address.Gia); Assert.Equal("11", address.Ddd); Assert.Equal("7107", address.Siafi); } [Fact] public async Task TheMaskIsStrippedBeforeTheRequest() { Server.Respond("/01001000", Reply.Of(200, Fixtures.SeAddress)); await Client.LookupAsync("01001-000"); Assert.Equal("/01001000", Assert.Single(Server.Calls).Path); } [Fact] public async Task ReadsTheCoordinate() { Server.Respond("/01001000", Reply.Of(200, Fixtures.SeAddress)); var address = await Client.LookupAsync("01001000"); var geo = address.Geo; Assert.NotNull(geo); Assert.Equal(-23.567202, geo.Lat, 6); Assert.Equal(-46.649659, geo.Lon, 6); Assert.Equal(1084, geo.RaioM); Assert.Equal("logradouro", geo.Precisao); Assert.True(geo.IsPrecise); } [Fact] public async Task AnAbsentCoordinateIsNull() { Server.Respond("/20040020", Reply.Of(200, Fixtures.NoGeoAddress)); var address = await Client.LookupAsync("20040020"); Assert.Null(address.Geo); Assert.Equal("Rio de Janeiro", address.Localidade); } [Fact] public async Task AMalformedCoordinateDoesNotLoseTheAddress() { Server.Respond("/01001000", Reply.Of(200, Fixtures.BrokenGeoAddress)); var address = await Client.LookupAsync("01001000"); Assert.Null(address.Geo); Assert.Equal("São Paulo", address.Localidade); } [Fact] public async Task ACoordinateOfTheWrongShapeIsDropped() { Server.Respond("/01001000", Reply.Of(200, """{"cep":"01001000","localidade":"X","geo":[]}""")); var address = await Client.LookupAsync("01001000"); Assert.Null(address.Geo); Assert.Equal("X", address.Localidade); } [Fact] public async Task AFieldThisLibraryDoesNotKnowIsKept() { Server.Respond("/01001000", Reply.Of(200, Fixtures.FutureAddress)); var address = await Client.LookupAsync("01001000"); Assert.Equal("São Paulo", address.Localidade); var extra = address.Extra; Assert.NotNull(extra); Assert.Equal("01000000", extra["cep_pai"].GetString()); } [Fact] public async Task AMissingCepThrows() { Server.Respond("/99999999", Reply.Of(404, Fixtures.NotFound)); var failure = await Assert.ThrowsAsync( () => Client.LookupAsync("99999999")); Assert.Contains("99999999", failure.Message, StringComparison.Ordinal); } [Fact] public async Task A404IsNeverRetried() { Server.Respond("/99999999", Reply.Of(404, Fixtures.NotFound)); await Assert.ThrowsAsync(() => Client.LookupAsync("99999999")); Assert.Equal(1, Server.Count("/99999999")); } [Fact] public async Task AMissingCepNamesTheMunicipalityItWouldBeIn() { Server.Respond("/25845000", Reply.Of(404, Fixtures.NotFoundWithFaixa)); var failure = await Assert.ThrowsAsync( () => Client.LookupAsync("25845000")); var faixa = failure.Faixa; Assert.NotNull(faixa); Assert.Equal("Areal", faixa.Localidade); Assert.Equal("RJ", faixa.UF); Assert.Equal("Rio de Janeiro", faixa.Estado); Assert.Equal("Sudeste", faixa.Regiao); Assert.Equal("3300225", faixa.Ibge); Assert.Equal("25845001", faixa.CepIni); Assert.Equal("25849899", faixa.CepFim); Assert.Equal(118, faixa.Ceps); } /// /// The CEP asked for, 25845000, is the DNE's own start for Areal. The range /// reports what exists, so it starts one above — and holds 118 CEPs across /// the ~4,900 numbers it spans, which is why the count travels with it. /// [Fact] public async Task TheRangeIsWhatExistsAndIsSparse() { Server.Respond("/25845000", Reply.Of(404, Fixtures.NotFoundWithFaixa)); var failure = await Assert.ThrowsAsync( () => Client.LookupAsync("25845000")); var faixa = failure.Faixa; Assert.NotNull(faixa); Assert.NotEqual("25845000", faixa.CepIni); Assert.True(faixa.Ceps < int.Parse(faixa.CepFim, CultureInfo.InvariantCulture) - int.Parse(faixa.CepIni, CultureInfo.InvariantCulture)); } [Fact] public async Task TheMessageMentionsTheMunicipality() { Server.Respond("/25845000", Reply.Of(404, Fixtures.NotFoundWithFaixa)); var failure = await Assert.ThrowsAsync( () => Client.LookupAsync("25845000")); Assert.Contains("25845000", failure.Message, StringComparison.Ordinal); Assert.Contains("Areal/RJ", failure.Message, StringComparison.Ordinal); } [Fact] public async Task AMissingCepInNobodysRangeHasNoMunicipality() { Server.Respond("/99999999", Reply.Of(404, Fixtures.NotFound)); var failure = await Assert.ThrowsAsync( () => Client.LookupAsync("99999999")); Assert.Null(failure.Faixa); Assert.Equal("CEP 99999999 does not exist in the DNE database", failure.Message); } [Fact] public async Task ARangeOfTheWrongShapeIsDropped() { Server.Respond("/25845000", Reply.Of(404, Fixtures.NotFoundBrokenFaixa)); var failure = await Assert.ThrowsAsync( () => Client.LookupAsync("25845000")); Assert.Null(failure.Faixa); } [Fact] public async Task ARangeNamingNoMunicipalityIsDropped() { Server.Respond("/25845000", Reply.Of(404, Fixtures.NotFoundNamelessFaixa)); var failure = await Assert.ThrowsAsync( () => Client.LookupAsync("25845000")); Assert.Null(failure.Faixa); } [Fact] public async Task AnUnreadable404BodyIsStillJustANotFound() { Server.Respond("/25845000", Reply.Of(404, "gateway")); var failure = await Assert.ThrowsAsync( () => Client.LookupAsync("25845000")); Assert.Null(failure.Faixa); } [Fact] public async Task A400CarriesTheApiMessage() { Server.Respond("/00000000", Reply.Of(400, Fixtures.InvalidCep)); var failure = await Assert.ThrowsAsync( () => Client.LookupAsync("00000000")); Assert.Equal("CEP deve ter 8 dígitos", failure.Message); Assert.Equal(1, Server.Count("/00000000")); } [Fact] public async Task AMalformedCepCostsNoRequest() { await Assert.ThrowsAsync(() => Client.LookupAsync("1234567")); Assert.Empty(Server.Calls); } [Fact] public async Task AnUnintelligibleBodyIsDiagnosed() { Server.Respond("/01001000", Reply.Of(200, "captive portal")); await Assert.ThrowsAsync(() => Client.LookupAsync("01001000")); } [Fact] public async Task AJsonBodyThatIsNotAnObjectIsDiagnosed() { Server.Respond("/01001000", Reply.Of(200, "null")); await Assert.ThrowsAsync(() => Client.LookupAsync("01001000")); } [Fact] public async Task ACancelledCallDoesNotBecomeANetworkError() { Server.Respond("/01001000", Reply.Of(200, Fixtures.SeAddress)); using var cancellation = new CancellationTokenSource(); await cancellation.CancelAsync(); await Assert.ThrowsAnyAsync( () => Client.LookupAsync("01001000", cancellation.Token)); } }