Browse Source

Merge pull request #223 from EasyAbp/jadyn/fix-flash-sale-uts

Fix flash-sales UTs
pull/224/head
Super 4 years ago
committed by GitHub
parent
commit
7dff5f45ba
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 42
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSaleOrderCanceledEventHandler.cs
  2. 31
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSaleOrderCreationResultEventHandler.cs
  3. 62
      plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSaleOrderCanceledEventHandlerTests.cs
  4. 7
      plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanAppServiceTests.cs
  5. 29
      plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Products/Products/FakeProductCache.cs

42
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSaleOrderCanceledEventHandler.cs

@ -1,8 +1,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using EasyAbp.Eshop.Products.Products;
using EasyAbp.EShop.Orders.Orders; using EasyAbp.EShop.Orders.Orders;
using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults;
using EasyAbp.EShop.Products.Products; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Volo.Abp; using Volo.Abp;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
@ -20,30 +19,14 @@ public class FlashSaleOrderCanceledEventHandler : IDistributedEventHandler<Order
protected IAbpApplication AbpApplication { get; } protected IAbpApplication AbpApplication { get; }
protected IFlashSaleInventoryManager FlashSaleInventoryManager { get; }
protected IProductCache ProductCache { get; }
protected IFlashSaleCurrentResultCache FlashSaleCurrentResultCache { get; }
protected ILogger<FlashSaleOrderCanceledEventHandler> Logger { get; }
public FlashSaleOrderCanceledEventHandler( public FlashSaleOrderCanceledEventHandler(
IFlashSaleResultRepository flashSaleResultRepository, IFlashSaleResultRepository flashSaleResultRepository,
IUnitOfWorkManager unitOfWorkManager, IUnitOfWorkManager unitOfWorkManager,
IAbpApplication abpApplication, IAbpApplication abpApplication)
IFlashSaleInventoryManager flashSaleInventoryManager,
IProductCache productCache,
IFlashSaleCurrentResultCache flashSaleCurrentResultCache,
ILogger<FlashSaleOrderCanceledEventHandler> logger)
{ {
FlashSaleResultRepository = flashSaleResultRepository; FlashSaleResultRepository = flashSaleResultRepository;
UnitOfWorkManager = unitOfWorkManager; UnitOfWorkManager = unitOfWorkManager;
AbpApplication = abpApplication; AbpApplication = abpApplication;
FlashSaleInventoryManager = flashSaleInventoryManager;
ProductCache = productCache;
FlashSaleCurrentResultCache = flashSaleCurrentResultCache;
Logger = logger;
} }
[UnitOfWork(true)] [UnitOfWork(true)]
@ -62,25 +45,10 @@ public class FlashSaleOrderCanceledEventHandler : IDistributedEventHandler<Order
UnitOfWorkManager.Current.OnCompleted(async () => UnitOfWorkManager.Current.OnCompleted(async () =>
{ {
if (eventData.Order.OrderLines.Count == 0) using var scope = AbpApplication.ServiceProvider.CreateScope();
{ var flashSaleCurrentResultCache = scope.ServiceProvider.GetRequiredService<IFlashSaleCurrentResultCache>();
Logger.LogWarning("OrderCanceled order {orderId} orderLines is empty.", eventData.Order.Id);
return;
}
var productId = eventData.Order.OrderLines[0].ProductId;
var productSkuId = eventData.Order.OrderLines[0].ProductSkuId;
var product = await ProductCache.GetAsync(productId);
// try to roll back the inventory.
if (!await FlashSaleInventoryManager.TryRollBackInventoryAsync(
eventData.TenantId, product.InventoryProviderName, eventData.Order.StoreId,
productId, productSkuId))
{
Logger.LogWarning("Failed to roll back the flash sale inventory.");
return; // avoid to remove cache if the rollback failed.
}
// remove the cache so the user can try to order again. // remove the cache so the user can try to order again.
await FlashSaleCurrentResultCache.RemoveAsync(flashSaleResult.PlanId, flashSaleResult.UserId); await flashSaleCurrentResultCache.RemoveAsync(flashSaleResult.PlanId, flashSaleResult.UserId);
}); });
} }
} }

31
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSaleOrderCreationResultEventHandler.cs

