mirror of
https://github.com/alex289/CleanArchitecture.git
synced 2025-07-12 00:25:05 +00:00
feat: Use NUnit for Integration tests
This commit is contained in:
parent
4bea7d66a8
commit
336bfc1d11
@ -10,6 +10,6 @@
|
|||||||
"Host": "localhost",
|
"Host": "localhost",
|
||||||
"Username": "guest",
|
"Username": "guest",
|
||||||
"Password": "guest",
|
"Password": "guest",
|
||||||
"Enabled": "False"
|
"Enabled": "True"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
|
<IsTestProject>true</IsTestProject>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
|
<IsTestProject>true</IsTestProject>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
|
<IsTestProject>true</IsTestProject>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
|
<IsTestProject>true</IsTestProject>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -12,17 +13,17 @@
|
|||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.7" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.7" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.7" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.7" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
||||||
|
<PackageReference Include="NUnit" Version="4.1.0" />
|
||||||
|
<PackageReference Include="NUnit.Analyzers" Version="4.2.0">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
|
||||||
<PackageReference Include="Respawn" Version="6.2.1" />
|
<PackageReference Include="Respawn" Version="6.2.1" />
|
||||||
<PackageReference Include="Testcontainers" Version="3.9.0" />
|
<PackageReference Include="Testcontainers" Version="3.9.0" />
|
||||||
<PackageReference Include="Testcontainers.MsSql" Version="3.9.0" />
|
<PackageReference Include="Testcontainers.MsSql" Version="3.9.0" />
|
||||||
<PackageReference Include="Testcontainers.RabbitMq" Version="3.9.0" />
|
<PackageReference Include="Testcontainers.RabbitMq" Version="3.9.0" />
|
||||||
<PackageReference Include="Testcontainers.Redis" Version="3.9.0" />
|
<PackageReference Include="Testcontainers.Redis" Version="3.9.0" />
|
||||||
<PackageReference Include="xunit" Version="2.9.0" />
|
|
||||||
<PackageReference Include="Xunit.Priority" Version="1.1.6" />
|
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
@ -34,5 +35,7 @@
|
|||||||
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Using Include="NUnit.Framework" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
namespace CleanArchitecture.IntegrationTests.Constants;
|
||||||
|
|
||||||
|
public static class Configuration
|
||||||
|
{
|
||||||
|
public const int RedisPort = 6379;
|
||||||
|
public const int MsSqlPort = 1433;
|
||||||
|
public const int RabbitMqPort = 5673;
|
||||||
|
}
|
@ -7,24 +7,17 @@ using CleanArchitecture.Application.ViewModels.Tenants;
|
|||||||
using CleanArchitecture.IntegrationTests.Extensions;
|
using CleanArchitecture.IntegrationTests.Extensions;
|
||||||
using CleanArchitecture.IntegrationTests.Fixtures;
|
using CleanArchitecture.IntegrationTests.Fixtures;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Xunit;
|
|
||||||
using Xunit.Priority;
|
|
||||||
|
|
||||||
namespace CleanArchitecture.IntegrationTests.Controller;
|
namespace CleanArchitecture.IntegrationTests.Controller;
|
||||||
|
|
||||||
[Collection("IntegrationTests")]
|
public sealed class TenantControllerTests
|
||||||
[TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)]
|
|
||||||
public sealed class TenantControllerTests : IClassFixture<TenantTestFixture>
|
|
||||||
{
|
{
|
||||||
private readonly TenantTestFixture _fixture;
|
private readonly TenantTestFixture _fixture = new();
|
||||||
|
|
||||||
public TenantControllerTests(TenantTestFixture fixture)
|
[OneTimeSetUp]
|
||||||
{
|
public async Task Setup() => await _fixture.SeedTestData();
|
||||||
_fixture = fixture;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Test, Order(0)]
|
||||||
[Priority(0)]
|
|
||||||
public async Task Should_Get_Tenant_By_Id()
|
public async Task Should_Get_Tenant_By_Id()
|
||||||
{
|
{
|
||||||
var response = await _fixture.ServerClient.GetAsync($"/api/v1/Tenant/{_fixture.CreatedTenantId}");
|
var response = await _fixture.ServerClient.GetAsync($"/api/v1/Tenant/{_fixture.CreatedTenantId}");
|
||||||
@ -41,8 +34,7 @@ public sealed class TenantControllerTests : IClassFixture<TenantTestFixture>
|
|||||||
message.Data.Users.Count().Should().Be(1);
|
message.Data.Users.Count().Should().Be(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Test, Order(1)]
|
||||||
[Priority(5)]
|
|
||||||
public async Task Should_Get_All_Tenants()
|
public async Task Should_Get_All_Tenants()
|
||||||
{
|
{
|
||||||
var response = await _fixture.ServerClient.GetAsync(
|
var response = await _fixture.ServerClient.GetAsync(
|
||||||
@ -63,8 +55,7 @@ public sealed class TenantControllerTests : IClassFixture<TenantTestFixture>
|
|||||||
.Users.Count().Should().Be(1);
|
.Users.Count().Should().Be(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Test, Order(2)]
|
||||||
[Priority(10)]
|
|
||||||
public async Task Should_Create_Tenant()
|
public async Task Should_Create_Tenant()
|
||||||
{
|
{
|
||||||
var request = new CreateTenantViewModel("Test Tenant 2");
|
var request = new CreateTenantViewModel("Test Tenant 2");
|
||||||
@ -89,8 +80,7 @@ public sealed class TenantControllerTests : IClassFixture<TenantTestFixture>
|
|||||||
tenantMessage.Data.Name.Should().Be(request.Name);
|
tenantMessage.Data.Name.Should().Be(request.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Test, Order(3)]
|
||||||
[Priority(15)]
|
|
||||||
public async Task Should_Update_Tenant()
|
public async Task Should_Update_Tenant()
|
||||||
{
|
{
|
||||||
var request = new UpdateTenantViewModel(_fixture.CreatedTenantId, "Test Tenant 3");
|
var request = new UpdateTenantViewModel(_fixture.CreatedTenantId, "Test Tenant 3");
|
||||||
@ -117,8 +107,7 @@ public sealed class TenantControllerTests : IClassFixture<TenantTestFixture>
|
|||||||
tenantMessage.Data.Name.Should().Be(request.Name);
|
tenantMessage.Data.Name.Should().Be(request.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Test, Order(4)]
|
||||||
[Priority(20)]
|
|
||||||
public async Task Should_Delete_Tenant()
|
public async Task Should_Delete_Tenant()
|
||||||
{
|
{
|
||||||
var response = await _fixture.ServerClient.DeleteAsync($"/api/v1/Tenant/{_fixture.CreatedTenantId}");
|
var response = await _fixture.ServerClient.DeleteAsync($"/api/v1/Tenant/{_fixture.CreatedTenantId}");
|
||||||
|
@ -10,24 +10,17 @@ using CleanArchitecture.IntegrationTests.Extensions;
|
|||||||
using CleanArchitecture.IntegrationTests.Fixtures;
|
using CleanArchitecture.IntegrationTests.Fixtures;
|
||||||
using CleanArchitecture.IntegrationTests.Infrastructure.Auth;
|
using CleanArchitecture.IntegrationTests.Infrastructure.Auth;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Xunit;
|
|
||||||
using Xunit.Priority;
|
|
||||||
|
|
||||||
namespace CleanArchitecture.IntegrationTests.Controller;
|
namespace CleanArchitecture.IntegrationTests.Controller;
|
||||||
|
|
||||||
[Collection("IntegrationTests")]
|
public sealed class UserControllerTests
|
||||||
[TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)]
|
|
||||||
public sealed class UserControllerTests : IClassFixture<UserTestFixture>
|
|
||||||
{
|
{
|
||||||
private readonly UserTestFixture _fixture;
|
private readonly UserTestFixture _fixture = new();
|
||||||
|
|
||||||
public UserControllerTests(UserTestFixture fixture)
|
[OneTimeSetUp]
|
||||||
{
|
public async Task Setup() => await GlobalSetupFixture.RespawnDatabaseAsync();
|
||||||
_fixture = fixture;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Test, Order(0)]
|
||||||
[Priority(0)]
|
|
||||||
public async Task Should_Get_All_User()
|
public async Task Should_Get_All_User()
|
||||||
{
|
{
|
||||||
var response = await _fixture.ServerClient.GetAsync("/api/v1/user");
|
var response = await _fixture.ServerClient.GetAsync("/api/v1/user");
|
||||||
@ -50,8 +43,7 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
|
|||||||
currentUser.LastName.Should().Be(TestAuthenticationOptions.LastName);
|
currentUser.LastName.Should().Be(TestAuthenticationOptions.LastName);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Test, Order(1)]
|
||||||
[Priority(5)]
|
|
||||||
public async Task Should_Get_User_By_Id()
|
public async Task Should_Get_User_By_Id()
|
||||||
{
|
{
|
||||||
var response = await _fixture.ServerClient.GetAsync("/api/v1/user/" + TestAuthenticationOptions.TestUserId);
|
var response = await _fixture.ServerClient.GetAsync("/api/v1/user/" + TestAuthenticationOptions.TestUserId);
|
||||||
@ -70,8 +62,7 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
|
|||||||
content.LastName.Should().Be(TestAuthenticationOptions.LastName);
|
content.LastName.Should().Be(TestAuthenticationOptions.LastName);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Test, Order(2)]
|
||||||
[Priority(10)]
|
|
||||||
public async Task Should_Create_User()
|
public async Task Should_Create_User()
|
||||||
{
|
{
|
||||||
var user = new CreateUserViewModel(
|
var user = new CreateUserViewModel(
|
||||||
@ -89,8 +80,7 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
|
|||||||
message?.Data.Should().NotBeEmpty();
|
message?.Data.Should().NotBeEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Test, Order(3)]
|
||||||
[Priority(15)]
|
|
||||||
public async Task Should_Login_User()
|
public async Task Should_Login_User()
|
||||||
{
|
{
|
||||||
var user = new LoginUserViewModel(
|
var user = new LoginUserViewModel(
|
||||||
@ -105,8 +95,7 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
|
|||||||
message?.Data.Should().NotBeEmpty();
|
message?.Data.Should().NotBeEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Test, Order(4)]
|
||||||
[Priority(20)]
|
|
||||||
public async Task Should_Get_The_Current_Active_Users()
|
public async Task Should_Get_The_Current_Active_Users()
|
||||||
{
|
{
|
||||||
var response = await _fixture.ServerClient.GetAsync("/api/v1/user/me");
|
var response = await _fixture.ServerClient.GetAsync("/api/v1/user/me");
|
||||||
@ -125,8 +114,7 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
|
|||||||
content.LastName.Should().Be(TestAuthenticationOptions.LastName);
|
content.LastName.Should().Be(TestAuthenticationOptions.LastName);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Test, Order(5)]
|
||||||
[Priority(25)]
|
|
||||||
public async Task Should_Update_User()
|
public async Task Should_Update_User()
|
||||||
{
|
{
|
||||||
var user = new UpdateUserViewModel(
|
var user = new UpdateUserViewModel(
|
||||||
@ -167,8 +155,7 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
|
|||||||
userContent.Role.Should().Be(user.Role);
|
userContent.Role.Should().Be(user.Role);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Test, Order(6)]
|
||||||
[Priority(30)]
|
|
||||||
public async Task Should_Change_User_Password()
|
public async Task Should_Change_User_Password()
|
||||||
{
|
{
|
||||||
var user = new ChangePasswordViewModel(
|
var user = new ChangePasswordViewModel(
|
||||||
@ -201,8 +188,7 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
|
|||||||
loginMessage?.Data.Should().NotBeEmpty();
|
loginMessage?.Data.Should().NotBeEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Test, Order(7)]
|
||||||
[Priority(35)]
|
|
||||||
public async Task Should_Delete_User()
|
public async Task Should_Delete_User()
|
||||||
{
|
{
|
||||||
var response = await _fixture.ServerClient.DeleteAsync("/api/v1/user/" + TestAuthenticationOptions.TestUserId);
|
var response = await _fixture.ServerClient.DeleteAsync("/api/v1/user/" + TestAuthenticationOptions.TestUserId);
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
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,7 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.Domain.Entities;
|
using CleanArchitecture.Domain.Entities;
|
||||||
using CleanArchitecture.Domain.Enums;
|
using CleanArchitecture.Domain.Enums;
|
||||||
using CleanArchitecture.Infrastructure.Database;
|
using CleanArchitecture.Infrastructure.Database;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace CleanArchitecture.IntegrationTests.Fixtures;
|
namespace CleanArchitecture.IntegrationTests.Fixtures;
|
||||||
|
|
||||||
@ -9,9 +11,11 @@ public sealed class TenantTestFixture : TestFixtureBase
|
|||||||
{
|
{
|
||||||
public Guid CreatedTenantId { get; } = Guid.NewGuid();
|
public Guid CreatedTenantId { get; } = Guid.NewGuid();
|
||||||
|
|
||||||
protected override void SeedTestData(ApplicationDbContext context)
|
public async Task SeedTestData()
|
||||||
{
|
{
|
||||||
base.SeedTestData(context);
|
await GlobalSetupFixture.RespawnDatabaseAsync();
|
||||||
|
|
||||||
|
using var context = Factory.Services.GetRequiredService<ApplicationDbContext>();
|
||||||
|
|
||||||
context.Tenants.Add(new Tenant(
|
context.Tenants.Add(new Tenant(
|
||||||
CreatedTenantId,
|
CreatedTenantId,
|
||||||
@ -26,6 +30,6 @@ public sealed class TenantTestFixture : TestFixtureBase
|
|||||||
"Test User",
|
"Test User",
|
||||||
UserRole.User));
|
UserRole.User));
|
||||||
|
|
||||||
context.SaveChanges();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,18 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Net.Http;
|
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;
|
||||||
using CleanArchitecture.IntegrationTests.Infrastructure.Auth;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace CleanArchitecture.IntegrationTests.Fixtures;
|
namespace CleanArchitecture.IntegrationTests.Fixtures;
|
||||||
|
|
||||||
public class TestFixtureBase : IAsyncLifetime
|
public class TestFixtureBase
|
||||||
{
|
{
|
||||||
public HttpClient ServerClient { get; }
|
public HttpClient ServerClient { get; }
|
||||||
protected CleanArchitectureWebApplicationFactory Factory { get; }
|
protected CleanArchitectureWebApplicationFactory Factory { get; }
|
||||||
@ -21,64 +14,16 @@ public class TestFixtureBase : IAsyncLifetime
|
|||||||
{
|
{
|
||||||
Factory = new CleanArchitectureWebApplicationFactory(
|
Factory = new CleanArchitectureWebApplicationFactory(
|
||||||
RegisterCustomServicesHandler,
|
RegisterCustomServicesHandler,
|
||||||
useTestAuthentication,
|
useTestAuthentication);
|
||||||
AccessorFixture.TestRunDbName);
|
|
||||||
|
|
||||||
ServerClient = Factory.CreateClient();
|
ServerClient = Factory.CreateClient();
|
||||||
ServerClient.Timeout = TimeSpan.FromMinutes(5);
|
ServerClient.Timeout = TimeSpan.FromMinutes(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void SeedTestData(ApplicationDbContext context)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
TestAuthenticationOptions.Password,
|
|
||||||
UserRole.Admin));
|
|
||||||
|
|
||||||
SeedTestData(dbContext);
|
|
||||||
await dbContext.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void RegisterCustomServicesHandler(
|
protected virtual void RegisterCustomServicesHandler(
|
||||||
IServiceCollection services,
|
IServiceCollection services,
|
||||||
ServiceProvider serviceProvider,
|
ServiceProvider serviceProvider,
|
||||||
IServiceProvider scopedServices)
|
IServiceProvider scopedServices)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task InitializeAsync()
|
|
||||||
{
|
|
||||||
await PrepareDatabaseAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task DisposeAsync()
|
|
||||||
{
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,7 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.Domain.Entities;
|
using CleanArchitecture.Domain.Entities;
|
||||||
using CleanArchitecture.Infrastructure.Database;
|
using CleanArchitecture.Infrastructure.Database;
|
||||||
using Grpc.Net.Client;
|
using Grpc.Net.Client;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace CleanArchitecture.IntegrationTests.Fixtures.gRPC;
|
namespace CleanArchitecture.IntegrationTests.Fixtures.gRPC;
|
||||||
|
|
||||||
@ -18,14 +20,16 @@ public sealed class GetTenantsByIdsTestFixture : TestFixtureBase
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SeedTestData(ApplicationDbContext context)
|
public async Task SeedTestData()
|
||||||
{
|
{
|
||||||
base.SeedTestData(context);
|
await GlobalSetupFixture.RespawnDatabaseAsync();
|
||||||
|
|
||||||
|
using var context = Factory.Services.GetRequiredService<ApplicationDbContext>();
|
||||||
|
|
||||||
var tenant = CreateTenant();
|
var tenant = CreateTenant();
|
||||||
|
|
||||||
context.Tenants.Add(tenant);
|
context.Tenants.Add(tenant);
|
||||||
context.SaveChanges();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Tenant CreateTenant()
|
public Tenant CreateTenant()
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.Domain.Constants;
|
using CleanArchitecture.Domain.Constants;
|
||||||
using CleanArchitecture.Domain.Entities;
|
using CleanArchitecture.Domain.Entities;
|
||||||
using CleanArchitecture.Domain.Enums;
|
using CleanArchitecture.Domain.Enums;
|
||||||
using CleanArchitecture.Infrastructure.Database;
|
using CleanArchitecture.Infrastructure.Database;
|
||||||
using Grpc.Net.Client;
|
using Grpc.Net.Client;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace CleanArchitecture.IntegrationTests.Fixtures.gRPC;
|
namespace CleanArchitecture.IntegrationTests.Fixtures.gRPC;
|
||||||
|
|
||||||
@ -20,14 +22,16 @@ public sealed class GetUsersByIdsTestFixture : TestFixtureBase
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SeedTestData(ApplicationDbContext context)
|
public async Task SeedTestData()
|
||||||
{
|
{
|
||||||
base.SeedTestData(context);
|
await GlobalSetupFixture.RespawnDatabaseAsync();
|
||||||
|
|
||||||
|
using var context = Factory.Services.GetRequiredService<ApplicationDbContext>();
|
||||||
|
|
||||||
var user = CreateUser();
|
var user = CreateUser();
|
||||||
|
|
||||||
context.Users.Add(user);
|
context.Users.Add(user);
|
||||||
context.SaveChanges();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public User CreateUser()
|
public User CreateUser()
|
||||||
|
75
CleanArchitecture.IntegrationTests/GlobalSetupFixture.cs
Normal file
75
CleanArchitecture.IntegrationTests/GlobalSetupFixture.cs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CleanArchitecture.IntegrationTests.Constants;
|
||||||
|
using Respawn;
|
||||||
|
using Testcontainers.MsSql;
|
||||||
|
using Testcontainers.RabbitMq;
|
||||||
|
using Testcontainers.Redis;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.IntegrationTests;
|
||||||
|
|
||||||
|
[SetUpFixture]
|
||||||
|
internal class GlobalSetupFixture
|
||||||
|
{
|
||||||
|
private static Respawner? s_respawner;
|
||||||
|
|
||||||
|
public static MsSqlContainer DatabaseContainer { get; } = new MsSqlBuilder()
|
||||||
|
.WithPortBinding(Configuration.MsSqlPort, assignRandomHostPort: true)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
public static RedisContainer RedisContainer { get; } = new RedisBuilder()
|
||||||
|
.WithPortBinding(Configuration.RedisPort, assignRandomHostPort: true)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
public static RabbitMqContainer RabbitContainer { get; } = new RabbitMqBuilder()
|
||||||
|
.WithPortBinding(Configuration.RabbitMqPort, assignRandomHostPort: true)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
public static string DatabaseConnectionString { get; private set; } = string.Empty;
|
||||||
|
|
||||||
|
[OneTimeSetUp]
|
||||||
|
public async Task SetUp()
|
||||||
|
{
|
||||||
|
await DatabaseContainer.StartAsync();
|
||||||
|
await RedisContainer.StartAsync();
|
||||||
|
await RabbitContainer.StartAsync();
|
||||||
|
|
||||||
|
DatabaseConnectionString = DatabaseContainer
|
||||||
|
.GetConnectionString()
|
||||||
|
.Replace("Database=master", $"Database=clean-architecture-{Guid.NewGuid()}");
|
||||||
|
}
|
||||||
|
|
||||||
|
[OneTimeTearDown]
|
||||||
|
public async Task TearDown()
|
||||||
|
{
|
||||||
|
await DatabaseContainer.DisposeAsync();
|
||||||
|
await RedisContainer.DisposeAsync();
|
||||||
|
await RabbitContainer.DisposeAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task RespawnDatabaseAsync()
|
||||||
|
{
|
||||||
|
if (s_respawner is null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
s_respawner = await Respawner.CreateAsync(
|
||||||
|
DatabaseConnectionString,
|
||||||
|
new RespawnerOptions
|
||||||
|
{
|
||||||
|
TablesToIgnore = ["__EFMigrationsHistory"]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Creation of the respawner can fail if the database has not been created yet
|
||||||
|
TestContext.WriteLine($"Failed to create respawner: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_respawner is not null)
|
||||||
|
{
|
||||||
|
await s_respawner.ResetAsync(DatabaseConnectionString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using CleanArchitecture.IntegrationTests.Constants;
|
||||||
using CleanArchitecture.Infrastructure.Database;
|
|
||||||
using CleanArchitecture.IntegrationTests.Infrastructure.Auth;
|
using CleanArchitecture.IntegrationTests.Infrastructure.Auth;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Mvc.Testing;
|
using Microsoft.AspNetCore.Mvc.Testing;
|
||||||
@ -12,26 +11,20 @@ namespace CleanArchitecture.IntegrationTests.Infrastructure;
|
|||||||
|
|
||||||
public sealed class CleanArchitectureWebApplicationFactory : WebApplicationFactory<Program>
|
public sealed class CleanArchitectureWebApplicationFactory : WebApplicationFactory<Program>
|
||||||
{
|
{
|
||||||
public delegate void AddCustomSeedDataHandler(ApplicationDbContext context);
|
|
||||||
|
|
||||||
public delegate void RegisterCustomServicesHandler(
|
public delegate void RegisterCustomServicesHandler(
|
||||||
IServiceCollection services,
|
IServiceCollection services,
|
||||||
ServiceProvider serviceProvider,
|
ServiceProvider serviceProvider,
|
||||||
IServiceProvider scopedServices);
|
IServiceProvider scopedServices);
|
||||||
|
|
||||||
private readonly string _instanceDatabaseName;
|
|
||||||
|
|
||||||
private readonly bool _addTestAuthentication;
|
private readonly bool _addTestAuthentication;
|
||||||
private readonly RegisterCustomServicesHandler? _registerCustomServicesHandler;
|
private readonly RegisterCustomServicesHandler? _registerCustomServicesHandler;
|
||||||
|
|
||||||
public CleanArchitectureWebApplicationFactory(
|
public CleanArchitectureWebApplicationFactory(
|
||||||
RegisterCustomServicesHandler? registerCustomServicesHandler,
|
RegisterCustomServicesHandler? registerCustomServicesHandler,
|
||||||
bool addTestAuthentication,
|
bool addTestAuthentication)
|
||||||
string instanceDatabaseName)
|
|
||||||
{
|
{
|
||||||
_registerCustomServicesHandler = registerCustomServicesHandler;
|
_registerCustomServicesHandler = registerCustomServicesHandler;
|
||||||
_addTestAuthentication = addTestAuthentication;
|
_addTestAuthentication = addTestAuthentication;
|
||||||
_instanceDatabaseName = instanceDatabaseName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ConfigureWebHost(IWebHostBuilder builder)
|
protected override void ConfigureWebHost(IWebHostBuilder builder)
|
||||||
@ -40,31 +33,18 @@ public sealed class CleanArchitectureWebApplicationFactory : WebApplicationFacto
|
|||||||
|
|
||||||
base.ConfigureWebHost(builder);
|
base.ConfigureWebHost(builder);
|
||||||
|
|
||||||
var configuration = new ConfigurationBuilder()
|
builder.ConfigureAppConfiguration(configuration =>
|
||||||
.Build();
|
|
||||||
|
|
||||||
builder.ConfigureAppConfiguration(configurationBuilder =>
|
|
||||||
{
|
{
|
||||||
configurationBuilder.AddEnvironmentVariables();
|
var redisPort = GlobalSetupFixture.RedisContainer.GetMappedPublicPort(Configuration.RedisPort);
|
||||||
|
|
||||||
var dbAccessor = DatabaseAccessor.GetOrCreateAsync(_instanceDatabaseName);
|
configuration.AddInMemoryCollection([
|
||||||
var redisAccessor = RedisAccessor.GetOrCreateAsync();
|
|
||||||
var rabbitAccessor = RabbitmqAccessor.GetOrCreateAsync();
|
|
||||||
|
|
||||||
// Overwrite default connection strings
|
|
||||||
configurationBuilder.AddInMemoryCollection([
|
|
||||||
new KeyValuePair<string, string?>(
|
new KeyValuePair<string, string?>(
|
||||||
"ConnectionStrings:DefaultConnection",
|
"ConnectionStrings:DefaultConnection",
|
||||||
dbAccessor.GetConnectionString()),
|
GlobalSetupFixture.DatabaseConnectionString),
|
||||||
new KeyValuePair<string, string?>(
|
new KeyValuePair<string, string?>(
|
||||||
"RedisHostName",
|
"RedisStackExchange:RedisConfigString",
|
||||||
redisAccessor.GetConnectionString()),
|
$"localhost:{redisPort},abortConnect=true")
|
||||||
new KeyValuePair<string, string?>(
|
|
||||||
"RabbitMQ:Host",
|
|
||||||
rabbitAccessor.GetConnectionString())
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
configuration = configurationBuilder.Build();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.ConfigureServices(services =>
|
builder.ConfigureServices(services =>
|
||||||
@ -82,38 +62,7 @@ public sealed class CleanArchitectureWebApplicationFactory : WebApplicationFacto
|
|||||||
|
|
||||||
using var scope = sp.CreateScope();
|
using var scope = sp.CreateScope();
|
||||||
var scopedServices = scope.ServiceProvider;
|
var scopedServices = scope.ServiceProvider;
|
||||||
|
|
||||||
var dbAccessor = DatabaseAccessor.GetOrCreateAsync(_instanceDatabaseName);
|
|
||||||
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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,126 +0,0 @@
|
|||||||
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];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,76 +0,0 @@
|
|||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
using CleanArchitecture.IntegrationTests.Fixtures;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace CleanArchitecture.IntegrationTests;
|
|
||||||
|
|
||||||
[CollectionDefinition("IntegrationTests", DisableParallelization = true)]
|
|
||||||
public sealed class IntegrationTestsCollection :
|
|
||||||
ICollectionFixture<AccessorFixture>
|
|
||||||
{
|
|
||||||
}
|
|
@ -2,33 +2,30 @@ using System.Net;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.IntegrationTests.Fixtures;
|
using CleanArchitecture.IntegrationTests.Fixtures;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Xunit;
|
|
||||||
using Xunit.Priority;
|
|
||||||
|
|
||||||
namespace CleanArchitecture.IntegrationTests.UtilityTests;
|
namespace CleanArchitecture.IntegrationTests.UtilityTests;
|
||||||
|
|
||||||
[Collection("IntegrationTests")]
|
public sealed class AuthTests
|
||||||
[TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)]
|
|
||||||
public sealed class AuthTests : IClassFixture<AuthTestFixure>
|
|
||||||
{
|
{
|
||||||
private readonly AuthTestFixure _fixture;
|
private readonly AuthTestFixure _fixture = new();
|
||||||
|
|
||||||
public AuthTests(AuthTestFixure fixture)
|
[OneTimeSetUp]
|
||||||
{
|
public async Task Setup() => await GlobalSetupFixture.RespawnDatabaseAsync();
|
||||||
_fixture = fixture;
|
|
||||||
}
|
[Datapoints]
|
||||||
|
public string[] values =
|
||||||
|
[
|
||||||
|
"/api/v1/user",
|
||||||
|
"/api/v1/user/me",
|
||||||
|
"/api/v1/user/d74b112a-ece0-443d-9b4f-85bc418822ca",
|
||||||
|
"/api/v1/tenant",
|
||||||
|
"/api/v1/tenant/d74b112a-ece0-443d-9b4f-85bc418822ca"
|
||||||
|
];
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("/api/v1/user")]
|
public async Task Should_Get_Unauthorized_If_Trying_To_Call_Endpoint_Without_Token(string url)
|
||||||
[InlineData("/api/v1/user/me")]
|
|
||||||
[InlineData("/api/v1/user/d74b112a-ece0-443d-9b4f-85bc418822ca")]
|
|
||||||
[InlineData("/api/v1/tenant")]
|
|
||||||
[InlineData("/api/v1/tenant/d74b112a-ece0-443d-9b4f-85bc418822ca")]
|
|
||||||
public async Task Should_Get_Unauthorized_If_Trying_To_Call_Endpoint_Without_Token(
|
|
||||||
string url)
|
|
||||||
{
|
{
|
||||||
var response = await _fixture.ServerClient.GetAsync(url);
|
var response = await _fixture.ServerClient.GetAsync(url);
|
||||||
|
|
||||||
response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
|
response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,24 +4,17 @@ using CleanArchitecture.IntegrationTests.Fixtures;
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Xunit;
|
|
||||||
using Xunit.Priority;
|
|
||||||
|
|
||||||
namespace CleanArchitecture.IntegrationTests.UtilityTests;
|
namespace CleanArchitecture.IntegrationTests.UtilityTests;
|
||||||
|
|
||||||
[Collection("IntegrationTests")]
|
public sealed class HealthChecksTests
|
||||||
[TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)]
|
|
||||||
public sealed class HealthChecksTests : IClassFixture<AuthTestFixure>
|
|
||||||
{
|
{
|
||||||
private readonly AuthTestFixure _fixture;
|
private readonly AuthTestFixure _fixture = new();
|
||||||
|
|
||||||
public HealthChecksTests(AuthTestFixure fixture)
|
[OneTimeSetUp]
|
||||||
{
|
public async Task Setup() => await GlobalSetupFixture.RespawnDatabaseAsync();
|
||||||
_fixture = fixture;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Test, Order(0)]
|
||||||
[Priority(0)]
|
|
||||||
public async Task Should_Return_Healthy()
|
public async Task Should_Return_Healthy()
|
||||||
{
|
{
|
||||||
var response = await _fixture.ServerClient.GetAsync("/healthz");
|
var response = await _fixture.ServerClient.GetAsync("/healthz");
|
||||||
|
@ -4,23 +4,17 @@ using System.Threading.Tasks;
|
|||||||
using CleanArchitecture.IntegrationTests.Fixtures.gRPC;
|
using CleanArchitecture.IntegrationTests.Fixtures.gRPC;
|
||||||
using CleanArchitecture.Proto.Tenants;
|
using CleanArchitecture.Proto.Tenants;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Xunit;
|
|
||||||
using Xunit.Priority;
|
|
||||||
|
|
||||||
namespace CleanArchitecture.IntegrationTests.gRPC;
|
namespace CleanArchitecture.IntegrationTests.gRPC;
|
||||||
|
|
||||||
[Collection("IntegrationTests")]
|
public sealed class GetTenantsByIdsTests
|
||||||
[TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)]
|
|
||||||
public sealed class GetTenantsByIdsTests : IClassFixture<GetTenantsByIdsTestFixture>
|
|
||||||
{
|
{
|
||||||
private readonly GetTenantsByIdsTestFixture _fixture;
|
private readonly GetTenantsByIdsTestFixture _fixture = new();
|
||||||
|
|
||||||
public GetTenantsByIdsTests(GetTenantsByIdsTestFixture fixture)
|
[OneTimeSetUp]
|
||||||
{
|
public async Task Setup() => await _fixture.SeedTestData();
|
||||||
_fixture = fixture;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Test]
|
||||||
public async Task Should_Get_Tenants_By_Ids()
|
public async Task Should_Get_Tenants_By_Ids()
|
||||||
{
|
{
|
||||||
var client = new TenantsApi.TenantsApiClient(_fixture.GrpcChannel);
|
var client = new TenantsApi.TenantsApiClient(_fixture.GrpcChannel);
|
||||||
|
@ -3,23 +3,17 @@ using System.Threading.Tasks;
|
|||||||
using CleanArchitecture.IntegrationTests.Fixtures.gRPC;
|
using CleanArchitecture.IntegrationTests.Fixtures.gRPC;
|
||||||
using CleanArchitecture.Proto.Users;
|
using CleanArchitecture.Proto.Users;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Xunit;
|
|
||||||
using Xunit.Priority;
|
|
||||||
|
|
||||||
namespace CleanArchitecture.IntegrationTests.gRPC;
|
namespace CleanArchitecture.IntegrationTests.gRPC;
|
||||||
|
|
||||||
[Collection("IntegrationTests")]
|
public sealed class GetUsersByIdsTests
|
||||||
[TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)]
|
|
||||||
public sealed class GetUsersByIdsTests : IClassFixture<GetUsersByIdsTestFixture>
|
|
||||||
{
|
{
|
||||||
private readonly GetUsersByIdsTestFixture _fixture;
|
private readonly GetUsersByIdsTestFixture _fixture = new();
|
||||||
|
|
||||||
public GetUsersByIdsTests(GetUsersByIdsTestFixture fixture)
|
[OneTimeSetUp]
|
||||||
{
|
public async Task Setup() => await _fixture.SeedTestData();
|
||||||
_fixture = fixture;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Test]
|
||||||
public async Task Should_Get_Users_By_Ids()
|
public async Task Should_Get_Users_By_Ids()
|
||||||
{
|
{
|
||||||
var client = new UsersApi.UsersApiClient(_fixture.GrpcChannel);
|
var client = new UsersApi.UsersApiClient(_fixture.GrpcChannel);
|
||||||
|
Loading…
Reference in New Issue
Block a user