Add tests for ReCClient static client initialization
Introduced `StaticReCClientTests` to validate the behavior of the `ReCClient` static client, ensuring deterministic and non-parallel execution due to process-wide state mutation. Added tests to cover various scenarios: - Null configuration callback throws `ArgumentNullException`. - Missing `BaseAddress` or `ConfigureClient` throws. - Conflicting `BaseAddress` and `ConfigureClient` throws. - Successful static client build and resolution via `Create`. - Subsequent `BuildStaticClient` calls throw exceptions. Included helper types for `ConfigureServices` validation and used `#pragma` directives to suppress warnings for obsolete members. Ensured test order with `[Order]` attributes.
This commit is contained in:
129
tests/ReC.Tests/Client/StaticReCClientTests.cs
Normal file
129
tests/ReC.Tests/Client/StaticReCClientTests.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using ReC.Client;
|
||||
|
||||
namespace ReC.Tests.Client;
|
||||
|
||||
// The static BuildStaticClient / Create entry-point mutates process-wide state and can only be
|
||||
// initialized once per AppDomain. All assertions must therefore live in a single, ordered,
|
||||
// non-parallel fixture so that the lifecycle is deterministic.
|
||||
[TestFixture]
|
||||
[NonParallelizable]
|
||||
public class StaticReCClientTests
|
||||
{
|
||||
// Validation tests run first. They all throw BEFORE the static state is mutated,
|
||||
// so they can run repeatedly without consuming the one-time build slot.
|
||||
|
||||
[Test, Order(10)]
|
||||
public void BuildStaticClient_with_null_configure_throws_ArgumentNullException()
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
Assert.Throws<ArgumentNullException>(() => ReCClient.BuildStaticClient((Action<StaticBuildConfiguration>)null!));
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
|
||||
[Test, Order(20)]
|
||||
public void BuildStaticClient_without_base_address_or_configure_client_throws()
|
||||
{
|
||||
#pragma warning disable CS0618
|
||||
var ex = Assert.Throws<InvalidOperationException>(
|
||||
() => ReCClient.BuildStaticClient(_ => { }));
|
||||
#pragma warning restore CS0618
|
||||
|
||||
Assert.That(ex!.Message, Does.Contain(nameof(StaticBuildConfiguration.BaseAddress)));
|
||||
Assert.That(ex.Message, Does.Contain(nameof(StaticBuildConfiguration.ConfigureClient)));
|
||||
}
|
||||
|
||||
[Test, Order(30)]
|
||||
public void BuildStaticClient_with_both_base_address_and_configure_client_throws()
|
||||
{
|
||||
#pragma warning disable CS0618
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => ReCClient.BuildStaticClient(cfg =>
|
||||
{
|
||||
cfg.BaseAddress = "https://example.invalid/";
|
||||
cfg.ConfigureClient = http => http.BaseAddress = new Uri("https://example.invalid/");
|
||||
}));
|
||||
#pragma warning restore CS0618
|
||||
|
||||
Assert.That(ex!.Message, Does.Contain("mutually exclusive"));
|
||||
}
|
||||
|
||||
// Successful build — consumes the single allowed BuildStaticClient slot for this test run.
|
||||
[Test, Order(100)]
|
||||
public void BuildStaticClient_with_configuration_callback_succeeds_and_Create_resolves_client()
|
||||
{
|
||||
var customLogger = NullLogger<ReCClient>.Instance;
|
||||
var configureServicesInvoked = false;
|
||||
|
||||
#pragma warning disable CS0618
|
||||
ReCClient.BuildStaticClient(cfg =>
|
||||
{
|
||||
cfg.BaseAddress = "https://example.invalid/";
|
||||
cfg.ConfigureOptions = opt => opt.LogSuccessfulRequests = true;
|
||||
cfg.Logger = customLogger;
|
||||
cfg.ConfigureServices = services =>
|
||||
{
|
||||
configureServicesInvoked = true;
|
||||
services.AddSingleton<IMarkerService, MarkerService>();
|
||||
};
|
||||
});
|
||||
|
||||
// First Create() triggers the Lazy<IServiceProvider> initialization.
|
||||
var client = ReCClient.Create();
|
||||
#pragma warning restore CS0618
|
||||
|
||||
Assert.That(client, Is.Not.Null);
|
||||
Assert.That(client.RecActions, Is.Not.Null);
|
||||
Assert.That(configureServicesInvoked, Is.True, "ConfigureServices callback should run when the provider is built.");
|
||||
}
|
||||
|
||||
[Test, Order(110)]
|
||||
public void Create_returns_a_client_using_the_existing_static_provider()
|
||||
{
|
||||
#pragma warning disable CS0618
|
||||
var client = ReCClient.Create();
|
||||
#pragma warning restore CS0618
|
||||
|
||||
Assert.That(client, Is.Not.Null);
|
||||
}
|
||||
|
||||
// Any subsequent BuildStaticClient call (regardless of overload) must fail.
|
||||
|
||||
[Test, Order(200)]
|
||||
public void Calling_BuildStaticClient_configuration_overload_a_second_time_throws()
|
||||
{
|
||||
#pragma warning disable CS0618
|
||||
var ex = Assert.Throws<InvalidOperationException>(() =>
|
||||
ReCClient.BuildStaticClient(cfg => cfg.BaseAddress = "https://other.invalid/"));
|
||||
#pragma warning restore CS0618
|
||||
|
||||
Assert.That(ex!.Message, Does.Contain("already built"));
|
||||
}
|
||||
|
||||
[Test, Order(210)]
|
||||
public void Calling_legacy_BuildStaticClient_string_overload_after_build_throws()
|
||||
{
|
||||
#pragma warning disable CS0618
|
||||
var ex = Assert.Throws<InvalidOperationException>(() =>
|
||||
ReCClient.BuildStaticClient("https://other.invalid/"));
|
||||
#pragma warning restore CS0618
|
||||
|
||||
Assert.That(ex!.Message, Does.Contain("already built"));
|
||||
}
|
||||
|
||||
[Test, Order(220)]
|
||||
public void Calling_legacy_BuildStaticClient_action_overload_after_build_throws()
|
||||
{
|
||||
#pragma warning disable CS0618
|
||||
var ex = Assert.Throws<InvalidOperationException>(() =>
|
||||
ReCClient.BuildStaticClient(http => http.BaseAddress = new Uri("https://other.invalid/")));
|
||||
#pragma warning restore CS0618
|
||||
|
||||
Assert.That(ex!.Message, Does.Contain("already built"));
|
||||
}
|
||||
|
||||
// Helper types used by the ConfigureServices assertion above.
|
||||
private interface IMarkerService { }
|
||||
private sealed class MarkerService : IMarkerService { }
|
||||
}
|
||||
Reference in New Issue
Block a user