@ -16,28 +16,19 @@ public class FlashSaleOrderCreationResultEventHandler : IDistributedEventHandler
ITransientDependency ITransientDependency
{ {
protected ILogger<FlashSaleOrderCreationResultEventHandler> Logger { get; } protected ILogger<FlashSaleOrderCreationResultEventHandler> Logger { get; }
protected IFlashSaleInventoryManager FlashSaleInventoryManager { get; }
protected IUnitOfWorkManager UnitOfWorkManager { get; } protected IUnitOfWorkManager UnitOfWorkManager { get; }
protected IObjectMapper ObjectMapper { get; }
protected IAbpApplication AbpApplication { get; } protected IAbpApplication AbpApplication { get; }
protected IFlashSaleCurrentResultCache FlashSaleCurrentResultCache { get; }
protected IFlashSaleResultRepository FlashSaleResultRepository { get; } protected IFlashSaleResultRepository FlashSaleResultRepository { get; }
public FlashSaleOrderCreationResultEventHandler( public FlashSaleOrderCreationResultEventHandler(
ILogger<FlashSaleOrderCreationResultEventHandler> logger, ILogger<FlashSaleOrderCreationResultEventHandler> logger,
IFlashSaleInventoryManager flashSaleInventoryManager,
IUnitOfWorkManager unitOfWorkManager, IUnitOfWorkManager unitOfWorkManager,
IObjectMapper objectMapper,
IAbpApplication abpApplication, IAbpApplication abpApplication,
IFlashSaleCurrentResultCache flashSaleCurrentResultCache,
IFlashSaleResultRepository flashSaleResultRepository) IFlashSaleResultRepository flashSaleResultRepository)
{ {
Logger = logger; Logger = logger;
FlashSaleInventoryManager = flashSaleInventoryManager;
UnitOfWorkManager = unitOfWorkManager; UnitOfWorkManager = unitOfWorkManager;
ObjectMapper = objectMapper;
AbpApplication = abpApplication; AbpApplication = abpApplication;
FlashSaleCurrentResultCache = flashSaleCurrentResultCache;
FlashSaleResultRepository = flashSaleResultRepository; FlashSaleResultRepository = flashSaleResultRepository;
} }
@ -61,14 +52,20 @@ public class FlashSaleOrderCreationResultEventHandler : IDistributedEventHandler
UnitOfWorkManager.Current.OnCompleted(async () => UnitOfWorkManager.Current.OnCompleted(async () =>
{ {
using var scope = AbpApplication.ServiceProvider.CreateScope();
var flashSaleInventoryManager = scope.ServiceProvider.GetRequiredService<IFlashSaleInventoryManager>();
var flashSaleCurrentResultCache = scope.ServiceProvider.GetRequiredService<IFlashSaleCurrentResultCache>();
var objectMapper = scope.ServiceProvider.GetRequiredService<IObjectMapper>();
if (eventData.Success) if (eventData.Success)
{ {
await ResetFlashSaleCurrentResultCacheAsync(flashSaleResult); await ResetFlashSaleCurrentResultCacheAsync(flashSaleResult, objectMapper, flashSaleCurrentResultCache);
} }
else else
{ {
// try to roll back the inventory. // try to roll back the inventory.
if (!await FlashSaleInventoryManager.TryRollBackInventoryAsync( if (!await flashSaleInventoryManager.TryRollBackInventoryAsync(
eventData.TenantId, eventData.ProductInventoryProviderName, eventData.StoreId, eventData.TenantId, eventData.ProductInventoryProviderName, eventData.StoreId,
eventData.ProductId, eventData.ProductSkuId)) eventData.ProductId, eventData.ProductSkuId))
{ {
@ -79,23 +76,19 @@ public class FlashSaleOrderCreationResultEventHandler : IDistributedEventHandler
// remove the cache so the user can try to order again. // remove the cache so the user can try to order again.
if (eventData.AllowToTryAgain) if (eventData.AllowToTryAgain)
{ {
await FlashSaleCurrentResultCache.RemoveAsync(flashSaleResult.PlanId, flashSaleResult.UserId); await flashSaleCurrentResultCache.RemoveAsync(flashSaleResult.PlanId, flashSaleResult.UserId);
} }
else else
{ {
await ResetFlashSaleCurrentResultCacheAsync(flashSaleResult); await ResetFlashSaleCurrentResultCacheAsync(flashSaleResult, objectMapper, flashSaleCurrentResultCache);
} }
} }
}); });
} }
protected virtual async Task ResetFlashSaleCurrentResultCacheAsync(FlashSaleResult flashSaleResult) protected virtual async Task ResetFlashSaleCurrentResultCacheAsync(FlashSaleResult flashSaleResult,
IObjectMapper objectMapper, IFlashSaleCurrentResultCache flashSaleCurrentResultCache)
{ {
using var scope = AbpApplication.ServiceProvider.CreateScope();
var objectMapper = scope.ServiceProvider.GetRequiredService<IObjectMapper>();
var flashSaleCurrentResultCache = scope.ServiceProvider.GetRequiredService<IFlashSaleCurrentResultCache>();
await flashSaleCurrentResultCache.SetAsync(flashSaleResult.PlanId, flashSaleResult.UserId, await flashSaleCurrentResultCache.SetAsync(flashSaleResult.PlanId, flashSaleResult.UserId,
new FlashSaleCurrentResultCacheItem new FlashSaleCurrentResultCacheItem
{ {

62
plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSaleOrderCanceledEventHandlerTests.cs

@ -21,15 +21,12 @@ public class FlashSaleOrderCanceledEventHandlerTests : FlashSalesApplicationTest
protected IFlashSaleCurrentResultCache FlashSaleCurrentResultCache { get; } protected IFlashSaleCurrentResultCache FlashSaleCurrentResultCache { get; }
protected IFlashSaleInventoryManager FlashSaleInventoryManager { get; }
private ProductDto Product1 { get; set; } private ProductDto Product1 { get; set; }
public FlashSaleOrderCanceledEventHandlerTests() public FlashSaleOrderCanceledEventHandlerTests()
{ {
ResultMarkAsFailedOrderCanceledEventHandler = GetRequiredService<FlashSaleOrderCanceledEventHandler>(); ResultMarkAsFailedOrderCanceledEventHandler = GetRequiredService<FlashSaleOrderCanceledEventHandler>();
FlashSaleCurrentResultCache = GetRequiredService<IFlashSaleCurrentResultCache>(); FlashSaleCurrentResultCache = GetRequiredService<IFlashSaleCurrentResultCache>();
FlashSaleInventoryManager = GetRequiredService<IFlashSaleInventoryManager>();
} }
protected override void AfterAddApplication(IServiceCollection services) protected override void AfterAddApplication(IServiceCollection services)
@ -40,9 +37,6 @@ public class FlashSaleOrderCanceledEventHandlerTests : FlashSalesApplicationTest
productAppService.GetAsync(FlashSalesTestData.Product1Id).Returns(Task.FromResult(Product1)); productAppService.GetAsync(FlashSalesTestData.Product1Id).Returns(Task.FromResult(Product1));
services.Replace(ServiceDescriptor.Singleton(productAppService)); services.Replace(ServiceDescriptor.Singleton(productAppService));
var flashSaleInventoryManager = Substitute.For<IFlashSaleInventoryManager>();
services.Replace(ServiceDescriptor.Singleton(flashSaleInventoryManager));
var flashSaleCurrentResultCache = Substitute.For<IFlashSaleCurrentResultCache>(); var flashSaleCurrentResultCache = Substitute.For<IFlashSaleCurrentResultCache>();
services.Replace(ServiceDescriptor.Singleton(flashSaleCurrentResultCache)); services.Replace(ServiceDescriptor.Singleton(flashSaleCurrentResultCache));
} }
@ -65,11 +59,6 @@ public class FlashSaleOrderCanceledEventHandlerTests : FlashSalesApplicationTest
} }
}); });
flashSaleResult = await MarkAsSuccessfulAsync(flashSaleResult.Id, orderCanceledEto.Order.Id); flashSaleResult = await MarkAsSuccessfulAsync(flashSaleResult.Id, orderCanceledEto.Order.Id);
FlashSaleInventoryManager
.TryRollBackInventoryAsync(flashSaleResult.TenantId,
Product1.InventoryProviderName, flashSaleResult.StoreId,
FlashSalesTestData.Product1Id, FlashSalesTestData.ProductSku1Id)
.Returns(Task.FromResult(true));
await ResultMarkAsFailedOrderCanceledEventHandler.HandleEventAsync(orderCanceledEto); await ResultMarkAsFailedOrderCanceledEventHandler.HandleEventAsync(orderCanceledEto);
@ -77,52 +66,10 @@ public class FlashSaleOrderCanceledEventHandlerTests : FlashSalesApplicationTest
existFlashSaleResult.Status.ShouldBe(FlashSaleResultStatus.Failed); existFlashSaleResult.Status.ShouldBe(FlashSaleResultStatus.Failed);
existFlashSaleResult.Reason.ShouldBe(FlashSaleResultFailedReason.OrderCanceled); existFlashSaleResult.Reason.ShouldBe(FlashSaleResultFailedReason.OrderCanceled);
await FlashSaleInventoryManager.Received()
.TryRollBackInventoryAsync(flashSaleResult.TenantId,
Product1.InventoryProviderName, flashSaleResult.StoreId,
FlashSalesTestData.Product1Id, FlashSalesTestData.ProductSku1Id);
await FlashSaleCurrentResultCache.Received() await FlashSaleCurrentResultCache.Received()
.RemoveAsync(flashSaleResult.PlanId, flashSaleResult.UserId); .RemoveAsync(flashSaleResult.PlanId, flashSaleResult.UserId);
} }
[Fact]
public async Task HandleEventAsync_Should_Not_RemoveResultCache_When_TryRollBackInventory_Failed()
{
var flashSaleResult = await CreateFlashSaleResultAsync();
var orderCanceledEto = new OrderCanceledEto(new OrderEto()
{
Id = Guid.NewGuid(),
StoreId = flashSaleResult.StoreId,
OrderLines = new List<OrderLineEto>()
{
new OrderLineEto()
{
ProductId = FlashSalesTestData.Product1Id,
ProductSkuId = FlashSalesTestData.ProductSku1Id
}
}
});
flashSaleResult = await MarkAsSuccessfulAsync(flashSaleResult.Id, orderCanceledEto.Order.Id);
FlashSaleInventoryManager
.TryRollBackInventoryAsync(flashSaleResult.TenantId,
Product1.InventoryProviderName, flashSaleResult.StoreId,
FlashSalesTestData.Product1Id, FlashSalesTestData.ProductSku1Id)
.Returns(Task.FromResult(false));
await ResultMarkAsFailedOrderCanceledEventHandler.HandleEventAsync(orderCanceledEto);
var existFlashSaleResult = await FlashSaleResultRepository.GetAsync(flashSaleResult.Id);
existFlashSaleResult.Status.ShouldBe(FlashSaleResultStatus.Failed);
existFlashSaleResult.Reason.ShouldBe(FlashSaleResultFailedReason.OrderCanceled);
await FlashSaleInventoryManager.Received()
.TryRollBackInventoryAsync(flashSaleResult.TenantId,
Product1.InventoryProviderName, flashSaleResult.StoreId,
FlashSalesTestData.Product1Id, FlashSalesTestData.ProductSku1Id);
await FlashSaleCurrentResultCache.DidNotReceive()
.RemoveAsync(flashSaleResult.PlanId, flashSaleResult.UserId);
}
[Fact] [Fact]
public async Task HandleEventAsync_Should_Skip_Handling_If_Result_Does_Not_Exist() public async Task HandleEventAsync_Should_Skip_Handling_If_Result_Does_Not_Exist()
{ {
@ -142,11 +89,6 @@ public class FlashSaleOrderCanceledEventHandlerTests : FlashSalesApplicationTest
}); });
flashSaleResult = await MarkAsSuccessfulAsync(flashSaleResult.Id, orderCanceledEto.Order.Id); flashSaleResult = await MarkAsSuccessfulAsync(flashSaleResult.Id, orderCanceledEto.Order.Id);
flashSaleResult = await MarkAsFailedAsync(flashSaleResult.Id, "UT"); flashSaleResult = await MarkAsFailedAsync(flashSaleResult.Id, "UT");
FlashSaleInventoryManager
.TryRollBackInventoryAsync(flashSaleResult.TenantId,
Product1.InventoryProviderName, flashSaleResult.StoreId,
FlashSalesTestData.Product1Id, FlashSalesTestData.ProductSku1Id)
.Returns(Task.FromResult(true));
await ResultMarkAsFailedOrderCanceledEventHandler.HandleEventAsync(orderCanceledEto); await ResultMarkAsFailedOrderCanceledEventHandler.HandleEventAsync(orderCanceledEto);
@ -154,10 +96,6 @@ public class FlashSaleOrderCanceledEventHandlerTests : FlashSalesApplicationTest
existFlashSaleResult.Status.ShouldBe(flashSaleResult.Status); existFlashSaleResult.Status.ShouldBe(flashSaleResult.Status);
existFlashSaleResult.Reason.ShouldBe(flashSaleResult.Reason); existFlashSaleResult.Reason.ShouldBe(flashSaleResult.Reason);
await FlashSaleInventoryManager.DidNotReceive()
.TryRollBackInventoryAsync(flashSaleResult.TenantId,
Product1.InventoryProviderName, flashSaleResult.StoreId,
FlashSalesTestData.Product1Id, FlashSalesTestData.ProductSku1Id);
await FlashSaleCurrentResultCache.DidNotReceive() await FlashSaleCurrentResultCache.DidNotReceive()
.RemoveAsync(flashSaleResult.PlanId, flashSaleResult.UserId); .RemoveAsync(flashSaleResult.PlanId, flashSaleResult.UserId);
} }

