1
0
mirror of https://github.com/bitwarden/server synced 2025-12-10 05:13:48 +00:00

[PM-17562] Refactor event integration methods / declarations in ServiceCollectionExtensions (#6118)

* [PM-17562] Refactor event integration methods / declarations in ServiceCollectionExtensions

* Refactored ServiceCollectionExtensions to use TryAdd and still launch unique listeneer services

* Updated unit tests to match new generic format for Listeners

* Fix method spacing

* Update README to reflect new integration setup in ServiceCollectionExtensions

* Move interfaces to I prefix; fix typo in subscription

* Fix reference to IIntegrationListenerConfiguration
This commit is contained in:
Brant DeBow
2025-07-29 11:22:21 -04:00
committed by GitHub
parent 43372b7168
commit a84e5554fb
19 changed files with 512 additions and 314 deletions

View File

@@ -0,0 +1,16 @@
using Bit.Core.Enums;
namespace Bit.Core.AdminConsole.Models.Data.EventIntegrations;
public class TestListenerConfiguration : IIntegrationListenerConfiguration
{
public string EventQueueName => "event_queue";
public string EventSubscriptionName => "event_subscription";
public string EventTopicName => "event_topic";
public IntegrationType IntegrationType => IntegrationType.Webhook;
public string IntegrationQueueName => "integration_queue";
public string IntegrationRetryQueueName => "integration_retry_queue";
public string IntegrationSubscriptionName => "integration_subscription";
public string IntegrationTopicName => "integration_topic";
public int MaxRetries => 3;
}

View File

@@ -1,5 +1,6 @@
using System.Text.Json;
using Azure.Messaging.ServiceBus;
using Bit.Core.AdminConsole.Models.Data.EventIntegrations;
using Bit.Core.Models.Data;
using Bit.Core.Services;
using Bit.Test.Common.AutoFixture;
@@ -14,20 +15,28 @@ namespace Bit.Core.Test.Services;
[SutProviderCustomize]
public class AzureServiceBusEventListenerServiceTests
{
private readonly IEventMessageHandler _handler = Substitute.For<IEventMessageHandler>();
private readonly ILogger<AzureServiceBusEventListenerService> _logger =
Substitute.For<ILogger<AzureServiceBusEventListenerService>>();
private const string _messageId = "messageId";
private readonly TestListenerConfiguration _config = new();
private SutProvider<AzureServiceBusEventListenerService> GetSutProvider()
private SutProvider<AzureServiceBusEventListenerService<TestListenerConfiguration>> GetSutProvider()
{
return new SutProvider<AzureServiceBusEventListenerService>()
.SetDependency(_handler)
.SetDependency(_logger)
.SetDependency("test-subscription", "subscriptionName")
return new SutProvider<AzureServiceBusEventListenerService<TestListenerConfiguration>>()
.SetDependency(_config)
.Create();
}
[Fact]
public void Constructor_CreatesProcessor()
{
var sutProvider = GetSutProvider();
sutProvider.GetDependency<IAzureServiceBusService>().Received(1).CreateProcessor(
Arg.Is(_config.EventTopicName),
Arg.Is(_config.EventSubscriptionName),
Arg.Any<ServiceBusProcessorOptions>()
);
}
[Theory, BitAutoData]
public async Task ProcessErrorAsync_LogsError(ProcessErrorEventArgs args)
{
@@ -35,7 +44,7 @@ public class AzureServiceBusEventListenerServiceTests
await sutProvider.Sut.ProcessErrorAsync(args);
_logger.Received(1).Log(
sutProvider.GetDependency<ILogger<AzureServiceBusEventListenerService<TestListenerConfiguration>>>().Received(1).Log(
LogLevel.Error,
Arg.Any<EventId>(),
Arg.Any<object>(),
@@ -49,7 +58,7 @@ public class AzureServiceBusEventListenerServiceTests
var sutProvider = GetSutProvider();
await sutProvider.Sut.ProcessReceivedMessageAsync(string.Empty, _messageId);
_logger.Received(1).Log(
sutProvider.GetDependency<ILogger<AzureServiceBusEventListenerService<TestListenerConfiguration>>>().Received(1).Log(
LogLevel.Error,
Arg.Any<EventId>(),
Arg.Any<object>(),
@@ -63,7 +72,7 @@ public class AzureServiceBusEventListenerServiceTests
var sutProvider = GetSutProvider();
await sutProvider.Sut.ProcessReceivedMessageAsync("{ Inavlid JSON }", _messageId);
_logger.Received(1).Log(
sutProvider.GetDependency<ILogger<AzureServiceBusEventListenerService<TestListenerConfiguration>>>().Received(1).Log(
LogLevel.Error,
Arg.Any<EventId>(),
Arg.Is<object>(o => o.ToString().Contains("Invalid JSON")),
@@ -80,7 +89,7 @@ public class AzureServiceBusEventListenerServiceTests
_messageId
);
_logger.Received(1).Log(
sutProvider.GetDependency<ILogger<AzureServiceBusEventListenerService<TestListenerConfiguration>>>().Received(1).Log(
LogLevel.Error,
Arg.Any<EventId>(),
Arg.Any<object>(),
@@ -97,7 +106,7 @@ public class AzureServiceBusEventListenerServiceTests
_messageId
);
_logger.Received(1).Log(
sutProvider.GetDependency<ILogger<AzureServiceBusEventListenerService<TestListenerConfiguration>>>().Received(1).Log(
LogLevel.Error,
Arg.Any<EventId>(),
Arg.Any<object>(),

View File

@@ -14,33 +14,38 @@ namespace Bit.Core.Test.Services;
[SutProviderCustomize]
public class AzureServiceBusIntegrationListenerServiceTests
{
private const int _maxRetries = 3;
private const string _topicName = "test_topic";
private const string _subscriptionName = "test_subscription";
private readonly IIntegrationHandler _handler = Substitute.For<IIntegrationHandler>();
private readonly IAzureServiceBusService _serviceBusService = Substitute.For<IAzureServiceBusService>();
private readonly ILogger<AzureServiceBusIntegrationListenerService> _logger =
Substitute.For<ILogger<AzureServiceBusIntegrationListenerService>>();
private readonly TestListenerConfiguration _config = new();
private SutProvider<AzureServiceBusIntegrationListenerService> GetSutProvider()
private SutProvider<AzureServiceBusIntegrationListenerService<TestListenerConfiguration>> GetSutProvider()
{
return new SutProvider<AzureServiceBusIntegrationListenerService>()
return new SutProvider<AzureServiceBusIntegrationListenerService<TestListenerConfiguration>>()
.SetDependency(_config)
.SetDependency(_handler)
.SetDependency(_serviceBusService)
.SetDependency(_topicName, "topicName")
.SetDependency(_subscriptionName, "subscriptionName")
.SetDependency(_maxRetries, "maxRetries")
.SetDependency(_logger)
.Create();
}
[Fact]
public void Constructor_CreatesProcessor()
{
var sutProvider = GetSutProvider();
sutProvider.GetDependency<IAzureServiceBusService>().Received(1).CreateProcessor(
Arg.Is(_config.IntegrationTopicName),
Arg.Is(_config.IntegrationSubscriptionName),
Arg.Any<ServiceBusProcessorOptions>()
);
}
[Theory, BitAutoData]
public async Task ProcessErrorAsync_LogsError(ProcessErrorEventArgs args)
{
var sutProvider = GetSutProvider();
await sutProvider.Sut.ProcessErrorAsync(args);
_logger.Received(1).Log(
sutProvider.GetDependency<ILogger<AzureServiceBusIntegrationListenerService<TestListenerConfiguration>>>().Received(1).Log(
LogLevel.Error,
Arg.Any<EventId>(),
Arg.Any<object>(),
@@ -70,7 +75,7 @@ public class AzureServiceBusIntegrationListenerServiceTests
public async Task HandleMessageAsync_FailureRetryableButTooManyRetries_PublishesToDeadLetterQueue(IntegrationMessage<WebhookIntegrationConfiguration> message)
{
var sutProvider = GetSutProvider();
message.RetryCount = _maxRetries;
message.RetryCount = _config.MaxRetries;
var result = new IntegrationHandlerResult(false, message);
result.Retryable = true;

View File

@@ -1,4 +1,5 @@
using System.Text.Json;
using Bit.Core.AdminConsole.Models.Data.EventIntegrations;
using Bit.Core.Models.Data;
using Bit.Core.Services;
using Bit.Test.Common.AutoFixture;
@@ -15,16 +16,12 @@ namespace Bit.Core.Test.Services;
[SutProviderCustomize]
public class RabbitMqEventListenerServiceTests
{
private const string _queueName = "test_queue";
private readonly IRabbitMqService _rabbitMqService = Substitute.For<IRabbitMqService>();
private readonly ILogger<RabbitMqEventListenerService> _logger = Substitute.For<ILogger<RabbitMqEventListenerService>>();
private readonly TestListenerConfiguration _config = new();
private SutProvider<RabbitMqEventListenerService> GetSutProvider()
private SutProvider<RabbitMqEventListenerService<TestListenerConfiguration>> GetSutProvider()
{
return new SutProvider<RabbitMqEventListenerService>()
.SetDependency(_rabbitMqService)
.SetDependency(_logger)
.SetDependency(_queueName, "queueName")
return new SutProvider<RabbitMqEventListenerService<TestListenerConfiguration>>()
.SetDependency(_config)
.Create();
}
@@ -35,8 +32,8 @@ public class RabbitMqEventListenerServiceTests
var cancellationToken = CancellationToken.None;
await sutProvider.Sut.StartAsync(cancellationToken);
await _rabbitMqService.Received(1).CreateEventQueueAsync(
Arg.Is(_queueName),
await sutProvider.GetDependency<IRabbitMqService>().Received(1).CreateEventQueueAsync(
Arg.Is(_config.EventQueueName),
Arg.Is(cancellationToken)
);
}
@@ -52,11 +49,11 @@ public class RabbitMqEventListenerServiceTests
exchange: string.Empty,
routingKey: string.Empty,
new BasicProperties(),
body: new byte[0]);
body: Array.Empty<byte>());
await sutProvider.Sut.ProcessReceivedMessageAsync(eventArgs);
_logger.Received(1).Log(
sutProvider.GetDependency<ILogger<RabbitMqEventListenerService<TestListenerConfiguration>>>().Received(1).Log(
LogLevel.Error,
Arg.Any<EventId>(),
Arg.Any<object>(),
@@ -75,11 +72,11 @@ public class RabbitMqEventListenerServiceTests
exchange: string.Empty,
routingKey: string.Empty,
new BasicProperties(),
body: JsonSerializer.SerializeToUtf8Bytes("{ Inavlid JSON"));
body: JsonSerializer.SerializeToUtf8Bytes("{ Invalid JSON"));
await sutProvider.Sut.ProcessReceivedMessageAsync(eventArgs);
_logger.Received(1).Log(
sutProvider.GetDependency<ILogger<RabbitMqEventListenerService<TestListenerConfiguration>>>().Received(1).Log(
LogLevel.Error,
Arg.Any<EventId>(),
Arg.Is<object>(o => o.ToString().Contains("Invalid JSON")),
@@ -102,7 +99,7 @@ public class RabbitMqEventListenerServiceTests
await sutProvider.Sut.ProcessReceivedMessageAsync(eventArgs);
_logger.Received(1).Log(
sutProvider.GetDependency<ILogger<RabbitMqEventListenerService<TestListenerConfiguration>>>().Received(1).Log(
LogLevel.Error,
Arg.Any<EventId>(),
Arg.Any<object>(),
@@ -125,7 +122,7 @@ public class RabbitMqEventListenerServiceTests
await sutProvider.Sut.ProcessReceivedMessageAsync(eventArgs);
_logger.Received(1).Log(
sutProvider.GetDependency<ILogger<RabbitMqEventListenerService<TestListenerConfiguration>>>().Received(1).Log(
LogLevel.Error,
Arg.Any<EventId>(),
Arg.Any<object>(),

View File

@@ -15,23 +15,17 @@ namespace Bit.Core.Test.Services;
[SutProviderCustomize]
public class RabbitMqIntegrationListenerServiceTests
{
private const int _maxRetries = 3;
private const string _queueName = "test_queue";
private const string _retryQueueName = "test_queue_retry";
private const string _routingKey = "test_routing_key";
private readonly DateTime _now = new DateTime(2014, 3, 2, 1, 0, 0, DateTimeKind.Utc);
private readonly IIntegrationHandler _handler = Substitute.For<IIntegrationHandler>();
private readonly IRabbitMqService _rabbitMqService = Substitute.For<IRabbitMqService>();
private readonly TestListenerConfiguration _config = new();
private SutProvider<RabbitMqIntegrationListenerService> GetSutProvider()
private SutProvider<RabbitMqIntegrationListenerService<TestListenerConfiguration>> GetSutProvider()
{
var sutProvider = new SutProvider<RabbitMqIntegrationListenerService>()
var sutProvider = new SutProvider<RabbitMqIntegrationListenerService<TestListenerConfiguration>>()
.SetDependency(_config)
.SetDependency(_handler)
.SetDependency(_rabbitMqService)
.SetDependency(_queueName, "queueName")
.SetDependency(_retryQueueName, "retryQueueName")
.SetDependency(_routingKey, "routingKey")
.SetDependency(_maxRetries, "maxRetries")
.WithFakeTimeProvider()
.Create();
sutProvider.GetDependency<FakeTimeProvider>().SetUtcNow(_now);
@@ -46,10 +40,10 @@ public class RabbitMqIntegrationListenerServiceTests
var cancellationToken = CancellationToken.None;
await sutProvider.Sut.StartAsync(cancellationToken);
await _rabbitMqService.Received(1).CreateIntegrationQueuesAsync(
Arg.Is(_queueName),
Arg.Is(_retryQueueName),
Arg.Is(_routingKey),
await sutProvider.GetDependency<IRabbitMqService>().Received(1).CreateIntegrationQueuesAsync(
Arg.Is(_config.IntegrationQueueName),
Arg.Is(_config.IntegrationRetryQueueName),
Arg.Is(((IIntegrationListenerConfiguration)_config).RoutingKey),
Arg.Is(cancellationToken)
);
}
@@ -101,7 +95,7 @@ public class RabbitMqIntegrationListenerServiceTests
await sutProvider.Sut.StartAsync(cancellationToken);
message.DelayUntilDate = null;
message.RetryCount = _maxRetries;
message.RetryCount = _config.MaxRetries;
var eventArgs = new BasicDeliverEventArgs(
consumerTag: string.Empty,
deliveryTag: 0,