mirror of
https://github.com/alex289/CleanArchitecture.git
synced 2025-08-22 19:28:34 +00:00
feat: Use TestContainers for integration tests (#56)
This commit is contained in:
commit
6a53c747aa
@ -72,7 +72,7 @@ builder.Services.AddLogging(x => x.AddSimpleConsole(console =>
|
||||
console.IncludeScopes = true;
|
||||
}));
|
||||
|
||||
if (builder.Environment.IsProduction())
|
||||
if (builder.Environment.IsProduction() || !string.IsNullOrWhiteSpace(builder.Configuration["RedisHostName"]))
|
||||
{
|
||||
builder.Services.AddStackExchangeRedisCache(options =>
|
||||
{
|
||||
@ -95,12 +95,8 @@ using (var scope = app.Services.CreateScope())
|
||||
var domainStoreDbContext = services.GetRequiredService<DomainNotificationStoreDbContext>();
|
||||
|
||||
appDbContext.EnsureMigrationsApplied();
|
||||
|
||||
if (app.Environment.EnvironmentName != "Integration")
|
||||
{
|
||||
storeDbContext.EnsureMigrationsApplied();
|
||||
domainStoreDbContext.EnsureMigrationsApplied();
|
||||
}
|
||||
storeDbContext.EnsureMigrationsApplied();
|
||||
domainStoreDbContext.EnsureMigrationsApplied();
|
||||
}
|
||||
|
||||
if (app.Environment.IsDevelopment())
|
||||
|
@ -13,6 +13,7 @@
|
||||
"Audience": "CleanArchitectureClient",
|
||||
"Secret": "sD3v061gf8BxXgmxcHssasjdlkasjd87439284)@#(*"
|
||||
},
|
||||
"RedisHostName": "",
|
||||
"RabbitMQ": {
|
||||
"Host": "localhost",
|
||||
"Username": "guest",
|
||||
|
@ -5,6 +5,7 @@
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"RedisHostName": "",
|
||||
"RabbitMQ": {
|
||||
"Host": "localhost",
|
||||
"Username": "guest",
|
||||
|
@ -10,10 +10,13 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.4" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="8.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||
<PackageReference Include="Respawn" Version="6.2.1" />
|
||||
<PackageReference Include="Testcontainers" Version="3.8.0" />
|
||||
<PackageReference Include="Testcontainers.MsSql" Version="3.8.0" />
|
||||
<PackageReference Include="Testcontainers.RabbitMq" Version="3.8.0" />
|
||||
<PackageReference Include="Testcontainers.Redis" Version="3.8.0" />
|
||||
<PackageReference Include="xunit" Version="2.7.0" />
|
||||
<PackageReference Include="Xunit.Priority" Version="1.1.6" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.7">
|
||||
|
@ -1,46 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace CleanArchitecture.IntegrationTests.Extensions;
|
||||
|
||||
public static class FunctionalTestsServiceCollectionExtensions
|
||||
{
|
||||
public static IServiceCollection SetupTestDatabase<TContext>(this IServiceCollection services,
|
||||
DbConnection connection) where TContext : DbContext
|
||||
{
|
||||
var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<TContext>));
|
||||
if (descriptor is not null)
|
||||
services.Remove(descriptor);
|
||||
|
||||
services.AddScoped(p =>
|
||||
DbContextOptionsFactory<TContext>(
|
||||
p,
|
||||
(_, options) => options
|
||||
.ConfigureWarnings(b => b.Log(CoreEventId.ManyServiceProvidersCreatedWarning))
|
||||
.UseLazyLoadingProxies()
|
||||
.UseSqlite(connection)));
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
private static DbContextOptions<TContext> DbContextOptionsFactory<TContext>(
|
||||
IServiceProvider applicationServiceProvider,
|
||||
Action<IServiceProvider, DbContextOptionsBuilder> optionsAction)
|
||||
where TContext : DbContext
|
||||
{
|
||||
var builder = new DbContextOptionsBuilder<TContext>(
|
||||
new DbContextOptions<TContext>(new Dictionary<Type, IDbContextOptionsExtension>()));
|
||||
|
||||
builder.UseApplicationServiceProvider(applicationServiceProvider);
|
||||
|
||||
optionsAction.Invoke(applicationServiceProvider, builder);
|
||||
|
||||
return builder.Options;
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using CleanArchitecture.IntegrationTests.Infrastructure;
|
||||
using Xunit;
|
||||
|
||||
namespace CleanArchitecture.IntegrationTests.Fixtures;
|
||||
|
||||
public sealed class AccessorFixture : IAsyncLifetime
|
||||
{
|
||||
public static string TestRunDbName { get; } = $"CleanArchitecture-Integration-{Guid.NewGuid()}";
|
||||
|
||||
public async Task DisposeAsync()
|
||||
{
|
||||
var db = DatabaseAccessor.GetOrCreateAsync(TestRunDbName);
|
||||
await db.DisposeAsync();
|
||||
|
||||
var redis = RedisAccessor.GetOrCreateAsync();
|
||||
await redis.DisposeAsync();
|
||||
|
||||
var rabbit = RabbitmqAccessor.GetOrCreateAsync();
|
||||
await rabbit.DisposeAsync();
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
var db = DatabaseAccessor.GetOrCreateAsync(TestRunDbName);
|
||||
await db.InitializeAsync();
|
||||
|
||||
var redis = RedisAccessor.GetOrCreateAsync();
|
||||
await redis.InitializeAsync();
|
||||
|
||||
var rabbit = RabbitmqAccessor.GetOrCreateAsync();
|
||||
await rabbit.InitializeAsync();
|
||||
}
|
||||
}
|
@ -1,27 +1,28 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using CleanArchitecture.Domain.Constants;
|
||||
using CleanArchitecture.Domain.Entities;
|
||||
using CleanArchitecture.Domain.Enums;
|
||||
using CleanArchitecture.Infrastructure.Database;
|
||||
using CleanArchitecture.IntegrationTests.Infrastructure;
|
||||
using CleanArchitecture.IntegrationTests.Infrastructure.Auth;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit;
|
||||
|
||||
namespace CleanArchitecture.IntegrationTests.Fixtures;
|
||||
|
||||
public class TestFixtureBase
|
||||
public class TestFixtureBase : IAsyncLifetime
|
||||
{
|
||||
public HttpClient ServerClient { get; }
|
||||
protected WebApplicationFactory<Program> Factory { get; }
|
||||
protected CleanArchitectureWebApplicationFactory Factory { get; }
|
||||
|
||||
public TestFixtureBase(bool useTestAuthentication = true)
|
||||
{
|
||||
Factory = new CleanArchitectureWebApplicationFactory(
|
||||
SeedTestData,
|
||||
RegisterCustomServicesHandler,
|
||||
useTestAuthentication);
|
||||
useTestAuthentication,
|
||||
AccessorFixture.TestRunDbName);
|
||||
|
||||
ServerClient = Factory.CreateClient();
|
||||
ServerClient.Timeout = TimeSpan.FromMinutes(5);
|
||||
@ -29,17 +30,39 @@ public class TestFixtureBase
|
||||
|
||||
protected virtual void SeedTestData(ApplicationDbContext context)
|
||||
{
|
||||
context.Users.Add(new User(
|
||||
}
|
||||
|
||||
private async Task PrepareDatabaseAsync()
|
||||
{
|
||||
await Factory.RespawnDatabaseAsync();
|
||||
|
||||
using var scope = Factory.Services.CreateScope();
|
||||
await using var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
|
||||
dbContext.Tenants.Add(new Tenant(
|
||||
Ids.Seed.TenantId,
|
||||
"Admin Tenant"));
|
||||
|
||||
dbContext.Users.Add(new User(
|
||||
Ids.Seed.UserId,
|
||||
Ids.Seed.TenantId,
|
||||
"admin@email.com",
|
||||
"Admin",
|
||||
"User",
|
||||
"$2a$12$Blal/uiFIJdYsCLTMUik/egLbfg3XhbnxBC6Sb5IKz2ZYhiU/MzL2",
|
||||
UserRole.Admin));
|
||||
|
||||
dbContext.Users.Add(new User(
|
||||
TestAuthenticationOptions.TestUserId,
|
||||
Ids.Seed.TenantId,
|
||||
TestAuthenticationOptions.Email,
|
||||
TestAuthenticationOptions.FirstName,
|
||||
TestAuthenticationOptions.LastName,
|
||||
// !Password123#
|
||||
"$2a$12$Blal/uiFIJdYsCLTMUik/egLbfg3XhbnxBC6Sb5IKz2ZYhiU/MzL2",
|
||||
TestAuthenticationOptions.Password,
|
||||
UserRole.Admin));
|
||||
|
||||
context.SaveChanges();
|
||||
SeedTestData(dbContext);
|
||||
await dbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
protected virtual void RegisterCustomServicesHandler(
|
||||
@ -48,4 +71,14 @@ public class TestFixtureBase
|
||||
IServiceProvider scopedServices)
|
||||
{
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
await PrepareDatabaseAsync();
|
||||
}
|
||||
|
||||
public Task DisposeAsync()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ public sealed class TestAuthenticationOptions : AuthenticationSchemeOptions
|
||||
public const string Email = "integration@tests.com";
|
||||
public const string FirstName = "Integration";
|
||||
public const string LastName = "Tests";
|
||||
public const string Password = "$2a$12$Blal/uiFIJdYsCLTMUik/egLbfg3XhbnxBC6Sb5IKz2ZYhiU/MzL2";
|
||||
public static Guid TestUserId = new("561e4300-94d6-4c3f-adf5-31c1bdbc64df");
|
||||
|
||||
public ClaimsIdentity Identity { get; } = new(
|
||||
|
@ -1,13 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using CleanArchitecture.Infrastructure.Database;
|
||||
using CleanArchitecture.Infrastructure.Extensions;
|
||||
using CleanArchitecture.IntegrationTests.Extensions;
|
||||
using CleanArchitecture.IntegrationTests.Infrastructure.Auth;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace CleanArchitecture.IntegrationTests.Infrastructure;
|
||||
@ -21,20 +19,19 @@ public sealed class CleanArchitectureWebApplicationFactory : WebApplicationFacto
|
||||
ServiceProvider serviceProvider,
|
||||
IServiceProvider scopedServices);
|
||||
|
||||
private readonly AddCustomSeedDataHandler? _addCustomSeedDataHandler;
|
||||
private readonly bool _addTestAuthentication;
|
||||
private readonly string _instanceDatabaseName;
|
||||
|
||||
private readonly SqliteConnection _connection = new("DataSource=:memory:");
|
||||
private readonly bool _addTestAuthentication;
|
||||
private readonly RegisterCustomServicesHandler? _registerCustomServicesHandler;
|
||||
|
||||
public CleanArchitectureWebApplicationFactory(
|
||||
AddCustomSeedDataHandler? addCustomSeedDataHandler,
|
||||
RegisterCustomServicesHandler? registerCustomServicesHandler,
|
||||
bool addTestAuthentication)
|
||||
bool addTestAuthentication,
|
||||
string instanceDatabaseName)
|
||||
{
|
||||
_addCustomSeedDataHandler = addCustomSeedDataHandler;
|
||||
_registerCustomServicesHandler = registerCustomServicesHandler;
|
||||
_addTestAuthentication = addTestAuthentication;
|
||||
_instanceDatabaseName = instanceDatabaseName;
|
||||
}
|
||||
|
||||
protected override void ConfigureWebHost(IWebHostBuilder builder)
|
||||
@ -43,14 +40,35 @@ public sealed class CleanArchitectureWebApplicationFactory : WebApplicationFacto
|
||||
|
||||
base.ConfigureWebHost(builder);
|
||||
|
||||
_connection.Open();
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.Build();
|
||||
|
||||
builder.ConfigureAppConfiguration(configurationBuilder =>
|
||||
{
|
||||
configurationBuilder.AddEnvironmentVariables();
|
||||
|
||||
var dbAccessor = DatabaseAccessor.GetOrCreateAsync(_instanceDatabaseName);
|
||||
var redisAccessor = RedisAccessor.GetOrCreateAsync();
|
||||
var rabbitAccessor = RabbitmqAccessor.GetOrCreateAsync();
|
||||
|
||||
// Overwrite default connection strings
|
||||
configurationBuilder.AddInMemoryCollection([
|
||||
new KeyValuePair<string, string?>(
|
||||
"ConnectionStrings:DefaultConnection",
|
||||
dbAccessor.GetConnectionString()),
|
||||
new KeyValuePair<string, string?>(
|
||||
"RedisHostName",
|
||||
redisAccessor.GetConnectionString()),
|
||||
new KeyValuePair<string, string?>(
|
||||
"RabbitMQ:Host",
|
||||
rabbitAccessor.GetConnectionString())
|
||||
]);
|
||||
|
||||
configuration = configurationBuilder.Build();
|
||||
});
|
||||
|
||||
builder.ConfigureServices(services =>
|
||||
{
|
||||
services.SetupTestDatabase<ApplicationDbContext>(_connection);
|
||||
services.SetupTestDatabase<EventStoreDbContext>(_connection);
|
||||
services.SetupTestDatabase<DomainNotificationStoreDbContext>(_connection);
|
||||
|
||||
if (_addTestAuthentication)
|
||||
{
|
||||
services.AddAuthentication(options =>
|
||||
@ -65,22 +83,37 @@ public sealed class CleanArchitectureWebApplicationFactory : WebApplicationFacto
|
||||
using var scope = sp.CreateScope();
|
||||
var scopedServices = scope.ServiceProvider;
|
||||
|
||||
var applicationDbContext = scopedServices.GetRequiredService<ApplicationDbContext>();
|
||||
var storeDbContext = scopedServices.GetRequiredService<EventStoreDbContext>();
|
||||
var domainStoreDbContext = scopedServices.GetRequiredService<DomainNotificationStoreDbContext>();
|
||||
var dbAccessor = DatabaseAccessor.GetOrCreateAsync(_instanceDatabaseName);
|
||||
dbAccessor.CreateDatabase(scopedServices);
|
||||
|
||||
applicationDbContext.EnsureMigrationsApplied();
|
||||
var redisAccessor = RedisAccessor.GetOrCreateAsync();
|
||||
redisAccessor.RegisterRedis(services, configuration);
|
||||
|
||||
var creator2 = (RelationalDatabaseCreator)storeDbContext.Database
|
||||
.GetService<IRelationalDatabaseCreator>();
|
||||
creator2.CreateTables();
|
||||
var rabbitAccessor = RabbitmqAccessor.GetOrCreateAsync();
|
||||
rabbitAccessor.RegisterRabbitmq(services, configuration);
|
||||
|
||||
var creator3 = (RelationalDatabaseCreator)domainStoreDbContext
|
||||
.Database.GetService<IRelationalDatabaseCreator>();
|
||||
creator3.CreateTables();
|
||||
|
||||
_addCustomSeedDataHandler?.Invoke(applicationDbContext);
|
||||
_registerCustomServicesHandler?.Invoke(services, sp, scopedServices);
|
||||
});
|
||||
}
|
||||
|
||||
public async Task RespawnDatabaseAsync()
|
||||
{
|
||||
var dbAccessor = DatabaseAccessor.GetOrCreateAsync(_instanceDatabaseName);
|
||||
await dbAccessor.RespawnDatabaseAsync();
|
||||
|
||||
var redisAccessor = RedisAccessor.GetOrCreateAsync();
|
||||
redisAccessor.ResetRedis();
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
var dbAccessor = DatabaseAccessor.GetOrCreateAsync(_instanceDatabaseName);
|
||||
await dbAccessor.DisposeAsync();
|
||||
|
||||
var redisAccessor = RedisAccessor.GetOrCreateAsync();
|
||||
await redisAccessor.DisposeAsync();
|
||||
|
||||
var rabbitAccessor = RabbitmqAccessor.GetOrCreateAsync();
|
||||
await rabbitAccessor.DisposeAsync();
|
||||
}
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading.Tasks;
|
||||
using CleanArchitecture.Infrastructure.Database;
|
||||
using CleanArchitecture.Infrastructure.Extensions;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Respawn;
|
||||
using Testcontainers.MsSql;
|
||||
|
||||
namespace CleanArchitecture.IntegrationTests.Infrastructure;
|
||||
|
||||
public sealed class DatabaseAccessor
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DatabaseAccessor> s_accessors = new();
|
||||
|
||||
private readonly string _instanceDatabaseName;
|
||||
private bool _databaseCreated = false;
|
||||
private readonly object _databaseCreationLock = new();
|
||||
|
||||
private const string _dbPassword = "234#AD224fD#ss";
|
||||
private static readonly MsSqlContainer s_dbContainer = new MsSqlBuilder()
|
||||
.WithPassword(_dbPassword)
|
||||
.WithPortBinding(1433)
|
||||
.Build();
|
||||
|
||||
public DatabaseAccessor(string instanceName)
|
||||
{
|
||||
_instanceDatabaseName = instanceName;
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
await s_dbContainer.StartAsync();
|
||||
}
|
||||
|
||||
public ApplicationDbContext CreateDatabase(IServiceProvider scopedServices)
|
||||
{
|
||||
var applicationDbContext = scopedServices.GetRequiredService<ApplicationDbContext>();
|
||||
|
||||
lock (_databaseCreationLock)
|
||||
{
|
||||
if (_databaseCreated)
|
||||
{
|
||||
return applicationDbContext;
|
||||
}
|
||||
|
||||
applicationDbContext.EnsureMigrationsApplied();
|
||||
|
||||
var eventsContext = scopedServices.GetRequiredService<EventStoreDbContext>();
|
||||
eventsContext.EnsureMigrationsApplied();
|
||||
|
||||
var notificationsContext = scopedServices.GetRequiredService<DomainNotificationStoreDbContext>();
|
||||
notificationsContext.EnsureMigrationsApplied();
|
||||
}
|
||||
|
||||
_databaseCreated = true;
|
||||
|
||||
return applicationDbContext;
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
// Reset the database to its original state
|
||||
var dropScript = $@"
|
||||
USE MASTER;
|
||||
|
||||
ALTER DATABASE [{_instanceDatabaseName}]
|
||||
SET multi_user WITH ROLLBACK IMMEDIATE;
|
||||
|
||||
ALTER DATABASE [{_instanceDatabaseName}]
|
||||
SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
|
||||
|
||||
DROP DATABASE [{_instanceDatabaseName}];";
|
||||
|
||||
await using (var con = new SqlConnection(GetConnectionString()))
|
||||
{
|
||||
await con.OpenAsync();
|
||||
|
||||
var cmd = new SqlCommand(dropScript, con);
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
await s_dbContainer.DisposeAsync();
|
||||
}
|
||||
|
||||
public async Task RespawnDatabaseAsync()
|
||||
{
|
||||
var connectionString = GetConnectionString();
|
||||
|
||||
var respawn = await Respawner.CreateAsync(
|
||||
connectionString,
|
||||
new RespawnerOptions
|
||||
{
|
||||
TablesToIgnore = ["__EFMigrationsHistory"]
|
||||
});
|
||||
|
||||
await respawn.ResetAsync(connectionString);
|
||||
}
|
||||
|
||||
public string GetConnectionString()
|
||||
{
|
||||
var conBuilder = new SqlConnectionStringBuilder()
|
||||
{
|
||||
DataSource = s_dbContainer.Hostname,
|
||||
InitialCatalog = _instanceDatabaseName,
|
||||
IntegratedSecurity = false,
|
||||
Password = _dbPassword,
|
||||
UserID = "sa",
|
||||
TrustServerCertificate = true
|
||||
};
|
||||
|
||||
return conBuilder.ToString();
|
||||
}
|
||||
|
||||
public static DatabaseAccessor GetOrCreateAsync(string instanceName)
|
||||
{
|
||||
if (!s_accessors.TryGetValue(instanceName, out _))
|
||||
{
|
||||
var accessor = new DatabaseAccessor(instanceName);
|
||||
s_accessors.TryAdd(instanceName, accessor);
|
||||
}
|
||||
|
||||
return s_accessors[instanceName];
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
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 StackExchange.Redis;
|
||||
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 void ResetRedis()
|
||||
{
|
||||
var redis = ConnectionMultiplexer.Connect(GetConnectionString());
|
||||
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var endpoints = redis.GetEndPoints();
|
||||
foreach (var endpoint in endpoints)
|
||||
{
|
||||
var server = redis.GetServer(endpoint);
|
||||
var keys = server.Keys();
|
||||
foreach (var key in keys)
|
||||
{
|
||||
db.KeyDelete(key);
|
||||
}
|
||||
}
|
||||
|
||||
redis.Close();
|
||||
}
|
||||
|
||||
public static RedisAccessor GetOrCreateAsync()
|
||||
{
|
||||
return s_accessors.GetOrAdd("redis", _ => new RedisAccessor());
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
using CleanArchitecture.IntegrationTests.Fixtures;
|
||||
using Xunit;
|
||||
|
||||
namespace CleanArchitecture.IntegrationTests;
|
||||
|
||||
[CollectionDefinition("IntegrationTests", DisableParallelization = true)]
|
||||
public sealed class IntegrationTestsCollection :
|
||||
ICollectionFixture<AccessorFixture>
|
||||
{
|
||||
}
|
@ -5,9 +5,12 @@ using CleanArchitecture.IntegrationTests.Fixtures.gRPC;
|
||||
using CleanArchitecture.Proto.Tenants;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
using Xunit.Priority;
|
||||
|
||||
namespace CleanArchitecture.IntegrationTests.gRPC;
|
||||
|
||||
[Collection("IntegrationTests")]
|
||||
[TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)]
|
||||
public sealed class GetTenantsByIdsTests : IClassFixture<GetTenantsByIdsTestFixture>
|
||||
{
|
||||
private readonly GetTenantsByIdsTestFixture _fixture;
|
||||
|
@ -4,9 +4,12 @@ using CleanArchitecture.IntegrationTests.Fixtures.gRPC;
|
||||
using CleanArchitecture.Proto.Users;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
using Xunit.Priority;
|
||||
|
||||
namespace CleanArchitecture.IntegrationTests.gRPC;
|
||||
|
||||
[Collection("IntegrationTests")]
|
||||
[TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)]
|
||||
public sealed class GetUsersByIdsTests : IClassFixture<GetUsersByIdsTestFixture>
|
||||
{
|
||||
private readonly GetUsersByIdsTestFixture _fixture;
|
||||
|
Loading…
Reference in New Issue
Block a user