Browse Source

Merge branch 'dev' of https://github.com/dotnet/eShopOnContainers into dev

pull/126/head
etomas 8 years ago
parent
commit
cfecbc5dba
12 changed files with 252 additions and 91 deletions
  1. +5
    -0
      docker-compose-external.yml
  2. +15
    -3
      src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs
  3. +7
    -4
      src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs
  4. +18
    -14
      src/Services/Basket/Basket.API/Startup.cs
  5. +4
    -0
      test/Services/FunctionalTests/FunctionalTests.csproj
  6. +34
    -0
      test/Services/FunctionalTests/Services/Basket/BasketScenariosBase.cs
  7. +29
    -0
      test/Services/FunctionalTests/Services/Basket/BasketTestsStartup.cs
  8. +0
    -55
      test/Services/FunctionalTests/Services/Catalog/CatalogScenarios.cs
  9. +2
    -0
      test/Services/FunctionalTests/Services/Catalog/CatalogScenariosBase.cs
  10. +0
    -15
      test/Services/FunctionalTests/Services/Catalog/CatalogTestsStartup.cs
  11. +132
    -0
      test/Services/FunctionalTests/Services/IntegrationEventsScenarios.cs
  12. +6
    -0
      test/Services/FunctionalTests/appsettings.json

+ 5
- 0
docker-compose-external.yml View File

@ -8,3 +8,8 @@ services:
image: redis
ports:
- "6379:6379"
rabbitmq:
image: rabbitmq
ports:
- "5672:5672"

+ 15
- 3
src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs View File

@ -14,7 +14,7 @@ using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
{
public class EventBusRabbitMQ : IEventBus
public class EventBusRabbitMQ : IEventBus, IDisposable
{
private readonly string _brokerName = "eshop_event_bus";
private readonly string _connectionString;
@ -31,6 +31,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
_handlers = new Dictionary<string, List<IIntegrationEventHandler>>();
_eventTypes = new List<Type>();
}
public void Publish(IntegrationEvent @event)
{
var eventName = @event.GetType().Name;
@ -92,14 +93,24 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
if (_handlers.Keys.Count == 0)
{
_queueName = string.Empty;
_connection.Item1.Close();
_connection.Item2.Close();
_connection.Item1.Dispose();
_connection.Item2.Dispose();
}
}
}
}
public void Dispose()
{
if (_connection != null)
{
_handlers.Clear();
_connection.Item1.Dispose();
_connection.Item2.Dispose();
}
}
private IModel GetChannel()
{
if (_connection != null)
@ -151,5 +162,6 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
}
}
}
}
}

+ 7
- 4
src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs View File

@ -20,7 +20,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Even
foreach (var id in userIds)
{
var basket = await _repository.GetBasket(id);
await UpdateBasket(@event.ProductId, @event.NewPrice, basket);
await UpdateBasket(@event.ProductId, @event.NewPrice, basket);
}
}
@ -31,9 +31,12 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Even
{
foreach (var item in itemsToUpdate)
{
var originalPrice = item.UnitPrice;
item.UnitPrice = newPrice;
item.OldUnitPrice = originalPrice;
if(item.UnitPrice != newPrice)
{
var originalPrice = item.UnitPrice;
item.UnitPrice = newPrice;
item.OldUnitPrice = originalPrice;
}
}
await _repository.UpdateBasket(basket);
}


+ 18
- 14
src/Services/Basket/Basket.API/Startup.cs View File

@ -13,6 +13,7 @@ using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Events;
using Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.EventHandling;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ;
using System;
namespace Microsoft.eShopOnContainers.Services.Basket.API
{
@ -80,13 +81,8 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
var serviceProvider = services.BuildServiceProvider();
var configuration = serviceProvider.GetRequiredService<IOptionsSnapshot<BasketSettings>>().Value;
var eventBus = new EventBusRabbitMQ(configuration.EventBusConnection);
services.AddSingleton<IEventBus>(eventBus);
services.AddSingleton<IEventBus>(provider => new EventBusRabbitMQ(configuration.EventBusConnection));
var catalogPriceHandler = serviceProvider.GetService<IIntegrationEventHandler<ProductPriceChangedIntegrationEvent>>();
eventBus.Subscribe<ProductPriceChangedIntegrationEvent>(catalogPriceHandler);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@ -100,20 +96,28 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
// Use frameworks
app.UseCors("CorsPolicy");
var identityUrl = Configuration.GetValue<string>("IdentityUrl");
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = identityUrl.ToString(),
ScopeName = "basket",
RequireHttpsMetadata = false
});
ConfigureAuth(app);
app.UseMvcWithDefaultRoute();
app.UseSwagger()
.UseSwaggerUi();
var catalogPriceHandler = app.ApplicationServices.GetService<IIntegrationEventHandler<ProductPriceChangedIntegrationEvent>>();
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();
eventBus.Subscribe<ProductPriceChangedIntegrationEvent>(catalogPriceHandler);
}
protected virtual void ConfigureAuth(IApplicationBuilder app)
{
var identityUrl = Configuration.GetValue<string>("IdentityUrl");
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = identityUrl.ToString(),
ScopeName = "basket",
RequireHttpsMetadata = false
});
}
}
}

