From df351a83083984b3ebe144f41d92728b40860bed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Kurzyniec?= Date: Fri, 3 Jan 2025 18:45:11 +0100 Subject: [PATCH] added unit tests for HttpGlobalExceptionFilter, excluded from code coverage some infrastructure code --- .github/workflows/dotnetcore.yml | 2 +- .../Configurations/BannerConfigurator.cs | 3 + .../Configurations/SerilogConfigurator.cs | 2 + .../FeatureFlagSwaggerDocumentFilter.cs | 2 + ...curityRequirementSwaggerOperationFilter.cs | 2 + .../Middlewares/ConnectionInfoMiddleware.cs | 21 ++--- .../Registrations/SwaggerRegistration.cs | 2 + .../Filters/HttpGlobalExceptionFilterTests.cs | 84 +++++++++++++++++++ 8 files changed, 107 insertions(+), 11 deletions(-) create mode 100644 test/HappyCode.NetCoreBoilerplate.Api.UnitTests/Infrastructure/Filters/HttpGlobalExceptionFilterTests.cs diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml index 6fc5f3f4b..dc9995e8a 100644 --- a/.github/workflows/dotnetcore.yml +++ b/.github/workflows/dotnetcore.yml @@ -17,7 +17,7 @@ on: env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: true - EXCLUDE_PATHS: '\"**/Program.cs,**/Startup.cs,**/Configurations/*.cs,**/Registrations/*.cs,**/Filters/*Swagger*Filter.cs,**/*Context.cs\"' + EXCLUDE_PATHS: '\"**/Program.cs,**/Startup.cs,**/*Context.cs\"' jobs: build-and-test: diff --git a/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Configurations/BannerConfigurator.cs b/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Configurations/BannerConfigurator.cs index ea0996789..526451875 100644 --- a/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Configurations/BannerConfigurator.cs +++ b/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Configurations/BannerConfigurator.cs @@ -1,5 +1,8 @@ +using System.Diagnostics.CodeAnalysis; + namespace HappyCode.NetCoreBoilerplate.Api.Infrastructure.Configurations; +[ExcludeFromCodeCoverage] public static class BannerConfigurator { private const string _cyan = "\x1b[96m"; diff --git a/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Configurations/SerilogConfigurator.cs b/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Configurations/SerilogConfigurator.cs index 3cee3f35e..750e8a23e 100644 --- a/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Configurations/SerilogConfigurator.cs +++ b/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Configurations/SerilogConfigurator.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using HappyCode.NetCoreBoilerplate.Api.Infrastructure.Logging; using Microsoft.Extensions.Configuration; using Serilog; @@ -5,6 +6,7 @@ namespace HappyCode.NetCoreBoilerplate.Api.Infrastructure.Configurations { + [ExcludeFromCodeCoverage] public static class SerilogConfigurator { public static Logger CreateLogger() diff --git a/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Filters/FeatureFlagSwaggerDocumentFilter.cs b/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Filters/FeatureFlagSwaggerDocumentFilter.cs index c08264871..dafff789e 100644 --- a/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Filters/FeatureFlagSwaggerDocumentFilter.cs +++ b/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Filters/FeatureFlagSwaggerDocumentFilter.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Linq; using HappyCode.NetCoreBoilerplate.Core; using Microsoft.FeatureManagement; @@ -7,6 +8,7 @@ namespace HappyCode.NetCoreBoilerplate.Api.Infrastructure.Filters { + [ExcludeFromCodeCoverage] public class FeatureFlagSwaggerDocumentFilter : IDocumentFilter { private readonly IFeatureManager _featureManager; diff --git a/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Filters/SecurityRequirementSwaggerOperationFilter.cs b/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Filters/SecurityRequirementSwaggerOperationFilter.cs index deb46eb99..74705d03e 100644 --- a/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Filters/SecurityRequirementSwaggerOperationFilter.cs +++ b/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Filters/SecurityRequirementSwaggerOperationFilter.cs @@ -2,9 +2,11 @@ using System.Linq; using Swashbuckle.AspNetCore.SwaggerGen; using Microsoft.AspNetCore.Authorization; +using System.Diagnostics.CodeAnalysis; namespace HappyCode.NetCoreBoilerplate.Api.Infrastructure.Filters { + [ExcludeFromCodeCoverage] public class SecurityRequirementSwaggerOperationFilter : IOperationFilter { public void Apply(OpenApiOperation operation, OperationFilterContext context) diff --git a/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Middlewares/ConnectionInfoMiddleware.cs b/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Middlewares/ConnectionInfoMiddleware.cs index 8280aeed2..074bb99b5 100644 --- a/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Middlewares/ConnectionInfoMiddleware.cs +++ b/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Middlewares/ConnectionInfoMiddleware.cs @@ -1,18 +1,19 @@ +using System.Diagnostics.CodeAnalysis; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; -namespace HappyCode.NetCoreBoilerplate.Api.Infrastructure.Middlewares +namespace HappyCode.NetCoreBoilerplate.Api.Infrastructure.Middlewares; + +[ExcludeFromCodeCoverage] +public class ConnectionInfoMiddleware(RequestDelegate next, ILogger logger) { - public class ConnectionInfoMiddleware(RequestDelegate next, ILogger logger) - { - private readonly RequestDelegate _next = next; - private readonly ILogger _logger = logger; + private readonly RequestDelegate _next = next; + private readonly ILogger _logger = logger; - public async Task InvokeAsync(HttpContext httpContext) - { - _logger.LogDebug("===> Connection: {Connection}", new { ConnectionId = httpContext.Connection.Id, LocalIP = httpContext.Connection.LocalIpAddress, RemoteIP = httpContext.Connection.RemoteIpAddress }); + public async Task InvokeAsync(HttpContext httpContext) + { + _logger.LogDebug("===> Connection: {Connection}", new { ConnectionId = httpContext.Connection.Id, LocalIP = httpContext.Connection.LocalIpAddress, RemoteIP = httpContext.Connection.RemoteIpAddress }); - await _next.Invoke(httpContext); - } + await _next.Invoke(httpContext); } } diff --git a/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Registrations/SwaggerRegistration.cs b/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Registrations/SwaggerRegistration.cs index 223c52165..925b4e6b9 100644 --- a/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Registrations/SwaggerRegistration.cs +++ b/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Registrations/SwaggerRegistration.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.IO; using HappyCode.NetCoreBoilerplate.Api.Infrastructure.Filters; using Microsoft.Extensions.Configuration; @@ -6,6 +7,7 @@ namespace HappyCode.NetCoreBoilerplate.Api.Infrastructure.Registrations { + [ExcludeFromCodeCoverage] public static class SwaggerRegistration { public static void AddSwagger(this IServiceCollection services, IConfiguration configuration) diff --git a/test/HappyCode.NetCoreBoilerplate.Api.UnitTests/Infrastructure/Filters/HttpGlobalExceptionFilterTests.cs b/test/HappyCode.NetCoreBoilerplate.Api.UnitTests/Infrastructure/Filters/HttpGlobalExceptionFilterTests.cs new file mode 100644 index 000000000..66280ba25 --- /dev/null +++ b/test/HappyCode.NetCoreBoilerplate.Api.UnitTests/Infrastructure/Filters/HttpGlobalExceptionFilterTests.cs @@ -0,0 +1,84 @@ +using System; +using FluentAssertions; +using HappyCode.NetCoreBoilerplate.Api.Infrastructure.Filters; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.Logging; +using Moq; +using Xunit; + +namespace HappyCode.NetCoreBoilerplate.Api.UnitTests.Infrastructure.Filters +{ + public class HttpGlobalExceptionFilterTests + { + private const string _exMessage = "Some exception test message"; + + private readonly HttpGlobalExceptionFilter _sut; + + private readonly Mock _envMock; + + public HttpGlobalExceptionFilterTests() + { + _envMock = new Mock(MockBehavior.Strict); + + _sut = new HttpGlobalExceptionFilter(_envMock.Object, new Mock>().Object); + } + + [Fact] + public void When_Development_Then_exception_details_returned() + { + //given + _envMock.SetupGet(x => x.EnvironmentName).Returns("Development"); + + //when + var context = GetMockedContext(); + _sut.OnException(context); + + //then + context.Result.Should().NotBeNull(); + context.Result.Should().BeOfType() + .Subject.StatusCode.Should().Be(StatusCodes.Status500InternalServerError); + context.Result.Should().BeOfType() + .Subject.Value.Should().BeOfType() + .Subject.Exception.Should().NotBeNullOrEmpty() + .And.Contain(_exMessage); + } + + [Fact] + public void When_not_Development_Then_exception_details_empty() + { + //given + _envMock.SetupGet(x => x.EnvironmentName).Returns("Other"); + + //when + var context = GetMockedContext(); + _sut.OnException(context); + + //then + context.Result.Should().NotBeNull(); + context.Result.Should().BeOfType() + .Subject.Value.Should().BeOfType() + .Subject.Exception.Should().BeNullOrEmpty(); + } + + private ExceptionContext GetMockedContext() + { + var actionContext = new ActionContext( + Mock.Of(), + Mock.Of(), + Mock.Of(), + Mock.Of() + ); + + return new ExceptionContext(actionContext, []) + { + Exception = new Exception(_exMessage), + }; + } + } +}