diff --git a/CleanAspCore.Api.Tests/Data/MigrationTests.cs b/CleanAspCore.Api.Tests/Data/MigrationTests.cs index 592ffef..6966d61 100644 --- a/CleanAspCore.Api.Tests/Data/MigrationTests.cs +++ b/CleanAspCore.Api.Tests/Data/MigrationTests.cs @@ -47,6 +47,7 @@ public async Task MigrationsUpAndDown_NoErrors2(MigrationScript migration) upResult.ExitCode.Should().Be(0, $"Error during migration up2: {upResult2.Stderr}"); } + [SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes")] private sealed class MigrationTestCases : IEnumerable { public IEnumerator GetEnumerator() diff --git a/CleanAspCore.Api.Tests/Features/Employees/CreateEmployeeTests.cs b/CleanAspCore.Api.Tests/Features/Employees/CreateEmployeeTests.cs index 2231b2b..562dac3 100644 --- a/CleanAspCore.Api.Tests/Features/Employees/CreateEmployeeTests.cs +++ b/CleanAspCore.Api.Tests/Features/Employees/CreateEmployeeTests.cs @@ -33,7 +33,7 @@ public async Task CreateEmployee_InvalidRequest_ReturnsBadRequest() { //Arrange var createEmployeeRequest = new CreateEmployeeRequestFaker() - .RuleFor(x => x.FirstName, string.Empty) + .RuleFor(x => x.FirstName, (string?)null) .Generate(); Sut.SeedData(context => diff --git a/CleanAspCore.Api.Tests/GlobalUsings.cs b/CleanAspCore.Api.Tests/GlobalUsings.cs index 0ad56b1..49498db 100644 --- a/CleanAspCore.Api.Tests/GlobalUsings.cs +++ b/CleanAspCore.Api.Tests/GlobalUsings.cs @@ -3,3 +3,4 @@ global using FluentAssertions; global using NUnit.Framework; global using CleanAspCore.Api.Tests.TestSetup; +global using System.Diagnostics.CodeAnalysis; diff --git a/CleanAspCore.Api.Tests/TestSetup/PooledDatabase.cs b/CleanAspCore.Api.Tests/TestSetup/PooledDatabase.cs index a2fab6d..e13bfba 100644 --- a/CleanAspCore.Api.Tests/TestSetup/PooledDatabase.cs +++ b/CleanAspCore.Api.Tests/TestSetup/PooledDatabase.cs @@ -21,10 +21,7 @@ public void EnsureDatabaseIsReadyForTest(IHost host) { _database.EnsureInitialized(host); // Clean the database before and not after the test so that after a test is run you can inspect the database. - Utils.RunWithoutSynchronizationContext(() => - { - _database.Clean().GetAwaiter().GetResult(); - }); + _database.Clean().RunSynchronouslyWithoutSynchronizationContext(); } public void Dispose() diff --git a/CleanAspCore.Api.Tests/TestSetup/ServiceCollectionExtensions.cs b/CleanAspCore.Api.Tests/TestSetup/ServiceCollectionExtensions.cs index 834cc40..c9a7960 100644 --- a/CleanAspCore.Api.Tests/TestSetup/ServiceCollectionExtensions.cs +++ b/CleanAspCore.Api.Tests/TestSetup/ServiceCollectionExtensions.cs @@ -22,8 +22,10 @@ public static void RegisterPostgreSqlContainer(this IServiceCollection services) }); services.AddTransient, PostgreSqlDatabasePoolPolicy>(); - var container = new PostgreSqlBuilder().Build(); - Utils.RunWithoutSynchronizationContext(() => container.StartAsync().Wait()); + var container = new PostgreSqlBuilder() + .WithReuse(true) + .Build(); + container.StartAsync().RunSynchronouslyWithoutSynchronizationContext(); services.AddSingleton(container); } diff --git a/CleanAspCore.Api.Tests/TestSetup/Utils.cs b/CleanAspCore.Api.Tests/TestSetup/TaskExtensions.cs similarity index 76% rename from CleanAspCore.Api.Tests/TestSetup/Utils.cs rename to CleanAspCore.Api.Tests/TestSetup/TaskExtensions.cs index 284562c..c10834a 100644 --- a/CleanAspCore.Api.Tests/TestSetup/Utils.cs +++ b/CleanAspCore.Api.Tests/TestSetup/TaskExtensions.cs @@ -1,8 +1,8 @@ namespace CleanAspCore.Api.Tests.TestSetup; -public static class Utils +public static class TaskExtensions { - public static void RunWithoutSynchronizationContext(Action action) + public static void RunSynchronouslyWithoutSynchronizationContext(this Task task) { // Capture the current synchronization context so we can restore it later. // We don't have to be afraid of other threads here as this is a ThreadStatic. @@ -10,7 +10,7 @@ public static void RunWithoutSynchronizationContext(Action action) try { SynchronizationContext.SetSynchronizationContext(null); - action(); + task.GetAwaiter().GetResult(); } finally { diff --git a/CleanAspCore/Extensions/FluentValidation/FluentValidationExtensions.cs b/CleanAspCore/Extensions/FluentValidation/FluentValidationExtensions.cs index 216c69e..2abfa21 100644 --- a/CleanAspCore/Extensions/FluentValidation/FluentValidationExtensions.cs +++ b/CleanAspCore/Extensions/FluentValidation/FluentValidationExtensions.cs @@ -9,7 +9,7 @@ public static void ValidateNullableReferences(this AbstractValidator properties = GetNonNullableProperties(new NullabilityInfoContext()); - validator.ApplyRuleToProperties(new GenericNotNullOrEmptyRule(), properties); + validator.ApplyRuleToProperties(new GenericNotNullRule(), properties); } private static IEnumerable GetNonNullableProperties(NullabilityInfoContext nullabilityInfoContext) => diff --git a/CleanAspCore/Extensions/FluentValidation/GenericNotNullOrEmptyRule.cs b/CleanAspCore/Extensions/FluentValidation/GenericNotNullRule.cs similarity index 53% rename from CleanAspCore/Extensions/FluentValidation/GenericNotNullOrEmptyRule.cs rename to CleanAspCore/Extensions/FluentValidation/GenericNotNullRule.cs index 8f59de0..138cbf9 100644 --- a/CleanAspCore/Extensions/FluentValidation/GenericNotNullOrEmptyRule.cs +++ b/CleanAspCore/Extensions/FluentValidation/GenericNotNullRule.cs @@ -1,6 +1,6 @@ namespace CleanAspCore.Extensions.FluentValidation; -public class GenericNotNullOrEmptyRule : IGenericRule +public class GenericNotNullRule : IGenericRule { - public void ApplyRule(IRuleBuilderInitial builder) => builder.NotNull().NotEmpty(); + public void ApplyRule(IRuleBuilderInitial builder) => builder.NotNull(); } diff --git a/CleanAspCore/Features/Departments/Endpoints/AddDepartments.cs b/CleanAspCore/Features/Departments/Endpoints/AddDepartments.cs index 564c6e4..bc714c3 100644 --- a/CleanAspCore/Features/Departments/Endpoints/AddDepartments.cs +++ b/CleanAspCore/Features/Departments/Endpoints/AddDepartments.cs @@ -11,6 +11,14 @@ public sealed class CreateDepartmentRequest public required string City { get; init; } } +public sealed class CreateDepartmentRequestValidator : AbstractValidator +{ + public CreateDepartmentRequestValidator() + { + this.ValidateNullableReferences(); + } +} + internal static class AddDepartments { public static async Task Handle(HrContext context, CreateDepartmentRequest createDepartmentRequest, CancellationToken cancellationToken) @@ -29,12 +37,4 @@ public static async Task Handle(HrContext context, CreateDepartm Name = department.Name, City = department.City }; - - private sealed class CreateDepartmentRequestValidator : AbstractValidator - { - public CreateDepartmentRequestValidator() - { - this.ValidateNullableReferences(); - } - } } diff --git a/CleanAspCore/Features/Employees/Endpoints/AddEmployee.cs b/CleanAspCore/Features/Employees/Endpoints/AddEmployee.cs index c6d21fd..c9ba4c0 100644 --- a/CleanAspCore/Features/Employees/Endpoints/AddEmployee.cs +++ b/CleanAspCore/Features/Employees/Endpoints/AddEmployee.cs @@ -15,6 +15,16 @@ public sealed class CreateEmployeeRequest public Guid JobId { get; init; } } +public sealed class CreateEmployeeRequestValidator : AbstractValidator +{ + public CreateEmployeeRequestValidator() + { + this.ValidateNullableReferences(); + + RuleFor(x => x.Email).EmailAddress(); + } +} + internal static class AddEmployee { internal static async Task Handle([FromBody] CreateEmployeeRequest request, HrContext context, [FromServices] IValidator validator, @@ -38,14 +48,4 @@ internal static async Task Handle([FromBody] CreateEmployeeReque DepartmentId = employee.DepartmentId, JobId = employee.JobId }; - - private sealed class CreateEmployeeRequestValidator : AbstractValidator - { - public CreateEmployeeRequestValidator() - { - this.ValidateNullableReferences(); - - RuleFor(x => x.Email).EmailAddress(); - } - } } diff --git a/CleanAspCore/Features/Employees/Endpoints/UpdateEmployeeById.cs b/CleanAspCore/Features/Employees/Endpoints/UpdateEmployeeById.cs index f9f9f39..13d4978 100644 --- a/CleanAspCore/Features/Employees/Endpoints/UpdateEmployeeById.cs +++ b/CleanAspCore/Features/Employees/Endpoints/UpdateEmployeeById.cs @@ -18,6 +18,16 @@ public sealed class UpdateEmployeeRequest public Guid? JobId { get; init; } } +public class UpdateEmployeeRequestValidator : AbstractValidator +{ + public UpdateEmployeeRequestValidator() + { + this.ValidateNullableReferences(); + + RuleFor(x => x.Email).EmailAddress(); + } +} + internal static class UpdateEmployeeById { internal static async Task> Handle( @@ -42,13 +52,3 @@ internal static async Task> Handle( }; } } - -public class UpdateEmployeeRequestValidator : AbstractValidator -{ - public UpdateEmployeeRequestValidator() - { - this.ValidateNullableReferences(); - - RuleFor(x => x.Email).EmailAddress(); - } -} diff --git a/CleanAspCore/Features/Jobs/Endpoints/AddJobs.cs b/CleanAspCore/Features/Jobs/Endpoints/AddJobs.cs index 12897d7..965485e 100644 --- a/CleanAspCore/Features/Jobs/Endpoints/AddJobs.cs +++ b/CleanAspCore/Features/Jobs/Endpoints/AddJobs.cs @@ -10,6 +10,14 @@ public sealed class CreateJobRequest public required string Name { get; init; } } +public sealed class CreateJobRequestValidator : AbstractValidator +{ + public CreateJobRequestValidator() + { + this.ValidateNullableReferences(); + } +} + internal static class AddJobs { internal static async Task Handle([FromBody] CreateJobRequest createJobRequest, HrContext context, CancellationToken cancellationToken) @@ -27,12 +35,4 @@ internal static async Task Handle([FromBody] CreateJobRequest cr Id = Guid.NewGuid(), Name = createJobRequest.Name }; - - private sealed class CreateJobRequestValidator : AbstractValidator - { - public CreateJobRequestValidator() - { - this.ValidateNullableReferences(); - } - } } diff --git a/CleanAspCore/Program.cs b/CleanAspCore/Program.cs index bc87b87..a1f1571 100644 --- a/CleanAspCore/Program.cs +++ b/CleanAspCore/Program.cs @@ -15,7 +15,7 @@ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme); -builder.Services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly(), includeInternalTypes: true); +builder.Services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly()); builder.Services.AddDbContext(); var app = builder.Build();