using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; using ZB.MOM.WW.CBDDC.Hosting.Configuration; using ZB.MOM.WW.CBDDC.Hosting.HostedServices; using ZB.MOM.WW.CBDDC.Hosting.Services; using ZB.MOM.WW.CBDDC.Network; namespace ZB.MOM.WW.CBDDC.Hosting.Tests; public class CBDDCHostingExtensionsTests { /// /// Verifies that adding CBDDC hosting throws when the service collection is null. /// [Fact] public void AddCBDDCHosting_WithNullServices_ThrowsArgumentNullException() { Should.Throw(() => CBDDCHostingExtensions.AddCBDDCHosting(null!, _ => { })); } /// /// Verifies that adding CBDDC hosting throws when the configuration delegate is null. /// [Fact] public void AddCBDDCHosting_WithNullConfigure_ThrowsArgumentNullException() { var services = new ServiceCollection(); Should.Throw(() => services.AddCBDDCHosting(null!)); } /// /// Verifies that single-cluster hosting registers expected services and configured options. /// [Fact] public void AddCBDDCHostingSingleCluster_RegistersExpectedServicesAndOptions() { var services = new ServiceCollection(); services.AddCBDDCHostingSingleCluster(options => { options.NodeId = "node-1"; options.TcpPort = 5055; options.PeerConfirmationLagThresholdMs = 45_000; options.PeerConfirmationCriticalLagThresholdMs = 180_000; }); var optionsDescriptor = services.SingleOrDefault(d => d.ServiceType == typeof(CBDDCHostingOptions)); optionsDescriptor.ShouldNotBeNull(); var options = optionsDescriptor.ImplementationInstance.ShouldBeOfType(); options.Cluster.NodeId.ShouldBe("node-1"); options.Cluster.TcpPort.ShouldBe(5055); options.Cluster.PeerConfirmationLagThresholdMs.ShouldBe(45_000); options.Cluster.PeerConfirmationCriticalLagThresholdMs.ShouldBe(180_000); ShouldContainService(services); ShouldContainService(services); ShouldContainHostedService(services); ShouldContainHostedService(services); using var provider = services.BuildServiceProvider(); var healthChecks = provider.GetRequiredService>().Value; var registration = healthChecks.Registrations.SingleOrDefault(r => r.Name == "cbddc"); registration.ShouldNotBeNull(); registration.FailureStatus.ShouldBe(HealthStatus.Unhealthy); registration.Tags.ShouldContain("db"); registration.Tags.ShouldContain("ready"); } /// /// Verifies that single-cluster hosting uses default options when no configuration delegate is provided. /// [Fact] public void AddCBDDCHostingSingleCluster_WithNullConfigure_UsesDefaults() { var services = new ServiceCollection(); services.AddCBDDCHostingSingleCluster(); var optionsDescriptor = services.SingleOrDefault(d => d.ServiceType == typeof(CBDDCHostingOptions)); optionsDescriptor.ShouldNotBeNull(); var options = optionsDescriptor.ImplementationInstance.ShouldBeOfType(); options.Cluster.ShouldNotBeNull(); } /// /// Verifies that health check registration is skipped when health checks are disabled. /// [Fact] public void AddCBDDCHosting_WithHealthChecksDisabled_DoesNotRegisterCBDDCHealthCheck() { var services = new ServiceCollection(); services.AddCBDDCHosting(options => { options.EnableHealthChecks = false; }); services.Any(d => d.ServiceType == typeof(IConfigureOptions)) .ShouldBeFalse(); } private static void ShouldContainService(IServiceCollection services) { services.Any(d => d.ServiceType == typeof(TService) && d.ImplementationType == typeof(TImplementation)) .ShouldBeTrue(); } private static void ShouldContainHostedService(IServiceCollection services) { services.Any(d => d.ServiceType == typeof(IHostedService) && d.ImplementationType == typeof(THostedService)) .ShouldBeTrue(); } }