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
+
+[](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;