From b864be9265545fa78ff8fb90a4824dfa7618e676 Mon Sep 17 00:00:00 2001 From: Steve Smith Date: Mon, 30 Oct 2017 11:53:29 -0700 Subject: [PATCH] Adding guards and more tests (#68) * Adding single entity by spec method to repository * Adding guards and more unit tests --- src/ApplicationCore/ApplicationCore.csproj | 1 + .../Exceptions/BasketNotFoundException.cs | 23 +++++++++++ .../Exceptions/GuardExtensions.cs | 14 +++++++ src/ApplicationCore/Interfaces/IRepository.cs | 2 +- src/ApplicationCore/Services/BasketService.cs | 6 +++ src/ApplicationCore/Services/OrderService.cs | 2 + src/Infrastructure/Data/EfRepository.cs | 6 +++ src/Infrastructure/Logging/LoggerAdapter.cs | 1 + .../BasketServiceTests/SetQuantities.cs | 40 +++++++++++++++++++ .../BasketServiceTests/TransferBasket.cs | 25 ++++++++++++ 10 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 src/ApplicationCore/Exceptions/BasketNotFoundException.cs create mode 100644 src/ApplicationCore/Exceptions/GuardExtensions.cs create mode 100644 tests/UnitTests/ApplicationCore/Services/BasketServiceTests/SetQuantities.cs create mode 100644 tests/UnitTests/ApplicationCore/Services/BasketServiceTests/TransferBasket.cs diff --git a/src/ApplicationCore/ApplicationCore.csproj b/src/ApplicationCore/ApplicationCore.csproj index e0c49c1de..f8d7d9a34 100644 --- a/src/ApplicationCore/ApplicationCore.csproj +++ b/src/ApplicationCore/ApplicationCore.csproj @@ -5,6 +5,7 @@ + diff --git a/src/ApplicationCore/Exceptions/BasketNotFoundException.cs b/src/ApplicationCore/Exceptions/BasketNotFoundException.cs new file mode 100644 index 000000000..7a3f804ea --- /dev/null +++ b/src/ApplicationCore/Exceptions/BasketNotFoundException.cs @@ -0,0 +1,23 @@ +using System; + +namespace ApplicationCore.Exceptions +{ + public class BasketNotFoundException : Exception + { + public BasketNotFoundException(int basketId) : base($"No basket found with id {basketId}") + { + } + + protected BasketNotFoundException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) + { + } + + public BasketNotFoundException(string message) : base(message) + { + } + + public BasketNotFoundException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/src/ApplicationCore/Exceptions/GuardExtensions.cs b/src/ApplicationCore/Exceptions/GuardExtensions.cs new file mode 100644 index 000000000..0c840a036 --- /dev/null +++ b/src/ApplicationCore/Exceptions/GuardExtensions.cs @@ -0,0 +1,14 @@ +using ApplicationCore.Exceptions; +using Microsoft.eShopWeb.ApplicationCore.Entities; + +namespace Ardalis.GuardClauses +{ + public static class FooGuard + { + public static void NullBasket(this IGuardClause guardClause, int basketId, Basket basket) + { + if (basket == null) + throw new BasketNotFoundException(basketId); + } + } +} \ No newline at end of file diff --git a/src/ApplicationCore/Interfaces/IRepository.cs b/src/ApplicationCore/Interfaces/IRepository.cs index a448152fa..9099188f4 100644 --- a/src/ApplicationCore/Interfaces/IRepository.cs +++ b/src/ApplicationCore/Interfaces/IRepository.cs @@ -1,12 +1,12 @@ using Microsoft.eShopWeb.ApplicationCore.Entities; using System.Collections.Generic; -using System.Threading.Tasks; namespace ApplicationCore.Interfaces { public interface IRepository where T : BaseEntity { T GetById(int id); + T GetSingleBySpec(ISpecification spec); IEnumerable ListAll(); IEnumerable List(ISpecification spec); T Add(T entity); diff --git a/src/ApplicationCore/Services/BasketService.cs b/src/ApplicationCore/Services/BasketService.cs index 209886dbc..f189812a3 100644 --- a/src/ApplicationCore/Services/BasketService.cs +++ b/src/ApplicationCore/Services/BasketService.cs @@ -4,6 +4,7 @@ using ApplicationCore.Specifications; using Microsoft.eShopWeb.ApplicationCore.Entities; using System.Linq; +using Ardalis.GuardClauses; namespace ApplicationCore.Services { @@ -43,6 +44,7 @@ public async Task DeleteBasketAsync(int basketId) public async Task GetBasketItemCountAsync(string userName) { + Guard.Against.NullOrEmpty(userName, nameof(userName)); var basketSpec = new BasketWithItemsSpecification(userName); var basket = (await _basketRepository.ListAsync(basketSpec)).FirstOrDefault(); if (basket == null) @@ -57,7 +59,9 @@ public async Task GetBasketItemCountAsync(string userName) public async Task SetQuantities(int basketId, Dictionary quantities) { + Guard.Against.Null(quantities, nameof(quantities)); var basket = await _basketRepository.GetByIdAsync(basketId); + Guard.Against.NullBasket(basketId, basket); foreach (var item in basket.Items) { if (quantities.TryGetValue(item.Id.ToString(), out var quantity)) @@ -71,6 +75,8 @@ public async Task SetQuantities(int basketId, Dictionary quantities public async Task TransferBasketAsync(string anonymousId, string userName) { + Guard.Against.NullOrEmpty(anonymousId, nameof(anonymousId)); + Guard.Against.NullOrEmpty(userName, nameof(userName)); var basketSpec = new BasketWithItemsSpecification(anonymousId); var basket = (await _basketRepository.ListAsync(basketSpec)).FirstOrDefault(); if (basket == null) return; diff --git a/src/ApplicationCore/Services/OrderService.cs b/src/ApplicationCore/Services/OrderService.cs index e666182cf..162e289ba 100644 --- a/src/ApplicationCore/Services/OrderService.cs +++ b/src/ApplicationCore/Services/OrderService.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Microsoft.eShopWeb.ApplicationCore.Entities; using System.Collections.Generic; +using Ardalis.GuardClauses; namespace ApplicationCore.Services { @@ -24,6 +25,7 @@ public OrderService(IAsyncRepository basketRepository, public async Task CreateOrderAsync(int basketId, Address shippingAddress) { var basket = await _basketRepository.GetByIdAsync(basketId); + Guard.Against.NullBasket(basketId, basket); var items = new List(); foreach (var item in basket.Items) { diff --git a/src/Infrastructure/Data/EfRepository.cs b/src/Infrastructure/Data/EfRepository.cs index b0cecfa10..47fe8a226 100644 --- a/src/Infrastructure/Data/EfRepository.cs +++ b/src/Infrastructure/Data/EfRepository.cs @@ -26,6 +26,12 @@ public virtual T GetById(int id) return _dbContext.Set().Find(id); } + public T GetSingleBySpec(ISpecification spec) + { + return List(spec).FirstOrDefault(); + } + + public virtual async Task GetByIdAsync(int id) { return await _dbContext.Set().FindAsync(id); diff --git a/src/Infrastructure/Logging/LoggerAdapter.cs b/src/Infrastructure/Logging/LoggerAdapter.cs index 93b544769..240c5b167 100644 --- a/src/Infrastructure/Logging/LoggerAdapter.cs +++ b/src/Infrastructure/Logging/LoggerAdapter.cs @@ -15,6 +15,7 @@ public void LogWarning(string message, params object[] args) { _logger.LogWarning(message, args); } + public void LogInformation(string message, params object[] args) { _logger.LogInformation(message, args); diff --git a/tests/UnitTests/ApplicationCore/Services/BasketServiceTests/SetQuantities.cs b/tests/UnitTests/ApplicationCore/Services/BasketServiceTests/SetQuantities.cs new file mode 100644 index 000000000..8e98009e9 --- /dev/null +++ b/tests/UnitTests/ApplicationCore/Services/BasketServiceTests/SetQuantities.cs @@ -0,0 +1,40 @@ +using ApplicationCore.Exceptions; +using ApplicationCore.Interfaces; +using ApplicationCore.Services; +using Microsoft.eShopWeb.ApplicationCore.Entities; +using Moq; +using System; +using Xunit; + +namespace UnitTests.ApplicationCore.Services.BasketServiceTests +{ + public class SetQuantities + { + private int _invalidId = -1; + private Mock> _mockBasketRepo; + + public SetQuantities() + { + _mockBasketRepo = new Mock>(); + } + + [Fact] + public async void ThrowsGivenInvalidBasketId() + { + var basketService = new BasketService(_mockBasketRepo.Object, null, null, null); + + await Assert.ThrowsAsync(async () => + await basketService.SetQuantities(_invalidId, new System.Collections.Generic.Dictionary())); + } + + [Fact] + public async void ThrowsGivenNullQuantities() + { + var basketService = new BasketService(null, null, null, null); + + await Assert.ThrowsAsync(async () => + await basketService.SetQuantities(123, null)); + } + + } +} diff --git a/tests/UnitTests/ApplicationCore/Services/BasketServiceTests/TransferBasket.cs b/tests/UnitTests/ApplicationCore/Services/BasketServiceTests/TransferBasket.cs new file mode 100644 index 000000000..f284f56cf --- /dev/null +++ b/tests/UnitTests/ApplicationCore/Services/BasketServiceTests/TransferBasket.cs @@ -0,0 +1,25 @@ +using ApplicationCore.Services; +using System; +using Xunit; + +namespace UnitTests.ApplicationCore.Services.BasketServiceTests +{ + public class TransferBasket + { + [Fact] + public async void ThrowsGivenNullAnonymousId() + { + var basketService = new BasketService(null, null, null, null); + + await Assert.ThrowsAsync(async () => await basketService.TransferBasketAsync(null, "steve")); + } + + [Fact] + public async void ThrowsGivenNullUserId() + { + var basketService = new BasketService(null, null, null, null); + + await Assert.ThrowsAsync(async () => await basketService.TransferBasketAsync("abcdefg", null)); + } + } +}