From 247e58e7ab4a8a4e97584995582b168ba4e106af Mon Sep 17 00:00:00 2001 From: Andrii Voznesenskyi Date: Sat, 21 Sep 2024 18:31:27 +0200 Subject: [PATCH] (#23) logging: add tests --- .../src/Paralax.CQRS.Logging.csproj | 9 ++ src/Paralax.Logging/scripts/build-and-pack.sh | 35 ++++++ .../scripts/test-and-collect-coverage.sh | 18 ++++ .../src/Paralax.Logging.csproj | 6 ++ ...orrelationContextLoggingMiddlewareTests.cs | 100 ++++++++++++++++++ src/Paralax.Logging/tests/ExtensionsTests.cs | 48 +++++++++ .../tests/ExtensionsWebHostTests.cs | 65 ++++++++++++ .../tests/LogLevelEndpointTests.cs | 0 .../tests/LoggingServiceTests.cs | 0 .../tests/Paralax.Logging.Tests.csproj | 40 +++++++ .../tests/Paralax.Logging.Tests.sln | 25 +++++ src/Paralax/src/Core/DecoratorAttribute.cs | 8 ++ src/Paralax/src/Core/ServiceId.cs | 2 +- 13 files changed, 355 insertions(+), 1 deletion(-) create mode 100644 src/Paralax.CQRS.Logging/src/Paralax.CQRS.Logging.csproj create mode 100755 src/Paralax.Logging/scripts/build-and-pack.sh create mode 100644 src/Paralax.Logging/scripts/test-and-collect-coverage.sh create mode 100644 src/Paralax.Logging/tests/CorrelationContextLoggingMiddlewareTests.cs create mode 100644 src/Paralax.Logging/tests/ExtensionsTests.cs create mode 100644 src/Paralax.Logging/tests/ExtensionsWebHostTests.cs create mode 100644 src/Paralax.Logging/tests/LogLevelEndpointTests.cs create mode 100644 src/Paralax.Logging/tests/LoggingServiceTests.cs create mode 100644 src/Paralax.Logging/tests/Paralax.Logging.Tests.csproj create mode 100644 src/Paralax.Logging/tests/Paralax.Logging.Tests.sln create mode 100644 src/Paralax/src/Core/DecoratorAttribute.cs diff --git a/src/Paralax.CQRS.Logging/src/Paralax.CQRS.Logging.csproj b/src/Paralax.CQRS.Logging/src/Paralax.CQRS.Logging.csproj new file mode 100644 index 0000000..125f4c9 --- /dev/null +++ b/src/Paralax.CQRS.Logging/src/Paralax.CQRS.Logging.csproj @@ -0,0 +1,9 @@ + + + + net9.0 + enable + enable + + + diff --git a/src/Paralax.Logging/scripts/build-and-pack.sh b/src/Paralax.Logging/scripts/build-and-pack.sh new file mode 100755 index 0000000..7e96e37 --- /dev/null +++ b/src/Paralax.Logging/scripts/build-and-pack.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +echo "Executing post-success scripts for branch $GITHUB_REF_NAME" +echo "Starting build and NuGet package creation for Paralax.Security..." + +cd src/Paralax.Security/src + +echo "Restoring NuGet packages..." +dotnet restore + +PACKAGE_VERSION="1.0.$GITHUB_RUN_NUMBER" +echo "Building and packing the Paralax.Security library..." +dotnet pack -c release /p:PackageVersion=$PACKAGE_VERSION --no-restore -o ./nupkg + +PACKAGE_PATH="./nupkg/Paralax.Security.$PACKAGE_VERSION.nupkg" + +if [ -f "$PACKAGE_PATH" ]; then + echo "Checking if the package is already signed..." + if dotnet nuget verify "$PACKAGE_PATH" | grep -q 'Package is signed'; then + echo "Package is already signed, skipping signing." + else + echo "Signing the NuGet package..." + dotnet nuget sign "$PACKAGE_PATH" \ + --certificate-path "$CERTIFICATE_PATH" \ + --timestamper http://timestamp.digicert.com + fi + + echo "Uploading Paralax.Security package to NuGet..." + dotnet nuget push "$PACKAGE_PATH" -k "$NUGET_API_KEY" \ + -s https://api.nuget.org/v3/index.json --skip-duplicate + echo "Package uploaded to NuGet." +else + echo "Error: Package $PACKAGE_PATH not found." + exit 1 +fi diff --git a/src/Paralax.Logging/scripts/test-and-collect-coverage.sh b/src/Paralax.Logging/scripts/test-and-collect-coverage.sh new file mode 100644 index 0000000..30fa605 --- /dev/null +++ b/src/Paralax.Logging/scripts/test-and-collect-coverage.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +echo "Running tests and collecting coverage for Paralax.Security..." + +cd src/Paralax.Security/src + +echo "Restoring NuGet packages..." +dotnet restore + +echo "Running tests and generating code coverage report..." +dotnet test --collect:"XPlat Code Coverage" --results-directory ./TestResults + +# Check if tests succeeded +if [ $? -ne 0 ]; then + echo "Tests failed. Exiting..." + exit 1 +fi + diff --git a/src/Paralax.Logging/src/Paralax.Logging.csproj b/src/Paralax.Logging/src/Paralax.Logging.csproj index 5b2d63f..ef8925c 100644 --- a/src/Paralax.Logging/src/Paralax.Logging.csproj +++ b/src/Paralax.Logging/src/Paralax.Logging.csproj @@ -19,4 +19,10 @@ + + + <_Parameter1>Paralax.Logging.Tests + + + diff --git a/src/Paralax.Logging/tests/CorrelationContextLoggingMiddlewareTests.cs b/src/Paralax.Logging/tests/CorrelationContextLoggingMiddlewareTests.cs new file mode 100644 index 0000000..a784b27 --- /dev/null +++ b/src/Paralax.Logging/tests/CorrelationContextLoggingMiddlewareTests.cs @@ -0,0 +1,100 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using Paralax.Logging; +using Moq; +using Xunit; + +namespace Paralax.Logging.Tests +{ + public class CorrelationContextLoggingMiddlewareTests + { + private readonly Mock> _loggerMock; + private readonly Mock _nextMock; + private readonly CorrelationContextLoggingMiddleware _middleware; + + public CorrelationContextLoggingMiddlewareTests() + { + // Set up the mocks + _loggerMock = new Mock>(); + _nextMock = new Mock(); + + // Instantiate the middleware with the mock logger + _middleware = new CorrelationContextLoggingMiddleware(_loggerMock.Object); + } + + [Fact] + public async Task InvokeAsync_Should_Log_Correlation_Headers_When_Activity_Exists() + { + // Arrange + var httpContext = new DefaultHttpContext(); + var baggage = new List> + { + new KeyValuePair("CorrelationId", "12345"), + new KeyValuePair("UserId", "User123") + }; + + // Simulate an Activity with baggage + var activity = new Activity("TestActivity"); + activity.AddBaggage("CorrelationId", "12345"); + activity.AddBaggage("UserId", "User123"); + activity.Start(); + + _nextMock.Setup(next => next(It.IsAny())).Returns(Task.CompletedTask); + + // Act + await _middleware.InvokeAsync(httpContext, _nextMock.Object); + + // Assert + _loggerMock.Verify(logger => + logger.BeginScope(It.Is>(d => + d.ContainsKey("CorrelationId") && d["CorrelationId"] == "12345" && + d.ContainsKey("UserId") && d["UserId"] == "User123")), Times.Once); + + // Cleanup + activity.Stop(); + } + + [Fact] + public async Task InvokeAsync_Should_Log_Empty_Headers_When_Activity_Is_Null() + { + // Arrange + var httpContext = new DefaultHttpContext(); + + // No active activity, meaning Activity.Current is null + Activity.Current = null; + + _nextMock.Setup(next => next(It.IsAny())).Returns(Task.CompletedTask); + + // Act + await _middleware.InvokeAsync(httpContext, _nextMock.Object); + + // Assert + _loggerMock.Verify(logger => + logger.BeginScope(It.Is>(d => d.Count == 0)), Times.Once); + } + + [Fact] + public async Task InvokeAsync_Should_Call_Next_Middleware() + { + // Arrange + var httpContext = new DefaultHttpContext(); + + // Simulate an empty Activity + var activity = new Activity("TestActivity").Start(); + _nextMock.Setup(next => next(It.IsAny())).Returns(Task.CompletedTask); + + // Act + await _middleware.InvokeAsync(httpContext, _nextMock.Object); + + // Assert + _nextMock.Verify(next => next(It.IsAny()), Times.Once); + + // Cleanup + activity.Stop(); + } + } +} diff --git a/src/Paralax.Logging/tests/ExtensionsTests.cs b/src/Paralax.Logging/tests/ExtensionsTests.cs new file mode 100644 index 0000000..9b6b2f1 --- /dev/null +++ b/src/Paralax.Logging/tests/ExtensionsTests.cs @@ -0,0 +1,48 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Moq; +using Serilog; +using Serilog.Core; +using Serilog.Events; +using Xunit; + +namespace Paralax.Logging.Tests +{ + public class ExtensionsTests + { + [Fact] + public void UseLogging_Should_Register_LoggingService_On_HostBuilder() + { + // Arrange + var hostBuilder = new HostBuilder(); + + // Act + hostBuilder.UseLogging(); + var host = hostBuilder.Build(); + var loggingService = host.Services.GetService(); + + // Assert + Assert.NotNull(loggingService); + } + + [Fact] + public void UseLogging_Should_Configure_Serilog_On_HostBuilder() + { + // Arrange + var hostBuilder = new HostBuilder(); + var loggerConfigurationMock = new Mock(); + + // Act + hostBuilder.UseLogging((context, loggerConfig) => + { + loggerConfig.WriteTo.Console(); + }); + + var host = hostBuilder.Build(); + + // Assert + var loggingService = host.Services.GetService(); + Assert.NotNull(loggingService); // Check if logging service is registered + } + } +} diff --git a/src/Paralax.Logging/tests/ExtensionsWebHostTests.cs b/src/Paralax.Logging/tests/ExtensionsWebHostTests.cs new file mode 100644 index 0000000..7fbad92 --- /dev/null +++ b/src/Paralax.Logging/tests/ExtensionsWebHostTests.cs @@ -0,0 +1,65 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using Serilog; +using Xunit; + +namespace Paralax.Logging.Tests +{ + public class ExtensionsWebHostTests + { + // Mock Startup class to ensure that web host configuration is valid + public class TestStartup + { + public void ConfigureServices(IServiceCollection services) + { + // Add required services here (logging, etc.) + services.AddSingleton(); // Example service registration + } + + // A Configure method is required by WebHostBuilder for setting up the request pipeline + public void Configure(IApplicationBuilder app) + { + // Minimal middleware setup, can be left empty or add minimal configurations + app.UseRouting(); + } + } + + [Fact] + public void UseLogging_Should_Register_LoggingService_On_WebHostBuilder() + { + // Arrange + var webHostBuilder = new WebHostBuilder() + .UseStartup(); // Provide a minimal Startup class + + // Act + webHostBuilder.UseLogging(); + var webHost = webHostBuilder.Build(); + var loggingService = webHost.Services.GetService(); + + // Assert + Assert.NotNull(loggingService); // Verify if the logging service is registered + } + + [Fact] + public void UseLogging_Should_Configure_Serilog_On_WebHostBuilder() + { + // Arrange + var webHostBuilder = new WebHostBuilder() + .UseStartup(); // Provide a minimal Startup class + + // Act + webHostBuilder.UseLogging((context, loggerConfig) => + { + loggerConfig.WriteTo.Console(); // Configure Serilog + }); + + var webHost = webHostBuilder.Build(); + + // Assert + var loggingService = webHost.Services.GetService(); + Assert.NotNull(loggingService); // Check if logging service is registered + } + } +} diff --git a/src/Paralax.Logging/tests/LogLevelEndpointTests.cs b/src/Paralax.Logging/tests/LogLevelEndpointTests.cs new file mode 100644 index 0000000..e69de29 diff --git a/src/Paralax.Logging/tests/LoggingServiceTests.cs b/src/Paralax.Logging/tests/LoggingServiceTests.cs new file mode 100644 index 0000000..e69de29 diff --git a/src/Paralax.Logging/tests/Paralax.Logging.Tests.csproj b/src/Paralax.Logging/tests/Paralax.Logging.Tests.csproj new file mode 100644 index 0000000..50c5e1a --- /dev/null +++ b/src/Paralax.Logging/tests/Paralax.Logging.Tests.csproj @@ -0,0 +1,40 @@ + + + + net9.0 + enable + enable + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Paralax.Logging/tests/Paralax.Logging.Tests.sln b/src/Paralax.Logging/tests/Paralax.Logging.Tests.sln new file mode 100644 index 0000000..b627ae3 --- /dev/null +++ b/src/Paralax.Logging/tests/Paralax.Logging.Tests.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Paralax.Logging.Tests", "Paralax.Logging.Tests.csproj", "{6171C645-820E-41DA-BAA6-CF33C2069427}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6171C645-820E-41DA-BAA6-CF33C2069427}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6171C645-820E-41DA-BAA6-CF33C2069427}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6171C645-820E-41DA-BAA6-CF33C2069427}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6171C645-820E-41DA-BAA6-CF33C2069427}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DD4E79DA-E238-4315-A58B-9595AC1B57BB} + EndGlobalSection +EndGlobal diff --git a/src/Paralax/src/Core/DecoratorAttribute.cs b/src/Paralax/src/Core/DecoratorAttribute.cs new file mode 100644 index 0000000..c39e517 --- /dev/null +++ b/src/Paralax/src/Core/DecoratorAttribute.cs @@ -0,0 +1,8 @@ +using System; + +namespace Paralax.Core; + +// Marker +public class DecoratorAttribute : Attribute +{ +} \ No newline at end of file diff --git a/src/Paralax/src/Core/ServiceId.cs b/src/Paralax/src/Core/ServiceId.cs index 2f78e4c..b6f42fb 100644 --- a/src/Paralax/src/Core/ServiceId.cs +++ b/src/Paralax/src/Core/ServiceId.cs @@ -1,7 +1,7 @@ using System; using Paralax.Types; -namespace Paralax +namespace Paralax.Core { internal class ServiceId : IServiceId {