Skip to content

Commit

Permalink
Repositories refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
Gramli committed Mar 5, 2024
1 parent f984730 commit 9174622
Show file tree
Hide file tree
Showing 10 changed files with 89 additions and 65 deletions.
Expand Up @@ -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<CancellationToken>())).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<CancellationToken>()), 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<CancellationToken>()))
.ReturnsAsync(Result.Ok<IEnumerable<LocationDto>>(new List<LocationDto>()));
.ReturnsAsync(new List<LocationDto>());

//Act
var result = await _uut.HandleAsync(EmptyRequest.Instance, CancellationToken.None);
Expand All @@ -74,10 +55,10 @@ public async Task InvalidLocation()
var locationDto = new LocationDto { Latitude = 1, Longitude = 1 };

_weatherRepositoryMock.Setup(x => x.GetFavorites(It.IsAny<CancellationToken>()))
.ReturnsAsync(Result.Ok<IEnumerable<LocationDto>>(new List<LocationDto>
.ReturnsAsync(new List<LocationDto>
{
locationDto,
}));
});

_locationValidatorMock.Setup(x => x.IsValid(It.IsAny<LocationDto>())).Returns(false);

Expand All @@ -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<CancellationToken>()))
.ReturnsAsync(Result.Ok<IEnumerable<LocationDto>>(new List<LocationDto>
.ReturnsAsync(new List<LocationDto>
{
locationDto
}));
locationDto,
});

_locationValidatorMock.Setup(x=>x.IsValid(It.IsAny<LocationDto>())).Returns(true);

Expand All @@ -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<CancellationToken>()))
.ReturnsAsync(Result.Ok<IEnumerable<LocationDto>>(new List<LocationDto>
.ReturnsAsync(new List<LocationDto>
{
locationDto,
new LocationDto(),
}));
});

_locationValidatorMock.Setup(x => x.IsValid(It.IsAny<LocationDto>())).Returns(true);

Expand Down Expand Up @@ -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<CancellationToken>()))
.ReturnsAsync(Result.Ok<IEnumerable<LocationDto>>(new List<LocationDto>
.ReturnsAsync(new List<LocationDto>
{
locationDto,
}));
});

_locationValidatorMock.Setup(x => x.IsValid(It.IsAny<LocationDto>())).Returns(true);
_currentWeatherValidatorMock.Setup(x => x.IsValid(It.IsAny<CurrentWeatherDto>())).Returns(false);
Expand All @@ -196,10 +177,10 @@ public async Task Success()
var locationDto = new LocationDto { Latitude = 1, Longitude = 1 };

_weatherRepositoryMock.Setup(x => x.GetFavorites(It.IsAny<CancellationToken>()))
.ReturnsAsync(Result.Ok<IEnumerable<LocationDto>>(new List<LocationDto>
.ReturnsAsync(new List<LocationDto>
{
locationDto,
}));
});

_locationValidatorMock.Setup(x => x.IsValid(It.IsAny<LocationDto>())).Returns(true);
_currentWeatherValidatorMock.Setup(x=>x.IsValid(It.IsAny<CurrentWeatherDto>())).Returns(true);
Expand Down
Expand Up @@ -14,15 +14,15 @@ public class GetForecastWeatherHandlerTests
private readonly Mock<IValidator<GetForecastWeatherQuery>> _getForecastWeatherQueryValidatorMock;
private readonly Mock<IValidator<ForecastWeatherDto>> _forecastWeatherValidatorMock;
private readonly Mock<IWeatherService> _weatherServiceMock;
private readonly Mock<ILogger<IGetCurrentWeatherHandler>> _loggerMock;
private readonly Mock<ILogger<IGetForecastWeatherHandler>> _loggerMock;

private readonly IGetForecastWeatherHandler _uut;
public GetForecastWeatherHandlerTests()
{
_getForecastWeatherQueryValidatorMock = new Mock<IValidator<GetForecastWeatherQuery>>();
_forecastWeatherValidatorMock = new Mock<IValidator<ForecastWeatherDto>>();
_weatherServiceMock = new Mock<IWeatherService>();
_loggerMock = new Mock<ILogger<IGetCurrentWeatherHandler>>();
_loggerMock = new Mock<ILogger<IGetForecastWeatherHandler>>();

_uut = new GetForecastWeatherHandler(_getForecastWeatherQueryValidatorMock.Object, _weatherServiceMock.Object, _forecastWeatherValidatorMock.Object, _loggerMock.Object);
}
Expand Down
@@ -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
{
Expand All @@ -12,6 +15,7 @@ public class WeatherCommandsRepositoryTests
private readonly Mock<DbSet<FavoriteLocationEntity>> _favoriteLocationEntityDbSetMock;
private readonly Mock<IMapper> _mapperMock;
private readonly Mock<TestWeatherContext> _weatherDbContextMock;
private readonly Mock<ILogger<IWeatherCommandsRepository>> _loggerMock;

private readonly IWeatherCommandsRepository _uut;

Expand All @@ -22,8 +26,9 @@ public WeatherCommandsRepositoryTests()
_weatherDbContextMock.Setup(x => x.FavoriteLocations).Returns(_favoriteLocationEntityDbSetMock.Object);

_mapperMock = new Mock<IMapper>();
_loggerMock = new Mock<ILogger<IWeatherCommandsRepository>>();

_uut = new WeatherCommandsRepository(_weatherDbContextMock.Object, _mapperMock.Object);
_uut = new WeatherCommandsRepository(_weatherDbContextMock.Object, _mapperMock.Object, _loggerMock.Object);
}

