using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using NSubstitute; using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories; using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums; using ZB.MOM.WW.ScadaBridge.NotificationOutbox.Delivery; using ZB.MOM.WW.ScadaBridge.NotificationService; namespace ZB.MOM.WW.ScadaBridge.NotificationOutbox.Tests; /// /// Task 17: Tests for — the /// DI registration extension for the Notification Outbox component. The extension binds /// from the ScadaBridge:NotificationOutbox /// configuration section and registers the channel delivery adapter(s). /// /// The Host wires both AddNotificationService and AddNotificationOutbox on the /// central node; these tests do the same so the /// adapter's SMTP dependencies (Func<ISmtpClientWrapper>, OAuth2TokenService, /// NotificationOptions) are satisfied. — which the /// takes directly and is registered scoped by the /// Configuration Database component — is supplied here by a lightweight stub. /// public class ServiceRegistrationTests { private static ServiceProvider BuildProvider() { var services = new ServiceCollection(); // Empty configuration: the options binding must still succeed and yield the // documented NotificationOutboxOptions defaults. var configuration = new ConfigurationBuilder().Build(); services.AddSingleton(configuration); services.AddLogging(); // INotificationRepository is registered scoped by the Configuration Database // component in production; a no-op stub stands in for it here. services.AddScoped(_ => Substitute.For()); services.AddNotificationService(); services.AddNotificationOutbox(); return services.BuildServiceProvider(); } [Fact] public void AddNotificationOutbox_RegistersNotificationOutboxOptions_WithDefaults() { using var provider = BuildProvider(); var options = provider.GetRequiredService>().Value; Assert.NotNull(options); Assert.Equal(TimeSpan.FromSeconds(10), options.DispatchInterval); Assert.Equal(100, options.DispatchBatchSize); } [Fact] public void AddNotificationOutbox_OptionsSection_IsTheNotificationOutboxConfigPath() { Assert.Equal( "ScadaBridge:NotificationOutbox", NotificationOutbox.ServiceCollectionExtensions.OptionsSection); } [Fact] public void AddNotificationOutbox_BindsNotificationOutboxOptions_FromConfiguration() { var services = new ServiceCollection(); var configuration = new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary { ["ScadaBridge:NotificationOutbox:DispatchBatchSize"] = "250", }) .Build(); services.AddSingleton(configuration); services.AddLogging(); services.AddScoped(_ => Substitute.For()); services.AddNotificationService(); services.AddNotificationOutbox(); using var provider = services.BuildServiceProvider(); var options = provider.GetRequiredService>().Value; Assert.Equal(250, options.DispatchBatchSize); } [Fact] public void AddNotificationOutbox_RegistersEmailDeliveryAdapter() { using var provider = BuildProvider(); using var scope = provider.CreateScope(); var adapter = scope.ServiceProvider.GetRequiredService(); Assert.NotNull(adapter); Assert.Equal(NotificationType.Email, adapter.Type); } [Fact] public void AddNotificationOutbox_RegistersSmsDeliveryAdapter() { using var provider = BuildProvider(); using var scope = provider.CreateScope(); var adapter = scope.ServiceProvider.GetRequiredService(); Assert.NotNull(adapter); Assert.Equal(NotificationType.Sms, adapter.Type); } [Fact] public void AddNotificationOutbox_RegistersBothAdapters_AsINotificationDeliveryAdapter() { using var provider = BuildProvider(); using var scope = provider.CreateScope(); var adapters = scope.ServiceProvider.GetServices().ToList(); // Both the Email and SMS channel adapters must resolve from the enumerable so the // dispatch sweep can route a notification to the adapter matching its Type. Assert.Equal(2, adapters.Count); // The set of advertised channel Types is exactly {Email, Sms}. Assert.Equal( new[] { NotificationType.Email, NotificationType.Sms }, adapters.Select(a => a.Type).OrderBy(t => t).ToArray()); // And the concrete adapter implementations are the expected pair. Assert.Contains(adapters, a => a is EmailNotificationDeliveryAdapter); Assert.Contains(adapters, a => a is SmsNotificationDeliveryAdapter); } [Fact] public void AddNotificationOutbox_RegistersSmsOptions_WithDefaults() { using var provider = BuildProvider(); var options = provider.GetRequiredService>().Value; Assert.NotNull(options); Assert.Equal(1600, options.MaxMessageLength); Assert.Equal(30, options.ConnectionTimeoutSeconds); } }