fix: Fix bug reset rabbitmq in test base

This commit is contained in:
meysamhadeli 2023-01-13 01:37:42 +03:30
parent a58fad17d8
commit 8f3d2b9c8c
8 changed files with 73 additions and 55 deletions

View File

@ -13,7 +13,6 @@ root = true
[*] [*]
charset = utf-8 charset = utf-8
indent_style = space indent_style = space
indent_size = 2
insert_final_newline = true insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
@ -310,7 +309,7 @@ dotnet_naming_symbols.other_public_protected_fields_group.applicable_accessibili
dotnet_naming_symbols.other_public_protected_fields_group.applicable_kinds = field dotnet_naming_symbols.other_public_protected_fields_group.applicable_kinds = field
dotnet_naming_rule.other_public_protected_fields_disallowed_rule.symbols = other_public_protected_fields_group dotnet_naming_rule.other_public_protected_fields_disallowed_rule.symbols = other_public_protected_fields_group
dotnet_naming_rule.other_public_protected_fields_disallowed_rule.style = disallowed_style dotnet_naming_rule.other_public_protected_fields_disallowed_rule.style = disallowed_style
dotnet_naming_rule.other_public_protected_fields_disallowed_rule.severity = error dotnet_naming_rule.other_public_protected_fields_disallowed_rule.severity = none
########################################## ##########################################
# StyleCop Field Naming Rules # StyleCop Field Naming Rules
@ -351,7 +350,6 @@ dotnet_naming_symbols.stylecop_private_fields_group.applicable_accessibilities =
dotnet_naming_symbols.stylecop_private_fields_group.applicable_kinds = field dotnet_naming_symbols.stylecop_private_fields_group.applicable_kinds = field
dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.symbols = stylecop_private_fields_group dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.symbols = stylecop_private_fields_group
dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.style = camel_case_style dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.style = camel_case_style
dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.severity = warning
# Local variables must be camelCase # Local variables must be camelCase
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1312.md # https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1312.md

View File

