Fix ordering and basked scenarios

This commit is contained in:
David Fowler 2023-05-01 17:32:57 -07:00
parent 2736eae13e
commit 2b0889f684
8 changed files with 84 additions and 58 deletions

View File

@ -10,21 +10,21 @@ namespace Microsoft.AspNetCore.Hosting
{ {
public static class IWebHostExtensions public static class IWebHostExtensions
{ {
public static bool IsInKubernetes(this IWebHost webHost) public static bool IsInKubernetes(this IServiceProvider services)
{ {
var cfg = webHost.Services.GetService<IConfiguration>(); var cfg = services.GetService<IConfiguration>();
var orchestratorType = cfg.GetValue<string>("OrchestratorType"); var orchestratorType = cfg.GetValue<string>("OrchestratorType");
return orchestratorType?.ToUpper() == "K8S"; return orchestratorType?.ToUpper() == "K8S";
} }
public static IWebHost MigrateDbContext<TContext>(this IWebHost webHost, Action<TContext, IServiceProvider> seeder) where TContext : DbContext public static IServiceProvider MigrateDbContext<TContext>(this IServiceProvider services, Action<TContext, IServiceProvider> seeder) where TContext : DbContext
{ {
var underK8s = webHost.IsInKubernetes(); var underK8s = services.IsInKubernetes();
using var scope = webHost.Services.CreateScope(); using var scope = services.CreateScope();
var services = scope.ServiceProvider; var scopeServices = scope.ServiceProvider;
var logger = services.GetRequiredService<ILogger<TContext>>(); var logger = scopeServices.GetRequiredService<ILogger<TContext>>();
var context = services.GetService<TContext>(); var context = scopeServices.GetService<TContext>();
try try
{ {
@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Hosting
if (underK8s) if (underK8s)
{ {
InvokeSeeder(seeder, context, services); InvokeSeeder(seeder, context, scopeServices);
} }
else else
{ {
@ -50,7 +50,7 @@ namespace Microsoft.AspNetCore.Hosting
//migration can't fail for network related exception. The retry options for DbContext only //migration can't fail for network related exception. The retry options for DbContext only
//apply to transient exceptions //apply to transient exceptions
// Note that this is NOT applied when running some orchestrators (let the orchestrator to recreate the failing service) // Note that this is NOT applied when running some orchestrators (let the orchestrator to recreate the failing service)
retry.Execute(() => InvokeSeeder(seeder, context, services)); retry.Execute(() => InvokeSeeder(seeder, context, scopeServices));
} }
logger.LogInformation("Migrated database associated with context {DbContextName}", typeof(TContext).Name); logger.LogInformation("Migrated database associated with context {DbContextName}", typeof(TContext).Name);
@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.Hosting
} }
} }
return webHost; return services;
} }
private static void InvokeSeeder<TContext>(Action<TContext, IServiceProvider> seeder, TContext context, IServiceProvider services) private static void InvokeSeeder<TContext>(Action<TContext, IServiceProvider> seeder, TContext context, IServiceProvider services)

View File

@ -6,6 +6,12 @@ namespace Basket.FunctionalTests.Base;
public class BasketScenarioBase : WebApplicationFactory<Program> public class BasketScenarioBase : WebApplicationFactory<Program>
{ {
private const string ApiUrlBase = "api/v1/basket"; private const string ApiUrlBase = "api/v1/basket";
public TestServer CreateServer()
{
return Server;
}
public static class Get public static class Get
{ {
public static string GetBasket(int id) public static string GetBasket(int id)

View File

@ -6,10 +6,6 @@
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<None Remove="appsettings.json" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="appsettings.json"> <Content Include="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

View File

@ -1,20 +1,15 @@
namespace Basket.FunctionalTests; namespace Basket.FunctionalTests;
public class BasketScenarios : BasketScenarioBase public class BasketScenarios :
BasketScenarioBase
{ {
private readonly HttpClient _httpClient;
public BasketScenarios()
{
_httpClient = CreateClient();
}
[Fact] [Fact]
public async Task Post_basket_and_response_ok_status_code() public async Task Post_basket_and_response_ok_status_code()
{ {
using var server = CreateServer();
var content = new StringContent(BuildBasket(), UTF8Encoding.UTF8, "application/json"); var content = new StringContent(BuildBasket(), UTF8Encoding.UTF8, "application/json");
var uri = "/api/v1/basket/"; var uri = "/api/v1/basket/";
var response = await _httpClient.PostAsync(uri, content); var response = await server.CreateClient().PostAsync(uri, content);
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
} }
@ -22,7 +17,8 @@ public class BasketScenarios : BasketScenarioBase
[Fact] [Fact]
public async Task Get_basket_and_response_ok_status_code() public async Task Get_basket_and_response_ok_status_code()
{ {
var response = await _httpClient using var server = CreateServer();
var response = await server.CreateClient()
.GetAsync(Get.GetBasket(1)); .GetAsync(Get.GetBasket(1));
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
} }
@ -30,9 +26,10 @@ public class BasketScenarios : BasketScenarioBase
[Fact] [Fact]
public async Task Send_Checkout_basket_and_response_ok_status_code() public async Task Send_Checkout_basket_and_response_ok_status_code()
{ {
using var server = CreateServer();
var contentBasket = new StringContent(BuildBasket(), UTF8Encoding.UTF8, "application/json"); var contentBasket = new StringContent(BuildBasket(), UTF8Encoding.UTF8, "application/json");
await _httpClient await server.CreateClient()
.PostAsync(Post.Basket, contentBasket); .PostAsync(Post.Basket, contentBasket);
var contentCheckout = new StringContent(BuildCheckout(), UTF8Encoding.UTF8, "application/json") var contentCheckout = new StringContent(BuildCheckout(), UTF8Encoding.UTF8, "application/json")
@ -40,7 +37,7 @@ public class BasketScenarios : BasketScenarioBase
Headers = { { "x-requestid", Guid.NewGuid().ToString() } } Headers = { { "x-requestid", Guid.NewGuid().ToString() } }
}; };
var response = await _httpClient var response = await server.CreateClient()
.PostAsync(Post.CheckoutOrder, contentCheckout); .PostAsync(Post.CheckoutOrder, contentCheckout);
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();

View File

@ -331,7 +331,7 @@ static class CustomExtensionsMethods
public static IServiceCollection AddCustomSwagger(this IServiceCollection services, IConfiguration configuration) public static IServiceCollection AddCustomSwagger(this IServiceCollection services, IConfiguration configuration)
{ {
services.AddSwaggerGen(options => return services.AddSwaggerGen(options =>
{ {
options.SwaggerDoc("v1", new OpenApiInfo options.SwaggerDoc("v1", new OpenApiInfo
{ {
@ -339,6 +339,9 @@ static class CustomExtensionsMethods
Version = "v1", Version = "v1",
Description = "The Ordering Service HTTP API" Description = "The Ordering Service HTTP API"
}); });
var identityUrl = configuration["IdentityUrlExternal"];
options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
{ {
Type = SecuritySchemeType.OAuth2, Type = SecuritySchemeType.OAuth2,
@ -346,8 +349,8 @@ static class CustomExtensionsMethods
{ {
Implicit = new OpenApiOAuthFlow() Implicit = new OpenApiOAuthFlow()
{ {
AuthorizationUrl = new Uri($"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize"), AuthorizationUrl = new Uri($"{identityUrl}/connect/authorize"),
TokenUrl = new Uri($"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/token"), TokenUrl = new Uri($"{identityUrl}/connect/token"),
Scopes = new Dictionary<string, string>() Scopes = new Dictionary<string, string>()
{ {
{ "orders", "Ordering API" } { "orders", "Ordering API" }
@ -355,9 +358,9 @@ static class CustomExtensionsMethods
} }
} }
}); });
options.OperationFilter<AuthorizeCheckOperationFilter>(); options.OperationFilter<AuthorizeCheckOperationFilter>();
}); });
return services;
} }
public static IServiceCollection AddCustomIntegrations(this IServiceCollection services, IConfiguration configuration) public static IServiceCollection AddCustomIntegrations(this IServiceCollection services, IConfiguration configuration)

View File

@ -6,6 +6,12 @@
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Content Include="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
<PackageReference Include="Microsoft.NET.Test.Sdk" /> <PackageReference Include="Microsoft.NET.Test.Sdk" />
@ -18,7 +24,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\Web\WebMVC\WebMVC.csproj" />
<ProjectReference Include="..\Ordering.API\Ordering.API.csproj" /> <ProjectReference Include="..\Ordering.API\Ordering.API.csproj" />
<ProjectReference Include="..\Ordering.Domain\Ordering.Domain.csproj" /> <ProjectReference Include="..\Ordering.Domain\Ordering.Domain.csproj" />
<ProjectReference Include="..\Ordering.Infrastructure\Ordering.Infrastructure.csproj" /> <ProjectReference Include="..\Ordering.Infrastructure\Ordering.Infrastructure.csproj" />

View File

@ -1,36 +1,42 @@
namespace Ordering.FunctionalTests; using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.Hosting;
public class OrderingScenarioBase namespace Ordering.FunctionalTests;
public class OrderingScenarioBase : WebApplicationFactory<Program>
{ {
public TestServer CreateServer() public TestServer CreateServer()
{ {
var path = Assembly.GetAssembly(typeof(OrderingScenarioBase)) Services.MigrateDbContext<OrderingContext>((context, services) =>
.Location; {
var env = services.GetService<IWebHostEnvironment>();
var settings = services.GetService<IOptions<OrderingSettings>>();
var logger = services.GetService<ILogger<OrderingContextSeed>>();
var hostBuilder = new WebHostBuilder() new OrderingContextSeed()
.UseContentRoot(Path.GetDirectoryName(path)) .SeedAsync(context, env, settings, logger)
.ConfigureAppConfiguration(cb => .Wait();
{ })
cb.AddJsonFile("appsettings.json", optional: false) .MigrateDbContext<IntegrationEventLogContext>((_, __) => { });
.AddEnvironmentVariables();
});
var testServer = new TestServer(hostBuilder); return Server;
}
testServer.Host protected override IHost CreateHost(IHostBuilder builder)
.MigrateDbContext<OrderingContext>((context, services) => {
{ builder.ConfigureServices(servies =>
var env = services.GetService<IWebHostEnvironment>(); {
var settings = services.GetService<IOptions<OrderingSettings>>(); servies.AddSingleton<IStartupFilter, AuthStartupFilter>();
var logger = services.GetService<ILogger<OrderingContextSeed>>(); });
new OrderingContextSeed() builder.ConfigureAppConfiguration(c =>
.SeedAsync(context, env, settings, logger) {
.Wait(); var directory = Path.GetDirectoryName(typeof(OrderingScenarioBase).Assembly.Location)!;
})
.MigrateDbContext<IntegrationEventLogContext>((_, __) => { });
return testServer; c.AddJsonFile(Path.Combine(directory, "appsettings.json"), optional: false);
});
return base.CreateHost(builder);
} }
public static class Get public static class Get
@ -48,4 +54,17 @@ public class OrderingScenarioBase
public static string CancelOrder = "api/v1/orders/cancel"; public static string CancelOrder = "api/v1/orders/cancel";
public static string ShipOrder = "api/v1/orders/ship"; public static string ShipOrder = "api/v1/orders/ship";
} }
private class AuthStartupFilter : IStartupFilter
{
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return app =>
{
app.UseMiddleware<AutoAuthorizeMiddleware>();
next(app);
};
}
}
} }

View File

@ -1,7 +1,6 @@
using System.Net; using System.Net;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using WebMVC.Services.ModelDTOs;
using Xunit; using Xunit;
namespace Ordering.FunctionalTests namespace Ordering.FunctionalTests
@ -16,6 +15,7 @@ namespace Ordering.FunctionalTests
var response = await server.CreateClient() var response = await server.CreateClient()
.GetAsync(Get.Orders); .GetAsync(Get.Orders);
var s = await response.Content.ReadAsStringAsync();
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
} }
@ -49,7 +49,7 @@ namespace Ordering.FunctionalTests
string BuildOrder() string BuildOrder()
{ {
var order = new OrderDTO() var order = new
{ {
OrderNumber = "-1" OrderNumber = "-1"
}; };