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(() => ReCClient.BuildStaticClient((Action)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( () => 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(() => 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.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(); }; }); // First Create() triggers the Lazy 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(() => 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(() => 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(() => 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 { } }