Merge pull request #77 from meysamhadeli/bug/fix-dispose-in-test-fixture

fix: Fix bug dispose service provider in test base
This commit is contained in:
Meysam Hadeli 2023-01-12 03:52:00 +03:30 committed by GitHub
commit c53e6674f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 42 additions and 103 deletions

View File

@ -102,6 +102,7 @@
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.1" />
<PackageReference Include="Testcontainers" Version="2.3.0" />
<PackageReference Include="Unchase.Swashbuckle.AspNetCore.Extensions" Version="2.7.1" />
<PackageReference Include="WebMotions.Fake.Authentication.JwtBearer" Version="7.0.0" />
<PackageReference Include="Yarp.ReverseProxy" Version="1.1.1" />
<PackageReference Include="Microsoft.Identity.Web" Version="2.0.5-preview" />

View File

@ -1,32 +0,0 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection;
namespace BuildingBlocks.TestBase.Auth;
//ref: https://blog.joaograssi.com/posts/2021/asp-net-core-testing-permission-protected-api-endpoints/
public static class AuthServiceCollectionExtensions
{
private static MockAuthUser GetMockUser() =>
new MockAuthUser(new Claim("sub", Guid.NewGuid().ToString()),
new Claim("email", "sam@test.com"));
public static AuthenticationBuilder AddTestAuthentication(this IServiceCollection services)
{
services.AddAuthorization(options =>
{
// AuthConstants.Scheme is just a scheme we define. I called it "TestAuth"
options.DefaultPolicy = new AuthorizationPolicyBuilder("Test")
.RequireAuthenticatedUser()
.Build();
});
// Register a default user, so all requests have it by default
services.AddScoped(_ => GetMockUser());
// Register our custom authentication handler
return services.AddAuthentication("Test")
.AddScheme<AuthenticationSchemeOptions, TestAuthHandler>("Test", options => { });
}
}

View File

@ -1,9 +0,0 @@
using System.Security.Claims;
namespace BuildingBlocks.TestBase.Auth;
public class MockAuthUser
{
public List<Claim> Claims { get; }
public MockAuthUser(params Claim[] claims) => Claims = claims.ToList();
}

View File

@ -1,43 +0,0 @@
using System.Security.Claims;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace BuildingBlocks.TestBase.Auth;
//ref: https://blog.joaograssi.com/posts/2021/asp-net-core-testing-permission-protected-api-endpoints/
public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
private readonly MockAuthUser _mockAuthUser;
public TestAuthHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
MockAuthUser mockAuthUser)
: base(options, logger, encoder, clock)
{
// 1. We get a "mock" user instance here via DI.
// we'll see how this work later, don't worry
_mockAuthUser = mockAuthUser;
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (_mockAuthUser.Claims.Count == 0)
return Task.FromResult(AuthenticateResult.Fail("Mock auth user not configured."));
// 2. Create the principal and the ticket
var identity = new ClaimsIdentity(_mockAuthUser.Claims, "Test");
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, "Test");
// 3. Authenticate the request
var result = AuthenticateResult.Success(ticket);
return Task.FromResult(result);
}
}

View File

