diff --git a/src/Modules/Faura.Infrastructure.IntegrationTesting/Factory/BaseWebApplicationFactory.cs b/src/Modules/Faura.Infrastructure.IntegrationTesting/Factory/BaseWebApplicationFactory.cs index 45dbcbc..a0f26c7 100644 --- a/src/Modules/Faura.Infrastructure.IntegrationTesting/Factory/BaseWebApplicationFactory.cs +++ b/src/Modules/Faura.Infrastructure.IntegrationTesting/Factory/BaseWebApplicationFactory.cs @@ -1,13 +1,13 @@ -using Faura.Infrastructure.IntegrationTesting.Constants; -using Faura.Infrastructure.IntegrationTesting.Seeders; +namespace Faura.Infrastructure.IntegrationTesting.Factory; + using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Faura.Infrastructure.IntegrationTesting.Constants; +using Faura.Infrastructure.IntegrationTesting.Seeders; using Xunit; -namespace Faura.Infrastructure.IntegrationTesting.Factory; - /// /// Base factory for integration tests with container setup and service customization. /// @@ -16,47 +16,67 @@ public abstract class BaseWebApplicationFactory IAsyncLifetime where TEntryPoint : class { - /// - /// Final configuration used during the test run. - /// - protected IConfiguration? Configuration; + private static readonly SemaphoreSlim _initializationLock = new(1, 1); + private static bool _isInitialized; + private static IConfiguration? _sharedConfiguration; + + protected IConfiguration? Configuration { get; set; } public async Task InitializeAsync() { - Environment.SetEnvironmentVariable( - "ASPNETCORE_ENVIRONMENT", - IntegrationTestConstants.Environment - ); + await _initializationLock.WaitAsync(); + try + { + if (!_isInitialized) + { + Environment.SetEnvironmentVariable( + "ASPNETCORE_ENVIRONMENT", + IntegrationTestConstants.Environment); - var testProjectPath = Directory.GetCurrentDirectory(); - var settingsFile = Path.Combine( - testProjectPath, - $"appsettings.{IntegrationTestConstants.Environment}.json" - ); + var testProjectPath = Directory.GetCurrentDirectory(); + var settingsFile = Path.Combine( + testProjectPath, + $"appsettings.{IntegrationTestConstants.Environment}.json"); - var configBuilder = new ConfigurationBuilder() - .AddJsonFile(settingsFile, optional: false) - .AddEnvironmentVariables(); + var configBuilder = new ConfigurationBuilder() + .AddJsonFile(settingsFile, optional: false) + .AddEnvironmentVariables(); - await ConfigureConfigurationAsync(configBuilder); + await ConfigureConfigurationAsync(configBuilder); + + var baseConfig = configBuilder.Build(); + _sharedConfiguration = await ConfigureTestContainersAsync(baseConfig); + + _isInitialized = true; + } - var baseConfig = configBuilder.Build(); - Configuration = await ConfigureTestContainersAsync(baseConfig); + Configuration = _sharedConfiguration; + } + finally + { + _initializationLock.Release(); + } } - public virtual Task DisposeAsync() => Task.CompletedTask; + public override async ValueTask DisposeAsync() + { + await base.DisposeAsync(); + GC.SuppressFinalize(this); + } + + async Task IAsyncLifetime.DisposeAsync() => await DisposeAsync(); protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.UseEnvironment(IntegrationTestConstants.Environment); - builder.ConfigureAppConfiguration( - (_, config) => - { - config.Sources.Clear(); - config.AddConfiguration(Configuration!); - } - ); + builder + .UseConfiguration(Configuration!) + .ConfigureAppConfiguration( + (_, config) => + { + config.AddConfiguration(Configuration!); + }); builder.ConfigureServices(services => { @@ -65,7 +85,10 @@ protected override void ConfigureWebHost(IWebHostBuilder builder) using var provider = services.BuildServiceProvider(validateScopes: true); using var scope = provider.CreateScope(); - RunSeedersAsync(scope.ServiceProvider).GetAwaiter().GetResult(); + + BaseWebApplicationFactory.RunSeedersAsync(scope.ServiceProvider) + .GetAwaiter() + .GetResult(); }); } @@ -79,26 +102,27 @@ protected virtual Task ConfigureConfigurationAsync(IConfigurationBuilder builder /// Starts containers and returns updated configuration (e.g., with connection strings). /// protected virtual Task ConfigureTestContainersAsync( - IConfiguration configuration - ) => Task.FromResult(configuration); + IConfiguration configuration) => Task.FromResult(configuration); /// /// Registers test-specific or mocked services. /// protected virtual void ConfigureTestServices( IServiceCollection services, - IConfiguration configuration - ) { } + IConfiguration configuration) + { + } /// /// Registers database context and ensures schema is created. /// protected virtual void ConfigureTestDatabase( IServiceCollection services, - IConfiguration configuration - ) { } + IConfiguration configuration) + { + } - private async Task RunSeedersAsync(IServiceProvider serviceProvider) + private static async Task RunSeedersAsync(IServiceProvider serviceProvider) { foreach (var seeder in serviceProvider.GetServices()) { @@ -113,4 +137,4 @@ private async Task RunSeedersAsync(IServiceProvider serviceProvider) } } } -} +} \ No newline at end of file diff --git a/src/Modules/Faura.Infrastructure.IntegrationTesting/Faura.Infrastructure.IntegrationTesting.csproj b/src/Modules/Faura.Infrastructure.IntegrationTesting/Faura.Infrastructure.IntegrationTesting.csproj index 6d62275..9864a52 100644 --- a/src/Modules/Faura.Infrastructure.IntegrationTesting/Faura.Infrastructure.IntegrationTesting.csproj +++ b/src/Modules/Faura.Infrastructure.IntegrationTesting/Faura.Infrastructure.IntegrationTesting.csproj @@ -1,45 +1,25 @@  - net8.0 enable enable - Faura Integration Testing - https://github.com/JosepFe/faura/blob/master/LICENSE + Faura Integration Testing Library git faura;testing;integrationTesting - README.md NET 8.0+ compatibility version - LICENSE - True - $(NoWarn);NETSDK1206;1701;1702;NU1608;CA1822 - - + + + - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + - - True - \ - - - True - \ - + + **/**/*.cs + - - + \ No newline at end of file diff --git a/src/Modules/Faura.Infrastructure.IntegrationTesting/Options/ContainerOptions.cs b/src/Modules/Faura.Infrastructure.IntegrationTesting/Options/ContainerOptions.cs index d9b65db..652e97b 100644 --- a/src/Modules/Faura.Infrastructure.IntegrationTesting/Options/ContainerOptions.cs +++ b/src/Modules/Faura.Infrastructure.IntegrationTesting/Options/ContainerOptions.cs @@ -4,7 +4,7 @@ public class ContainerOptions { public string Image { get; set; } = string.Empty; public string Name { get; set; } = string.Empty; - public int Port { get; set; } + public int? Port { get; set; } public string Username { get; set; } = string.Empty; public string Password { get; set; } = string.Empty; public string Database { get; set; } = string.Empty; diff --git a/src/Modules/Faura.Infrastructure.IntegrationTesting/Options/TestContainerOptions.cs b/src/Modules/Faura.Infrastructure.IntegrationTesting/Options/TestContainerOptions.cs index a161db2..7d0d3f8 100644 --- a/src/Modules/Faura.Infrastructure.IntegrationTesting/Options/TestContainerOptions.cs +++ b/src/Modules/Faura.Infrastructure.IntegrationTesting/Options/TestContainerOptions.cs @@ -1,9 +1,7 @@ -namespace Faura.Infrastructure.IntegrationTesting.Options; +namespace Faura.Infrastructure.IntegrationTesting.Options; public class TestContainerOptions { - public ContainerOptions Mongo { get; set; } = new(); - public ContainerOptions Redis { get; set; } = new(); + public ContainerOptions SqlServer { get; set; } = new (); public ContainerOptions Postgres { get; set; } = new(); - public ContainerOptions SqlServer { get; set; } = new(); } diff --git a/src/Modules/Faura.Infrastructure.IntegrationTesting/Seeders/TestDataSeeder.cs b/src/Modules/Faura.Infrastructure.IntegrationTesting/Seeders/TestDataSeeder.cs index 76b270c..dc483c6 100644 --- a/src/Modules/Faura.Infrastructure.IntegrationTesting/Seeders/TestDataSeeder.cs +++ b/src/Modules/Faura.Infrastructure.IntegrationTesting/Seeders/TestDataSeeder.cs @@ -1,7 +1,7 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; +namespace Faura.Infrastructure.IntegrationTesting.Seeders; -namespace Faura.Infrastructure.IntegrationTesting.Seeders; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; public abstract class TestDataSeeder : ITestDataSeeder where TContext : DbContext diff --git a/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Configurations/MongoContainerConfiguration.cs b/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Configurations/MongoContainerConfiguration.cs index b4b40eb..214237f 100644 --- a/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Configurations/MongoContainerConfiguration.cs +++ b/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Configurations/MongoContainerConfiguration.cs @@ -1,11 +1,13 @@ using Faura.Infrastructure.IntegrationTesting.Options; -using Faura.Infrastructure.IntegrationTesting.TestContainers.Constants; using Faura.Infrastructure.IntegrationTesting.TestContainers.Core; namespace Faura.Infrastructure.IntegrationTesting.TestContainers.Configurations; public class MongoContainerConfiguration : ITestContainerConfiguration { + private const string DefaultImage = "mongo:7-jammy"; + private const int MongoInternalPort = 27017; + private readonly ContainerOptions _options; public MongoContainerConfiguration(ContainerOptions options) @@ -15,17 +17,44 @@ public MongoContainerConfiguration(ContainerOptions options) public string Image => string.IsNullOrWhiteSpace(_options.Image) - ? ContainerDefaultsConstants.Images.Mongo + ? DefaultImage : _options.Image; - public int Port => _options.Port != 0 ? _options.Port : ContainerDefaultsConstants.Ports.Mongo; + public int? Port => _options.Port; + + public int InternalPort => MongoInternalPort; public string Username => _options.Username ?? string.Empty; + public string Password => _options.Password ?? string.Empty; + public string Database => _options.Database ?? "test"; - public Dictionary GetEnvironmentVariables() => new(); + public Dictionary GetEnvironmentVariables() + { + var env = new Dictionary(); + + if (!string.IsNullOrWhiteSpace(Username) && !string.IsNullOrWhiteSpace(Password)) + { + env["MONGO_INITDB_ROOT_USERNAME"] = Username; + env["MONGO_INITDB_ROOT_PASSWORD"] = Password; + } - public string BuildConnectionString(int mappedPort) => - $"mongodb://localhost:{mappedPort}/{Database}"; -} + if (!string.IsNullOrWhiteSpace(Database)) + { + env["MONGO_INITDB_DATABASE"] = Database; + } + + return env; + } + + public string BuildConnectionString(string host, int mappedPort) + { + if (!string.IsNullOrWhiteSpace(Username) && !string.IsNullOrWhiteSpace(Password)) + { + return $"mongodb://{Username}:{Password}@{host}:{mappedPort}/{Database}?authSource=admin"; + } + + return $"mongodb://{host}:{mappedPort}/{Database}"; + } +} \ No newline at end of file diff --git a/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Configurations/PostgresContainerConfiguration.cs b/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Configurations/PostgresContainerConfiguration.cs index 05ce5f2..49ffd04 100644 --- a/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Configurations/PostgresContainerConfiguration.cs +++ b/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Configurations/PostgresContainerConfiguration.cs @@ -1,11 +1,13 @@ -using Faura.Infrastructure.IntegrationTesting.Options; -using Faura.Infrastructure.IntegrationTesting.TestContainers.Constants; -using Faura.Infrastructure.IntegrationTesting.TestContainers.Core; +namespace Faura.Infrastructure.IntegrationTesting.TestContainers.Configurations; -namespace Faura.Infrastructure.IntegrationTesting.TestContainers.Configurations; +using Faura.Infrastructure.IntegrationTesting.Options; +using Faura.Infrastructure.IntegrationTesting.TestContainers.Core; public class PostgresContainerConfiguration : ITestContainerConfiguration { + private const string DefaultImage = "postgres:15-alpine"; + private const int PostgresInternalPort = 5432; + private readonly ContainerOptions _options; public PostgresContainerConfiguration(ContainerOptions options) @@ -15,11 +17,12 @@ public PostgresContainerConfiguration(ContainerOptions options) public string Image => string.IsNullOrWhiteSpace(_options.Image) - ? ContainerDefaultsConstants.Images.Postgres + ? DefaultImage : _options.Image; - public int Port => - _options.Port != 0 ? _options.Port : ContainerDefaultsConstants.Ports.Postgres; + public int? Port => _options.Port; + + public int InternalPort => PostgresInternalPort; public string Username => _options.Username ?? "postgres"; public string Password => _options.Password ?? "postgres"; @@ -33,6 +36,6 @@ public Dictionary GetEnvironmentVariables() => ["POSTGRES_DB"] = Database, }; - public string BuildConnectionString(int mappedPort) => - $"Host=localhost;Port={mappedPort};Username={Username};Password={Password};Database={Database}"; + public string BuildConnectionString(string host, int mappedPort) => + $"Host={host};Port={mappedPort};Username={Username};Password={Password};Database={Database}"; } diff --git a/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Configurations/RedisContainerConfiguration.cs b/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Configurations/RedisContainerConfiguration.cs index c123cb0..33712e6 100644 --- a/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Configurations/RedisContainerConfiguration.cs +++ b/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Configurations/RedisContainerConfiguration.cs @@ -1,11 +1,13 @@ using Faura.Infrastructure.IntegrationTesting.Options; -using Faura.Infrastructure.IntegrationTesting.TestContainers.Constants; using Faura.Infrastructure.IntegrationTesting.TestContainers.Core; namespace Faura.Infrastructure.IntegrationTesting.TestContainers.Configurations; public class RedisContainerConfiguration : ITestContainerConfiguration { + private const string DefaultImage = "redis:7-alpine"; + private const int RedisInternalPort = 6379; + private readonly ContainerOptions _options; public RedisContainerConfiguration(ContainerOptions options) @@ -15,16 +17,45 @@ public RedisContainerConfiguration(ContainerOptions options) public string Image => string.IsNullOrWhiteSpace(_options.Image) - ? ContainerDefaultsConstants.Images.Redis + ? DefaultImage : _options.Image; - public int Port => _options.Port != 0 ? _options.Port : ContainerDefaultsConstants.Ports.Redis; + public int? Port => _options.Port; + + public int InternalPort => RedisInternalPort; public string Username => _options.Username ?? string.Empty; + public string Password => _options.Password ?? string.Empty; - public string Database => _options.Database ?? string.Empty; - public Dictionary GetEnvironmentVariables() => new(); + public string Database => _options.Database ?? "0"; - public string BuildConnectionString(int mappedPort) => $"localhost:{mappedPort}"; -} + public Dictionary GetEnvironmentVariables() + { + var env = new Dictionary(); + + if (!string.IsNullOrWhiteSpace(Password)) + { + env["REDIS_PASSWORD"] = Password; + } + + return env; + } + + public string BuildConnectionString(string host, int mappedPort) + { + var connectionString = $"{host}:{mappedPort}"; + + if (!string.IsNullOrWhiteSpace(Password)) + { + connectionString += $",password={Password}"; + } + + if (!string.IsNullOrWhiteSpace(Database) && Database != "0") + { + connectionString += $",defaultDatabase={Database}"; + } + + return connectionString; + } +} \ No newline at end of file diff --git a/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Configurations/SqlServerContainerConfiguration.cs b/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Configurations/SqlServerContainerConfiguration.cs index 3c3efb4..613d675 100644 --- a/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Configurations/SqlServerContainerConfiguration.cs +++ b/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Configurations/SqlServerContainerConfiguration.cs @@ -1,33 +1,36 @@ -using Faura.Infrastructure.IntegrationTesting.Options; -using Faura.Infrastructure.IntegrationTesting.TestContainers.Constants; -using Faura.Infrastructure.IntegrationTesting.TestContainers.Core; - namespace Faura.Infrastructure.IntegrationTesting.TestContainers.Configurations; +using Faura.Infrastructure.IntegrationTesting.Options; +using Faura.Infrastructure.IntegrationTesting.TestContainers.Core; + public class SqlServerContainerConfiguration : ITestContainerConfiguration { + private const string DefaultImage = "mcr.microsoft.com/mssql/server:2022-lts"; + private const int SqlServerInternalPort = 1433; private readonly ContainerOptions _options; - public SqlServerContainerConfiguration(ContainerOptions options) - { - _options = options; - } + public SqlServerContainerConfiguration(ContainerOptions options) => _options = options; public string Image => string.IsNullOrWhiteSpace(_options.Image) - ? ContainerDefaultsConstants.Images.SqlServer + ? DefaultImage : _options.Image; - public int Port => - _options.Port != 0 ? _options.Port : ContainerDefaultsConstants.Ports.SqlServer; + public int? Port =>_options.Port; + public int InternalPort => SqlServerInternalPort; public string Username => _options.Username ?? "sa"; public string Password => _options.Password ?? "Your_strong_password123!"; public string Database => _options.Database ?? "TestDb"; public Dictionary GetEnvironmentVariables() => - new() { ["ACCEPT_EULA"] = "Y", ["SA_PASSWORD"] = Password }; - - public string BuildConnectionString(int mappedPort) => - $"Server=localhost,{mappedPort};Database={Database};User Id={Username};Password={Password};TrustServerCertificate=True"; + new() + { + ["ACCEPT_EULA"] = "Y", + ["SA_PASSWORD"] = Password, + ["MSSQL_PID"] = "Developer", + }; + + public string BuildConnectionString(string host, int mappedPort) => + $"Server={host},{mappedPort};Database={Database};User Id={Username};Password={Password};TrustServerCertificate=True"; } diff --git a/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Constants/ContainerDefaultsConstants.cs b/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Constants/ContainerDefaultsConstants.cs deleted file mode 100644 index 448ed09..0000000 --- a/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Constants/ContainerDefaultsConstants.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Faura.Infrastructure.IntegrationTesting.TestContainers.Constants; - -public static class ContainerDefaultsConstants -{ - public static class Images - { - public const string Mongo = "mongo:6"; - public const string Redis = "redis:7"; - public const string Postgres = "postgres:15-alpine"; - public const string SqlServer = "mcr.microsoft.com/mssql/server:2022-lts"; - } - - public static class Ports - { - public const int Mongo = 27017; - public const int Redis = 6379; - public const int Postgres = 5432; - public const int SqlServer = 1433; - } -} diff --git a/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Core/ITestContainerConfiguration.cs b/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Core/ITestContainerConfiguration.cs index ef727fc..c2bb0d1 100644 --- a/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Core/ITestContainerConfiguration.cs +++ b/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Core/ITestContainerConfiguration.cs @@ -3,11 +3,12 @@ public interface ITestContainerConfiguration { string Image { get; } - int Port { get; } + int? Port { get; } + int InternalPort { get; } string Username { get; } string Password { get; } string Database { get; } Dictionary GetEnvironmentVariables(); - string BuildConnectionString(int mappedPort); + string BuildConnectionString(string host, int mappedPort); } diff --git a/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Core/TestContainerInstance.cs b/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Core/TestContainerInstance.cs index 0232f37..51ae0d4 100644 --- a/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Core/TestContainerInstance.cs +++ b/src/Modules/Faura.Infrastructure.IntegrationTesting/TestContainers/Core/TestContainerInstance.cs @@ -1,16 +1,14 @@ -using DotNet.Testcontainers.Builders; -using DotNet.Testcontainers.Containers; - namespace Faura.Infrastructure.IntegrationTesting.TestContainers.Core; +using DotNet.Testcontainers.Builders; +using DotNet.Testcontainers.Containers; + public class TestContainerInstance where T : ITestContainerConfiguration { private readonly T _config; private readonly IContainer _container; - public string ConnectionString { get; private set; } = string.Empty; - public TestContainerInstance(T config) { _config = config; @@ -19,7 +17,13 @@ public TestContainerInstance(T config) .WithImage(_config.Image) .WithName($"testcontainer-{Guid.NewGuid():N}") .WithCleanUp(true) - .WithPortBinding(_config.Port, true); + .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(_config.InternalPort)); + + builder = _config.Port switch + { + null or 0 => builder.WithPortBinding(_config.InternalPort, true), + var port => builder.WithPortBinding(port.Value, _config.InternalPort) + }; foreach (var kvp in _config.GetEnvironmentVariables()) { @@ -29,11 +33,14 @@ public TestContainerInstance(T config) _container = builder.Build(); } + public string ConnectionString { get; private set; } = string.Empty; + public async Task StartAsync() { await _container.StartAsync(); - var mappedPort = _container.GetMappedPublicPort(_config.Port); - ConnectionString = _config.BuildConnectionString(mappedPort); + var host = _container.Hostname; + var mappedPort = _container.GetMappedPublicPort(_config.InternalPort); + ConnectionString = _config.BuildConnectionString(host, mappedPort); } public Task StopAsync() => _container.StopAsync(); diff --git a/src/Templates/Faura.IntegrationTest/Configuration/CustomWebApplicationFactory.cs b/src/Templates/Faura.IntegrationTest/Configuration/CustomWebApplicationFactory.cs index 7e37e39..d836e97 100644 --- a/src/Templates/Faura.IntegrationTest/Configuration/CustomWebApplicationFactory.cs +++ b/src/Templates/Faura.IntegrationTest/Configuration/CustomWebApplicationFactory.cs @@ -1,33 +1,21 @@ -using DotNet.Testcontainers.Containers; -using Faura.Infrastructure.IntegrationTesting.Factory; -using Faura.Infrastructure.IntegrationTesting.Options; -using Faura.Infrastructure.IntegrationTesting.Seeders; -using Faura.Infrastructure.IntegrationTesting.TestContainers.Configurations; -using Faura.Infrastructure.IntegrationTesting.TestContainers.Core; -using Faura.Infrastructure.UnitOfWork.Common; +using Faura.Infrastructure.UnitOfWork.Common; using Faura.Infrastructure.UnitOfWork.Enums; using Faura.IntegrationTest.Seeders; +using Faura.WebAPI.Infrastructure.Persistence; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; -using YourNamespace.Data; +using Faura.Infrastructure.IntegrationTesting.Factory; +using Faura.Infrastructure.IntegrationTesting.Options; +using Faura.Infrastructure.IntegrationTesting.Seeders; +using Faura.Infrastructure.IntegrationTesting.TestContainers.Configurations; +using Faura.Infrastructure.IntegrationTesting.TestContainers.Core; namespace Faura.IntegrationTest.Configuration; -public class CustomWebApplicationFactory : BaseWebApplicationFactory - where TEntryPoint : class +public class CustomWebApplicationFactory : BaseWebApplicationFactory { - private IContainer? _postgresContainer; - - public override async Task DisposeAsync() - { - if (_postgresContainer is not null) - { - await _postgresContainer.StopAsync(); - } - } - protected override async Task ConfigureTestContainersAsync( IConfiguration configuration ) diff --git a/src/Templates/Faura.IntegrationTest/Configuration/IntegrationTestBase.cs b/src/Templates/Faura.IntegrationTest/Configuration/IntegrationTestBase.cs new file mode 100644 index 0000000..54c647a --- /dev/null +++ b/src/Templates/Faura.IntegrationTest/Configuration/IntegrationTestBase.cs @@ -0,0 +1,38 @@ +namespace Faura.IntegrationTest.Configuration; + +using Faura.WebAPI.Domain; +using Faura.WebAPI.Infrastructure.Persistence; +using Microsoft.Extensions.DependencyInjection; + +public abstract class IntegrationTestBase : IClassFixture, IAsyncLifetime +{ + protected readonly HttpClient Client; + private readonly IServiceScope _scope; + protected readonly EmployeeDbContext DbContext; + protected readonly IEmployeeRepository EmployeeRepository; + + protected IntegrationTestBase(CustomWebApplicationFactory factory) + { + Client = factory.CreateClient(); + _scope = factory.Services.CreateScope(); + DbContext = _scope.ServiceProvider.GetRequiredService(); + EmployeeRepository = _scope.ServiceProvider.GetRequiredService(); + } + + public async Task InitializeAsync() + { + await DbContext.Database.EnsureCreatedAsync(); + await SeedTestDataAsync(); + } + + protected virtual async Task SeedTestDataAsync() + { + await Task.CompletedTask; + } + + public Task DisposeAsync() + { + _scope?.Dispose(); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/Templates/Faura.IntegrationTest/Faura.IntegrationTest.csproj b/src/Templates/Faura.IntegrationTest/Faura.IntegrationTest.csproj index 57cd07a..20f5ae8 100644 --- a/src/Templates/Faura.IntegrationTest/Faura.IntegrationTest.csproj +++ b/src/Templates/Faura.IntegrationTest/Faura.IntegrationTest.csproj @@ -1,33 +1,34 @@ - + + + net8.0 + enable + enable + false + true + - - net8.0 - enable - enable + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + - false - true - + + + + - - - - - + + + - - - - - - - - - - - - PreserveNewest - - - - + + + PreserveNewest + + + \ No newline at end of file diff --git a/src/Templates/Faura.IntegrationTest/Seeders/EmployeeTestDataSeeder.cs b/src/Templates/Faura.IntegrationTest/Seeders/EmployeeTestDataSeeder.cs index fc22111..65855c8 100644 --- a/src/Templates/Faura.IntegrationTest/Seeders/EmployeeTestDataSeeder.cs +++ b/src/Templates/Faura.IntegrationTest/Seeders/EmployeeTestDataSeeder.cs @@ -1,8 +1,8 @@ -using Faura.Infrastructure.IntegrationTesting.Seeders; -using Faura.WebAPI.Domain; +using Faura.WebAPI.Domain; using Faura.WebAPI.Domain.Entities; +using Faura.WebAPI.Infrastructure.Persistence; using Microsoft.Extensions.DependencyInjection; -using YourNamespace.Data; +using Faura.Infrastructure.IntegrationTesting.Seeders; namespace Faura.IntegrationTest.Seeders; diff --git a/src/Templates/Faura.IntegrationTest/UseCases/EmployeeTests.cs b/src/Templates/Faura.IntegrationTest/UseCases/EmployeeTests.cs new file mode 100644 index 0000000..6b25b0a --- /dev/null +++ b/src/Templates/Faura.IntegrationTest/UseCases/EmployeeTests.cs @@ -0,0 +1,71 @@ +using Faura.IntegrationTest.Configuration; +using Faura.WebAPI.Controllers; +using Faura.WebAPI.Domain.Entities; +using System.Net.Http.Json; +namespace Faura.IntegrationTest.UseCases; +public class EmployeeTests : IntegrationTestBase +{ + public EmployeeTests(CustomWebApplicationFactory factory) : base(factory) { } + + protected override async Task SeedTestDataAsync() + { + await EmployeeRepository.CreateAsync( + new Employee("Custom", "Seed", "custom@example.com") + ); + await EmployeeRepository.CreateAsync( + new Employee("Another", "User", "another@example.com") + ); + } + + [Fact] + public async Task Should_Return_Employees() + { + // Act + var response = await Client.GetAsync("/Employee"); + // Assert + response.EnsureSuccessStatusCode(); + var data = await response.Content.ReadFromJsonAsync>(); + Assert.NotNull(data); + Assert.NotEmpty(data); + } + + [Fact] + public async Task Should_Create_Employee() + { + // Arrange + var request = new CreateEmployeeRequest("John", "Doe", "john.doe@example.com"); + // Act + var response = await Client.PostAsJsonAsync("/Employee", request); + // Assert + response.EnsureSuccessStatusCode(); + var createdEmployee = await response.Content.ReadFromJsonAsync(); + Assert.NotNull(createdEmployee); + Assert.Equal("John", createdEmployee.FirstName); + Assert.Equal("Doe", createdEmployee.LastName); + Assert.Equal("john.doe@example.com", createdEmployee.Email); + } + + [Fact] + public async Task Should_Create_Multiple_Employees_With_Transaction() + { + // Arrange + var request = new CreateMultipleEmployeesRequest( + "Jane", + "Smith", + "jane.smith@example.com", + "Bob", + "Johnson", + "bob.johnson@example.com" + ); + // Act + var response = await Client.PostAsJsonAsync("/Employee/multiple", request); + // Assert + response.EnsureSuccessStatusCode(); + var createdEmployees = await response.Content.ReadFromJsonAsync>(); + Assert.NotNull(createdEmployees); + Assert.Equal(2, createdEmployees.Count()); + var employeeList = createdEmployees.ToList(); + Assert.Equal("Jane", employeeList[0].FirstName); + Assert.Equal("Bob", employeeList[1].FirstName); + } +} \ No newline at end of file diff --git a/src/Templates/Faura.IntegrationTest/UseCases/WeatherForecastTests.cs b/src/Templates/Faura.IntegrationTest/UseCases/WeatherForecastTests.cs deleted file mode 100644 index 72f9e31..0000000 --- a/src/Templates/Faura.IntegrationTest/UseCases/WeatherForecastTests.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Net.Http.Json; -using Faura.IntegrationTest.Configuration; -using Faura.WebAPI.Controllers; - -namespace Faura.IntegrationTest.UseCases; - -public class WeatherForecastTests : IClassFixture> -{ - private readonly HttpClient _client; - - public WeatherForecastTests(CustomWebApplicationFactory factory) - { - _client = factory.CreateClient(); - } - - [Fact] - public async Task Should_Return_WeatherForecast() - { - // Act - var response = await _client.GetAsync("/WeatherForecast"); - - // Assert - response.EnsureSuccessStatusCode(); - - var data = await response.Content.ReadFromJsonAsync>(); - Assert.NotNull(data); - Assert.NotEmpty(data); - } -} diff --git a/src/Templates/Faura.IntegrationTest/appsettings.Test.json b/src/Templates/Faura.IntegrationTest/appsettings.Test.json index 282e307..3406c6e 100644 --- a/src/Templates/Faura.IntegrationTest/appsettings.Test.json +++ b/src/Templates/Faura.IntegrationTest/appsettings.Test.json @@ -19,7 +19,7 @@ "Containers": { "Postgres": { "Image": "postgres:15-alpine", - "Port": 5432, + "Port": 5233, "Username": "testuser", "Password": "testpass", "Database": "testdb" diff --git a/src/Templates/Faura.WebAPI/Application/EmployeeService.cs b/src/Templates/Faura.WebAPI/Application/EmployeeService.cs new file mode 100644 index 0000000..06b8e31 --- /dev/null +++ b/src/Templates/Faura.WebAPI/Application/EmployeeService.cs @@ -0,0 +1,71 @@ +using Faura.Infrastructure.Logger.Extensions; +using Faura.WebAPI.Domain; +using Faura.WebAPI.Domain.Entities; +using Faura.WebAPI.Infrastructure.Persistence; + +namespace Faura.WebAPI.Application; + +public class EmployeeService : IEmployeeService +{ + private readonly ILogger _logger; + private readonly IEmployeeRepository _employeeRepository; + private readonly IEmployeeUoW _uoW; + + public EmployeeService( + ILogger logger, + IEmployeeRepository employeeRepository, + IEmployeeUoW uoW + ) + { + _logger = logger; + _employeeRepository = employeeRepository; + _uoW = uoW; + } + + public async Task> GetEmployeesAsync() + { + _logger.LogFauraInformation("Starting Get Employees"); + return await _employeeRepository.GetAsync(); + } + + public async Task CreateEmployeeAsync(string firstName, string lastName, string email) + { + _logger.LogFauraInformation($"Creating employee: {firstName} {lastName}"); + var employee = new Employee(firstName, lastName, email); + var result = await _employeeRepository.CreateAsync(employee); + return result; + } + + public async Task> CreateMultipleEmployeesWithTransactionAsync( + string firstName1, + string lastName1, + string email1, + string firstName2, + string lastName2, + string email2 + ) + { + _logger.LogFauraInformation("Creating multiple employees with transaction"); + + var transaction = await _uoW.GetDbTransaction(); + + var employee1 = await _employeeRepository.CreateAsync( + new Employee(firstName1, lastName1, email1), + false, + false + ); + + var employee2 = await _employeeRepository.CreateAsync( + new Employee(firstName2, lastName2, email2), + false, + false + ); + + // send event 1 + // send event 2 + + await _uoW.CommitTransaction(transaction); + + return [employee1, employee2]; + } +} diff --git a/src/Templates/Faura.WebAPI/Application/IEmployeeService.cs b/src/Templates/Faura.WebAPI/Application/IEmployeeService.cs new file mode 100644 index 0000000..0bba349 --- /dev/null +++ b/src/Templates/Faura.WebAPI/Application/IEmployeeService.cs @@ -0,0 +1,17 @@ +using Faura.WebAPI.Domain.Entities; + +namespace Faura.WebAPI.Application; + +public interface IEmployeeService +{ + Task> GetEmployeesAsync(); + Task CreateEmployeeAsync(string firstName, string lastName, string email); + Task> CreateMultipleEmployeesWithTransactionAsync( + string firstName1, + string lastName1, + string email1, + string firstName2, + string lastName2, + string email2 + ); +} diff --git a/src/Templates/Faura.WebAPI/Bootstrappers/ApplicationBootstrapper.cs b/src/Templates/Faura.WebAPI/Bootstrappers/ApplicationBootstrapper.cs index 562e81a..fadbcac 100644 --- a/src/Templates/Faura.WebAPI/Bootstrappers/ApplicationBootstrapper.cs +++ b/src/Templates/Faura.WebAPI/Bootstrappers/ApplicationBootstrapper.cs @@ -1,43 +1,15 @@ -using Faura.Infrastructure.UnitOfWork; -using Faura.Infrastructure.UnitOfWork.Common; -using Faura.Infrastructure.UnitOfWork.Enums; -using Faura.WebAPI.Domain; +using Faura.WebAPI.Application; using Faura.WebAPI.Infrastructure.Persistence; -using YourNamespace.Data; namespace Faura.WebAPI.Bootstrappers; public static class ApplicationBootstrapper { public static WebApplicationBuilder RegisterApplicationDependencies( - this WebApplicationBuilder builder - ) + this WebApplicationBuilder builder) { - builder.Services.SetupDatabase(builder.Configuration); - - builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); return builder; } - - /// - /// Setup here your database connections. - /// - /// - /// - private static void SetupDatabase( - this IServiceCollection services, - IConfiguration configuration - ) - { - services.SetupUnitOfWork(); - services - .ConfigureDatabase( - configuration, - "Employee", - DatabaseType.PostgreSQL, - ServiceLifetime.Scoped - ) - .ConfigureAwait(false); - } } diff --git a/src/Templates/Faura.WebAPI/Bootstrappers/InfrastructureBootstrapper.cs b/src/Templates/Faura.WebAPI/Bootstrappers/InfrastructureBootstrapper.cs new file mode 100644 index 0000000..a8b8b87 --- /dev/null +++ b/src/Templates/Faura.WebAPI/Bootstrappers/InfrastructureBootstrapper.cs @@ -0,0 +1,47 @@ +namespace Faura.WebAPI.Bootstrappers; + +using Faura.Infrastructure.UnitOfWork.Common; +using Faura.Infrastructure.UnitOfWork.Enums; +using Faura.WebAPI.Domain; +using Faura.WebAPI.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; + +public static class InfrastructureBootstrapper +{ + public static IServiceCollection RegisterInfrastructureDependencies(this IServiceCollection services, IConfiguration configuration) + { + services.AddDatabase(configuration); + services.AddRepositories(); + + return services; + } + + public static async Task ConfigureInfrastructureAsync(this WebApplication app, IWebHostEnvironment env) + { + await MigrateDatabaseAsync(app, env); + return app; + } + + private static async Task MigrateDatabaseAsync(WebApplication app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + using var scope = app.Services.CreateScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + await dbContext.Database.MigrateAsync(); + } + } + + private static IServiceCollection AddDatabase(this IServiceCollection services, IConfiguration configuration) + { + services.ConfigureDatabase(configuration, "Employee", DatabaseType.PostgreSQL); + return services; + } + + private static IServiceCollection AddRepositories(this IServiceCollection services) + { + services.AddScoped(); + + return services; + } +} \ No newline at end of file diff --git a/src/Templates/Faura.WebAPI/Controllers/EmployeeController.cs b/src/Templates/Faura.WebAPI/Controllers/EmployeeController.cs new file mode 100644 index 0000000..b0b3ed9 --- /dev/null +++ b/src/Templates/Faura.WebAPI/Controllers/EmployeeController.cs @@ -0,0 +1,59 @@ +using Faura.WebAPI.Application; +using Faura.WebAPI.Domain.Entities; +using Microsoft.AspNetCore.Mvc; + +namespace Faura.WebAPI.Controllers; + +[ApiController] +[Route("[controller]")] +public class EmployeeController : ControllerBase +{ + private readonly IEmployeeService _employeeService; + + public EmployeeController(IEmployeeService employeeService) + { + _employeeService = employeeService; + } + + [HttpGet(Name = "GetEmployees")] + public async Task> Get() + { + return await _employeeService.GetEmployeesAsync(); + } + + [HttpPost(Name = "CreateEmployee")] + public async Task Create([FromBody] CreateEmployeeRequest request) + { + return await _employeeService.CreateEmployeeAsync( + request.FirstName, + request.LastName, + request.Email + ); + } + + [HttpPost("multiple", Name = "CreateMultipleEmployees")] + public async Task> CreateMultiple( + [FromBody] CreateMultipleEmployeesRequest request + ) + { + return await _employeeService.CreateMultipleEmployeesWithTransactionAsync( + request.FirstName1, + request.LastName1, + request.Email1, + request.FirstName2, + request.LastName2, + request.Email2 + ); + } +} + +public record CreateEmployeeRequest(string FirstName, string LastName, string Email); + +public record CreateMultipleEmployeesRequest( + string FirstName1, + string LastName1, + string Email1, + string FirstName2, + string LastName2, + string Email2 +); diff --git a/src/Templates/Faura.WebAPI/Controllers/WeatherForecast.cs b/src/Templates/Faura.WebAPI/Controllers/WeatherForecast.cs deleted file mode 100644 index 0664827..0000000 --- a/src/Templates/Faura.WebAPI/Controllers/WeatherForecast.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Faura.WebAPI.Controllers; - -public class WeatherForecast -{ - public DateOnly Date { get; set; } - - public int TemperatureC { get; set; } - - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - - public string? Summary { get; set; } -} diff --git a/src/Templates/Faura.WebAPI/Controllers/WeatherForecastController.cs b/src/Templates/Faura.WebAPI/Controllers/WeatherForecastController.cs deleted file mode 100644 index cc8e2df..0000000 --- a/src/Templates/Faura.WebAPI/Controllers/WeatherForecastController.cs +++ /dev/null @@ -1,82 +0,0 @@ -using Faura.WebAPI.Domain; -using Faura.WebAPI.Domain.Entities; -using Faura.WebAPI.Infrastructure.Persistence; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace Faura.WebAPI.Controllers; - -[ApiController] -[Route("[controller]")] -public class WeatherForecastController : ControllerBase -{ - private static readonly string[] Summaries = new[] - { - "Freezing", - "Bracing", - "Chilly", - "Cool", - "Mild", - "Warm", - "Balmy", - "Hot", - "Sweltering", - "Scorching", - }; - - private readonly ILogger _logger; - private readonly IEmployeeRepository _employeeRepository; - private readonly IEmployeeUoW _uoW; - - public WeatherForecastController( - ILogger logger, - IEmployeeRepository employeeRepository, - IEmployeeUoW uoW - ) - { - _logger = logger; - _employeeRepository = employeeRepository; - _uoW = uoW; - } - - [HttpGet(Name = "GetWeatherForecast")] - public async Task> Get() - { - var res = await _employeeRepository.GetAsync(); - - var transaction = await _uoW.GetDbTransaction(); - - var res2 = await _employeeRepository.CreateAsync( - new Employee("Josep", "Ferrandis", "algo@example.com"), - false, - false - ); - var res3 = await _employeeRepository.CreateAsync( - new Employee("Josep", "Ferrandis", "algo@example.com"), - false, - false - ); - - // send event 1 - // send event 2 - - await _uoW.CommitTransaction(transaction); - - return Enumerable - .Range(1, 5) - .Select(index => new WeatherForecast - { - Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), - TemperatureC = Random.Shared.Next(-20, 55), - Summary = Summaries[Random.Shared.Next(Summaries.Length)], - }) - .ToArray(); - } - - [HttpGet("Test")] - [Authorize] - public string GetTst() - { - return "Hola"; - } -} diff --git a/src/Templates/Faura.WebAPI/Domain/EmployeeRepository.cs b/src/Templates/Faura.WebAPI/Domain/EmployeeRepository.cs index 3e2038c..673ce98 100644 --- a/src/Templates/Faura.WebAPI/Domain/EmployeeRepository.cs +++ b/src/Templates/Faura.WebAPI/Domain/EmployeeRepository.cs @@ -1,6 +1,6 @@ using Faura.Infrastructure.UnitOfWork.Repositories; using Faura.WebAPI.Domain.Entities; -using YourNamespace.Data; +using Faura.WebAPI.Infrastructure.Persistence; namespace Faura.WebAPI.Domain; diff --git a/src/Templates/Faura.WebAPI/Faura.WebAPI.csproj b/src/Templates/Faura.WebAPI/Faura.WebAPI.csproj index ff18345..de8f301 100644 --- a/src/Templates/Faura.WebAPI/Faura.WebAPI.csproj +++ b/src/Templates/Faura.WebAPI/Faura.WebAPI.csproj @@ -9,6 +9,14 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + @@ -23,4 +31,8 @@ + + + + diff --git a/src/Templates/Faura.WebAPI/Infrastructure/Persistence/EmployeeDbContext.cs b/src/Templates/Faura.WebAPI/Infrastructure/Persistence/EmployeeDbContext.cs index 3a220b6..a9284f8 100644 --- a/src/Templates/Faura.WebAPI/Infrastructure/Persistence/EmployeeDbContext.cs +++ b/src/Templates/Faura.WebAPI/Infrastructure/Persistence/EmployeeDbContext.cs @@ -1,7 +1,7 @@ using Faura.WebAPI.Domain.Entities; using Microsoft.EntityFrameworkCore; -namespace YourNamespace.Data; +namespace Faura.WebAPI.Infrastructure.Persistence; public class EmployeeDbContext : DbContext { diff --git a/src/Templates/Faura.WebAPI/Infrastructure/Persistence/EmployeeUoW.cs b/src/Templates/Faura.WebAPI/Infrastructure/Persistence/EmployeeUoW.cs index 8658c6b..b7e1f46 100644 --- a/src/Templates/Faura.WebAPI/Infrastructure/Persistence/EmployeeUoW.cs +++ b/src/Templates/Faura.WebAPI/Infrastructure/Persistence/EmployeeUoW.cs @@ -1,5 +1,4 @@ using Faura.Infrastructure.UnitOfWork.UnitOfWork; -using YourNamespace.Data; namespace Faura.WebAPI.Infrastructure.Persistence; diff --git a/src/Templates/Faura.WebAPI/Infrastructure/Persistence/Migrations/20251211113454_InitialCreate.Designer.cs b/src/Templates/Faura.WebAPI/Infrastructure/Persistence/Migrations/20251211113454_InitialCreate.Designer.cs new file mode 100644 index 0000000..61c4331 --- /dev/null +++ b/src/Templates/Faura.WebAPI/Infrastructure/Persistence/Migrations/20251211113454_InitialCreate.Designer.cs @@ -0,0 +1,58 @@ +// +using Faura.WebAPI.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Faura.WebAPI.Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(EmployeeDbContext))] + [Migration("20251211113454_InitialCreate")] + partial class InitialCreate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.14") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Faura.WebAPI.Domain.Entities.Employee", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Email") + .IsRequired() + .HasColumnType("text") + .HasColumnName("email"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("first_name"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("last_name"); + + b.HasKey("Id"); + + b.ToTable("employee"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Templates/Faura.WebAPI/Infrastructure/Persistence/Migrations/20251211113454_InitialCreate.cs b/src/Templates/Faura.WebAPI/Infrastructure/Persistence/Migrations/20251211113454_InitialCreate.cs new file mode 100644 index 0000000..4e280bd --- /dev/null +++ b/src/Templates/Faura.WebAPI/Infrastructure/Persistence/Migrations/20251211113454_InitialCreate.cs @@ -0,0 +1,37 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Faura.WebAPI.Infrastructure.Persistence.Migrations +{ + /// + public partial class InitialCreate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "employee", + columns: table => new + { + id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + first_name = table.Column(type: "text", nullable: false), + last_name = table.Column(type: "text", nullable: false), + email = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_employee", x => x.id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "employee"); + } + } +} diff --git a/src/Templates/Faura.WebAPI/Infrastructure/Persistence/Migrations/EmployeeDbContextModelSnapshot.cs b/src/Templates/Faura.WebAPI/Infrastructure/Persistence/Migrations/EmployeeDbContextModelSnapshot.cs new file mode 100644 index 0000000..36972f5 --- /dev/null +++ b/src/Templates/Faura.WebAPI/Infrastructure/Persistence/Migrations/EmployeeDbContextModelSnapshot.cs @@ -0,0 +1,55 @@ +// +using Faura.WebAPI.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Faura.WebAPI.Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(EmployeeDbContext))] + partial class EmployeeDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.14") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Faura.WebAPI.Domain.Entities.Employee", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Email") + .IsRequired() + .HasColumnType("text") + .HasColumnName("email"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("first_name"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("last_name"); + + b.HasKey("Id"); + + b.ToTable("employee"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Templates/Faura.WebAPI/Program.cs b/src/Templates/Faura.WebAPI/Program.cs index cb68196..93e756f 100644 --- a/src/Templates/Faura.WebAPI/Program.cs +++ b/src/Templates/Faura.WebAPI/Program.cs @@ -5,16 +5,19 @@ builder.RegisterDependencies(); builder.RegisterApplicationDependencies(); +builder.Services.RegisterInfrastructureDependencies(builder.Configuration); +builder.Services.RegisterOptions(builder.Configuration); var app = builder.Build(); app.ConfigureCommonFauraWebApplication(); +await app.ConfigureInfrastructureAsync(app.Environment); -app.Run(); +await app.RunAsync(); +// This is needed for the integration tests +#pragma warning disable S1118 // Utility classes should not have public constructors, public partial class Program +#pragma warning restore S1118 // Utility classes should not have public constructors { - protected Program() - { - } } diff --git a/src/Templates/Faura.WebAPI/Properties/launchSettings.json b/src/Templates/Faura.WebAPI/Properties/launchSettings.json index b41f448..4f0c04d 100644 --- a/src/Templates/Faura.WebAPI/Properties/launchSettings.json +++ b/src/Templates/Faura.WebAPI/Properties/launchSettings.json @@ -16,7 +16,7 @@ "launchUrl": "swagger", "applicationUrl": "http://localhost:5069", "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Local", + "ASPNETCORE_ENVIRONMENT": "Development", "FAURA_ENVIRONMENT": "Development" } }, @@ -27,7 +27,7 @@ "launchUrl": "swagger", "applicationUrl": "https://localhost:7140;http://localhost:5069", "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Local", + "ASPNETCORE_ENVIRONMENT": "Development", "FAURA_ENVIRONMENT": "Development" } }, diff --git a/src/Templates/Faura.WebAPI/appsettings.Development.json b/src/Templates/Faura.WebAPI/appsettings.Development.json index 043d80b..6c76527 100644 --- a/src/Templates/Faura.WebAPI/appsettings.Development.json +++ b/src/Templates/Faura.WebAPI/appsettings.Development.json @@ -1,47 +1,47 @@ { - "Logging": { - "MinimumLevel": { - "Default": "Information", - "Override": { - "Microsoft": "Error", - "System": "Error" - } - }, - "ApplicationName": "FauraApp", - "Outputs": { - "Console": { - "Enable": true, - "LogTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj} | CorrelationId: {CorrelationId} | ApplicationName: {ApplicationName}{NewLine}{Exception}" - } - } + "Logging": { + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Error", + "System": "Error" + } }, + "ApplicationName": "FauraApp", + "Outputs": { + "Console": { + "Enable": true, + "LogTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj} | CorrelationId: {CorrelationId} | ApplicationName: {ApplicationName}{NewLine}{Exception}" + } + } + }, - "Swagger": { - "Authentication": { - "OAuth2": { - "Enable": true, - "Name": "OAuth2", - "AuthenticationURL": "{AuthenticationURL-PlaceHolder}", - "Scopes": { - "openid": "openid", - "profile": "profile" - } - }, - "Bearer": { - "Enable": true, - "Name": "Bearer" - }, - "BasicAuth": { - "Enable": false, - "Name": "Basic" - }, - "ApiKey": { - "Enable": false, - "Name": "X-API-Key", - "In": "Header" // "Header" o "Query" - } + "Swagger": { + "Authentication": { + "OAuth2": { + "Enable": false, + "Name": "OAuth2", + "AuthenticationURL": "{AuthenticationURL-PlaceHolder}", + "Scopes": { + "openid": "openid", + "profile": "profile" } - }, + }, + "Bearer": { + "Enable": false, + "Name": "Bearer" + }, + "BasicAuth": { + "Enable": false, + "Name": "Basic" + }, + "ApiKey": { + "Enable": false, + "Name": "X-API-Key", + "In": "Header" // "Header" o "Query" + } + } + }, "JWT": { @@ -50,7 +50,7 @@ "Audience": "account" }, - "ConnectionStrings": { - "Employee": "{connectionString_placeholder}" - } + "ConnectionStrings": { + "Employee": "{connectionString_placeholder}" + } } diff --git a/src/Templates/Faura.WebAPI/appsettings.json b/src/Templates/Faura.WebAPI/appsettings.json index 183b5fa..6c76527 100644 --- a/src/Templates/Faura.WebAPI/appsettings.json +++ b/src/Templates/Faura.WebAPI/appsettings.json @@ -19,7 +19,7 @@ "Swagger": { "Authentication": { "OAuth2": { - "Enable": true, + "Enable": false, "Name": "OAuth2", "AuthenticationURL": "{AuthenticationURL-PlaceHolder}", "Scopes": { @@ -28,7 +28,7 @@ } }, "Bearer": { - "Enable": true, + "Enable": false, "Name": "Bearer" }, "BasicAuth": {