Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<Copyright>Avanade 2019</Copyright>
<PackageProjectUrl>https://github.com/Avanade/Liquid-Application-Framework</PackageProjectUrl>
<PackageIcon>logo.png</PackageIcon>
<Version>8.0.0</Version>
<Version>8.0.1</Version>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Description>
The Liquid.Messaging.ServiceBus provides producer and consumer patterns to allow the send and consumption of Messaging inside your microservice.
Expand Down
2 changes: 1 addition & 1 deletion src/Liquid.Messaging.ServiceBus/ServiceBusConsumer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
///<inheritdoc/>
public class ServiceBusConsumer<TEntity> : ILiquidConsumer<TEntity>
{
private ServiceBusProcessor _messageProcessor;

Check warning on line 17 in src/Liquid.Messaging.ServiceBus/ServiceBusConsumer.cs

View workflow job for this annotation

GitHub Actions / call-reusable-build-workflow / build

Remove the field '_messageProcessor' and declare it as a local variable in the relevant methods. (https://rules.sonarsource.com/csharp/RSPEC-1450)

Check warning on line 17 in src/Liquid.Messaging.ServiceBus/ServiceBusConsumer.cs

View workflow job for this annotation

GitHub Actions / call-reusable-build-workflow / build

Remove the field '_messageProcessor' and declare it as a local variable in the relevant methods. (https://rules.sonarsource.com/csharp/RSPEC-1450)

Check warning on line 17 in src/Liquid.Messaging.ServiceBus/ServiceBusConsumer.cs

View workflow job for this annotation

GitHub Actions / call-reusable-build-workflow / build

Remove the field '_messageProcessor' and declare it as a local variable in the relevant methods. (https://rules.sonarsource.com/csharp/RSPEC-1450)

private readonly IServiceBusFactory _factory;

Expand All @@ -34,7 +34,7 @@
public ServiceBusConsumer(IServiceBusFactory factory, string settingsName)
{
_factory = factory ?? throw new ArgumentNullException(nameof(factory));
_settingsName = settingsName;
_settingsName = settingsName ?? throw new ArgumentNullException(nameof(settingsName));
}

///<inheritdoc/>
Expand Down
25 changes: 20 additions & 5 deletions src/Liquid.Messaging.ServiceBus/ServiceBusFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,17 @@
}

///<inheritdoc/>
public ServiceBusProcessor GetProcessor(string settingsName)

Check warning on line 25 in src/Liquid.Messaging.ServiceBus/ServiceBusFactory.cs

View workflow job for this annotation

GitHub Actions / call-reusable-build-workflow / build

Rename parameter 'settingsName' to 'entityPath' to match the interface declaration. (https://rules.sonarsource.com/csharp/RSPEC-927)

Check warning on line 25 in src/Liquid.Messaging.ServiceBus/ServiceBusFactory.cs

View workflow job for this annotation

GitHub Actions / call-reusable-build-workflow / build

Rename parameter 'settingsName' to 'entityPath' to match the interface declaration. (https://rules.sonarsource.com/csharp/RSPEC-927)

Check warning on line 25 in src/Liquid.Messaging.ServiceBus/ServiceBusFactory.cs

View workflow job for this annotation

GitHub Actions / call-reusable-build-workflow / build

Rename parameter 'settingsName' to 'entityPath' to match the interface declaration. (https://rules.sonarsource.com/csharp/RSPEC-927)
{
try
{
var config = _options.Settings.FirstOrDefault(x => x.EntityPath == settingsName);

if (config == null)
{
throw new ArgumentOutOfRangeException(nameof(settingsName), $"The settings name '{settingsName}' is not found in the configuration.");
}

var options = new ServiceBusProcessorOptions();

options.ReceiveMode = config.PeekLockMode ? ServiceBusReceiveMode.PeekLock : ServiceBusReceiveMode.ReceiveAndDelete;
Expand All @@ -42,7 +47,7 @@
processor = serviceBusClient.CreateProcessor(config.EntityPath, options);
}
else
{
{
processor = serviceBusClient.CreateProcessor(config.EntityPath, config.Subscription, options);
}

Expand All @@ -55,10 +60,15 @@
}

///<inheritdoc/>
public ServiceBusSender GetSender(string settingsName)

Check warning on line 63 in src/Liquid.Messaging.ServiceBus/ServiceBusFactory.cs

View workflow job for this annotation

GitHub Actions / call-reusable-build-workflow / build

Rename parameter 'settingsName' to 'entityPath' to match the interface declaration. (https://rules.sonarsource.com/csharp/RSPEC-927)

Check warning on line 63 in src/Liquid.Messaging.ServiceBus/ServiceBusFactory.cs

View workflow job for this annotation

GitHub Actions / call-reusable-build-workflow / build

Rename parameter 'settingsName' to 'entityPath' to match the interface declaration. (https://rules.sonarsource.com/csharp/RSPEC-927)
{
var config = _options.Settings.FirstOrDefault(x => x.EntityPath == settingsName);

if (config == null)
{
throw new ArgumentOutOfRangeException(nameof(settingsName), $"The settings name '{settingsName}' is not found in the configuration.");
}

try
{
var serviceBusClient = new ServiceBusClient(config.ConnectionString);
Expand All @@ -73,19 +83,24 @@
}

///<inheritdoc/>
public ServiceBusReceiver GetReceiver(string settingsName)

Check warning on line 86 in src/Liquid.Messaging.ServiceBus/ServiceBusFactory.cs

View workflow job for this annotation

GitHub Actions / call-reusable-build-workflow / build

Rename parameter 'settingsName' to 'entityPath' to match the interface declaration. (https://rules.sonarsource.com/csharp/RSPEC-927)

Check warning on line 86 in src/Liquid.Messaging.ServiceBus/ServiceBusFactory.cs

View workflow job for this annotation

GitHub Actions / call-reusable-build-workflow / build

Rename parameter 'settingsName' to 'entityPath' to match the interface declaration. (https://rules.sonarsource.com/csharp/RSPEC-927)
{
var config = _options.Settings.FirstOrDefault(x => x.EntityPath == settingsName);

if (config == null)
{
throw new ArgumentOutOfRangeException(nameof(settingsName), $"The settings name '{settingsName}' is not found in the configuration.");
}

try
{
var options = new ServiceBusReceiverOptions();

options.ReceiveMode = config.PeekLockMode ? ServiceBusReceiveMode.PeekLock : ServiceBusReceiveMode.ReceiveAndDelete;
var serviceBusClient = new ServiceBusClient(config.ConnectionString);
var receiver = serviceBusClient.CreateReceiver(config.EntityPath, options);

var serviceBusClient = new ServiceBusClient(config.ConnectionString);

var receiver = serviceBusClient.CreateReceiver(config.EntityPath, options);

return receiver;
}
Expand Down
18 changes: 14 additions & 4 deletions test/Liquid.Messaging.ServiceBus.Tests/ServiceBusConsumerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,16 @@ public ServiceBusConsumerTest() : base(_factory, "test")
}

[Fact]
public void RegisterMessageHandler_WhenRegisteredSucessfully_StartProcessingReceivedCall()
public async Task RegisterMessageHandler_WhenRegisteredSucessfully_StartProcessingReceivedCall()
{
var messageReceiver = Substitute.For<ServiceBusProcessor>();
_factory.GetProcessor(Arg.Any<string>()).Returns(messageReceiver);

ConsumeMessageAsync += ProcessMessageAsyncMock;

RegisterMessageHandler();
await RegisterMessageHandler();

messageReceiver.Received(1).StartProcessingAsync();
await messageReceiver.Received(1).StartProcessingAsync();
}

[Fact]
Expand All @@ -46,7 +46,6 @@ public async Task RegisterMessageHandler_WhenConsumeMessageAssyncIsNull_ThrowNot
await Assert.ThrowsAsync<NotImplementedException>(() => RegisterMessageHandler());
}


[Fact]
public async Task MessageHandler_WhenProcessExecutedSucessfully()
{
Expand Down Expand Up @@ -85,6 +84,17 @@ public async Task ErrorHandler_WhenProcessErrorExecuted_ThrowsMessagingConsumerE
await Assert.ThrowsAsync<MessagingConsumerException>(() => ErrorHandler(processError));
}

[Fact]
public void Constructor_WhenFactoryIsNull_ThrowsArgumentNullException()
{
Assert.Throws<ArgumentNullException>(() => new ServiceBusConsumer<EntityMock>(null, "test"));
}

[Fact]
public void Constructor_WhenSettingsNameIsNull_ThrowsArgumentNullException()
{
Assert.Throws<ArgumentNullException>(() => new ServiceBusConsumer<EntityMock>(_factory, null));
}

#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
private async Task ProcessMessageAsyncMock(ConsumerMessageEventArgs<EntityMock> args, CancellationToken cancellationToken)
Expand Down
136 changes: 136 additions & 0 deletions test/Liquid.Messaging.ServiceBus.Tests/ServiceBusFactoryTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Azure.Messaging.ServiceBus;
using Liquid.Core.Exceptions;
using Liquid.Messaging.ServiceBus;
using Liquid.Messaging.ServiceBus.Settings;
using Microsoft.Extensions.Options;
using NSubstitute;
using Xunit;

namespace Liquid.Messaging.ServiceBus.Tests
{
public class ServiceBusFactoryTests
{
private readonly ServiceBusSettings _settings;
private readonly IOptions<ServiceBusSettings> _options;

public ServiceBusFactoryTests()
{
_settings = new ServiceBusSettings
{
Settings = new List<ServiceBusEntitySettings>
{
new ServiceBusEntitySettings
{
EntityPath = "queue1",
ConnectionString = "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=key",
PeekLockMode = true,
MaxConcurrentCalls = 5,
Subscription = null
},
new ServiceBusEntitySettings
{
EntityPath = "topic1",
ConnectionString = "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=key",
PeekLockMode = false,
MaxConcurrentCalls = 2,
Subscription = "sub1"
}
}
};
_options = Substitute.For<IOptions<ServiceBusSettings>>();
_options.Value.Returns(_settings);
}

[Fact]
public void GetSender_ReturnsSender_WhenConfigExists()
{
// Arrange
var factory = new ServiceBusFactory(_options);

// Act
var sender = factory.GetSender("queue1");

// Assert
Assert.NotNull(sender);
Assert.IsType<ServiceBusSender>(sender);
}

[Fact]
public void GetSender_ThrowsArgumentOutOfRangeException_WhenConfigMissing()
{
// Arrange
var factory = new ServiceBusFactory(_options);

// Act & Assert
var ex = Assert.Throws<ArgumentOutOfRangeException>(() => factory.GetSender("notfound"));
Assert.Contains("notfound", ex.Message);
}

[Fact]
public void GetProcessor_ReturnsProcessor_WhenQueueConfigExists()
{
// Arrange
var factory = new ServiceBusFactory(_options);

// Act
var processor = factory.GetProcessor("queue1");

// Assert
Assert.NotNull(processor);
Assert.IsType<ServiceBusProcessor>(processor);
}

[Fact]
public void GetProcessor_ReturnsProcessor_WhenTopicConfigExists()
{
// Arrange
var factory = new ServiceBusFactory(_options);

// Act
var processor = factory.GetProcessor("topic1");

// Assert
Assert.NotNull(processor);
Assert.IsType<ServiceBusProcessor>(processor);
}

[Fact]
public void GetProcessor_ThrowsMessagingMissingConfigurationException_WhenConfigMissing()
{
// Arrange
var factory = new ServiceBusFactory(_options);

// Act & Assert
var ex = Assert.Throws<MessagingMissingConfigurationException>(() => factory.GetProcessor("notfound"));
Assert.Contains("notfound", ex.Message);
}

[Fact]
public void GetReceiver_ReturnsReceiver_WhenConfigExists()
{
// Arrange
var factory = new ServiceBusFactory(_options);

// Act
var receiver = factory.GetReceiver("queue1");

// Assert
Assert.NotNull(receiver);
Assert.IsType<ServiceBusReceiver>(receiver);
}

[Fact]
public void GetReceiver_ThrowsArgumentOutOfRangeException_WhenConfigMissing()
{
// Arrange
var factory = new ServiceBusFactory(_options);

// Act & Assert
var ex = Assert.Throws<ArgumentOutOfRangeException>(() => factory.GetReceiver("notfound"));
Assert.Contains("notfound", ex.Message);
}
}
}
Loading