diff --git a/src/Tests/UnitTests/Weather.Core.UnitTests/Queries/GetFavoritesHandlerTests.cs b/src/Tests/UnitTests/Weather.Core.UnitTests/Queries/GetFavoritesHandlerTests.cs index 2a4d849..076244a 100644 --- a/src/Tests/UnitTests/Weather.Core.UnitTests/Queries/GetFavoritesHandlerTests.cs +++ b/src/Tests/UnitTests/Weather.Core.UnitTests/Queries/GetFavoritesHandlerTests.cs @@ -32,31 +32,12 @@ public GetFavoritesHandlerTests() _loggerMock.Object); } - [Fact] - public async Task GetFavorites_Failed() - { - //Arrange - var failMessage = "Some fail message"; - - _weatherRepositoryMock.Setup(x => x.GetFavorites(It.IsAny())).ReturnsAsync(Result.Fail(failMessage)); - - //Act - var result = await _uut.HandleAsync(EmptyRequest.Instance, CancellationToken.None); - - //Assert - Assert.Equal(HttpStatusCode.InternalServerError, result.StatusCode); - Assert.Single(result.Errors); - Assert.Equal(ErrorMessages.ExternalApiError, result.Errors.Single()); - _weatherRepositoryMock.Verify(x => x.GetFavorites(It.IsAny()), Times.Once); - _loggerMock.VerifyLog(LogLevel.Error, LogEvents.FavoriteWeathersGetFromDatabase, failMessage, Times.Once()); - } - [Fact] public async Task GetFavorites_Empty() { //Arrange _weatherRepositoryMock.Setup(x => x.GetFavorites(It.IsAny())) - .ReturnsAsync(Result.Ok>(new List())); + .ReturnsAsync(new List()); //Act var result = await _uut.HandleAsync(EmptyRequest.Instance, CancellationToken.None); @@ -74,10 +55,10 @@ public async Task InvalidLocation() var locationDto = new LocationDto { Latitude = 1, Longitude = 1 }; _weatherRepositoryMock.Setup(x => x.GetFavorites(It.IsAny())) - .ReturnsAsync(Result.Ok>(new List + .ReturnsAsync(new List { locationDto, - })); + }); _locationValidatorMock.Setup(x => x.IsValid(It.IsAny())).Returns(false); @@ -101,10 +82,10 @@ public async Task EmptyResult_GetCurrentWeather_Fail() var locationDto = new LocationDto { Latitude = 1, Longitude = 1 }; _weatherRepositoryMock.Setup(x => x.GetFavorites(It.IsAny())) - .ReturnsAsync(Result.Ok>(new List + .ReturnsAsync(new List { - locationDto - })); + locationDto, + }); _locationValidatorMock.Setup(x=>x.IsValid(It.IsAny())).Returns(true); @@ -129,11 +110,11 @@ public async Task One_Of_GetCurrentWeather_Failed() var locationDto = new LocationDto { Latitude = 1, Longitude = 1 }; _weatherRepositoryMock.Setup(x => x.GetFavorites(It.IsAny())) - .ReturnsAsync(Result.Ok>(new List + .ReturnsAsync(new List { locationDto, new LocationDto(), - })); + }); _locationValidatorMock.Setup(x => x.IsValid(It.IsAny())).Returns(true); @@ -166,10 +147,10 @@ public async Task GetCurrentWeather_Validation_Fail() var locationDto = new LocationDto { Latitude = 1, Longitude = 1 }; _weatherRepositoryMock.Setup(x => x.GetFavorites(It.IsAny())) - .ReturnsAsync(Result.Ok>(new List + .ReturnsAsync(new List { locationDto, - })); + }); _locationValidatorMock.Setup(x => x.IsValid(It.IsAny())).Returns(true); _currentWeatherValidatorMock.Setup(x => x.IsValid(It.IsAny())).Returns(false); @@ -196,10 +177,10 @@ public async Task Success() var locationDto = new LocationDto { Latitude = 1, Longitude = 1 }; _weatherRepositoryMock.Setup(x => x.GetFavorites(It.IsAny())) - .ReturnsAsync(Result.Ok>(new List + .ReturnsAsync(new List { locationDto, - })); + }); _locationValidatorMock.Setup(x => x.IsValid(It.IsAny())).Returns(true); _currentWeatherValidatorMock.Setup(x=>x.IsValid(It.IsAny())).Returns(true); diff --git a/src/Tests/UnitTests/Weather.Core.UnitTests/Queries/GetForecastWeatherHandlerTests.cs b/src/Tests/UnitTests/Weather.Core.UnitTests/Queries/GetForecastWeatherHandlerTests.cs index 102015a..f535227 100644 --- a/src/Tests/UnitTests/Weather.Core.UnitTests/Queries/GetForecastWeatherHandlerTests.cs +++ b/src/Tests/UnitTests/Weather.Core.UnitTests/Queries/GetForecastWeatherHandlerTests.cs @@ -14,7 +14,7 @@ public class GetForecastWeatherHandlerTests private readonly Mock> _getForecastWeatherQueryValidatorMock; private readonly Mock> _forecastWeatherValidatorMock; private readonly Mock _weatherServiceMock; - private readonly Mock> _loggerMock; + private readonly Mock> _loggerMock; private readonly IGetForecastWeatherHandler _uut; public GetForecastWeatherHandlerTests() @@ -22,7 +22,7 @@ public GetForecastWeatherHandlerTests() _getForecastWeatherQueryValidatorMock = new Mock>(); _forecastWeatherValidatorMock = new Mock>(); _weatherServiceMock = new Mock(); - _loggerMock = new Mock>(); + _loggerMock = new Mock>(); _uut = new GetForecastWeatherHandler(_getForecastWeatherQueryValidatorMock.Object, _weatherServiceMock.Object, _forecastWeatherValidatorMock.Object, _loggerMock.Object); } diff --git a/src/Tests/UnitTests/Weather.Infrastructure.UnitTests/Database/Repositories/WeatherCommandsRepositoryTests.cs b/src/Tests/UnitTests/Weather.Infrastructure.UnitTests/Database/Repositories/WeatherCommandsRepositoryTests.cs index 38c946e..abf9682 100644 --- a/src/Tests/UnitTests/Weather.Infrastructure.UnitTests/Database/Repositories/WeatherCommandsRepositoryTests.cs +++ b/src/Tests/UnitTests/Weather.Infrastructure.UnitTests/Database/Repositories/WeatherCommandsRepositoryTests.cs @@ -1,9 +1,12 @@ using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; using Weather.Core.Abstractions; using Weather.Domain.Commands; using Weather.Domain.Dtos; +using Weather.Domain.Logging; using Weather.Infrastructure.Database.EFContext.Entities; using Weather.Infrastructure.Database.Repositories; +using Weather.UnitTests.Common.Extensions; namespace Weather.Infrastructure.UnitTests.Database.Repositories { @@ -12,6 +15,7 @@ public class WeatherCommandsRepositoryTests private readonly Mock> _favoriteLocationEntityDbSetMock; private readonly Mock _mapperMock; private readonly Mock _weatherDbContextMock; + private readonly Mock> _loggerMock; private readonly IWeatherCommandsRepository _uut; @@ -22,8 +26,9 @@ public WeatherCommandsRepositoryTests() _weatherDbContextMock.Setup(x => x.FavoriteLocations).Returns(_favoriteLocationEntityDbSetMock.Object); _mapperMock = new Mock(); + _loggerMock = new Mock>(); - _uut = new WeatherCommandsRepository(_weatherDbContextMock.Object, _mapperMock.Object); + _uut = new WeatherCommandsRepository(_weatherDbContextMock.Object, _mapperMock.Object, _loggerMock.Object); } [Fact] @@ -45,6 +50,27 @@ public async Task AddFavoriteLocation_Success() _favoriteLocationEntityDbSetMock.Verify(x => x.AddAsync(It.IsAny(), It.IsAny()), Times.Once); } + [Fact] + public async Task AddFavoriteLocation_Failed() + { + //Arrange + var addFacoriteCommand = new AddFavoriteCommand { Location = new LocationDto { Latitude = 1, Longitude = 1 } }; + var favoriteLocationEntity = new FavoriteLocationEntity(); + + _mapperMock.Setup(x => x.Map(It.IsAny())).Returns(favoriteLocationEntity); + _favoriteLocationEntityDbSetMock.Setup(x => x.AddAsync(It.IsAny(), It.IsAny())).Throws(new DbUpdateException()); + + //Act + var result = await _uut.AddFavoriteLocation(addFacoriteCommand, CancellationToken.None); + + //Assert + Assert.True(result.IsFailed); + _loggerMock.VerifyLog(LogLevel.Error, LogEvents.FavoriteWeathersStoreToDatabase, Times.Once()); + _mapperMock.Verify(x => x.Map(It.IsAny()), Times.Once); + _weatherDbContextMock.Verify(x => x.SaveChangesAsync(It.IsAny()), Times.Never); + _favoriteLocationEntityDbSetMock.Verify(x => x.AddAsync(It.IsAny(), It.IsAny()), Times.Once); + } + [Fact] public async Task AddFavoriteLocation_Throw() { diff --git a/src/Tests/UnitTests/Weather.Infrastructure.UnitTests/Weather.Infrastructure.UnitTests.csproj b/src/Tests/UnitTests/Weather.Infrastructure.UnitTests/Weather.Infrastructure.UnitTests.csproj index 7def488..9b60b61 100644 --- a/src/Tests/UnitTests/Weather.Infrastructure.UnitTests/Weather.Infrastructure.UnitTests.csproj +++ b/src/Tests/UnitTests/Weather.Infrastructure.UnitTests/Weather.Infrastructure.UnitTests.csproj @@ -27,6 +27,7 @@ + diff --git a/src/Weather.Core/Abstractions/IWeatherQueriesRepository.cs b/src/Weather.Core/Abstractions/IWeatherQueriesRepository.cs index 8375093..9941738 100644 --- a/src/Weather.Core/Abstractions/IWeatherQueriesRepository.cs +++ b/src/Weather.Core/Abstractions/IWeatherQueriesRepository.cs @@ -5,6 +5,6 @@ namespace Weather.Core.Abstractions { public interface IWeatherQueriesRepository { - Task>> GetFavorites(CancellationToken cancellationToken); + Task> GetFavorites(CancellationToken cancellationToken); } } diff --git a/src/Weather.Core/Queries/GetFavoritesHandler.cs b/src/Weather.Core/Queries/GetFavoritesHandler.cs index 9ba92c8..d7c4489 100644 --- a/src/Weather.Core/Queries/GetFavoritesHandler.cs +++ b/src/Weather.Core/Queries/GetFavoritesHandler.cs @@ -37,18 +37,12 @@ public async Task> HandleAsync(EmptyReques { var favoriteLocationsResult = await _weatherQueriesRepository.GetFavorites(cancellationToken); - if(favoriteLocationsResult.IsFailed) - { - _logger.LogError(LogEvents.FavoriteWeathersGetFromDatabase, favoriteLocationsResult.Errors.JoinToMessage()); - return HttpDataResponses.AsInternalServerError(ErrorMessages.ExternalApiError); - } - - if(!favoriteLocationsResult.Value.HasAny()) + if(!favoriteLocationsResult.HasAny()) { return HttpDataResponses.AsNoContent(); } - return await GetFavoritesAsync(favoriteLocationsResult.Value, cancellationToken); + return await GetFavoritesAsync(favoriteLocationsResult, cancellationToken); } diff --git a/src/Weather.Core/Queries/GetForecastWeatherHandler.cs b/src/Weather.Core/Queries/GetForecastWeatherHandler.cs index b759c94..5738688 100644 --- a/src/Weather.Core/Queries/GetForecastWeatherHandler.cs +++ b/src/Weather.Core/Queries/GetForecastWeatherHandler.cs @@ -17,12 +17,12 @@ internal sealed class GetForecastWeatherHandler : IGetForecastWeatherHandler private readonly IValidator _getForecastWeatherQueryValidator; private readonly IValidator _forecastWeatherValidator; private readonly IWeatherService _weatherService; - private readonly ILogger _logger; + private readonly ILogger _logger; public GetForecastWeatherHandler( IValidator getForecastWeatherQueryValidator, IWeatherService weatherService, IValidator forecastWeatherValidator, - ILogger logger) + ILogger logger) { _getForecastWeatherQueryValidator = Guard.Against.Null(getForecastWeatherQueryValidator); _weatherService = Guard.Against.Null(weatherService); diff --git a/src/Weather.Infrastructure/Database/Repositories/WeatherCommandsRepository.cs b/src/Weather.Infrastructure/Database/Repositories/WeatherCommandsRepository.cs index bcd96ed..e678491 100644 --- a/src/Weather.Infrastructure/Database/Repositories/WeatherCommandsRepository.cs +++ b/src/Weather.Infrastructure/Database/Repositories/WeatherCommandsRepository.cs @@ -1,29 +1,39 @@ using Ardalis.GuardClauses; using AutoMapper; using FluentResults; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; using Weather.Core.Abstractions; using Weather.Domain.Commands; +using Weather.Domain.Logging; using Weather.Infrastructure.Database.EFContext; using Weather.Infrastructure.Database.EFContext.Entities; namespace Weather.Infrastructure.Database.Repositories { - internal sealed class WeatherCommandsRepository : IWeatherCommandsRepository + internal sealed class WeatherCommandsRepository : RepositoryBase, IWeatherCommandsRepository { - private readonly IMapper _mapper; - private readonly WeatherContext _weatherContext; - public WeatherCommandsRepository(WeatherContext weatherContext, IMapper mapper) - { - _weatherContext = Guard.Against.Null(weatherContext); - _mapper = Guard.Against.Null(mapper); + private readonly ILogger _logger; + public WeatherCommandsRepository(WeatherContext weatherContext, IMapper mapper, ILogger logger) + : base(weatherContext, mapper) + { + _logger = Guard.Against.Null(logger); } public async Task> AddFavoriteLocation(AddFavoriteCommand addFavoriteCommand, CancellationToken cancellationToken) { var locationEntity = _mapper.Map(addFavoriteCommand.Location); - await _weatherContext.FavoriteLocations.AddAsync(locationEntity); - await _weatherContext.SaveChangesAsync(cancellationToken); - return Result.Ok(locationEntity.Id); + try + { + await _weatherContext.FavoriteLocations.AddAsync(locationEntity); + await _weatherContext.SaveChangesAsync(cancellationToken); + return Result.Ok(locationEntity.Id); + } + catch(DbUpdateException ex) + { + _logger.LogError(LogEvents.FavoriteWeathersStoreToDatabase, ex, "Can't add favorite locations into database."); + return Result.Fail(ex.Message); + } } } } diff --git a/src/Weather.Infrastructure/Database/Repositories/WeatherQueriesRepository.cs b/src/Weather.Infrastructure/Database/Repositories/WeatherQueriesRepository.cs index b9b9a5f..953e58e 100644 --- a/src/Weather.Infrastructure/Database/Repositories/WeatherQueriesRepository.cs +++ b/src/Weather.Infrastructure/Database/Repositories/WeatherQueriesRepository.cs @@ -8,20 +8,15 @@ namespace Weather.Infrastructure.Database.Repositories { - internal sealed class WeatherQueriesRepository : IWeatherQueriesRepository + internal sealed class WeatherQueriesRepository : RepositoryBase, IWeatherQueriesRepository { - private readonly IMapper _mapper; - private readonly WeatherContext _weatherContext; public WeatherQueriesRepository(WeatherContext weatherContext, IMapper mapper) - { - _weatherContext = Guard.Against.Null(weatherContext); - _mapper = Guard.Against.Null(mapper); - } - public async Task>> GetFavorites(CancellationToken cancellationToken) + : base(weatherContext, mapper) { } + + public async Task> GetFavorites(CancellationToken cancellationToken) { var facoriteLocationEntities = await _weatherContext.FavoriteLocations.ToListAsync(cancellationToken); - var resultData = _mapper.Map>(facoriteLocationEntities); - return Result.Ok((IEnumerable)resultData); + return _mapper.Map>(facoriteLocationEntities); } } } diff --git a/src/Weather.Infrastructure/Database/RepositoryBase.cs b/src/Weather.Infrastructure/Database/RepositoryBase.cs new file mode 100644 index 0000000..2759fac --- /dev/null +++ b/src/Weather.Infrastructure/Database/RepositoryBase.cs @@ -0,0 +1,17 @@ +using Ardalis.GuardClauses; +using AutoMapper; +using Weather.Infrastructure.Database.EFContext; + +namespace Weather.Infrastructure.Database +{ + internal abstract class RepositoryBase + { + protected readonly IMapper _mapper; + protected readonly WeatherContext _weatherContext; + protected RepositoryBase(WeatherContext weatherContext, IMapper mapper) + { + _weatherContext = Guard.Against.Null(weatherContext); + _mapper = Guard.Against.Null(mapper); + } + } +}