+ 4
- 0
test/Services/FunctionalTests/FunctionalTests.csproj View File

@ -13,12 +13,16 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Services\Basket\Basket.API\Basket.API.csproj" />
<ProjectReference Include="..\..\..\src\Services\Catalog\Catalog.API\Catalog.API.csproj" />
<ProjectReference Include="..\..\..\src\Services\Ordering\Ordering.API\Ordering.API.csproj" />
<ProjectReference Include="..\..\..\src\Web\WebMVC\WebMVC.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>


+ 34
- 0
test/Services/FunctionalTests/Services/Basket/BasketScenariosBase.cs View File

@ -0,0 +1,34 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace FunctionalTests.Services.Basket
{
public class BasketScenariosBase
{
public TestServer CreateServer()
{
var webHostBuilder = new WebHostBuilder();
webHostBuilder.UseContentRoot(Directory.GetCurrentDirectory());
webHostBuilder.UseStartup<BasketTestsStartup>();
return new TestServer(webHostBuilder);
}
public static class Get
{
public static string GetBasketByCustomer(string customerId)
{
return $"/{customerId}";
}
}
public static class Post
{
public static string CreateBasket = "/";
}
}
}

+ 29
- 0
test/Services/FunctionalTests/Services/Basket/BasketTestsStartup.cs View File

@ -0,0 +1,29 @@
using Microsoft.eShopOnContainers.Services.Basket.API;
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Builder;
using FunctionalTests.Middleware;
namespace FunctionalTests.Services.Basket
{
public class BasketTestsStartup : Startup
{
public BasketTestsStartup(IHostingEnvironment env) : base(env)
{
}
protected override void ConfigureAuth(IApplicationBuilder app)
{
if (Configuration["isTest"] == bool.TrueString.ToLowerInvariant())
{
app.UseMiddleware<AutoAuthorizeMiddleware>();
}
else
{
base.ConfigureAuth(app);
}
}
}
}

+ 0
- 55
test/Services/FunctionalTests/Services/Catalog/CatalogScenarios.cs View File

@ -1,55 +0,0 @@
using Microsoft.eShopOnContainers.Services.Catalog.API.Model;
using Microsoft.eShopOnContainers.Services.Catalog.API.ViewModel;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace FunctionalTests.Services.Catalog
{
public class CatalogScenarios : CatalogScenariosBase
{
[Fact]
public async Task Post_update_a_catalogitem_price_and_catalogitem_is_returned_modified()
{
using (var server = CreateServer())
{
var client = server.CreateClient();
// Arrange
var itemToModify = GetCatalogItem();
var newPrice = new Random().Next(1, 200);
itemToModify.Price = newPrice;
// Act
var postRes = await client.PostAsync(Post.UpdateCatalogProduct,
new StringContent(JsonConvert.SerializeObject(itemToModify),
UTF8Encoding.UTF8, "application/json"));
var response = await client.GetAsync(Get.ProductByName(itemToModify.Name));
var result = JsonConvert.DeserializeObject<PaginatedItemsViewModel<CatalogItem>>(await response.Content.ReadAsStringAsync());
var item = result.Data.First();
// Assert
Assert.Equal(result.Count, 1);
Assert.Equal(itemToModify.Id, item.Id);
Assert.Equal(newPrice, item.Price);
}
}
private CatalogItem GetCatalogItem()
{
return new CatalogItem()
{
Id = 1,
Price = 12.5M,
Name = ".NET Bot Black Sweatshirt"
};
}
}
}

+ 2
- 0
test/Services/FunctionalTests/Services/Catalog/CatalogScenariosBase.cs View File

