0
0
mirror of https://github.com/alex289/CleanArchitecture.git synced 2025-07-05 21:43:56 +00:00

feat: Add rabbit and redis to integration tests

This commit is contained in:
alex289 2024-04-25 19:59:53 +02:00
parent dca566cbf6
commit 4a8c102d00
No known key found for this signature in database
GPG Key ID: 573F77CD2D87F863
10 changed files with 174 additions and 18 deletions

View File

@ -72,7 +72,7 @@ builder.Services.AddLogging(x => x.AddSimpleConsole(console =>
console.IncludeScopes = true; console.IncludeScopes = true;
})); }));
if (builder.Environment.IsProduction()) if (builder.Environment.IsProduction() || !string.IsNullOrWhiteSpace(builder.Configuration["RedisHostName"]))
{ {
builder.Services.AddStackExchangeRedisCache(options => builder.Services.AddStackExchangeRedisCache(options =>
{ {
@ -95,12 +95,8 @@ using (var scope = app.Services.CreateScope())
var domainStoreDbContext = services.GetRequiredService<DomainNotificationStoreDbContext>(); var domainStoreDbContext = services.GetRequiredService<DomainNotificationStoreDbContext>();
appDbContext.EnsureMigrationsApplied(); appDbContext.EnsureMigrationsApplied();
storeDbContext.EnsureMigrationsApplied();
if (app.Environment.EnvironmentName != "Integration") domainStoreDbContext.EnsureMigrationsApplied();
{
storeDbContext.EnsureMigrationsApplied();
domainStoreDbContext.EnsureMigrationsApplied();
}
} }
if (app.Environment.IsDevelopment()) if (app.Environment.IsDevelopment())

View File

@ -13,6 +13,7 @@
"Audience": "CleanArchitectureClient", "Audience": "CleanArchitectureClient",
"Secret": "sD3v061gf8BxXgmxcHssasjdlkasjd87439284)@#(*" "Secret": "sD3v061gf8BxXgmxcHssasjdlkasjd87439284)@#(*"
}, },
"RedisHostName": "",
"RabbitMQ": { "RabbitMQ": {
"Host": "localhost", "Host": "localhost",
"Username": "guest", "Username": "guest",

View File

@ -5,6 +5,7 @@
"Microsoft.AspNetCore": "Warning" "Microsoft.AspNetCore": "Warning"
} }
}, },
"RedisHostName": "",
"RabbitMQ": { "RabbitMQ": {
"Host": "localhost", "Host": "localhost",
"Username": "guest", "Username": "guest",

View File

@ -5,7 +5,7 @@ using Xunit;
namespace CleanArchitecture.IntegrationTests.Fixtures; namespace CleanArchitecture.IntegrationTests.Fixtures;
public sealed class DatabaseFixture : IAsyncLifetime public sealed class AccessorFixture : IAsyncLifetime
{ {
public static string TestRunDbName { get; } = $"CleanArchitecture-Integration-{Guid.NewGuid()}"; public static string TestRunDbName { get; } = $"CleanArchitecture-Integration-{Guid.NewGuid()}";
@ -13,11 +13,23 @@ public sealed class DatabaseFixture : IAsyncLifetime
{ {
var db = DatabaseAccessor.GetOrCreateAsync(TestRunDbName); var db = DatabaseAccessor.GetOrCreateAsync(TestRunDbName);
await db.DisposeAsync(); await db.DisposeAsync();
var redis = RedisAccessor.GetOrCreateAsync();
await redis.DisposeAsync();
var rabbit = RabbitmqAccessor.GetOrCreateAsync();
await rabbit.DisposeAsync();
} }
public async Task InitializeAsync() public async Task InitializeAsync()
{ {
var db = DatabaseAccessor.GetOrCreateAsync(TestRunDbName); var db = DatabaseAccessor.GetOrCreateAsync(TestRunDbName);
await db.InitializeAsync(); await db.InitializeAsync();
var redis = RedisAccessor.GetOrCreateAsync();
await redis.InitializeAsync();
var rabbit = RabbitmqAccessor.GetOrCreateAsync();
await rabbit.InitializeAsync();
} }
} }

View File

@ -22,7 +22,7 @@ public class TestFixtureBase : IAsyncLifetime
Factory = new CleanArchitectureWebApplicationFactory( Factory = new CleanArchitectureWebApplicationFactory(
RegisterCustomServicesHandler, RegisterCustomServicesHandler,
useTestAuthentication, useTestAuthentication,
DatabaseFixture.TestRunDbName); AccessorFixture.TestRunDbName);
ServerClient = Factory.CreateClient(); ServerClient = Factory.CreateClient();
ServerClient.Timeout = TimeSpan.FromMinutes(5); ServerClient.Timeout = TimeSpan.FromMinutes(5);

View File

@ -40,18 +40,31 @@ public sealed class CleanArchitectureWebApplicationFactory : WebApplicationFacto
base.ConfigureWebHost(builder); base.ConfigureWebHost(builder);
var configuration = new ConfigurationBuilder()
.Build();
builder.ConfigureAppConfiguration(configurationBuilder => builder.ConfigureAppConfiguration(configurationBuilder =>
{ {
configurationBuilder.AddEnvironmentVariables(); configurationBuilder.AddEnvironmentVariables();
var accessor = DatabaseAccessor.GetOrCreateAsync(_instanceDatabaseName); var dbAccessor = DatabaseAccessor.GetOrCreateAsync(_instanceDatabaseName);
var redisAccessor = RedisAccessor.GetOrCreateAsync();
var rabbitAccessor = RabbitmqAccessor.GetOrCreateAsync();
// Overwrite default connection string to our test instance db // Overwrite default connection strings
configurationBuilder.AddInMemoryCollection([ configurationBuilder.AddInMemoryCollection([
new KeyValuePair<string, string?>( new KeyValuePair<string, string?>(
"ConnectionStrings:DefaultConnection", "ConnectionStrings:DefaultConnection",
accessor.GetConnectionString()) dbAccessor.GetConnectionString()),
new KeyValuePair<string, string?>(
"RedisHostName",
redisAccessor.GetConnectionString()),
new KeyValuePair<string, string?>(
"RabbitMQ:Host",
rabbitAccessor.GetConnectionString())
]); ]);
configuration = configurationBuilder.Build();
}); });
builder.ConfigureServices(services => builder.ConfigureServices(services =>
@ -70,8 +83,14 @@ public sealed class CleanArchitectureWebApplicationFactory : WebApplicationFacto
using var scope = sp.CreateScope(); using var scope = sp.CreateScope();
var scopedServices = scope.ServiceProvider; var scopedServices = scope.ServiceProvider;
var accessor = DatabaseAccessor.GetOrCreateAsync(_instanceDatabaseName); var dbAccessor = DatabaseAccessor.GetOrCreateAsync(_instanceDatabaseName);
var applicationDbContext = accessor.CreateDatabase(scopedServices); dbAccessor.CreateDatabase(scopedServices);
var redisAccessor = RedisAccessor.GetOrCreateAsync();
redisAccessor.RegisterRedis(services, configuration);
var rabbitAccessor = RabbitmqAccessor.GetOrCreateAsync();
rabbitAccessor.RegisterRabbitmq(services, configuration);
_registerCustomServicesHandler?.Invoke(services, sp, scopedServices); _registerCustomServicesHandler?.Invoke(services, sp, scopedServices);
}); });
@ -85,7 +104,13 @@ public sealed class CleanArchitectureWebApplicationFactory : WebApplicationFacto
public override async ValueTask DisposeAsync() public override async ValueTask DisposeAsync()
{ {
var accessor = DatabaseAccessor.GetOrCreateAsync(_instanceDatabaseName); var dbAccessor = DatabaseAccessor.GetOrCreateAsync(_instanceDatabaseName);
await accessor.DisposeAsync(); await dbAccessor.DisposeAsync();
var redisAccessor = RedisAccessor.GetOrCreateAsync();
await redisAccessor.DisposeAsync();
var rabbitAccessor = RabbitmqAccessor.GetOrCreateAsync();
await rabbitAccessor.DisposeAsync();
} }
} }

