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.
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
}
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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.