@ -18,9 +18,7 @@ public static class Extensions
where TContext : DbContext, IDbContext where TContext : DbContext, IDbContext
{ {
services.AddOptions<DatabaseOptions>() services.AddValidateOptions<DatabaseOptions>();
.BindConfiguration(nameof(DatabaseOptions))
.ValidateDataAnnotations();
services.AddDbContext<TContext>((sp, options) => services.AddDbContext<TContext>((sp, options) =>
{ {

View File

@ -1,6 +1,5 @@
using System.Reflection; using System.Reflection;
using BuildingBlocks.Core.Event; using BuildingBlocks.Core.Event;
using BuildingBlocks.Utils;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using Humanizer; using Humanizer;
using MassTransit; using MassTransit;
@ -21,9 +20,7 @@ public static class Extensions
public static IServiceCollection AddCustomMassTransit(this IServiceCollection services, public static IServiceCollection AddCustomMassTransit(this IServiceCollection services,
IWebHostEnvironment env, Assembly assembly) IWebHostEnvironment env, Assembly assembly)
{ {
services.AddOptions<RabbitMqOptions>() services.AddValidateOptions<RabbitMqOptions>();
.BindConfiguration(nameof(RabbitMqOptions))
.ValidateDataAnnotations();
if (env.IsEnvironment("test")) if (env.IsEnvironment("test"))
{ {
@ -34,10 +31,7 @@ public static class Extensions
} }
else else
{ {
services.AddMassTransit(configure => services.AddMassTransit(configure => { SetupMasstransitConfigurations(services, configure, assembly); });
{
SetupMasstransitConfigurations(services, configure, assembly);
});
} }
return services; return services;

View File

@ -3,6 +3,8 @@ using Microsoft.Extensions.DependencyInjection;
namespace BuildingBlocks.Mongo namespace BuildingBlocks.Mongo
{ {
using Web;
public static class Extensions public static class Extensions
{ {
public static IServiceCollection AddMongoDbContext<TContext>( public static IServiceCollection AddMongoDbContext<TContext>(
@ -18,14 +20,14 @@ namespace BuildingBlocks.Mongo
where TContextImplementation : MongoDbContext, TContextService where TContextImplementation : MongoDbContext, TContextService
{ {
services.Configure<MongoOptions>(configuration.GetSection(nameof(MongoOptions))); services.Configure<MongoOptions>(configuration.GetSection(nameof(MongoOptions)));
if (configurator is { }) if (configurator is { })
{ {
services.Configure(nameof(MongoOptions), configurator); services.Configure(nameof(MongoOptions), configurator);
} }
else else
{ {
services.AddOptions<MongoOptions>().Bind(configuration.GetSection(nameof(MongoOptions))) services.AddValidateOptions<MongoOptions>();
.ValidateDataAnnotations();
} }
services.AddScoped(typeof(TContextService), typeof(TContextImplementation)); services.AddScoped(typeof(TContextService), typeof(TContextImplementation));

View File

@ -9,9 +9,7 @@ public static class Extensions
{ {
public static IServiceCollection AddPersistMessageProcessor(this IServiceCollection services) public static IServiceCollection AddPersistMessageProcessor(this IServiceCollection services)
{ {
services.AddOptions<PersistMessageOptions>() services.AddValidateOptions<PersistMessageOptions>();
.BindConfiguration(nameof(PersistMessageOptions))
.ValidateDataAnnotations();
services.AddDbContext<PersistMessageDbContext>(options => services.AddDbContext<PersistMessageDbContext>(options =>
{ {

View File

@ -2,7 +2,6 @@
using BuildingBlocks.Core.Event; using BuildingBlocks.Core.Event;
using BuildingBlocks.Core.Model; using BuildingBlocks.Core.Model;
using BuildingBlocks.EFCore; using BuildingBlocks.EFCore;
using BuildingBlocks.MassTransit;
using BuildingBlocks.Mongo; using BuildingBlocks.Mongo;
using BuildingBlocks.PersistMessageProcessor; using BuildingBlocks.PersistMessageProcessor;
using BuildingBlocks.Web; using BuildingBlocks.Web;
@ -19,7 +18,6 @@ using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using MongoDB.Driver; using MongoDB.Driver;
using NSubstitute; using NSubstitute;
using Respawn; using Respawn;
@ -28,44 +26,49 @@ using Serilog;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
using ILogger = Serilog.ILogger; using ILogger = Serilog.ILogger;
namespace BuildingBlocks.TestBase;
using System.Net; using System.Net;
using System.Security.Claims; using System.Security.Claims;
using WebMotions.Fake.Authentication.JwtBearer; using WebMotions.Fake.Authentication.JwtBearer;
namespace BuildingBlocks.TestBase;
public class TestFixture<TEntryPoint> : IAsyncLifetime public class TestFixture<TEntryPoint> : IAsyncLifetime
where TEntryPoint : class where TEntryPoint : class
{ {
private readonly WebApplicationFactory<TEntryPoint> _factory; private readonly WebApplicationFactory<TEntryPoint> _factory;
private int Timeout => 120; // Second private int Timeout => 120; // Second
private ITestHarness TestHarness => ServiceProvider?.GetTestHarness();
public MsSqlTestcontainer MsSqlTestContainer; private Action<IServiceCollection> TestRegistrationServices { get; set; }
public MsSqlTestcontainer MsSqlPersistTestContainer; private MsSqlTestcontainer MsSqlTestContainer;
private MsSqlTestcontainer MsSqlPersistTestContainer;
public RabbitMqTestcontainer RabbitMqTestContainer; public RabbitMqTestcontainer RabbitMqTestContainer;
public MongoDbTestcontainer MongoDbTestContainer; public MongoDbTestcontainer MongoDbTestContainer;
private ITestHarness TestHarness => ServiceProvider?.GetTestHarness();
public HttpClient HttpClient public HttpClient HttpClient
{ {
get get
{ {
var claims = new Dictionary<string, object> { { ClaimTypes.Name, "test@sample.com" }, { ClaimTypes.Role, "admin" }, }; var claims =
new Dictionary<string, object>
{
{ ClaimTypes.Name, "test@sample.com" }, { ClaimTypes.Role, "admin" },
};
var httpClient = _factory?.CreateClient(); var httpClient = _factory?.CreateClient();
httpClient.SetFakeBearerToken(claims); httpClient.SetFakeBearerToken(claims);
return httpClient; return httpClient;
} }
} }
public GrpcChannel Channel => GrpcChannel.ForAddress(HttpClient.BaseAddress!, new GrpcChannelOptions { HttpClient = HttpClient }); public GrpcChannel Channel =>
public Action<IServiceCollection> TestRegistrationServices { get; set; } GrpcChannel.ForAddress(HttpClient.BaseAddress!, new GrpcChannelOptions { HttpClient = HttpClient });
public IServiceProvider ServiceProvider => _factory?.Services; public IServiceProvider ServiceProvider => _factory?.Services;
public IConfiguration Configuration => _factory?.Services.GetRequiredService<IConfiguration>(); public IConfiguration Configuration => _factory?.Services.GetRequiredService<IConfiguration>();
public ILogger Logger { get; set; } public ILogger Logger { get; set; }
public TestFixture() protected TestFixture()
{ {
_factory = new WebApplicationFactory<TEntryPoint>() _factory = new WebApplicationFactory<TEntryPoint>()
.WithWebHostBuilder(builder => .WithWebHostBuilder(builder =>
@ -98,7 +101,7 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
public async Task DisposeAsync() public async Task DisposeAsync()
{ {
await StopTestContainerAsync(); await StopTestContainerAsync();
_factory?.DisposeAsync(); await _factory.DisposeAsync();
} }
public virtual void RegisterServices(Action<IServiceCollection> services) public virtual void RegisterServices(Action<IServiceCollection> services)
@ -119,13 +122,13 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
return null; return null;
} }
public async Task ExecuteScopeAsync(Func<IServiceProvider, Task> action) protected async Task ExecuteScopeAsync(Func<IServiceProvider, Task> action)
{ {
using var scope = ServiceProvider.CreateScope(); using var scope = ServiceProvider.CreateScope();
await action(scope.ServiceProvider); await action(scope.ServiceProvider);
} }
public async Task<T> ExecuteScopeAsync<T>(Func<IServiceProvider, Task<T>> action) protected async Task<T> ExecuteScopeAsync<T>(Func<IServiceProvider, Task<T>> action)
{ {
using var scope = ServiceProvider.CreateScope(); using var scope = ServiceProvider.CreateScope();
@ -157,7 +160,7 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
public async Task Publish<TMessage>(TMessage message, CancellationToken cancellationToken = default) public async Task Publish<TMessage>(TMessage message, CancellationToken cancellationToken = default)
where TMessage : class, IEvent where TMessage : class, IEvent
{ {
await TestHarness.Bus.Publish<TMessage>(message, cancellationToken); await TestHarness.Bus.Publish(message, cancellationToken);
} }
public async Task<bool> WaitForPublishing<TMessage>(CancellationToken cancellationToken = default) public async Task<bool> WaitForPublishing<TMessage>(CancellationToken cancellationToken = default)
@ -188,7 +191,7 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
} }
// Ref: https://tech.energyhelpline.com/in-memory-testing-with-masstransit/ // Ref: https://tech.energyhelpline.com/in-memory-testing-with-masstransit/
public async Task<bool> WaitUntilConditionMet(Func<Task<bool>> conditionToMet, int? timeoutSecond = null) private async Task<bool> WaitUntilConditionMet(Func<Task<bool>> conditionToMet, int? timeoutSecond = null)
{ {
var time = timeoutSecond ?? Timeout; var time = timeoutSecond ?? Timeout;
@ -197,7 +200,10 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
var meet = await conditionToMet.Invoke(); var meet = await conditionToMet.Invoke();
while (!meet) while (!meet)
{ {
if (timeoutExpired) return false; if (timeoutExpired)
{
return false;
}
await Task.Delay(100); await Task.Delay(100);
meet = await conditionToMet.Invoke(); meet = await conditionToMet.Invoke();
@ -257,9 +263,13 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
configuration.AddInMemoryCollection(new KeyValuePair<string, string>[] configuration.AddInMemoryCollection(new KeyValuePair<string, string>[]
{ {
new("DatabaseOptions:DefaultConnection", MsSqlTestContainer.ConnectionString + "TrustServerCertificate=True"), new("DatabaseOptions:DefaultConnection", MsSqlTestContainer.ConnectionString + "TrustServerCertificate=True"),
new("PersistMessageOptions:ConnectionString", MsSqlPersistTestContainer.ConnectionString + "TrustServerCertificate=True"), new("RabbitMqOptions:HostName", RabbitMqTestContainer.Hostname), new("PersistMessageOptions:ConnectionString", MsSqlPersistTestContainer.ConnectionString + "TrustServerCertificate=True"),
new("RabbitMqOptions:UserName", RabbitMqTestContainer.Username), new("RabbitMqOptions:Password", RabbitMqTestContainer.Password), new("RabbitMqOptions:Port", RabbitMqTestContainer.Port.ToString()), new("RabbitMqOptions:HostName", RabbitMqTestContainer.Hostname),
new("MongoOptions:ConnectionString", MongoDbTestContainer.ConnectionString), new("MongoOptions:DatabaseName", MongoDbTestContainer.Database) 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)
}); });
} }
@ -314,7 +324,10 @@ public class TestWriteFixture<TEntryPoint, TWContext> : TestFixture<TEntryPoint>
{ {
return ExecuteDbContextAsync(db => return ExecuteDbContextAsync(db =>
{ {
foreach (var entity in entities) db.Set<T>().Add(entity); foreach (var entity in entities)
{
db.Set<T>().Add(entity);
}
return db.SaveChangesAsync(); return db.SaveChangesAsync();
}); });
@ -446,10 +459,8 @@ public class TestFixtureCore<TEntryPoint> : IAsyncLifetime
private async Task InitSqlAsync() private async Task InitSqlAsync()
{ {
await ResetSqlAsync(); var databaseOptions = Fixture.ServiceProvider.GetRequiredService<DatabaseOptions>();
var persistOptions = Fixture.ServiceProvider.GetRequiredService<PersistMessageOptions>();
var databaseOptions = Fixture.ServiceProvider.GetRequiredService<IOptions<DatabaseOptions>>()?.Value;
var persistOptions = Fixture.ServiceProvider.GetRequiredService<IOptions<PersistMessageOptions>>()?.Value;
if (!string.IsNullOrEmpty(persistOptions?.ConnectionString)) if (!string.IsNullOrEmpty(persistOptions?.ConnectionString))
{ {
@ -475,15 +486,20 @@ public class TestFixtureCore<TEntryPoint> : IAsyncLifetime
private async Task ResetSqlAsync() private async Task ResetSqlAsync()
{ {
if (PersistDbConnection is not null) if (PersistDbConnection is not null)
{
await _reSpawnerPersistDb.ResetAsync(PersistDbConnection); await _reSpawnerPersistDb.ResetAsync(PersistDbConnection);
}
if (DefaultDbConnection is not null) if (DefaultDbConnection is not null)
{
await _reSpawnerDefaultDb.ResetAsync(DefaultDbConnection); await _reSpawnerDefaultDb.ResetAsync(DefaultDbConnection);
}
} }
private async Task ResetMongoAsync(CancellationToken cancellationToken = default) private async Task ResetMongoAsync(CancellationToken cancellationToken = default)
{ {
//https://stackoverflow.com/questions/3366397/delete-everything-in-a-mongodb-database //https://stackoverflow.com/questions/3366397/delete-everything-in-a-mongodb-database
MongoClient dbClient = new MongoClient(Fixture.MongoDbTestContainer?.ConnectionString); var dbClient = new MongoClient(Fixture.MongoDbTestContainer?.ConnectionString);
var collections = await dbClient.GetDatabase(Fixture.MongoDbTestContainer?.Database) var collections = await dbClient.GetDatabase(Fixture.MongoDbTestContainer?.Database)
.ListCollectionsAsync(cancellationToken: cancellationToken); .ListCollectionsAsync(cancellationToken: cancellationToken);
@ -498,10 +514,8 @@ public class TestFixtureCore<TEntryPoint> : IAsyncLifetime
{ {
var port = Fixture.RabbitMqTestContainer?.GetMappedPublicPort(15672) ?? 15672; var port = Fixture.RabbitMqTestContainer?.GetMappedPublicPort(15672) ?? 15672;
var rabbitmqOptions = Fixture.ServiceProvider.GetRequiredService<IOptions<RabbitMqOptions>>()?.Value; var managementClient = new ManagementClient(Fixture.RabbitMqTestContainer?.Hostname, Fixture.RabbitMqTestContainer?.Username,
Fixture.RabbitMqTestContainer?.Password, port);
var managementClient = new ManagementClient(rabbitmqOptions?.HostName, rabbitmqOptions?.UserName,
rabbitmqOptions?.Password, port);
var bd = await managementClient.GetBindingsAsync(cancellationToken); var bd = await managementClient.GetBindingsAsync(cancellationToken);
var bindings = bd.Where(x => !string.IsNullOrEmpty(x.Source) && !string.IsNullOrEmpty(x.Destination)); var bindings = bd.Where(x => !string.IsNullOrEmpty(x.Source) && !string.IsNullOrEmpty(x.Destination));
@ -528,7 +542,10 @@ public class TestFixtureCore<TEntryPoint> : IAsyncLifetime
using var scope = Fixture.ServiceProvider.CreateScope(); using var scope = Fixture.ServiceProvider.CreateScope();
var seeders = scope.ServiceProvider.GetServices<IDataSeeder>(); var seeders = scope.ServiceProvider.GetServices<IDataSeeder>();
foreach (var seeder in seeders) await seeder.SeedAllAsync(); foreach (var seeder in seeders)
{
await seeder.SeedAllAsync();
}
} }
} }

View File

@ -4,6 +4,9 @@ using Microsoft.Extensions.DependencyInjection;
namespace BuildingBlocks.Web; namespace BuildingBlocks.Web;
using MassTransit;
using Microsoft.Extensions.Options;
public static class ConfigurationExtensions public static class ConfigurationExtensions
{ {
public static TModel GetOptions<TModel>(this IConfiguration configuration, string section) where TModel : new() public static TModel GetOptions<TModel>(this IConfiguration configuration, string section) where TModel : new()
@ -27,4 +30,13 @@ public static class ConfigurationExtensions
app.Configuration?.GetSection(section).Bind(model); app.Configuration?.GetSection(section).Bind(model);
return model; return model;
} }
public static void AddValidateOptions<TModel>(this IServiceCollection service) where TModel : class, new()
{
service.AddOptions<TModel>()
.BindConfiguration(typeof(TModel).Name)
.ValidateDataAnnotations();
service.AddSingleton(x => x.GetRequiredService<IOptions<TModel>>().Value);
}
} }

View File

@ -1,5 +1,4 @@
using AutoBogus; using AutoBogus;
using Identity.Identity.Features.RegisterNewUser;
using Identity.Identity.Features.RegisterNewUser.Commands.V1; using Identity.Identity.Features.RegisterNewUser.Commands.V1;
namespace Integration.Test.Fakes; namespace Integration.Test.Fakes;
@ -8,7 +7,7 @@ public class FakeRegisterNewUserCommand : AutoFaker<RegisterNewUserCommand>
{ {
public FakeRegisterNewUserCommand() public FakeRegisterNewUserCommand()
{ {
RuleFor(r => r.Username, _ => "TestUser"); RuleFor(r => r.Username, x => x.Random.Uuid().ToString());
RuleFor(r => r.Password, _ => "Password@123"); RuleFor(r => r.Password, _ => "Password@123");
RuleFor(r => r.ConfirmPassword, _ => "Password@123"); RuleFor(r => r.ConfirmPassword, _ => "Password@123");
RuleFor(r => r.Email, _ => "test@test.com"); RuleFor(r => r.Email, _ => "test@test.com");