View File

@ -18,7 +18,7 @@ public sealed class DatabaseAccessor
private bool _databaseCreated = false; private bool _databaseCreated = false;
private readonly object _databaseCreationLock = new(); private readonly object _databaseCreationLock = new();
private const string _dbPassword = "12345678##as"; private const string _dbPassword = "234#AD224fD#ss";
private static readonly MsSqlContainer s_dbContainer = new MsSqlBuilder() private static readonly MsSqlContainer s_dbContainer = new MsSqlBuilder()
.WithPassword(_dbPassword) .WithPassword(_dbPassword)
.WithPortBinding(1433) .WithPortBinding(1433)
@ -61,6 +61,7 @@ public sealed class DatabaseAccessor
public async ValueTask DisposeAsync() public async ValueTask DisposeAsync()
{ {
// Reset the database to its original state
var dropScript = $@" var dropScript = $@"
USE MASTER; USE MASTER;

View File

@ -0,0 +1,64 @@
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
using CleanArchitecture.Domain.Rabbitmq;
using CleanArchitecture.Domain.Rabbitmq.Extensions;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Testcontainers.RabbitMq;
using RabbitMqConfiguration = CleanArchitecture.Domain.Rabbitmq.RabbitMqConfiguration;
namespace CleanArchitecture.IntegrationTests.Infrastructure;
public sealed class RabbitmqAccessor
{
private static readonly ConcurrentDictionary<string, RabbitmqAccessor> s_accessors = new();
private static readonly RabbitMqContainer s_rabbitContainer = new RabbitMqBuilder()
.WithPortBinding(5672)
.Build();
public async Task InitializeAsync()
{
await s_rabbitContainer.StartAsync();
}
public async ValueTask DisposeAsync()
{
await s_rabbitContainer.DisposeAsync();
}
public string GetConnectionString()
{
return s_rabbitContainer.GetConnectionString();
}
public void RegisterRabbitmq(IServiceCollection serviceCollection, IConfiguration configuration)
{
var rabbitService = serviceCollection.FirstOrDefault(x =>
x.ServiceType == typeof(RabbitMqHandler));
if (rabbitService != null)
{
serviceCollection.Remove(rabbitService);
}
var rabbitConfig = serviceCollection.FirstOrDefault(x =>
x.ServiceType == typeof(RabbitMqConfiguration));
if (rabbitConfig != null)
{
serviceCollection.Remove(rabbitConfig);
}
serviceCollection.AddRabbitMqHandler(
configuration,
"RabbitMQ");
}
public static RabbitmqAccessor GetOrCreateAsync()
{
return s_accessors.GetOrAdd("rabbimq", _ => new RabbitmqAccessor());
}
}

View File

@ -0,0 +1,56 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Testcontainers.Redis;
namespace CleanArchitecture.IntegrationTests.Infrastructure;
public sealed class RedisAccessor
{
private static readonly ConcurrentDictionary<string, RedisAccessor> s_accessors = new();
private static readonly RedisContainer s_redisContainer = new RedisBuilder()
.WithPortBinding(6379)
.Build();
public async Task InitializeAsync()
{
await s_redisContainer.StartAsync();
}
public async ValueTask DisposeAsync()
{
await s_redisContainer.DisposeAsync();
}
public string GetConnectionString()
{
return s_redisContainer.GetConnectionString();
}
public void RegisterRedis(IServiceCollection serviceCollection, IConfiguration configuration)
{
var distributedCache = serviceCollection.FirstOrDefault(x =>
x.ServiceType == typeof(IDistributedCache));
if (distributedCache != null)
{
serviceCollection.Remove(distributedCache);
}
serviceCollection.AddStackExchangeRedisCache(options =>
{
options.Configuration = configuration["RedisHostName"];
options.InstanceName = "clean-architecture";
});
}
public static RedisAccessor GetOrCreateAsync()
{
return s_accessors.GetOrAdd("redis", _ => new RedisAccessor());
}
}

View File

@ -5,6 +5,6 @@ namespace CleanArchitecture.IntegrationTests;
[CollectionDefinition("IntegrationTests", DisableParallelization = true)] [CollectionDefinition("IntegrationTests", DisableParallelization = true)]
public sealed class IntegrationTestsCollection : public sealed class IntegrationTestsCollection :
ICollectionFixture<DatabaseFixture> ICollectionFixture<AccessorFixture>
{ {
} }