mirror of
https://github.com/bitwarden/server
synced 2025-12-21 10:43:44 +00:00
Refactor event integration service collection extensions into their own extension (#6714)
* Add CQRS and caching support for OrganizationIntegrationConfigurations * Refactor event integration service collection extensions into their own extension
This commit is contained in:
@@ -1,12 +1,19 @@
|
||||
using Bit.Core.AdminConsole.EventIntegrations.OrganizationIntegrationConfigurations.Interfaces;
|
||||
using Bit.Core.AdminConsole.EventIntegrations.OrganizationIntegrations.Interfaces;
|
||||
using Bit.Core.AdminConsole.Models.Data.EventIntegrations;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.AdminConsole.Services;
|
||||
using Bit.Core.AdminConsole.Services.NoopImplementations;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.Bot.Builder;
|
||||
using Microsoft.Bot.Builder.Integration.AspNet.Core;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using NSubstitute;
|
||||
using StackExchange.Redis;
|
||||
using Xunit;
|
||||
@@ -185,6 +192,666 @@ public class EventIntegrationServiceCollectionExtensionsTests
|
||||
Assert.Single(createCmdDescriptors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsRabbitMqEnabled_AllSettingsPresent_ReturnsTrue()
|
||||
{
|
||||
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
|
||||
{
|
||||
["GlobalSettings:EventLogging:RabbitMq:HostName"] = "localhost",
|
||||
["GlobalSettings:EventLogging:RabbitMq:Username"] = "user",
|
||||
["GlobalSettings:EventLogging:RabbitMq:Password"] = "pass",
|
||||
["GlobalSettings:EventLogging:RabbitMq:EventExchangeName"] = "exchange"
|
||||
});
|
||||
|
||||
Assert.True(EventIntegrationsServiceCollectionExtensions.IsRabbitMqEnabled(globalSettings));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsRabbitMqEnabled_MissingHostName_ReturnsFalse()
|
||||
{
|
||||
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
|
||||
{
|
||||
["GlobalSettings:EventLogging:RabbitMq:HostName"] = null,
|
||||
["GlobalSettings:EventLogging:RabbitMq:Username"] = "user",
|
||||
["GlobalSettings:EventLogging:RabbitMq:Password"] = "pass",
|
||||
["GlobalSettings:EventLogging:RabbitMq:EventExchangeName"] = "exchange"
|
||||
});
|
||||
|
||||
Assert.False(EventIntegrationsServiceCollectionExtensions.IsRabbitMqEnabled(globalSettings));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsRabbitMqEnabled_MissingUsername_ReturnsFalse()
|
||||
{
|
||||
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
|
||||
{
|
||||
["GlobalSettings:EventLogging:RabbitMq:HostName"] = "localhost",
|
||||
["GlobalSettings:EventLogging:RabbitMq:Username"] = null,
|
||||
["GlobalSettings:EventLogging:RabbitMq:Password"] = "pass",
|
||||
["GlobalSettings:EventLogging:RabbitMq:EventExchangeName"] = "exchange"
|
||||
});
|
||||
|
||||
Assert.False(EventIntegrationsServiceCollectionExtensions.IsRabbitMqEnabled(globalSettings));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsRabbitMqEnabled_MissingPassword_ReturnsFalse()
|
||||
{
|
||||
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
|
||||
{
|
||||
["GlobalSettings:EventLogging:RabbitMq:HostName"] = "localhost",
|
||||
["GlobalSettings:EventLogging:RabbitMq:Username"] = "user",
|
||||
["GlobalSettings:EventLogging:RabbitMq:Password"] = null,
|
||||
["GlobalSettings:EventLogging:RabbitMq:EventExchangeName"] = "exchange"
|
||||
});
|
||||
|
||||
Assert.False(EventIntegrationsServiceCollectionExtensions.IsRabbitMqEnabled(globalSettings));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsRabbitMqEnabled_MissingExchangeName_ReturnsFalse()
|
||||
{
|
||||
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
|
||||
{
|
||||
["GlobalSettings:EventLogging:RabbitMq:HostName"] = "localhost",
|
||||
["GlobalSettings:EventLogging:RabbitMq:Username"] = "user",
|
||||
["GlobalSettings:EventLogging:RabbitMq:Password"] = "pass",
|
||||
["GlobalSettings:EventLogging:RabbitMq:EventExchangeName"] = null
|
||||
});
|
||||
|
||||
Assert.False(EventIntegrationsServiceCollectionExtensions.IsRabbitMqEnabled(globalSettings));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsAzureServiceBusEnabled_AllSettingsPresent_ReturnsTrue()
|
||||
{
|
||||
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
|
||||
{
|
||||
["GlobalSettings:EventLogging:AzureServiceBus:ConnectionString"] = "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=test;SharedAccessKey=test",
|
||||
["GlobalSettings:EventLogging:AzureServiceBus:EventTopicName"] = "events"
|
||||
});
|
||||
|
||||
Assert.True(EventIntegrationsServiceCollectionExtensions.IsAzureServiceBusEnabled(globalSettings));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsAzureServiceBusEnabled_MissingConnectionString_ReturnsFalse()
|
||||
{
|
||||
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
|
||||
{
|
||||
["GlobalSettings:EventLogging:AzureServiceBus:ConnectionString"] = null,
|
||||
["GlobalSettings:EventLogging:AzureServiceBus:EventTopicName"] = "events"
|
||||
});
|
||||
|
||||
Assert.False(EventIntegrationsServiceCollectionExtensions.IsAzureServiceBusEnabled(globalSettings));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsAzureServiceBusEnabled_MissingTopicName_ReturnsFalse()
|
||||
{
|
||||
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
|
||||
{
|
||||
["GlobalSettings:EventLogging:AzureServiceBus:ConnectionString"] = "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=test;SharedAccessKey=test",
|
||||
["GlobalSettings:EventLogging:AzureServiceBus:EventTopicName"] = null
|
||||
});
|
||||
|
||||
Assert.False(EventIntegrationsServiceCollectionExtensions.IsAzureServiceBusEnabled(globalSettings));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddSlackService_AllSettingsPresent_RegistersSlackService()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
|
||||
{
|
||||
["GlobalSettings:Slack:ClientId"] = "test-client-id",
|
||||
["GlobalSettings:Slack:ClientSecret"] = "test-client-secret",
|
||||
["GlobalSettings:Slack:Scopes"] = "test-scopes"
|
||||
});
|
||||
|
||||
services.TryAddSingleton(globalSettings);
|
||||
services.AddLogging();
|
||||
services.AddSlackService(globalSettings);
|
||||
|
||||
var provider = services.BuildServiceProvider();
|
||||
var slackService = provider.GetService<ISlackService>();
|
||||
|
||||
Assert.NotNull(slackService);
|
||||
Assert.IsType<SlackService>(slackService);
|
||||
|
||||
var httpClientDescriptor = services.FirstOrDefault(s =>
|
||||
s.ServiceType == typeof(IHttpClientFactory));
|
||||
Assert.NotNull(httpClientDescriptor);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddSlackService_SettingsMissing_RegistersNoopService()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
|
||||
{
|
||||
["GlobalSettings:Slack:ClientId"] = null,
|
||||
["GlobalSettings:Slack:ClientSecret"] = null,
|
||||
["GlobalSettings:Slack:Scopes"] = null
|
||||
});
|
||||
|
||||
services.AddSlackService(globalSettings);
|
||||
|
||||
var provider = services.BuildServiceProvider();
|
||||
var slackService = provider.GetService<ISlackService>();
|
||||
|
||||
Assert.NotNull(slackService);
|
||||
Assert.IsType<NoopSlackService>(slackService);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddTeamsService_AllSettingsPresent_RegistersTeamsServices()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
|
||||
{
|
||||
["GlobalSettings:Teams:ClientId"] = "test-client-id",
|
||||
["GlobalSettings:Teams:ClientSecret"] = "test-client-secret",
|
||||
["GlobalSettings:Teams:Scopes"] = "test-scopes"
|
||||
});
|
||||
|
||||
services.TryAddSingleton(globalSettings);
|
||||
services.AddLogging();
|
||||
services.TryAddScoped(_ => Substitute.For<IOrganizationIntegrationRepository>());
|
||||
services.AddTeamsService(globalSettings);
|
||||
|
||||
var provider = services.BuildServiceProvider();
|
||||
|
||||
var teamsService = provider.GetService<ITeamsService>();
|
||||
Assert.NotNull(teamsService);
|
||||
Assert.IsType<TeamsService>(teamsService);
|
||||
|
||||
var bot = provider.GetService<IBot>();
|
||||
Assert.NotNull(bot);
|
||||
Assert.IsType<TeamsService>(bot);
|
||||
|
||||
var adapter = provider.GetService<IBotFrameworkHttpAdapter>();
|
||||
Assert.NotNull(adapter);
|
||||
Assert.IsType<BotFrameworkHttpAdapter>(adapter);
|
||||
|
||||
var httpClientDescriptor = services.FirstOrDefault(s =>
|
||||
s.ServiceType == typeof(IHttpClientFactory));
|
||||
Assert.NotNull(httpClientDescriptor);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddTeamsService_SettingsMissing_RegistersNoopService()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
|
||||
{
|
||||
["GlobalSettings:Teams:ClientId"] = null,
|
||||
["GlobalSettings:Teams:ClientSecret"] = null,
|
||||
["GlobalSettings:Teams:Scopes"] = null
|
||||
});
|
||||
|
||||
services.AddTeamsService(globalSettings);
|
||||
|
||||
var provider = services.BuildServiceProvider();
|
||||
var teamsService = provider.GetService<ITeamsService>();
|
||||
|
||||
Assert.NotNull(teamsService);
|
||||
Assert.IsType<NoopTeamsService>(teamsService);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddRabbitMqIntegration_RegistersEventIntegrationHandler()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var listenerConfig = new TestListenerConfiguration();
|
||||
|
||||
// Add required dependencies
|
||||
services.TryAddSingleton(Substitute.For<IEventIntegrationPublisher>());
|
||||
services.TryAddSingleton(Substitute.For<IIntegrationFilterService>());
|
||||
services.TryAddKeyedSingleton(EventIntegrationsCacheConstants.CacheName, Substitute.For<IFusionCache>());
|
||||
services.TryAddSingleton(Substitute.For<IOrganizationIntegrationConfigurationRepository>());
|
||||
services.TryAddSingleton(Substitute.For<IGroupRepository>());
|
||||
services.TryAddSingleton(Substitute.For<IOrganizationRepository>());
|
||||
services.TryAddSingleton(Substitute.For<IOrganizationUserRepository>());
|
||||
services.AddLogging();
|
||||
|
||||
services.AddRabbitMqIntegration<WebhookIntegrationConfigurationDetails, TestListenerConfiguration>(listenerConfig);
|
||||
|
||||
var provider = services.BuildServiceProvider();
|
||||
var handler = provider.GetRequiredKeyedService<IEventMessageHandler>(listenerConfig.RoutingKey);
|
||||
|
||||
Assert.NotNull(handler);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddRabbitMqIntegration_RegistersEventListenerService()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var listenerConfig = new TestListenerConfiguration();
|
||||
|
||||
// Add required dependencies
|
||||
services.TryAddSingleton(Substitute.For<IEventIntegrationPublisher>());
|
||||
services.TryAddSingleton(Substitute.For<IIntegrationFilterService>());
|
||||
services.TryAddKeyedSingleton(EventIntegrationsCacheConstants.CacheName, Substitute.For<IFusionCache>());
|
||||
services.TryAddSingleton(Substitute.For<IOrganizationIntegrationConfigurationRepository>());
|
||||
services.TryAddSingleton(Substitute.For<IGroupRepository>());
|
||||
services.TryAddSingleton(Substitute.For<IOrganizationRepository>());
|
||||
services.TryAddSingleton(Substitute.For<IOrganizationUserRepository>());
|
||||
services.TryAddSingleton(Substitute.For<IRabbitMqService>());
|
||||
services.AddLogging();
|
||||
|
||||
var beforeCount = services.Count(s => s.ServiceType == typeof(IHostedService));
|
||||
services.AddRabbitMqIntegration<WebhookIntegrationConfigurationDetails, TestListenerConfiguration>(listenerConfig);
|
||||
var afterCount = services.Count(s => s.ServiceType == typeof(IHostedService));
|
||||
|
||||
// AddRabbitMqIntegration should register 2 hosted services (Event + Integration listeners)
|
||||
Assert.Equal(2, afterCount - beforeCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddRabbitMqIntegration_RegistersIntegrationListenerService()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var listenerConfig = new TestListenerConfiguration();
|
||||
|
||||
// Add required dependencies
|
||||
services.TryAddSingleton(Substitute.For<IEventIntegrationPublisher>());
|
||||
services.TryAddSingleton(Substitute.For<IIntegrationFilterService>());
|
||||
services.TryAddKeyedSingleton(EventIntegrationsCacheConstants.CacheName, Substitute.For<IFusionCache>());
|
||||
services.TryAddSingleton(Substitute.For<IOrganizationIntegrationConfigurationRepository>());
|
||||
services.TryAddSingleton(Substitute.For<IGroupRepository>());
|
||||
services.TryAddSingleton(Substitute.For<IOrganizationRepository>());
|
||||
services.TryAddSingleton(Substitute.For<IOrganizationUserRepository>());
|
||||
services.TryAddSingleton(Substitute.For<IRabbitMqService>());
|
||||
services.TryAddSingleton(Substitute.For<IIntegrationHandler<WebhookIntegrationConfigurationDetails>>());
|
||||
services.TryAddSingleton(TimeProvider.System);
|
||||
services.AddLogging();
|
||||
|
||||
var beforeCount = services.Count(s => s.ServiceType == typeof(IHostedService));
|
||||
services.AddRabbitMqIntegration<WebhookIntegrationConfigurationDetails, TestListenerConfiguration>(listenerConfig);
|
||||
var afterCount = services.Count(s => s.ServiceType == typeof(IHostedService));
|
||||
|
||||
// AddRabbitMqIntegration should register 2 hosted services (Event + Integration listeners)
|
||||
Assert.Equal(2, afterCount - beforeCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddAzureServiceBusIntegration_RegistersEventIntegrationHandler()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var listenerConfig = new TestListenerConfiguration();
|
||||
|
||||
// Add required dependencies
|
||||
services.TryAddSingleton(Substitute.For<IEventIntegrationPublisher>());
|
||||
services.TryAddSingleton(Substitute.For<IIntegrationFilterService>());
|
||||
services.TryAddKeyedSingleton(EventIntegrationsCacheConstants.CacheName, Substitute.For<IFusionCache>());
|
||||
services.TryAddSingleton(Substitute.For<IOrganizationIntegrationConfigurationRepository>());
|
||||
services.TryAddSingleton(Substitute.For<IGroupRepository>());
|
||||
services.TryAddSingleton(Substitute.For<IOrganizationRepository>());
|
||||
services.TryAddSingleton(Substitute.For<IOrganizationUserRepository>());
|
||||
services.AddLogging();
|
||||
|
||||
services.AddAzureServiceBusIntegration<WebhookIntegrationConfigurationDetails, TestListenerConfiguration>(listenerConfig);
|
||||
|
||||
var provider = services.BuildServiceProvider();
|
||||
var handler = provider.GetRequiredKeyedService<IEventMessageHandler>(listenerConfig.RoutingKey);
|
||||
|
||||
Assert.NotNull(handler);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddAzureServiceBusIntegration_RegistersEventListenerService()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var listenerConfig = new TestListenerConfiguration();
|
||||
|
||||
// Add required dependencies
|
||||
services.TryAddSingleton(Substitute.For<IEventIntegrationPublisher>());
|
||||
services.TryAddSingleton(Substitute.For<IIntegrationFilterService>());
|
||||
services.TryAddKeyedSingleton(EventIntegrationsCacheConstants.CacheName, Substitute.For<IFusionCache>());
|
||||
services.TryAddSingleton(Substitute.For<IOrganizationIntegrationConfigurationRepository>());
|
||||
services.TryAddSingleton(Substitute.For<IGroupRepository>());
|
||||
services.TryAddSingleton(Substitute.For<IOrganizationRepository>());
|
||||
services.TryAddSingleton(Substitute.For<IOrganizationUserRepository>());
|
||||
services.TryAddSingleton(Substitute.For<IAzureServiceBusService>());
|
||||
services.AddLogging();
|
||||
|
||||
var beforeCount = services.Count(s => s.ServiceType == typeof(IHostedService));
|
||||
services.AddAzureServiceBusIntegration<WebhookIntegrationConfigurationDetails, TestListenerConfiguration>(listenerConfig);
|
||||
var afterCount = services.Count(s => s.ServiceType == typeof(IHostedService));
|
||||
|
||||
// AddAzureServiceBusIntegration should register 2 hosted services (Event + Integration listeners)
|
||||
Assert.Equal(2, afterCount - beforeCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddAzureServiceBusIntegration_RegistersIntegrationListenerService()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var listenerConfig = new TestListenerConfiguration();
|
||||
|
||||
// Add required dependencies
|
||||
services.TryAddSingleton(Substitute.For<IEventIntegrationPublisher>());
|
||||
services.TryAddSingleton(Substitute.For<IIntegrationFilterService>());
|
||||
services.TryAddKeyedSingleton(EventIntegrationsCacheConstants.CacheName, Substitute.For<IFusionCache>());
|
||||
services.TryAddSingleton(Substitute.For<IOrganizationIntegrationConfigurationRepository>());
|
||||
services.TryAddSingleton(Substitute.For<IGroupRepository>());
|
||||
services.TryAddSingleton(Substitute.For<IOrganizationRepository>());
|
||||
services.TryAddSingleton(Substitute.For<IOrganizationUserRepository>());
|
||||
services.TryAddSingleton(Substitute.For<IAzureServiceBusService>());
|
||||
services.TryAddSingleton(Substitute.For<IIntegrationHandler<WebhookIntegrationConfigurationDetails>>());
|
||||
services.AddLogging();
|
||||
|
||||
var beforeCount = services.Count(s => s.ServiceType == typeof(IHostedService));
|
||||
services.AddAzureServiceBusIntegration<WebhookIntegrationConfigurationDetails, TestListenerConfiguration>(listenerConfig);
|
||||
var afterCount = services.Count(s => s.ServiceType == typeof(IHostedService));
|
||||
|
||||
// AddAzureServiceBusIntegration should register 2 hosted services (Event + Integration listeners)
|
||||
Assert.Equal(2, afterCount - beforeCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddEventIntegrationServices_RegistersCommonServices()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var globalSettings = CreateGlobalSettings([]);
|
||||
|
||||
// Add prerequisites
|
||||
services.TryAddSingleton(globalSettings);
|
||||
services.TryAddSingleton(Substitute.For<IConnectionMultiplexer>());
|
||||
services.AddLogging();
|
||||
|
||||
services.AddEventIntegrationServices(globalSettings);
|
||||
|
||||
// Verify common services are registered
|
||||
Assert.Contains(services, s => s.ServiceType == typeof(IIntegrationFilterService));
|
||||
Assert.Contains(services, s => s.ServiceType == typeof(TimeProvider));
|
||||
|
||||
// Verify HttpClients for handlers are registered
|
||||
var httpClientDescriptors = services.Where(s => s.ServiceType == typeof(IHttpClientFactory)).ToList();
|
||||
Assert.NotEmpty(httpClientDescriptors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddEventIntegrationServices_RegistersIntegrationHandlers()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var globalSettings = CreateGlobalSettings([]);
|
||||
|
||||
// Add prerequisites
|
||||
services.TryAddSingleton(globalSettings);
|
||||
services.TryAddSingleton(Substitute.For<IConnectionMultiplexer>());
|
||||
services.AddLogging();
|
||||
|
||||
services.AddEventIntegrationServices(globalSettings);
|
||||
|
||||
// Verify integration handlers are registered
|
||||
Assert.Contains(services, s => s.ServiceType == typeof(IIntegrationHandler<SlackIntegrationConfigurationDetails>));
|
||||
Assert.Contains(services, s => s.ServiceType == typeof(IIntegrationHandler<WebhookIntegrationConfigurationDetails>));
|
||||
Assert.Contains(services, s => s.ServiceType == typeof(IIntegrationHandler<DatadogIntegrationConfigurationDetails>));
|
||||
Assert.Contains(services, s => s.ServiceType == typeof(IIntegrationHandler<TeamsIntegrationConfigurationDetails>));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddEventIntegrationServices_RabbitMqEnabled_RegistersRabbitMqListeners()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
|
||||
{
|
||||
["GlobalSettings:EventLogging:RabbitMq:HostName"] = "localhost",
|
||||
["GlobalSettings:EventLogging:RabbitMq:Username"] = "user",
|
||||
["GlobalSettings:EventLogging:RabbitMq:Password"] = "pass",
|
||||
["GlobalSettings:EventLogging:RabbitMq:EventExchangeName"] = "exchange"
|
||||
});
|
||||
|
||||
// Add prerequisites
|
||||
services.TryAddSingleton(globalSettings);
|
||||
services.TryAddSingleton(Substitute.For<IConnectionMultiplexer>());
|
||||
services.AddLogging();
|
||||
|
||||
var beforeCount = services.Count(s => s.ServiceType == typeof(IHostedService));
|
||||
services.AddEventIntegrationServices(globalSettings);
|
||||
var afterCount = services.Count(s => s.ServiceType == typeof(IHostedService));
|
||||
|
||||
// Should register 11 hosted services for RabbitMQ: 1 repository + 5*2 integration listeners (event+integration)
|
||||
Assert.Equal(11, afterCount - beforeCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddEventIntegrationServices_AzureServiceBusEnabled_RegistersAzureListeners()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
|
||||
{
|
||||
["GlobalSettings:EventLogging:AzureServiceBus:ConnectionString"] = "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=test;SharedAccessKey=test",
|
||||
["GlobalSettings:EventLogging:AzureServiceBus:EventTopicName"] = "events"
|
||||
});
|
||||
|
||||
// Add prerequisites
|
||||
services.TryAddSingleton(globalSettings);
|
||||
services.TryAddSingleton(Substitute.For<IConnectionMultiplexer>());
|
||||
services.AddLogging();
|
||||
|
||||
var beforeCount = services.Count(s => s.ServiceType == typeof(IHostedService));
|
||||
services.AddEventIntegrationServices(globalSettings);
|
||||
var afterCount = services.Count(s => s.ServiceType == typeof(IHostedService));
|
||||
|
||||
// Should register 11 hosted services for Azure Service Bus: 1 repository + 5*2 integration listeners (event+integration)
|
||||
Assert.Equal(11, afterCount - beforeCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddEventIntegrationServices_BothEnabled_AzureServiceBusTakesPrecedence()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
|
||||
{
|
||||
["GlobalSettings:EventLogging:RabbitMq:HostName"] = "localhost",
|
||||
["GlobalSettings:EventLogging:RabbitMq:Username"] = "user",
|
||||
["GlobalSettings:EventLogging:RabbitMq:Password"] = "pass",
|
||||
["GlobalSettings:EventLogging:RabbitMq:EventExchangeName"] = "exchange",
|
||||
["GlobalSettings:EventLogging:AzureServiceBus:ConnectionString"] = "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=test;SharedAccessKey=test",
|
||||
["GlobalSettings:EventLogging:AzureServiceBus:EventTopicName"] = "events"
|
||||
});
|
||||
|
||||
// Add prerequisites
|
||||
services.TryAddSingleton(globalSettings);
|
||||
services.TryAddSingleton(Substitute.For<IConnectionMultiplexer>());
|
||||
services.AddLogging();
|
||||
|
||||
var beforeCount = services.Count(s => s.ServiceType == typeof(IHostedService));
|
||||
services.AddEventIntegrationServices(globalSettings);
|
||||
var afterCount = services.Count(s => s.ServiceType == typeof(IHostedService));
|
||||
|
||||
// Should register 11 hosted services for Azure Service Bus: 1 repository + 5*2 integration listeners (event+integration)
|
||||
// NO RabbitMQ services should be enabled because ASB takes precedence
|
||||
Assert.Equal(11, afterCount - beforeCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddEventIntegrationServices_NeitherEnabled_RegistersNoListeners()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var globalSettings = CreateGlobalSettings([]);
|
||||
|
||||
// Add prerequisites
|
||||
services.TryAddSingleton(globalSettings);
|
||||
services.TryAddSingleton(Substitute.For<IConnectionMultiplexer>());
|
||||
services.AddLogging();
|
||||
|
||||
var beforeCount = services.Count(s => s.ServiceType == typeof(IHostedService));
|
||||
services.AddEventIntegrationServices(globalSettings);
|
||||
var afterCount = services.Count(s => s.ServiceType == typeof(IHostedService));
|
||||
|
||||
// Should register no hosted services when neither RabbitMQ nor Azure Service Bus is enabled
|
||||
Assert.Equal(0, afterCount - beforeCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddEventWriteServices_AzureServiceBusEnabled_RegistersAzureServices()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
|
||||
{
|
||||
["GlobalSettings:EventLogging:AzureServiceBus:ConnectionString"] = "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=test;SharedAccessKey=test",
|
||||
["GlobalSettings:EventLogging:AzureServiceBus:EventTopicName"] = "events"
|
||||
});
|
||||
|
||||
services.AddEventWriteServices(globalSettings);
|
||||
|
||||
Assert.Contains(services, s => s.ServiceType == typeof(IEventIntegrationPublisher) && s.ImplementationType == typeof(AzureServiceBusService));
|
||||
Assert.Contains(services, s => s.ServiceType == typeof(IEventWriteService) && s.ImplementationType == typeof(EventIntegrationEventWriteService));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddEventWriteServices_RabbitMqEnabled_RegistersRabbitMqServices()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
|
||||
{
|
||||
["GlobalSettings:EventLogging:RabbitMq:HostName"] = "localhost",
|
||||
["GlobalSettings:EventLogging:RabbitMq:Username"] = "user",
|
||||
["GlobalSettings:EventLogging:RabbitMq:Password"] = "pass",
|
||||
["GlobalSettings:EventLogging:RabbitMq:EventExchangeName"] = "exchange"
|
||||
});
|
||||
|
||||
services.AddEventWriteServices(globalSettings);
|
||||
|
||||
Assert.Contains(services, s => s.ServiceType == typeof(IEventIntegrationPublisher) && s.ImplementationType == typeof(RabbitMqService));
|
||||
Assert.Contains(services, s => s.ServiceType == typeof(IEventWriteService) && s.ImplementationType == typeof(EventIntegrationEventWriteService));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddEventWriteServices_EventsConnectionStringPresent_RegistersAzureQueueService()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
|
||||
{
|
||||
["GlobalSettings:Events:ConnectionString"] = "DefaultEndpointsProtocol=https;AccountName=test;AccountKey=test;EndpointSuffix=core.windows.net"
|
||||
});
|
||||
|
||||
services.AddEventWriteServices(globalSettings);
|
||||
|
||||
Assert.Contains(services, s => s.ServiceType == typeof(IEventWriteService) && s.ImplementationType == typeof(AzureQueueEventWriteService));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddEventWriteServices_SelfHosted_RegistersRepositoryService()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
|
||||
{
|
||||
["GlobalSettings:SelfHosted"] = "true"
|
||||
});
|
||||
|
||||
services.AddEventWriteServices(globalSettings);
|
||||
|
||||
Assert.Contains(services, s => s.ServiceType == typeof(IEventWriteService) && s.ImplementationType == typeof(RepositoryEventWriteService));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddEventWriteServices_NothingEnabled_RegistersNoopService()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var globalSettings = CreateGlobalSettings([]);
|
||||
|
||||
services.AddEventWriteServices(globalSettings);
|
||||
|
||||
Assert.Contains(services, s => s.ServiceType == typeof(IEventWriteService) && s.ImplementationType == typeof(NoopEventWriteService));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddEventWriteServices_AzureTakesPrecedenceOverRabbitMq()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
|
||||
{
|
||||
["GlobalSettings:EventLogging:AzureServiceBus:ConnectionString"] = "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=test;SharedAccessKey=test",
|
||||
["GlobalSettings:EventLogging:AzureServiceBus:EventTopicName"] = "events",
|
||||
["GlobalSettings:EventLogging:RabbitMq:HostName"] = "localhost",
|
||||
["GlobalSettings:EventLogging:RabbitMq:Username"] = "user",
|
||||
["GlobalSettings:EventLogging:RabbitMq:Password"] = "pass",
|
||||
["GlobalSettings:EventLogging:RabbitMq:EventExchangeName"] = "exchange"
|
||||
});
|
||||
|
||||
services.AddEventWriteServices(globalSettings);
|
||||
|
||||
// Should use Azure Service Bus, not RabbitMQ
|
||||
Assert.Contains(services, s => s.ServiceType == typeof(IEventIntegrationPublisher) && s.ImplementationType == typeof(AzureServiceBusService));
|
||||
Assert.DoesNotContain(services, s => s.ServiceType == typeof(IEventIntegrationPublisher) && s.ImplementationType == typeof(RabbitMqService));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddAzureServiceBusListeners_AzureServiceBusEnabled_RegistersAzureServiceBusServices()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
|
||||
{
|
||||
["GlobalSettings:EventLogging:AzureServiceBus:ConnectionString"] = "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=test;SharedAccessKey=test",
|
||||
["GlobalSettings:EventLogging:AzureServiceBus:EventTopicName"] = "events"
|
||||
});
|
||||
|
||||
// Add prerequisites
|
||||
services.TryAddSingleton(globalSettings);
|
||||
services.TryAddSingleton(Substitute.For<IConnectionMultiplexer>());
|
||||
services.AddLogging();
|
||||
|
||||
services.AddAzureServiceBusListeners(globalSettings);
|
||||
|
||||
Assert.Contains(services, s => s.ServiceType == typeof(IAzureServiceBusService));
|
||||
Assert.Contains(services, s => s.ServiceType == typeof(IEventRepository));
|
||||
Assert.Contains(services, s => s.ServiceType == typeof(AzureTableStorageEventHandler));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddAzureServiceBusListeners_AzureServiceBusDisabled_ReturnsEarly()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var globalSettings = CreateGlobalSettings([]);
|
||||
|
||||
var initialCount = services.Count;
|
||||
services.AddAzureServiceBusListeners(globalSettings);
|
||||
var finalCount = services.Count;
|
||||
|
||||
Assert.Equal(initialCount, finalCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddRabbitMqListeners_RabbitMqEnabled_RegistersRabbitMqServices()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
|
||||
{
|
||||
["GlobalSettings:EventLogging:RabbitMq:HostName"] = "localhost",
|
||||
["GlobalSettings:EventLogging:RabbitMq:Username"] = "user",
|
||||
["GlobalSettings:EventLogging:RabbitMq:Password"] = "pass",
|
||||
["GlobalSettings:EventLogging:RabbitMq:EventExchangeName"] = "exchange"
|
||||
});
|
||||
|
||||
// Add prerequisites
|
||||
services.TryAddSingleton(globalSettings);
|
||||
services.TryAddSingleton(Substitute.For<IConnectionMultiplexer>());
|
||||
services.AddLogging();
|
||||
|
||||
services.AddRabbitMqListeners(globalSettings);
|
||||
|
||||
Assert.Contains(services, s => s.ServiceType == typeof(IRabbitMqService));
|
||||
Assert.Contains(services, s => s.ServiceType == typeof(EventRepositoryHandler));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddRabbitMqListeners_RabbitMqDisabled_ReturnsEarly()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var globalSettings = CreateGlobalSettings([]);
|
||||
|
||||
var initialCount = services.Count;
|
||||
services.AddRabbitMqListeners(globalSettings);
|
||||
var finalCount = services.Count;
|
||||
|
||||
Assert.Equal(initialCount, finalCount);
|
||||
}
|
||||
|
||||
private static GlobalSettings CreateGlobalSettings(Dictionary<string, string?> data)
|
||||
{
|
||||
var config = new ConfigurationBuilder()
|
||||
|
||||
Reference in New Issue
Block a user