7
plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanAppServiceTests.cs

@ -30,15 +30,12 @@ public class FlashSalePlanAppServiceTests : FlashSalesApplicationTestBase
protected IDistributedEventBus DistributedEventBus { get; } protected IDistributedEventBus DistributedEventBus { get; }
protected IDistributedCache<ProductCacheItem, Guid> ProductDistributedCache { get; }
private ProductDto Product1 { get; set; } private ProductDto Product1 { get; set; }
public FlashSalePlanAppServiceTests() public FlashSalePlanAppServiceTests()
{ {
AppService = GetRequiredService<FlashSalePlanAppService>(); AppService = GetRequiredService<FlashSalePlanAppService>();
DistributedEventBus = GetRequiredService<IDistributedEventBus>(); DistributedEventBus = GetRequiredService<IDistributedEventBus>();
ProductDistributedCache = GetRequiredService<IDistributedCache<ProductCacheItem, Guid>>();
} }
protected override void AfterAddApplication(IServiceCollection services) protected override void AfterAddApplication(IServiceCollection services)
@ -59,6 +56,8 @@ public class FlashSalePlanAppServiceTests : FlashSalesApplicationTestBase
info.AddOrUpdateProperty<string>("key1"); info.AddOrUpdateProperty<string>("key1");
}); });
services.Replace(ServiceDescriptor.Transient<IProductCache, FakeProductCache>());
base.AfterAddApplication(services); base.AfterAddApplication(services);
} }
@ -338,13 +337,11 @@ public class FlashSalePlanAppServiceTests : FlashSalesApplicationTestBase
Product1.IsPublished = true; Product1.IsPublished = true;
Product1.InventoryStrategy = InventoryStrategy.ReduceAfterPlacing; Product1.InventoryStrategy = InventoryStrategy.ReduceAfterPlacing;
await ProductDistributedCache.RemoveAsync(Product1.Id);
await AppService.PreOrderAsync(plan.Id) await AppService.PreOrderAsync(plan.Id)
.ShouldThrowAsync<UnexpectedInventoryStrategyException>(); .ShouldThrowAsync<UnexpectedInventoryStrategyException>();
Product1.InventoryStrategy = InventoryStrategy.FlashSales; Product1.InventoryStrategy = InventoryStrategy.FlashSales;
await ProductDistributedCache.RemoveAsync(Product1.Id);
var plan2 = await CreateFlashSalePlanAsync(isPublished: false); var plan2 = await CreateFlashSalePlanAsync(isPublished: false);
await AppService.PreOrderAsync(plan2.Id) await AppService.PreOrderAsync(plan2.Id)

29
plugins/FlashSales/test/EasyAbp.EShop.Plugins.FlashSales.Application.Tests/EasyAbp/EShop/Products/Products/FakeProductCache.cs

@ -0,0 +1,29 @@
using System;
using System.Threading.Tasks;
using EasyAbp.EShop.Products.Products.Dtos;
using Volo.Abp.ObjectMapping;
namespace EasyAbp.EShop.Products.Products;
public class FakeProductCache : IProductCache
{
protected IProductAppService ProductAppService { get; }
protected IObjectMapper ObjectMapper { get; }
public FakeProductCache(IProductAppService productAppService, IObjectMapper objectMapper)
{
ProductAppService = productAppService;
ObjectMapper = objectMapper;
}
public async Task<ProductCacheItem> GetAsync(Guid productId)
{
var dto = await ProductAppService.GetAsync(productId);
return ObjectMapper.Map<ProductDto, ProductCacheItem>(dto);
}
public Task RemoveAsync(Guid productId)
{
return Task.CompletedTask;
}
}
Loading…
Cancel
Save