@ -5,7 +5,6 @@ using BuildingBlocks.EFCore;
using BuildingBlocks.MassTransit;
using BuildingBlocks.Mongo;
using BuildingBlocks.PersistMessageProcessor;
using BuildingBlocks.TestBase.Auth;
using BuildingBlocks.Web;
using DotNet.Testcontainers.Containers;
using EasyNetQ.Management.Client;
@ -32,6 +31,10 @@ using ILogger = Serilog.ILogger;
namespace BuildingBlocks.TestBase;
using System.Net;
using System.Security.Claims;
using WebMotions.Fake.Authentication.JwtBearer;
public class TestFixture<TEntryPoint> : IAsyncLifetime
where TEntryPoint : class
{
@ -44,7 +47,18 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
public MongoDbTestcontainer MongoDbTestContainer;
private ITestHarness TestHarness => ServiceProvider?.GetTestHarness();
public HttpClient HttpClient => _factory?.CreateClient();
public HttpClient HttpClient
{
get
{
var claims = new Dictionary<string, object> { { ClaimTypes.Name, "test@sample.com" }, { ClaimTypes.Role, "admin" }, };
var httpClient = _factory?.CreateClient();
httpClient.SetFakeBearerToken(claims);
return httpClient;
}
}
public GrpcChannel Channel => GrpcChannel.ForAddress(HttpClient.BaseAddress!, new GrpcChannelOptions { HttpClient = HttpClient });
public Action<IServiceCollection> TestRegistrationServices { get; set; }
public IServiceProvider ServiceProvider => _factory?.Services;
@ -63,7 +77,15 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
{
TestRegistrationServices?.Invoke(services);
services.ReplaceSingleton(AddHttpContextAccessorMock);
services.AddTestAuthentication();
// add authentication using a fake jwt bearer - we can use SetAdminUser method to set authenticate user to existing HttContextAccessor
// https://github.com/webmotions/fake-authentication-jwtbearer
// https://github.com/webmotions/fake-authentication-jwtbearer/issues/14
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = FakeJwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = FakeJwtBearerDefaults.AuthenticationScheme;
}).AddFakeJwtBearer();
});
});
}
@ -235,13 +257,9 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
configuration.AddInMemoryCollection(new KeyValuePair<string, string>[]
{
new("DatabaseOptions:DefaultConnection", MsSqlTestContainer.ConnectionString + "TrustServerCertificate=True"),
new("PersistMessageOptions:ConnectionString", MsSqlPersistTestContainer.ConnectionString + "TrustServerCertificate=True"),
new("RabbitMqOptions:HostName", RabbitMqTestContainer.Hostname),
new("RabbitMqOptions:UserName", RabbitMqTestContainer.Username),
new("RabbitMqOptions:Password", RabbitMqTestContainer.Password),
new("RabbitMqOptions:Port", RabbitMqTestContainer.Port.ToString()),
new("MongoOptions:ConnectionString", MongoDbTestContainer.ConnectionString),
new("MongoOptions:DatabaseName", MongoDbTestContainer.Database)
new("PersistMessageOptions:ConnectionString", MsSqlPersistTestContainer.ConnectionString + "TrustServerCertificate=True"), new("RabbitMqOptions:HostName", RabbitMqTestContainer.Hostname),
new("RabbitMqOptions:UserName", RabbitMqTestContainer.Username), new("RabbitMqOptions:Password", RabbitMqTestContainer.Password), new("RabbitMqOptions:Port", RabbitMqTestContainer.Port.ToString()),
new("MongoOptions:ConnectionString", MongoDbTestContainer.ConnectionString), new("MongoOptions:DatabaseName", MongoDbTestContainer.Database)
});
}
@ -365,7 +383,6 @@ public class TestWriteFixture<TEntryPoint, TWContext> : TestFixture<TEntryPoint>
}
}
public class TestReadFixture<TEntryPoint, TRContext> : TestFixture<TEntryPoint>
where TEntryPoint : class
where TRContext : MongoDbContext
@ -428,7 +445,6 @@ public class TestFixtureCore<TEntryPoint> : IAsyncLifetime
_reSpawnerPersistDb = await Respawner.CreateAsync(PersistDbConnection,
new RespawnerOptions { TablesToIgnore = new Table[] { "__EFMigrationsHistory" }, });
await _reSpawnerPersistDb.ResetAsync(PersistDbConnection);
}
if (!string.IsNullOrEmpty(databaseOptions?.DefaultConnection))
@ -439,18 +455,25 @@ public class TestFixtureCore<TEntryPoint> : IAsyncLifetime
_reSpawnerDefaultDb = await Respawner.CreateAsync(DefaultDbConnection,
new RespawnerOptions { TablesToIgnore = new Table[] { "__EFMigrationsHistory" }, });
await _reSpawnerDefaultDb.ResetAsync(DefaultDbConnection);
await SeedDataAsync();
}
}
public async Task DisposeAsync()
{
await ResetSqlAsync();
await ResetMongoAsync();
await ResetRabbitMqAsync();
}
private async Task ResetSqlAsync()
{
if (PersistDbConnection is not null)
await _reSpawnerPersistDb.ResetAsync(PersistDbConnection);
if (DefaultDbConnection is not null)
await _reSpawnerDefaultDb.ResetAsync(DefaultDbConnection);
}
private async Task ResetMongoAsync(CancellationToken cancellationToken = default)
{
//https://stackoverflow.com/questions/3366397/delete-everything-in-a-mongodb-database
@ -517,7 +540,6 @@ public abstract class TestReadBase<TEntryPoint, TRContext> : TestFixtureCore<TEn
public TestReadFixture<TEntryPoint, TRContext> Fixture { get; }
}
public abstract class TestWriteBase<TEntryPoint, TWContext> : TestFixtureCore<TEntryPoint>
//,IClassFixture<IntegrationTestFactory<TEntryPoint, TWContext>>
where TEntryPoint : class

View File

@ -20,7 +20,6 @@ var app = builder.Build();
app.MapMinimalEndpoints();
app.UseAuthentication();
app.UseAuthorization();
app.UseRouting();
app.UseHttpsRedirection();
app.UseInfrastructure();

View File

@ -21,7 +21,6 @@ var app = builder.Build();
app.MapMinimalEndpoints();
app.UseAuthentication();
app.UseAuthorization();
app.UseRouting();
app.UseHttpsRedirection();
app.UseInfrastructure();

View File

@ -10,6 +10,8 @@ using Xunit;
namespace EndToEnd.Test.Flight.Features;
using BuildingBlocks.Contracts.EventBus.Messages;
public class GetFlightByIdTests: FlightEndToEndTestBase
{
public GetFlightByIdTests(TestFixture<Program, FlightDbContext, FlightReadDbContext> integrationTestFixture) : base(integrationTestFixture)
@ -23,6 +25,8 @@ public class GetFlightByIdTests: FlightEndToEndTestBase
//Arrange
var command = new FakeCreateFlightCommand().Generate();
await Fixture.SendAsync(command);
(await Fixture.WaitForPublishing<FlightCreated>()).Should().Be(true);
(await Fixture.WaitForConsuming<FlightCreated>()).Should().Be(true);
(await Fixture.ShouldProcessedPersistInternalCommand<CreateFlightMongoCommand>()).Should().Be(true);
// Act

View File

@ -20,7 +20,6 @@ var app = builder.Build();
app.MapMinimalEndpoints();
app.UseAuthentication();
app.UseAuthorization();
app.UseRouting();
app.UseHttpsRedirection();
app.UseInfrastructure();

View File

@ -20,7 +20,6 @@ var app = builder.Build();
app.MapMinimalEndpoints();
app.UseAuthentication();
app.UseAuthorization();
app.UseRouting();
app.UseHttpsRedirection();
app.UseInfrastructure();