[Fact]
Expand All @@ -45,6 +50,27 @@ public async Task AddFavoriteLocation_Success()
_favoriteLocationEntityDbSetMock.Verify(x => x.AddAsync(It.IsAny<FavoriteLocationEntity>(), It.IsAny<CancellationToken>()), 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<FavoriteLocationEntity>(It.IsAny<LocationDto>())).Returns(favoriteLocationEntity);
_favoriteLocationEntityDbSetMock.Setup(x => x.AddAsync(It.IsAny<FavoriteLocationEntity>(), It.IsAny<CancellationToken>())).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<FavoriteLocationEntity>(It.IsAny<LocationDto>()), Times.Once);
_weatherDbContextMock.Verify(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Never);
_favoriteLocationEntityDbSetMock.Verify(x => x.AddAsync(It.IsAny<FavoriteLocationEntity>(), It.IsAny<CancellationToken>()), Times.Once);
}

[Fact]
public async Task AddFavoriteLocation_Throw()
{
Expand Down
Expand Up @@ -27,6 +27,7 @@
<ProjectReference Include="..\..\..\Weather.Core\Weather.Core.csproj" />
<ProjectReference Include="..\..\..\Weather.Infrastructure\Weather.Infrastructure.csproj" />
<ProjectReference Include="..\..\..\Wheaterbit.Client\Wheaterbit.Client.csproj" />
<ProjectReference Include="..\Weather.UnitTests.Common\Weather.UnitTests.Common.csproj" />
</ItemGroup>

</Project>
2 changes: 1 addition & 1 deletion src/Weather.Core/Abstractions/IWeatherQueriesRepository.cs
Expand Up @@ -5,6 +5,6 @@ namespace Weather.Core.Abstractions
{
public interface IWeatherQueriesRepository
{
Task<Result<IEnumerable<LocationDto>>> GetFavorites(CancellationToken cancellationToken);
Task<IEnumerable<LocationDto>> GetFavorites(CancellationToken cancellationToken);
}
}
10 changes: 2 additions & 8 deletions src/Weather.Core/Queries/GetFavoritesHandler.cs
Expand Up @@ -37,18 +37,12 @@ public async Task<HttpDataResponse<FavoritesWeatherDto>> HandleAsync(EmptyReques
{
var favoriteLocationsResult = await _weatherQueriesRepository.GetFavorites(cancellationToken);

if(favoriteLocationsResult.IsFailed)
{
_logger.LogError(LogEvents.FavoriteWeathersGetFromDatabase, favoriteLocationsResult.Errors.JoinToMessage());
return HttpDataResponses.AsInternalServerError<FavoritesWeatherDto>(ErrorMessages.ExternalApiError);
}

if(!favoriteLocationsResult.Value.HasAny())
if(!favoriteLocationsResult.HasAny())
{
return HttpDataResponses.AsNoContent<FavoritesWeatherDto>();
}

return await GetFavoritesAsync(favoriteLocationsResult.Value, cancellationToken);
return await GetFavoritesAsync(favoriteLocationsResult, cancellationToken);

}

Expand Down
4 changes: 2 additions & 2 deletions src/Weather.Core/Queries/GetForecastWeatherHandler.cs
Expand Up @@ -17,12 +17,12 @@ internal sealed class GetForecastWeatherHandler : IGetForecastWeatherHandler
private readonly IValidator<GetForecastWeatherQuery> _getForecastWeatherQueryValidator;
private readonly IValidator<ForecastWeatherDto> _forecastWeatherValidator;
private readonly IWeatherService _weatherService;
private readonly ILogger<IGetCurrentWeatherHandler> _logger;
private readonly ILogger<IGetForecastWeatherHandler> _logger;
public GetForecastWeatherHandler(
IValidator<GetForecastWeatherQuery> getForecastWeatherQueryValidator,
IWeatherService weatherService,
IValidator<ForecastWeatherDto> forecastWeatherValidator,
ILogger<IGetCurrentWeatherHandler> logger)
ILogger<IGetForecastWeatherHandler> logger)
{
_getForecastWeatherQueryValidator = Guard.Against.Null(getForecastWeatherQueryValidator);
_weatherService = Guard.Against.Null(weatherService);
Expand Down
@@ -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<IWeatherCommandsRepository> _logger;
public WeatherCommandsRepository(WeatherContext weatherContext, IMapper mapper, ILogger<IWeatherCommandsRepository> logger)
: base(weatherContext, mapper)
{
_logger = Guard.Against.Null(logger);
}

public async Task<Result<int>> AddFavoriteLocation(AddFavoriteCommand addFavoriteCommand, CancellationToken cancellationToken)
{
var locationEntity = _mapper.Map<FavoriteLocationEntity>(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);
}
}
}
}
Expand Up @@ -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<Result<IEnumerable<LocationDto>>> GetFavorites(CancellationToken cancellationToken)
: base(weatherContext, mapper) { }

public async Task<IEnumerable<LocationDto>> GetFavorites(CancellationToken cancellationToken)
{
var facoriteLocationEntities = await _weatherContext.FavoriteLocations.ToListAsync(cancellationToken);
var resultData = _mapper.Map<List<LocationDto>>(facoriteLocationEntities);
return Result.Ok((IEnumerable<LocationDto>)resultData);
return _mapper.Map<List<LocationDto>>(facoriteLocationEntities);
}
}
}
17 changes: 17 additions & 0 deletions 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);
}
}
}

0 comments on commit 9174622

Please sign in to comment.