diff --git a/.csharpierignore b/.csharpierignore index 7e3489e..cf72dd9 100644 --- a/.csharpierignore +++ b/.csharpierignore @@ -1,3 +1,3 @@ -**/nuget.config -**/_snapshots/ -**/_snapshot/ +**/[Nn]u[Gg]et.config +**/*.verified.* +**/*.received.* diff --git a/.editorconfig b/.editorconfig index 175c36f..93483f4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -44,18 +44,22 @@ generated_code = true # XML project files [*.{slnx,csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj,nativeproj,locproj}] indent_size = 2 +max_line_length = 200 # Xml build files [*.builds] indent_size = 2 +max_line_length = 200 # Xml files [*.{xml,stylecop,resx,ruleset}] indent_size = 2 +max_line_length = 200 # XML config files [*.{props,targets,ruleset,config,nuspec,vsixmanifest,vsct}] indent_size = 2 +max_line_length = 200 # JSON files [*.json] @@ -86,10 +90,6 @@ insert_final_newline = false [*.sln] indent_style = tab -[*.{received,verified}.txt] -insert_final_newline = false -trim_trailing_whitespace = false - [*.{cs,csx,vb,vbx}] # .NET Code Style Settings # See https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference @@ -266,19 +266,18 @@ dotnet_diagnostic.IDE0290.severity = sugges # [CSharpier] Incompatible rules deactivated # https://csharpier.com/docs/IntegratingWithLinters#code-analysis-rules dotnet_diagnostic.IDE0055.severity = none -dotnet_diagnostic.SA1000.severity = none -dotnet_diagnostic.SA1009.severity = none -dotnet_diagnostic.SA1111.severity = none -dotnet_diagnostic.SA1118.severity = none -dotnet_diagnostic.SA1137.severity = none -dotnet_diagnostic.SA1413.severity = none -dotnet_diagnostic.SA1500.severity = none -dotnet_diagnostic.SA1501.severity = none -dotnet_diagnostic.SA1502.severity = none -dotnet_diagnostic.SA1504.severity = none -dotnet_diagnostic.SA1515.severity = none -dotnet_diagnostic.SA1516.severity = none # Support for NetEvolve.Arguments Methods # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1062#null-check-validation-methods dotnet_code_quality.CA1062.null_check_validation_methods = M:NetEvolve.Arguments.Argument.ThrowIfNull(System.Object,System.String)|M:NetEvolve.Arguments.Argument.ThrowIfNull(System.Void*,System.String)|M:NetEvolve.Arguments.Argument.ThrowIfNullOrEmpty(System.String,System.String)|M:NetEvolve.Arguments.Argument.ThrowIfNullOrEmpty``1(System.Collections.Generic.IEnumerable{``0},System.String)|M:NetEvolve.Arguments.Argument.ThrowIfNullOrWhiteSpace(System.String,System.String) + +# Disable all style rules for generated code +[*.{received,verified}.*] +generated_code = true +# Disable all style rules for migrations +dotnet_analyzer_diagnostic.severity = none + +[**/Migrations/*.{cs,csx,vb,vbx}] +generated_code = true +# Disable all style rules for migrations +dotnet_analyzer_diagnostic.severity = none diff --git a/Directory.Build.props b/Directory.Build.props index 65711a7..e1acf40 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -13,6 +13,5 @@ https://github.com/dailydevops/http.correlation.git $(PackageProjectUrl)/releases http;tracking;correlation;request id; - NetEvolve.Http.Correlation diff --git a/Directory.Packages.props b/Directory.Packages.props index 2bc8ed2..68d26b8 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -13,6 +13,7 @@ + diff --git a/Http.Correlation.slnx b/Http.Correlation.slnx index 898b0e6..812dbb3 100644 --- a/Http.Correlation.slnx +++ b/Http.Correlation.slnx @@ -10,14 +10,13 @@ - - + @@ -26,6 +25,8 @@ + + diff --git a/src/NetEvolve.Http.Correlation.Abstractions/Abstractions/IHttpCorrelationAccessor.cs b/src/NetEvolve.Http.Correlation.Abstractions/Abstractions/IHttpCorrelationAccessor.cs index 5682ec4..6121c56 100644 --- a/src/NetEvolve.Http.Correlation.Abstractions/Abstractions/IHttpCorrelationAccessor.cs +++ b/src/NetEvolve.Http.Correlation.Abstractions/Abstractions/IHttpCorrelationAccessor.cs @@ -10,7 +10,7 @@ public interface IHttpCorrelationAccessor /// /// Gets the current Correlation Id from the . /// - string CorrelationId { get; } + string CorrelationId { get; internal set; } /// /// Gets the header name, which is used. diff --git a/src/NetEvolve.Http.Correlation.AspNetCore/HttpCorrelationBuilder.cs b/src/NetEvolve.Http.Correlation.Abstractions/HttpCorrelationBuilder.cs similarity index 100% rename from src/NetEvolve.Http.Correlation.AspNetCore/HttpCorrelationBuilder.cs rename to src/NetEvolve.Http.Correlation.Abstractions/HttpCorrelationBuilder.cs diff --git a/src/NetEvolve.Http.Correlation.AspNetCore/HttpCorrelationBuilderExtensions.cs b/src/NetEvolve.Http.Correlation.Abstractions/HttpCorrelationBuilderExtensions.cs similarity index 100% rename from src/NetEvolve.Http.Correlation.AspNetCore/HttpCorrelationBuilderExtensions.cs rename to src/NetEvolve.Http.Correlation.Abstractions/HttpCorrelationBuilderExtensions.cs diff --git a/src/NetEvolve.Http.Correlation.Abstractions/NetEvolve.Http.Correlation.Abstractions.csproj b/src/NetEvolve.Http.Correlation.Abstractions/NetEvolve.Http.Correlation.Abstractions.csproj index 9380f88..c4490db 100644 --- a/src/NetEvolve.Http.Correlation.Abstractions/NetEvolve.Http.Correlation.Abstractions.csproj +++ b/src/NetEvolve.Http.Correlation.Abstractions/NetEvolve.Http.Correlation.Abstractions.csproj @@ -1,6 +1,7 @@ - + $(_TargetFrameworks) + NetEvolve.Http.Correlation Abstractions for the uniform use of Http.Correlation. Based on the primary Http header `X-Correlation-ID` as well as the alternative Http header `X-Request-ID`. @@ -9,6 +10,10 @@ + + + + diff --git a/src/NetEvolve.Http.Correlation.AspNetCore/ApplicationBuilderExtensions.cs b/src/NetEvolve.Http.Correlation.AspNetCore/ApplicationBuilderExtensions.cs index c586954..a10e3d4 100644 --- a/src/NetEvolve.Http.Correlation.AspNetCore/ApplicationBuilderExtensions.cs +++ b/src/NetEvolve.Http.Correlation.AspNetCore/ApplicationBuilderExtensions.cs @@ -1,4 +1,4 @@ -namespace NetEvolve.Http.Correlation; +namespace NetEvolve.Http.Correlation.AspNetCore; using System; using Microsoft.AspNetCore.Builder; diff --git a/src/NetEvolve.Http.Correlation.AspNetCore/HttpCorrelationAccessor.cs b/src/NetEvolve.Http.Correlation.AspNetCore/HttpCorrelationAccessor.cs index fed6506..90ed278 100644 --- a/src/NetEvolve.Http.Correlation.AspNetCore/HttpCorrelationAccessor.cs +++ b/src/NetEvolve.Http.Correlation.AspNetCore/HttpCorrelationAccessor.cs @@ -1,4 +1,4 @@ -namespace NetEvolve.Http.Correlation; +namespace NetEvolve.Http.Correlation.AspNetCore; using Microsoft.AspNetCore.Http; using NetEvolve.Http.Correlation.Abstractions; @@ -7,12 +7,17 @@ internal sealed class HttpCorrelationAccessor : IHttpCorrelationAccessor { private readonly IHttpContextAccessor _httpContextAccessor; + private string? _correlationId; public HttpCorrelationAccessor(IHttpContextAccessor httpContextAccessor) => _httpContextAccessor = httpContextAccessor; /// - public string CorrelationId => _httpContextAccessor.HttpContext!.TraceIdentifier; + public string CorrelationId + { + get => _correlationId ??= _httpContextAccessor.HttpContext!.TraceIdentifier; + set => _correlationId = value; + } /// public string HeaderName { get; set; } = CorrelationConstants.HeaderName1; diff --git a/src/NetEvolve.Http.Correlation.AspNetCore/HttpCorrelationMiddleware.cs b/src/NetEvolve.Http.Correlation.AspNetCore/HttpCorrelationMiddleware.cs index a0a0795..8aeda2d 100644 --- a/src/NetEvolve.Http.Correlation.AspNetCore/HttpCorrelationMiddleware.cs +++ b/src/NetEvolve.Http.Correlation.AspNetCore/HttpCorrelationMiddleware.cs @@ -1,4 +1,4 @@ -namespace NetEvolve.Http.Correlation; +namespace NetEvolve.Http.Correlation.AspNetCore; using System; using System.Collections.Generic; diff --git a/src/NetEvolve.Http.Correlation.AspNetCore/NetEvolve.Http.Correlation.AspNetCore.csproj b/src/NetEvolve.Http.Correlation.AspNetCore/NetEvolve.Http.Correlation.AspNetCore.csproj index 74952ca..7fd6b2e 100644 --- a/src/NetEvolve.Http.Correlation.AspNetCore/NetEvolve.Http.Correlation.AspNetCore.csproj +++ b/src/NetEvolve.Http.Correlation.AspNetCore/NetEvolve.Http.Correlation.AspNetCore.csproj @@ -4,6 +4,7 @@ Implementation of AspNetCore middleware to use Http.Correlation. Based on the primary Http header `X-Correlation-ID` as well as the alternative Http header `X-Request-ID`. + $(PackageTags);aspnetcore @@ -11,4 +12,8 @@ + + + + diff --git a/src/NetEvolve.Http.Correlation.AspNetCore/ServiceCollectionExtensions.cs b/src/NetEvolve.Http.Correlation.AspNetCore/ServiceCollectionExtensions.cs index f294528..efced26 100644 --- a/src/NetEvolve.Http.Correlation.AspNetCore/ServiceCollectionExtensions.cs +++ b/src/NetEvolve.Http.Correlation.AspNetCore/ServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -namespace NetEvolve.Http.Correlation; +namespace NetEvolve.Http.Correlation.AspNetCore; using System; using Microsoft.Extensions.DependencyInjection; diff --git a/src/NetEvolve.Http.Correlation.Azure.Functions/FunctionsCorrelationAccessor.cs b/src/NetEvolve.Http.Correlation.Azure.Functions/FunctionsCorrelationAccessor.cs new file mode 100644 index 0000000..8711595 --- /dev/null +++ b/src/NetEvolve.Http.Correlation.Azure.Functions/FunctionsCorrelationAccessor.cs @@ -0,0 +1,19 @@ +namespace NetEvolve.Http.Correlation.Azure.Functions; + +using Microsoft.Azure.Functions.Worker; +using NetEvolve.Http.Correlation.Abstractions; + +internal sealed class FunctionsCorrelationAccessor : IHttpCorrelationAccessor +{ + private string? _correlationId; + + public FunctionContext Context { get; set; } = default!; + + public string CorrelationId + { + get => _correlationId ??= Context.InvocationId; + set => _correlationId = value; + } + + public string HeaderName { get; set; } = string.Empty; +} diff --git a/src/NetEvolve.Http.Correlation.Azure.Functions/FunctionsCorrelationMiddleware.cs b/src/NetEvolve.Http.Correlation.Azure.Functions/FunctionsCorrelationMiddleware.cs new file mode 100644 index 0000000..27ec081 --- /dev/null +++ b/src/NetEvolve.Http.Correlation.Azure.Functions/FunctionsCorrelationMiddleware.cs @@ -0,0 +1,97 @@ +namespace NetEvolve.Http.Correlation.Azure.Functions; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Http; +using Microsoft.Azure.Functions.Worker.Middleware; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using NetEvolve.Http.Correlation.Abstractions; +using static NetEvolve.Http.Correlation.CorrelationConstants; + +internal sealed class FunctionsCorrelationMiddleware : IFunctionsWorkerMiddleware +{ + private readonly ILogger _logger; + + public FunctionsCorrelationMiddleware(ILogger logger) => _logger = logger; + + public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next) + { + ArgumentNullException.ThrowIfNull(context); + + var accessor = context.InstanceServices.GetRequiredService(); + accessor.Context = context; + + var httpRequestData = await context.GetHttpRequestDataAsync().ConfigureAwait(false); + + if (httpRequestData is null) + { + await next(context).ConfigureAwait(false); + return; + } + + if (!GetIdFromHeader(httpRequestData, out var correlationId, out var usedHeaderName)) + { + correlationId = GeneratedId(context); + } + + accessor.CorrelationId = correlationId; + accessor.HeaderName = usedHeaderName; + + var scopeProperties = new Dictionary(StringComparer.Ordinal) + { + { usedHeaderName, correlationId }, + }; + + using (_logger.BeginScope(scopeProperties)) + { + await next(context).ConfigureAwait(false); + + // Add correlation ID to response headers after function execution + var httpResponseData = context.GetInvocationResult().Value as HttpResponseData; + if (httpResponseData is not null && !httpResponseData.Headers.Contains(usedHeaderName)) + { + httpResponseData.Headers.Add(usedHeaderName, correlationId); + } + } + } + + private static string GeneratedId(FunctionContext context) => + context.InstanceServices.GetService()?.GenerateId() ?? context.InvocationId; + + private static bool GetIdFromHeader( + HttpRequestData httpRequestData, + out string correlationId, + out string usedHeaderName + ) + { + usedHeaderName = HeaderName1; + correlationId = string.Empty; + + if (httpRequestData.Headers.TryGetValues(HeaderName1, out var headerValues1)) + { + var firstValue = headerValues1.FirstOrDefault(); + if (!string.IsNullOrWhiteSpace(firstValue)) + { + correlationId = firstValue; + return true; + } + } + + if (httpRequestData.Headers.TryGetValues(HeaderName2, out var headerValues2)) + { + var firstValue = headerValues2.FirstOrDefault(); + if (!string.IsNullOrWhiteSpace(firstValue)) + { + correlationId = firstValue; + usedHeaderName = HeaderName2; + return true; + } + } + + return false; + } +} diff --git a/src/NetEvolve.Http.Correlation.Azure.Functions/FunctionsWorkerApplicationBuilderExtensions.cs b/src/NetEvolve.Http.Correlation.Azure.Functions/FunctionsWorkerApplicationBuilderExtensions.cs new file mode 100644 index 0000000..d8bde09 --- /dev/null +++ b/src/NetEvolve.Http.Correlation.Azure.Functions/FunctionsWorkerApplicationBuilderExtensions.cs @@ -0,0 +1,35 @@ +namespace NetEvolve.Http.Correlation.Azure.Functions; + +using System; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using NetEvolve.Http.Correlation.Abstractions; + +/// +/// Extensions for . +/// +public static class FunctionsWorkerApplicationBuilderExtensions +{ + /// + /// Adds the to the Azure Functions worker middleware pipeline. + /// + /// The instance. + /// The instance. + public static IFunctionsWorkerApplicationBuilder UseHttpCorrelation(this IFunctionsWorkerApplicationBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + using (var scopedServices = builder.Services.BuildServiceProvider().CreateScope()) + { + if (scopedServices.ServiceProvider.GetService() is null) + { + throw new InvalidOperationException( + $"The required services for this function were not found. Please run `services.{nameof(ServiceCollectionExtensions.AddHttpCorrelation)}()` in advance." + ); + } + } + + return builder.UseMiddleware(); + } +} diff --git a/src/NetEvolve.Http.Correlation.Azure.Functions/NetEvolve.Http.Correlation.Azure.Functions.csproj b/src/NetEvolve.Http.Correlation.Azure.Functions/NetEvolve.Http.Correlation.Azure.Functions.csproj new file mode 100644 index 0000000..e4e251d --- /dev/null +++ b/src/NetEvolve.Http.Correlation.Azure.Functions/NetEvolve.Http.Correlation.Azure.Functions.csproj @@ -0,0 +1,22 @@ + + + $(_TargetFrameworks) + + + Implementation of Azure.Functions middleware to use Http.Correlation. Based on the primary Http header `X-Correlation-ID` as well as the alternative Http header `X-Request-ID`. + $(PackageTags);azure;functions + + + + + + + + + + + + + + + diff --git a/src/NetEvolve.Http.Correlation.Azure.Functions/README.md b/src/NetEvolve.Http.Correlation.Azure.Functions/README.md new file mode 100644 index 0000000..bf33fce --- /dev/null +++ b/src/NetEvolve.Http.Correlation.Azure.Functions/README.md @@ -0,0 +1,105 @@ +# NetEvolve.Http.Correlation.Azure.Functions + +[![Nuget](https://img.shields.io/nuget/v/NetEvolve.Http.Correlation.Azure.Functions)](https://www.nuget.org/packages/NetEvolve.Http.Correlation.Azure.Functions) + +Azure Functions isolated worker middleware for managing HTTP correlation IDs across distributed requests. + +## Overview + +This package provides middleware and services to automatically handle correlation IDs in Azure Functions isolated worker applications. It ensures that incoming HTTP-triggered functions preserve incoming correlation headers and that new correlation IDs are generated when needed. + +## Key Features + +- **Automatic Correlation ID Handling**: Reads and preserves correlation IDs from incoming HTTP requests +- **Flexible Header Support**: Supports `X-Correlation-ID` (primary) and `X-Request-ID` (fallback) +- **Configurable ID Generation**: Pluggable correlation ID providers with default Sequential GUID implementation +- **Response Headers**: Includes correlation ID in response headers +- **Dependency Injection**: Seamless integration with Azure Functions DI container +- **Multi-Framework Support**: Compatible with .NET 8.0, 9.0, and 10.0 + +## Installation + +```bash +dotnet add package NetEvolve.Http.Correlation.Azure.Functions +``` + +## Usage + +### Basic Setup + +Configure services and middleware in your `Program.cs`: + +```csharp +using NetEvolve.Http.Correlation; + +var host = new HostBuilder() + .ConfigureFunctionsWorkerDefaults((context, builder) => + { + // Add middleware to the pipeline + builder.Services.AddHttpCorrelation(); + builder.UseHttpCorrelation(); + }) + .Build(); + +host.Run(); +``` + +### Advanced Configuration + +Use alternative ID generators: + +```csharp +var host = new HostBuilder() + .ConfigureFunctionsWorkerDefaults((context, builder) => + { + // Use GUID V7 (available in .NET 9.0+) + builder.Services + .AddHttpCorrelation() + .WithGuidV7Generator(); + + builder.UseHttpCorrelation(); + }) + .Build(); +``` + +### Accessing Correlation IDs + +Inject `IHttpCorrelationAccessor` to access the current correlation ID: + +```csharp +public class MyFunction +{ + private readonly IHttpCorrelationAccessor _correlationAccessor; + + public MyFunction(IHttpCorrelationAccessor correlationAccessor) + { + _correlationAccessor = correlationAccessor; + } + + [Function("MyFunction")] + public HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestData req) + { + var correlationId = _correlationAccessor.CorrelationId; + Console.WriteLine($"Processing request with correlation ID: {correlationId}"); + + var response = req.CreateResponse(HttpStatusCode.OK); + return response; + } +} +``` + +## Related Packages + +- **[NetEvolve.Http.Correlation.Abstractions](https://www.nuget.org/packages/NetEvolve.Http.Correlation.Abstractions)** - Core abstractions and interfaces +- **[NetEvolve.Http.Correlation.HttpClient](https://www.nuget.org/packages/NetEvolve.Http.Correlation.HttpClient)** - Correlation forwarding for HTTP clients +- **[NetEvolve.Http.Correlation.Ulid](https://www.nuget.org/packages/NetEvolve.Http.Correlation.Ulid)** - Alternative ULID-based provider +- **[NetEvolve.Http.Correlation.TestGenerator](https://www.nuget.org/packages/NetEvolve.Http.Correlation.TestGenerator)** - Test-friendly provider + +## Dependencies + +- `NetEvolve.Http.Correlation.Abstractions` +- `Microsoft.Azure.Functions.Worker.Core` + +## License + +Licensed under the MIT License. See [LICENSE](https://github.com/dailydevops/http.correlation/blob/main/LICENSE) for details. diff --git a/src/NetEvolve.Http.Correlation.Azure.Functions/ServiceCollectionExtensions.cs b/src/NetEvolve.Http.Correlation.Azure.Functions/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..9d964fa --- /dev/null +++ b/src/NetEvolve.Http.Correlation.Azure.Functions/ServiceCollectionExtensions.cs @@ -0,0 +1,28 @@ +namespace NetEvolve.Http.Correlation.Azure.Functions; + +using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using NetEvolve.Http.Correlation.Abstractions; + +/// +/// Extensions for . +/// +public static class ServiceCollectionExtensions +{ + /// + /// Enables the Http Correlation services. + /// + /// The instance. + /// An instance. + public static IHttpCorrelationBuilder AddHttpCorrelation(this IServiceCollection services) + { + ArgumentNullException.ThrowIfNull(services); + + services + .AddScoped(sp => sp.GetRequiredService()) + .TryAddScoped(); + + return new HttpCorrelationBuilder(services); + } +} diff --git a/src/NetEvolve.Http.Correlation.HttpClient/HttpClientBuilderExtensions.cs b/src/NetEvolve.Http.Correlation.HttpClient/HttpClientBuilderExtensions.cs index a6983a8..014afde 100644 --- a/src/NetEvolve.Http.Correlation.HttpClient/HttpClientBuilderExtensions.cs +++ b/src/NetEvolve.Http.Correlation.HttpClient/HttpClientBuilderExtensions.cs @@ -1,4 +1,4 @@ -namespace NetEvolve.Http.Correlation; +namespace NetEvolve.Http.Correlation.HttpClient; using System; using Microsoft.Extensions.DependencyInjection; diff --git a/src/NetEvolve.Http.Correlation.HttpClient/HttpCorrelationIdHandler.cs b/src/NetEvolve.Http.Correlation.HttpClient/HttpCorrelationIdHandler.cs index 375a25d..7fba817 100644 --- a/src/NetEvolve.Http.Correlation.HttpClient/HttpCorrelationIdHandler.cs +++ b/src/NetEvolve.Http.Correlation.HttpClient/HttpCorrelationIdHandler.cs @@ -1,4 +1,4 @@ -namespace NetEvolve.Http.Correlation; +namespace NetEvolve.Http.Correlation.HttpClient; using System.Net.Http; using System.Threading; diff --git a/src/NetEvolve.Http.Correlation.TestGenerator/HttpCorrelationBuilderExtensions.cs b/src/NetEvolve.Http.Correlation.TestGenerator/HttpCorrelationBuilderExtensions.cs index aa8205d..56d944d 100644 --- a/src/NetEvolve.Http.Correlation.TestGenerator/HttpCorrelationBuilderExtensions.cs +++ b/src/NetEvolve.Http.Correlation.TestGenerator/HttpCorrelationBuilderExtensions.cs @@ -1,4 +1,4 @@ -namespace NetEvolve.Http.Correlation; +namespace NetEvolve.Http.Correlation.TestGenerator; using System; using Microsoft.Extensions.DependencyInjection.Extensions; diff --git a/src/NetEvolve.Http.Correlation.TestGenerator/TestGeneratorCorrelationIdProvider.cs b/src/NetEvolve.Http.Correlation.TestGenerator/TestGeneratorCorrelationIdProvider.cs index f269e88..14f0769 100644 --- a/src/NetEvolve.Http.Correlation.TestGenerator/TestGeneratorCorrelationIdProvider.cs +++ b/src/NetEvolve.Http.Correlation.TestGenerator/TestGeneratorCorrelationIdProvider.cs @@ -1,4 +1,4 @@ -namespace NetEvolve.Http.Correlation; +namespace NetEvolve.Http.Correlation.TestGenerator; using NetEvolve.Http.Correlation.Abstractions; diff --git a/src/NetEvolve.Http.Correlation.Ulid/HttpCorrelationBuilderExtensions.cs b/src/NetEvolve.Http.Correlation.Ulid/HttpCorrelationBuilderExtensions.cs index f02676d..344a7f5 100644 --- a/src/NetEvolve.Http.Correlation.Ulid/HttpCorrelationBuilderExtensions.cs +++ b/src/NetEvolve.Http.Correlation.Ulid/HttpCorrelationBuilderExtensions.cs @@ -1,4 +1,4 @@ -namespace NetEvolve.Http.Correlation; +namespace NetEvolve.Http.Correlation.Ulid; using System; using Microsoft.Extensions.DependencyInjection.Extensions; diff --git a/src/NetEvolve.Http.Correlation.Ulid/ULIDCorrelationIdProvider.cs b/src/NetEvolve.Http.Correlation.Ulid/ULIDCorrelationIdProvider.cs index 5cc7e12..6b8b48b 100644 --- a/src/NetEvolve.Http.Correlation.Ulid/ULIDCorrelationIdProvider.cs +++ b/src/NetEvolve.Http.Correlation.Ulid/ULIDCorrelationIdProvider.cs @@ -1,4 +1,4 @@ -namespace NetEvolve.Http.Correlation; +namespace NetEvolve.Http.Correlation.Ulid; using System; using NetEvolve.Http.Correlation.Abstractions; diff --git a/tests/NetEvolve.Http.Correlation.AspNetCore.Tests.Integration/HttpCorrelationMiddlewareTests.cs b/tests/NetEvolve.Http.Correlation.AspNetCore.Tests.Integration/HttpCorrelationMiddlewareTests.cs index 7c3eed9..20fbddd 100644 --- a/tests/NetEvolve.Http.Correlation.AspNetCore.Tests.Integration/HttpCorrelationMiddlewareTests.cs +++ b/tests/NetEvolve.Http.Correlation.AspNetCore.Tests.Integration/HttpCorrelationMiddlewareTests.cs @@ -3,6 +3,7 @@ using System; using System.Linq; using System.Threading.Tasks; +using NetEvolve.Http.Correlation; using TUnit.Assertions.Extensions; using TUnit.Core; diff --git a/tests/NetEvolve.Http.Correlation.AspNetCore.Tests.Integration/TestBase.cs b/tests/NetEvolve.Http.Correlation.AspNetCore.Tests.Integration/TestBase.cs index c1efad4..0b2900b 100644 --- a/tests/NetEvolve.Http.Correlation.AspNetCore.Tests.Integration/TestBase.cs +++ b/tests/NetEvolve.Http.Correlation.AspNetCore.Tests.Integration/TestBase.cs @@ -10,6 +10,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using NetEvolve.Http.Correlation.Abstractions; +using NetEvolve.Http.Correlation.AspNetCore; public abstract class TestBase { diff --git a/tests/NetEvolve.Http.Correlation.AspNetCore.Tests.Unit/ApplicationBuilderExtensionsTests.cs b/tests/NetEvolve.Http.Correlation.AspNetCore.Tests.Unit/ApplicationBuilderExtensionsTests.cs index 5996abc..269dd5d 100644 --- a/tests/NetEvolve.Http.Correlation.AspNetCore.Tests.Unit/ApplicationBuilderExtensionsTests.cs +++ b/tests/NetEvolve.Http.Correlation.AspNetCore.Tests.Unit/ApplicationBuilderExtensionsTests.cs @@ -4,6 +4,8 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; +using NetEvolve.Http.Correlation; +using NetEvolve.Http.Correlation.AspNetCore; using TUnit.Assertions.Extensions; using TUnit.Core; diff --git a/tests/NetEvolve.Http.Correlation.AspNetCore.Tests.Unit/HttpCorrelationBuilderExtensionsTests.cs b/tests/NetEvolve.Http.Correlation.AspNetCore.Tests.Unit/HttpCorrelationBuilderExtensionsTests.cs index 102ba8e..7bb4cc8 100644 --- a/tests/NetEvolve.Http.Correlation.AspNetCore.Tests.Unit/HttpCorrelationBuilderExtensionsTests.cs +++ b/tests/NetEvolve.Http.Correlation.AspNetCore.Tests.Unit/HttpCorrelationBuilderExtensionsTests.cs @@ -3,6 +3,7 @@ using System; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; +using NetEvolve.Http.Correlation; using NetEvolve.Http.Correlation.Abstractions; using TUnit.Assertions.Extensions; using TUnit.Core; diff --git a/tests/NetEvolve.Http.Correlation.AspNetCore.Tests.Unit/ServiceCollectionExtensionsTests.cs b/tests/NetEvolve.Http.Correlation.AspNetCore.Tests.Unit/ServiceCollectionExtensionsTests.cs index f609f24..ca15b55 100644 --- a/tests/NetEvolve.Http.Correlation.AspNetCore.Tests.Unit/ServiceCollectionExtensionsTests.cs +++ b/tests/NetEvolve.Http.Correlation.AspNetCore.Tests.Unit/ServiceCollectionExtensionsTests.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using NetEvolve.Http.Correlation.Abstractions; +using NetEvolve.Http.Correlation.AspNetCore; using TUnit.Assertions.Extensions; using TUnit.Core; diff --git a/tests/NetEvolve.Http.Correlation.Azure.Functions.Tests.Integration/FunctionsCorrelationMiddlewareTests.cs b/tests/NetEvolve.Http.Correlation.Azure.Functions.Tests.Integration/FunctionsCorrelationMiddlewareTests.cs new file mode 100644 index 0000000..855cf49 --- /dev/null +++ b/tests/NetEvolve.Http.Correlation.Azure.Functions.Tests.Integration/FunctionsCorrelationMiddlewareTests.cs @@ -0,0 +1,143 @@ +namespace NetEvolve.Http.Correlation.Azure.Functions.Tests.Integration; + +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using NetEvolve.Http.Correlation; +using NSubstitute; +using TUnit.Assertions.Extensions; +using TUnit.Core; + +public class FunctionsCorrelationMiddlewareTests : TestBase +{ + [Test] + public async Task Invoke_WithNullContext_ThrowsArgumentNullException() + { + // Arrange + await using var provider = new ServiceCollection() + .AddLogging() + .AddHttpCorrelation() + .Services.BuildServiceProvider(); + + var middleware = new FunctionsCorrelationMiddleware( + provider.GetRequiredService>() + ); + + FunctionContext context = null!; + + // Act / Assert + _ = await Assert + .That(async () => await middleware.Invoke(context, _ => Task.CompletedTask)) + .Throws() + .WithParameterName("context"); + } + + [Test] + public async Task UseHttpCorrelation_WithNonHttpFunction_CallsNext() + { + // Arrange / Act — no requestSetup means GetHttpRequestDataAsync() returns null → passes through + var result = await RunAsync(); + + // Assert + _ = await Assert.That(result.NextCalled).IsTrue(); + } + + [Test] + public async Task UseHttpCorrelation_WithNonHttpFunction_WithGenerator_CallsNext() + { + // Arrange / Act + var result = await RunAsync(correlationBuilder: b => b.WithGuidGenerator()); + + // Assert + _ = await Assert.That(result.NextCalled).IsTrue(); + } + + [Test] + public async Task UseHttpCorrelation_WithHeaderName1_Expected() + { + // Arrange + var testCorrelationId = Guid.NewGuid().ToString("N"); + + // Act + var result = await RunAsync(requestSetup: req => + req.Headers.Add(CorrelationConstants.HeaderName1, testCorrelationId) + ); + + // Assert + using (Assert.Multiple()) + { + _ = await Assert.That(result.NextCalled).IsTrue(); + _ = await Assert.That(result.CorrelationId).IsEqualTo(testCorrelationId); + _ = await Assert.That(result.HeaderName).IsEqualTo(CorrelationConstants.HeaderName1); + } + } + + [Test] + public async Task UseHttpCorrelation_WithHeaderName2_Expected() + { + // Arrange + var testCorrelationId = Guid.NewGuid().ToString("N"); + + // Act + var result = await RunAsync(requestSetup: req => + req.Headers.Add(CorrelationConstants.HeaderName2, testCorrelationId) + ); + + // Assert + using (Assert.Multiple()) + { + _ = await Assert.That(result.NextCalled).IsTrue(); + _ = await Assert.That(result.CorrelationId).IsEqualTo(testCorrelationId); + _ = await Assert.That(result.HeaderName).IsEqualTo(CorrelationConstants.HeaderName2); + } + } + + [Test] + public async Task UseHttpCorrelation_WithoutGenerator_FallsBackToInvocationId() + { + // Arrange / Act — no correlation header, no generator → falls back to InvocationId + var result = await RunAsync(requestSetup: _ => { }); + + // Assert + using (Assert.Multiple()) + { + _ = await Assert.That(result.NextCalled).IsTrue(); + _ = await Assert.That(result.CorrelationId).IsEqualTo("test-invocation-id"); + } + } + + [Test] + public async Task UseHttpCorrelation_WithGenerator_GeneratesId() + { + // Arrange / Act — no correlation header, but generator registered → uses generated ID + var result = await RunAsync(correlationBuilder: b => b.WithGuidGenerator(), requestSetup: _ => { }); + + // Assert + using (Assert.Multiple()) + { + _ = await Assert.That(result.NextCalled).IsTrue(); + _ = await Assert.That(result.CorrelationId).IsNotEmpty(); + _ = await Assert.That(Guid.TryParse(result.CorrelationId, out _)).IsTrue(); + } + } + +#if NET9_0_OR_GREATER + [Test] + public async Task UseHttpCorrelation_WithGuidV7Generator_GeneratesId() + { + // Arrange / Act + var result = await RunAsync(correlationBuilder: b => b.WithGuidV7Generator(), requestSetup: _ => { }); + + // Assert + using (Assert.Multiple()) + { + _ = await Assert.That(result.NextCalled).IsTrue(); + _ = await Assert.That(result.CorrelationId).IsNotEmpty(); + _ = await Assert.That(Guid.TryParse(result.CorrelationId, out _)).IsTrue(); + } + } +#endif +} diff --git a/tests/NetEvolve.Http.Correlation.Azure.Functions.Tests.Integration/NetEvolve.Http.Correlation.Azure.Functions.Tests.Integration.csproj b/tests/NetEvolve.Http.Correlation.Azure.Functions.Tests.Integration/NetEvolve.Http.Correlation.Azure.Functions.Tests.Integration.csproj new file mode 100644 index 0000000..2453223 --- /dev/null +++ b/tests/NetEvolve.Http.Correlation.Azure.Functions.Tests.Integration/NetEvolve.Http.Correlation.Azure.Functions.Tests.Integration.csproj @@ -0,0 +1,17 @@ + + + $(_TargetFrameworks) + + + + + + + + + + + + + + diff --git a/tests/NetEvolve.Http.Correlation.Azure.Functions.Tests.Integration/TestBase.cs b/tests/NetEvolve.Http.Correlation.Azure.Functions.Tests.Integration/TestBase.cs new file mode 100644 index 0000000..9313fd4 --- /dev/null +++ b/tests/NetEvolve.Http.Correlation.Azure.Functions.Tests.Integration/TestBase.cs @@ -0,0 +1,127 @@ +namespace NetEvolve.Http.Correlation.Azure.Functions.Tests.Integration; + +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Http; +using Microsoft.Azure.Functions.Worker.Middleware; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using NetEvolve.Http.Correlation.Abstractions; +using NSubstitute; + +/// +/// Test infrastructure for integration tests. +/// +public abstract class TestBase +{ + /// + /// Invokes the middleware with the given configuration and returns the captured results. + /// + protected static async ValueTask RunAsync( + Action? correlationBuilder = null, + Action? serviceBuilder = null, + Action? requestSetup = null + ) + { + var services = new ServiceCollection().AddLogging(); + serviceBuilder?.Invoke(services); + + var builder = services.AddHttpCorrelation(); + correlationBuilder?.Invoke(builder); + + await using var serviceProvider = services.BuildServiceProvider(); + + var context = Substitute.For(); + _ = context.InvocationId.Returns("test-invocation-id"); + + using var scope = serviceProvider.CreateScope(); + _ = context.InstanceServices.Returns(scope.ServiceProvider); + + if (requestSetup is not null) + { + var requestData = new TestHttpRequestData(context); + requestSetup(requestData); + + var httpRequestDataFeature = Substitute.For(); + +#pragma warning disable CA2012 // NSubstitute fluent setup: ValueTask is intercepted by the substitute proxy, not actually consumed here + _ = httpRequestDataFeature + .GetHttpRequestDataAsync(context) + .Returns(new ValueTask(requestData)); +#pragma warning restore CA2012 + + var features = Substitute.For(); + _ = features.Get().Returns(httpRequestDataFeature); + _ = context.Features.Returns(features); + } + + var middleware = new FunctionsCorrelationMiddleware( + scope.ServiceProvider.GetRequiredService>() + ); + + var nextCalled = false; + FunctionExecutionDelegate next = _ => + { + nextCalled = true; + return Task.CompletedTask; + }; + + await middleware.Invoke(context, next).ConfigureAwait(false); + + var functionsAccessor = scope.ServiceProvider.GetRequiredService(); + + return new TestRunResult(nextCalled, functionsAccessor.CorrelationId, functionsAccessor.HeaderName); + } +} + +/// +/// Concrete implementation for integration tests. +/// +public sealed class TestHttpRequestData : HttpRequestData +{ + private readonly HttpHeadersCollection _headers = new(); + + public TestHttpRequestData(FunctionContext functionContext) + : base(functionContext) { } + + public override HttpHeadersCollection Headers => _headers; + + public override IReadOnlyCollection Cookies => []; + + public override Uri Url => new Uri("https://test.example.com/api/test"); + + public override IEnumerable Identities => []; + + public override string Method => "GET"; + + public override Stream Body => Stream.Null; + + public override HttpResponseData CreateResponse() => new TestHttpResponseData(FunctionContext); +} + +/// +/// Concrete implementation for integration tests. +/// +internal sealed class TestHttpResponseData : HttpResponseData +{ + public TestHttpResponseData(FunctionContext functionContext) + : base(functionContext) { } + + public override HttpStatusCode StatusCode { get; set; } = HttpStatusCode.OK; + + public override HttpHeadersCollection Headers { get; set; } = new HttpHeadersCollection(); + + public override Stream Body { get; set; } = Stream.Null; + + public override HttpCookies Cookies => null!; +} + +/// +/// The result of a test run through . +/// +public sealed record TestRunResult(bool NextCalled, string CorrelationId, string HeaderName); diff --git a/tests/NetEvolve.Http.Correlation.Azure.Functions.Tests.Unit/FunctionsWorkerApplicationBuilderExtensionsTests.cs b/tests/NetEvolve.Http.Correlation.Azure.Functions.Tests.Unit/FunctionsWorkerApplicationBuilderExtensionsTests.cs new file mode 100644 index 0000000..c40746b --- /dev/null +++ b/tests/NetEvolve.Http.Correlation.Azure.Functions.Tests.Unit/FunctionsWorkerApplicationBuilderExtensionsTests.cs @@ -0,0 +1,54 @@ +namespace NetEvolve.Http.Correlation.Azure.Functions.Tests.Unit; + +using System; +using System.Threading.Tasks; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Extensions.DependencyInjection; +using NetEvolve.Http.Correlation; +using NSubstitute; +using TUnit.Assertions.Extensions; +using TUnit.Core; + +public class FunctionsWorkerApplicationBuilderExtensionsTests +{ + [Test] + public async Task UseHttpCorrelation_BuilderNull_ThrowsArgumentNullException() + { + // Arrange + IFunctionsWorkerApplicationBuilder builder = null!; + + // Act / Assert + _ = await Assert + .That(() => builder.UseHttpCorrelation()) + .Throws() + .WithParameterName("builder"); + } + + [Test] + public async Task UseHttpCorrelation_ServicesNotRegistered_ThrowsInvalidOperationException() + { + // Arrange + var services = new ServiceCollection(); + var builder = Substitute.For(); + _ = builder.Services.Returns(services); + + // Act / Assert + _ = await Assert.That(() => builder.UseHttpCorrelation()).Throws(); + } + + [Test] + public async Task UseHttpCorrelation_Builder_Expected() + { + // Arrange + var services = new ServiceCollection(); + _ = services.AddHttpCorrelation().WithGuidGenerator(); + var builder = Substitute.For(); + _ = builder.Services.Returns(services); + + // Act + var result = builder.UseHttpCorrelation(); + + // Assert + _ = await Assert.That(result).IsNotNull(); + } +} diff --git a/tests/NetEvolve.Http.Correlation.Azure.Functions.Tests.Unit/HttpCorrelationBuilderExtensionsTests.cs b/tests/NetEvolve.Http.Correlation.Azure.Functions.Tests.Unit/HttpCorrelationBuilderExtensionsTests.cs new file mode 100644 index 0000000..0060417 --- /dev/null +++ b/tests/NetEvolve.Http.Correlation.Azure.Functions.Tests.Unit/HttpCorrelationBuilderExtensionsTests.cs @@ -0,0 +1,98 @@ +namespace NetEvolve.Http.Correlation.Azure.Functions.Tests.Unit; + +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using NetEvolve.Http.Correlation; +using NetEvolve.Http.Correlation.Abstractions; +using TUnit.Assertions.Extensions; +using TUnit.Core; + +public class HttpCorrelationBuilderExtensionsTests +{ + [Test] + public async Task WithGuidGenerator_BuilderNull_ThrowsArgumentNullException() + { + // Arrange + IHttpCorrelationBuilder builder = null!; + + // Act / Assert + _ = await Assert + .That(() => builder.WithGuidGenerator()) + .Throws() + .WithParameterName("builder"); + } + + [Test] + public async Task WithGuidGenerator_Builder_Expected() + { + // Arrange + var services = new ServiceCollection(); + var builder = new HttpCorrelationBuilder(services); + + // Act + var result = builder.WithGuidGenerator().WithGuidGenerator(); + + // Assert + using (Assert.Multiple()) + { + _ = await Assert.That(result).IsNotNull(); + _ = await Assert + .That(services) + .Contains(s => + s.ServiceType == typeof(IHttpCorrelationIdProvider) + && s.Lifetime == ServiceLifetime.Singleton + && !string.IsNullOrEmpty(s.ImplementationType!.FullName) + && s.ImplementationType.FullName.Equals( + "NetEvolve.Http.Correlation.Generators.GuidCorrelationIdProvider", + StringComparison.Ordinal + ) + ); + _ = await Assert.That(services.Count).IsEqualTo(1); + } + } + +#if NET9_0_OR_GREATER + [Test] + public async Task WithGuidV7Generator_BuilderNull_ThrowsArgumentNullException() + { + // Arrange + IHttpCorrelationBuilder builder = null!; + + // Act / Assert + _ = await Assert + .That(() => builder.WithGuidV7Generator()) + .Throws() + .WithParameterName("builder"); + } + + [Test] + public async Task WithGuidV7Generator_Builder_Expected() + { + // Arrange + var services = new ServiceCollection(); + var builder = new HttpCorrelationBuilder(services); + + // Act + var result = builder.WithGuidV7Generator().WithGuidV7Generator(); + + // Assert + using (Assert.Multiple()) + { + _ = await Assert.That(result).IsNotNull(); + _ = await Assert + .That(services) + .Contains(s => + s.ServiceType == typeof(IHttpCorrelationIdProvider) + && s.Lifetime == ServiceLifetime.Singleton + && !string.IsNullOrEmpty(s.ImplementationType!.FullName) + && s.ImplementationType.FullName.Equals( + "NetEvolve.Http.Correlation.Generators.GuidV7CorrelationIdProvider", + StringComparison.Ordinal + ) + ); + _ = await Assert.That(services.Count).IsEqualTo(1); + } + } +#endif +} diff --git a/tests/NetEvolve.Http.Correlation.Azure.Functions.Tests.Unit/NetEvolve.Http.Correlation.Azure.Functions.Tests.Unit.csproj b/tests/NetEvolve.Http.Correlation.Azure.Functions.Tests.Unit/NetEvolve.Http.Correlation.Azure.Functions.Tests.Unit.csproj new file mode 100644 index 0000000..61f17da --- /dev/null +++ b/tests/NetEvolve.Http.Correlation.Azure.Functions.Tests.Unit/NetEvolve.Http.Correlation.Azure.Functions.Tests.Unit.csproj @@ -0,0 +1,17 @@ + + + $(_TargetFrameworks) + + + + + + + + + + + + + + diff --git a/tests/NetEvolve.Http.Correlation.Azure.Functions.Tests.Unit/ServiceCollectionExtensionsTests.cs b/tests/NetEvolve.Http.Correlation.Azure.Functions.Tests.Unit/ServiceCollectionExtensionsTests.cs new file mode 100644 index 0000000..f4d04d5 --- /dev/null +++ b/tests/NetEvolve.Http.Correlation.Azure.Functions.Tests.Unit/ServiceCollectionExtensionsTests.cs @@ -0,0 +1,61 @@ +namespace NetEvolve.Http.Correlation.Azure.Functions.Tests.Unit; + +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using NetEvolve.Http.Correlation; +using NetEvolve.Http.Correlation.Abstractions; +using TUnit.Assertions.Extensions; +using TUnit.Core; + +public class ServiceCollectionExtensionsTests +{ + [Test] + public async Task AddHttpCorrelation_ServicesNull_ThrowsArgumentNullException() + { + // Arrange + IServiceCollection services = null!; + + // Act / Assert + _ = await Assert + .That(() => services.AddHttpCorrelation()) + .Throws() + .WithParameterName("services"); + } + + [Test] + public async Task AddHttpCorrelation_Services_Expected() + { + // Arrange + var services = new ServiceCollection(); + + // Act + var result = services.AddHttpCorrelation(); + + // Assert + using (Assert.Multiple()) + { + _ = await Assert.That(result).IsTypeOf(); + _ = await Assert.That(services.Count).IsEqualTo(2); + _ = await Assert + .That( + services.Any(s => + s.ServiceType == typeof(IHttpCorrelationAccessor) + && s.Lifetime == ServiceLifetime.Scoped + && s.ImplementationFactory is not null + ) + ) + .IsTrue(); + _ = await Assert + .That( + services.Any(s => + s.ServiceType == typeof(FunctionsCorrelationAccessor) + && s.Lifetime == ServiceLifetime.Scoped + && s.ImplementationType == typeof(FunctionsCorrelationAccessor) + ) + ) + .IsTrue(); + } + } +} diff --git a/tests/NetEvolve.Http.Correlation.HttpClient.Tests.Unit/HttpClientBuilderExtensionsTests.cs b/tests/NetEvolve.Http.Correlation.HttpClient.Tests.Unit/HttpClientBuilderExtensionsTests.cs index c901e5d..c24227c 100644 --- a/tests/NetEvolve.Http.Correlation.HttpClient.Tests.Unit/HttpClientBuilderExtensionsTests.cs +++ b/tests/NetEvolve.Http.Correlation.HttpClient.Tests.Unit/HttpClientBuilderExtensionsTests.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; +using NetEvolve.Http.Correlation.HttpClient; using TUnit.Assertions.Extensions; using TUnit.Core; diff --git a/tests/NetEvolve.Http.Correlation.TestGenerator.Tests.Unit/HttpCorrelationBuilderExtensionsTests.cs b/tests/NetEvolve.Http.Correlation.TestGenerator.Tests.Unit/HttpCorrelationBuilderExtensionsTests.cs index 94668ba..9e877ff 100644 --- a/tests/NetEvolve.Http.Correlation.TestGenerator.Tests.Unit/HttpCorrelationBuilderExtensionsTests.cs +++ b/tests/NetEvolve.Http.Correlation.TestGenerator.Tests.Unit/HttpCorrelationBuilderExtensionsTests.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using NetEvolve.Http.Correlation.Abstractions; +using NetEvolve.Http.Correlation.TestGenerator; using TUnit.Assertions.Extensions; using TUnit.Core; diff --git a/tests/NetEvolve.Http.Correlation.Ulid.Tests.Unit/HttpCorrelationBuilderExtensionsTests.cs b/tests/NetEvolve.Http.Correlation.Ulid.Tests.Unit/HttpCorrelationBuilderExtensionsTests.cs index 90a1c60..29ec3d3 100644 --- a/tests/NetEvolve.Http.Correlation.Ulid.Tests.Unit/HttpCorrelationBuilderExtensionsTests.cs +++ b/tests/NetEvolve.Http.Correlation.Ulid.Tests.Unit/HttpCorrelationBuilderExtensionsTests.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using NetEvolve.Http.Correlation.Abstractions; +using NetEvolve.Http.Correlation.Ulid; using TUnit.Assertions.Extensions; using TUnit.Core;