diff --git a/CleanArchitecture.Api/Program.cs b/CleanArchitecture.Api/Program.cs index 4a88a42..62a34d3 100644 --- a/CleanArchitecture.Api/Program.cs +++ b/CleanArchitecture.Api/Program.cs @@ -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(); appDbContext.EnsureMigrationsApplied(); - - if (app.Environment.EnvironmentName != "Integration") - { - storeDbContext.EnsureMigrationsApplied(); - domainStoreDbContext.EnsureMigrationsApplied(); - } + storeDbContext.EnsureMigrationsApplied(); + domainStoreDbContext.EnsureMigrationsApplied(); } if (app.Environment.IsDevelopment()) diff --git a/CleanArchitecture.Api/appsettings.Development.json b/CleanArchitecture.Api/appsettings.Development.json index 3506cff..342ecd0 100644 --- a/CleanArchitecture.Api/appsettings.Development.json +++ b/CleanArchitecture.Api/appsettings.Development.json @@ -13,6 +13,7 @@ "Audience": "CleanArchitectureClient", "Secret": "sD3v061gf8BxXgmxcHssasjdlkasjd87439284)@#(*" }, + "RedisHostName": "", "RabbitMQ": { "Host": "localhost", "Username": "guest", diff --git a/CleanArchitecture.Api/appsettings.Integration.json b/CleanArchitecture.Api/appsettings.Integration.json index 1913b40..973f184 100644 --- a/CleanArchitecture.Api/appsettings.Integration.json +++ b/CleanArchitecture.Api/appsettings.Integration.json @@ -5,6 +5,7 @@ "Microsoft.AspNetCore": "Warning" } }, + "RedisHostName": "", "RabbitMQ": { "Host": "localhost", "Username": "guest", diff --git a/CleanArchitecture.IntegrationTests/CleanArchitecture.IntegrationTests.csproj b/CleanArchitecture.IntegrationTests/CleanArchitecture.IntegrationTests.csproj index 71a3270..992b24b 100644 --- a/CleanArchitecture.IntegrationTests/CleanArchitecture.IntegrationTests.csproj +++ b/CleanArchitecture.IntegrationTests/CleanArchitecture.IntegrationTests.csproj @@ -10,10 +10,13 @@ - - + + + + + diff --git a/CleanArchitecture.IntegrationTests/Extensions/FunctionalTestsServiceCollectionExtensions.cs b/CleanArchitecture.IntegrationTests/Extensions/FunctionalTestsServiceCollectionExtensions.cs deleted file mode 100644 index f3c9b08..0000000 --- a/CleanArchitecture.IntegrationTests/Extensions/FunctionalTestsServiceCollectionExtensions.cs +++ /dev/null @@ -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(this IServiceCollection services, - DbConnection connection) where TContext : DbContext - { - var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions)); - if (descriptor is not null) - services.Remove(descriptor); - - services.AddScoped(p => - DbContextOptionsFactory( - p, - (_, options) => options - .ConfigureWarnings(b => b.Log(CoreEventId.ManyServiceProvidersCreatedWarning)) - .UseLazyLoadingProxies() - .UseSqlite(connection))); - - return services; - } - - private static DbContextOptions DbContextOptionsFactory( - IServiceProvider applicationServiceProvider, - Action optionsAction) - where TContext : DbContext - { - var builder = new DbContextOptionsBuilder( - new DbContextOptions(new Dictionary())); - - builder.UseApplicationServiceProvider(applicationServiceProvider); - - optionsAction.Invoke(applicationServiceProvider, builder); - - return builder.Options; - } -} \ No newline at end of file diff --git a/CleanArchitecture.IntegrationTests/Fixtures/AccessorFixture.cs b/CleanArchitecture.IntegrationTests/Fixtures/AccessorFixture.cs new file mode 100644 index 0000000..a98b43d --- /dev/null +++ b/CleanArchitecture.IntegrationTests/Fixtures/AccessorFixture.cs @@ -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(); + } +} \ No newline at end of file diff --git a/CleanArchitecture.IntegrationTests/Fixtures/TestFixtureBase.cs b/CleanArchitecture.IntegrationTests/Fixtures/TestFixtureBase.cs index 6a8d633..3ba93ae 100644 --- a/CleanArchitecture.IntegrationTests/Fixtures/TestFixtureBase.cs +++ b/CleanArchitecture.IntegrationTests/Fixtures/TestFixtureBase.cs @@ -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 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(); + + 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; + } } \ No newline at end of file diff --git a/CleanArchitecture.IntegrationTests/Infrastructure/Auth/TestAuthenticationOptions.cs b/CleanArchitecture.IntegrationTests/Infrastructure/Auth/TestAuthenticationOptions.cs index 1eb58b4..967f8bf 100644 --- a/CleanArchitecture.IntegrationTests/Infrastructure/Auth/TestAuthenticationOptions.cs +++ b/CleanArchitecture.IntegrationTests/Infrastructure/Auth/TestAuthenticationOptions.cs @@ -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( diff --git a/CleanArchitecture.IntegrationTests/Infrastructure/CleanArchitectureWebApplicationFactory.cs b/CleanArchitecture.IntegrationTests/Infrastructure/CleanArchitectureWebApplicationFactory.cs index da21af7..2fde656 100644 --- a/CleanArchitecture.IntegrationTests/Infrastructure/CleanArchitectureWebApplicationFactory.cs +++ b/CleanArchitecture.IntegrationTests/Infrastructure/CleanArchitectureWebApplicationFactory.cs @@ -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( + "ConnectionStrings:DefaultConnection", + dbAccessor.GetConnectionString()), + new KeyValuePair( + "RedisHostName", + redisAccessor.GetConnectionString()), + new KeyValuePair( + "RabbitMQ:Host", + rabbitAccessor.GetConnectionString()) + ]); + + configuration = configurationBuilder.Build(); + }); builder.ConfigureServices(services => { - services.SetupTestDatabase(_connection); - services.SetupTestDatabase(_connection); - services.SetupTestDatabase(_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(); - var storeDbContext = scopedServices.GetRequiredService(); - var domainStoreDbContext = scopedServices.GetRequiredService(); + var dbAccessor = DatabaseAccessor.GetOrCreateAsync(_instanceDatabaseName); + dbAccessor.CreateDatabase(scopedServices); - applicationDbContext.EnsureMigrationsApplied(); + var redisAccessor = RedisAccessor.GetOrCreateAsync(); + redisAccessor.RegisterRedis(services, configuration); - var creator2 = (RelationalDatabaseCreator)storeDbContext.Database - .GetService(); - creator2.CreateTables(); + var rabbitAccessor = RabbitmqAccessor.GetOrCreateAsync(); + rabbitAccessor.RegisterRabbitmq(services, configuration); - var creator3 = (RelationalDatabaseCreator)domainStoreDbContext - .Database.GetService(); - 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(); + } } \ No newline at end of file diff --git a/CleanArchitecture.IntegrationTests/Infrastructure/DatabaseAccessor.cs b/CleanArchitecture.IntegrationTests/Infrastructure/DatabaseAccessor.cs new file mode 100644 index 0000000..ab1d837 --- /dev/null +++ b/CleanArchitecture.IntegrationTests/Infrastructure/DatabaseAccessor.cs @@ -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 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(); + + lock (_databaseCreationLock) + { + if (_databaseCreated) + { + return applicationDbContext; + } + + applicationDbContext.EnsureMigrationsApplied(); + + var eventsContext = scopedServices.GetRequiredService(); + eventsContext.EnsureMigrationsApplied(); + + var notificationsContext = scopedServices.GetRequiredService(); + 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]; + } +} \ No newline at end of file diff --git a/CleanArchitecture.IntegrationTests/Infrastructure/RabbitmqAccessor.cs b/CleanArchitecture.IntegrationTests/Infrastructure/RabbitmqAccessor.cs new file mode 100644 index 0000000..0e5ea8d --- /dev/null +++ b/CleanArchitecture.IntegrationTests/Infrastructure/RabbitmqAccessor.cs @@ -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 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()); + } +} \ No newline at end of file diff --git a/CleanArchitecture.IntegrationTests/Infrastructure/RedisAccessor.cs b/CleanArchitecture.IntegrationTests/Infrastructure/RedisAccessor.cs new file mode 100644 index 0000000..f461ab8 --- /dev/null +++ b/CleanArchitecture.IntegrationTests/Infrastructure/RedisAccessor.cs @@ -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 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()); + } +} \ No newline at end of file diff --git a/CleanArchitecture.IntegrationTests/IntegrationTestsCollection.cs b/CleanArchitecture.IntegrationTests/IntegrationTestsCollection.cs new file mode 100644 index 0000000..f44a4e5 --- /dev/null +++ b/CleanArchitecture.IntegrationTests/IntegrationTestsCollection.cs @@ -0,0 +1,10 @@ +using CleanArchitecture.IntegrationTests.Fixtures; +using Xunit; + +namespace CleanArchitecture.IntegrationTests; + +[CollectionDefinition("IntegrationTests", DisableParallelization = true)] +public sealed class IntegrationTestsCollection : + ICollectionFixture +{ +} \ No newline at end of file diff --git a/CleanArchitecture.IntegrationTests/gRPC/GetTenantsByIdsTests.cs b/CleanArchitecture.IntegrationTests/gRPC/GetTenantsByIdsTests.cs index d6c2e8b..bef8dbf 100644 --- a/CleanArchitecture.IntegrationTests/gRPC/GetTenantsByIdsTests.cs +++ b/CleanArchitecture.IntegrationTests/gRPC/GetTenantsByIdsTests.cs @@ -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 { private readonly GetTenantsByIdsTestFixture _fixture; diff --git a/CleanArchitecture.IntegrationTests/gRPC/GetUsersByIdsTests.cs b/CleanArchitecture.IntegrationTests/gRPC/GetUsersByIdsTests.cs index 6576493..2353300 100644 --- a/CleanArchitecture.IntegrationTests/gRPC/GetUsersByIdsTests.cs +++ b/CleanArchitecture.IntegrationTests/gRPC/GetUsersByIdsTests.cs @@ -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 { private readonly GetUsersByIdsTestFixture _fixture;