diff --git a/TUnit.Aspire.Tests/IntegrationTestFixture.cs b/TUnit.Aspire.Tests/IntegrationTestFixture.cs index 0ced93c13e..07d445f773 100644 --- a/TUnit.Aspire.Tests/IntegrationTestFixture.cs +++ b/TUnit.Aspire.Tests/IntegrationTestFixture.cs @@ -1,3 +1,6 @@ +using Aspire.Hosting.Testing; +using Microsoft.Extensions.DependencyInjection; + namespace TUnit.Aspire.Tests; /// @@ -10,4 +13,34 @@ public class IntegrationTestFixture : AspireFixture TimeSpan.FromSeconds(120); protected override ResourceWaitBehavior WaitBehavior => ResourceWaitBehavior.AllRunning; + + protected override void ConfigureBuilder(IDistributedApplicationTestingBuilder builder) + { + builder.Services.AddSingleton(); + builder.Services.AddTransient(); + builder.Services.ConfigureHttpClientDefaults(http => + http.AddHttpMessageHandler()); + } + + public int HttpHandlerInvocationCount + => App.Services.GetRequiredService().Count; +} + +internal sealed class HttpHandlerInvocationCounter +{ + private int _count; + + public int Count => Volatile.Read(ref _count); + + public void Increment() => Interlocked.Increment(ref _count); +} + +internal sealed class CountingDelegatingHandler(HttpHandlerInvocationCounter counter) : DelegatingHandler +{ + protected override Task SendAsync( + HttpRequestMessage request, CancellationToken cancellationToken) + { + counter.Increment(); + return base.SendAsync(request, cancellationToken); + } } diff --git a/TUnit.Aspire.Tests/OtlpCorrelationIntegrationTests.cs b/TUnit.Aspire.Tests/OtlpCorrelationIntegrationTests.cs index 27e81c5187..1fc52f971b 100644 --- a/TUnit.Aspire.Tests/OtlpCorrelationIntegrationTests.cs +++ b/TUnit.Aspire.Tests/OtlpCorrelationIntegrationTests.cs @@ -226,6 +226,23 @@ public async Task TraceDemo_Request_ProducesNestedCorrelatedLogs() await Assert.That(output).Contains(nestedMarker); } + /// + /// Regression coverage for thomhurst/TUnit#5956 — CreateHttpClient must route through + /// the AppHost's so that s + /// registered via ConfigureHttpClientDefaults in ConfigureBuilder fire. + /// + [Test] + public async Task CreateHttpClient_Invokes_Registered_DelegatingHandler() + { + var before = fixture.HttpHandlerInvocationCount; + + var client = fixture.CreateHttpClient(ServiceName); + using var response = await client.GetAsync("/health"); + + await Assert.That((int)response.StatusCode).IsEqualTo(200); + await Assert.That(fixture.HttpHandlerInvocationCount).IsGreaterThan(before); + } + /// /// Polls until it contains the expected marker /// or a timeout is reached. The OTLP SDK batches log exports, so there's inherent latency diff --git a/TUnit.Aspire/AspireFixture.cs b/TUnit.Aspire/AspireFixture.cs index f4744ec315..199ef8bbfa 100644 --- a/TUnit.Aspire/AspireFixture.cs +++ b/TUnit.Aspire/AspireFixture.cs @@ -29,7 +29,6 @@ public class AspireFixture : IAsyncInitializer, IAsyncDisposable { private DistributedApplication? _app; private OtlpReceiver? _otlpReceiver; - private SocketsHttpHandler? _httpHandler; /// /// The running Aspire distributed application. @@ -39,33 +38,15 @@ public class AspireFixture : IAsyncInitializer, IAsyncDisposable "App not initialized. Ensure InitializeAsync has completed."); /// - /// Creates an for the named resource. - /// When is true, the returned client - /// automatically propagates W3C traceparent and baggage headers - /// (including tunit.test.id) to the SUT for cross-process correlation. - /// Otherwise, delegates to Aspire's default CreateHttpClient. + /// Creates an for the named resource via the AppHost's + /// , so any registered + /// in participates in the pipeline. /// /// The name of the resource to connect to. /// Optional endpoint name if the resource exposes multiple endpoints. /// An configured to connect to the resource. public HttpClient CreateHttpClient(string resourceName, string? endpointName = null) - { - if (!EnableTelemetryCollection) - { - return App.CreateHttpClient(resourceName, endpointName); - } - - _httpHandler ??= new SocketsHttpHandler - { - // Match Aspire's CreateHttpClient behavior: trust dev certs for HTTPS resources - SslOptions = { RemoteCertificateValidationCallback = (_, _, _, _) => true }, - }; - - return new HttpClient(_httpHandler, disposeHandler: false) - { - BaseAddress = App.GetEndpoint(resourceName, endpointName), - }; - } + => App.CreateHttpClient(resourceName, endpointName); /// /// Gets the connection string for the named resource. @@ -387,9 +368,6 @@ public virtual async ValueTask DisposeAsync() _otlpReceiver = null; } - _httpHandler?.Dispose(); - _httpHandler = null; - GC.SuppressFinalize(this); }