@ -24,6 +24,8 @@ namespace FunctionalTests.Services.Catalog
{
public static string Orders = "api/v1/orders";
public static string Items = "api/v1/catalog/items";
public static string ProductByName(string name)
{
return $"api/v1/catalog/items/withname/{name}";


+ 0
- 15
test/Services/FunctionalTests/Services/Catalog/CatalogTestsStartup.cs View File

@ -1,15 +0,0 @@
using Microsoft.eShopOnContainers.Services.Catalog.API;
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.AspNetCore.Hosting;
namespace FunctionalTests.Services.Catalog
{
public class CatalogTestsStartup : Startup
{
public CatalogTestsStartup(IHostingEnvironment env) : base(env)
{
}
}
}

+ 132
- 0
test/Services/FunctionalTests/Services/IntegrationEventsScenarios.cs View File

@ -0,0 +1,132 @@
using FunctionalTests.Services.Basket;
using FunctionalTests.Services.Catalog;
using Microsoft.eShopOnContainers.Services.Basket.API.Model;
using Microsoft.eShopOnContainers.Services.Catalog.API.Model;
using Microsoft.eShopOnContainers.Services.Catalog.API.ViewModel;
using Newtonsoft.Json;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Xunit;
using System.Net.Http;
using System.Threading;
namespace FunctionalTests.Services
{
public class IntegrationEventsScenarios
{
[Fact]
public async Task Post_update_product_price_and_catalog_and_basket_list_modified()
{
decimal priceModification = 0.15M;
string userId = "JohnId";
using (var catalogServer = new CatalogScenariosBase().CreateServer())
using (var basketServer = new BasketScenariosBase().CreateServer())
{
var catalogClient = catalogServer.CreateClient();
var basketClient = basketServer.CreateClient();
// GIVEN a product catalog list
var originalCatalogProducts = await GetCatalogAsync(catalogClient);
// AND a user basket filled with products
var basket = ComposeBasket(userId, originalCatalogProducts.Data.Take(3));
var res = await basketClient.PostAsync(
BasketScenariosBase.Post.CreateBasket,
new StringContent(JsonConvert.SerializeObject(basket), UTF8Encoding.UTF8, "application/json")
);
// WHEN the price of one product is modified in the catalog
var itemToModify = basket.Items[2];
var oldPrice = itemToModify.UnitPrice;
var newPrice = oldPrice + priceModification;
var pRes = await catalogClient.PostAsync(CatalogScenariosBase.Post.UpdateCatalogProduct, new StringContent(ChangePrice(itemToModify, newPrice), UTF8Encoding.UTF8, "application/json"));
var modifiedCatalogProducts = await GetCatalogAsync(catalogClient);
var itemUpdated = await GetUpdatedBasketItem(newPrice, itemToModify.ProductId, userId, basketClient);
if (itemUpdated == null)
{
Assert.False(true, $"The basket service has not been updated.");
}
else
{
//THEN the product price changes in the catalog
Assert.Equal(newPrice, modifiedCatalogProducts.Data.Single(it => it.Id == int.Parse(itemToModify.ProductId)).Price);
// AND the products in the basket reflects the changed priced and the original price
Assert.Equal(newPrice, itemUpdated.UnitPrice);
Assert.Equal(oldPrice, itemUpdated.OldUnitPrice);
}
}
}
private async Task<BasketItem> GetUpdatedBasketItem(decimal newPrice, string productId, string userId, HttpClient basketClient)
{
bool continueLoop = true;
var counter = 0;
BasketItem itemUpdated = null;
while (continueLoop && counter < 20)
{
//get the basket and verify that the price of the modified product is updated
var basketGetResponse = await basketClient.GetAsync(BasketScenariosBase.Get.GetBasketByCustomer(userId));
var basketUpdated = JsonConvert.DeserializeObject<CustomerBasket>(await basketGetResponse.Content.ReadAsStringAsync());
itemUpdated = basketUpdated.Items.Single(pr => pr.ProductId == productId);
if (itemUpdated.UnitPrice == newPrice)
{
continueLoop = false;
}
else
{
counter++;
await Task.Delay(100);
}
}
return itemUpdated;
}
private async Task<PaginatedItemsViewModel<CatalogItem>> GetCatalogAsync(HttpClient catalogClient)
{
var response = await catalogClient.GetAsync(CatalogScenariosBase.Get.Items);
var items = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<PaginatedItemsViewModel<CatalogItem>>(items);
}
private string ChangePrice(BasketItem itemToModify, decimal newPrice)
{
var item = new CatalogItem()
{
Id = int.Parse(itemToModify.ProductId),
Price = newPrice
};
return JsonConvert.SerializeObject(item);
}
private CustomerBasket ComposeBasket(string customerId, IEnumerable<CatalogItem> items)
{
var basket = new CustomerBasket(customerId);
foreach (var item in items)
{
basket.Items.Add(new BasketItem()
{
Id = Guid.NewGuid().ToString(),
UnitPrice = item.Price,
PictureUrl = item.PictureUri,
ProductId = item.Id.ToString(),
OldUnitPrice = 0,
ProductName = item.Name,
Quantity = 1
});
}
return basket;
}
}
}

+ 6
- 0
test/Services/FunctionalTests/appsettings.json View File

@ -0,0 +1,6 @@
{
"ConnectionString": "127.0.0.1",
"IdentityUrl": "http://localhost:5105",
"isTest": "true",
"EventBusConnection": "localhost"
}

Loading…
Cancel
Save