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 @@ -69,6 +69,18 @@ public void Heartbeat_Can_Be_Set_Manually()
Assert.Equal(TimeSpan.FromSeconds(10), options.Heartbeat);
}

[Fact]
public void Heartbeat_Can_Be_Overridden_After_Expires()
{
var options = new NatsCommandQueueOptions();

options.Expires = TimeSpan.FromSeconds(60);
Assert.Equal(TimeSpan.FromSeconds(5), options.Heartbeat);

options.Heartbeat = TimeSpan.FromSeconds(15);
Assert.Equal(TimeSpan.FromSeconds(15), options.Heartbeat);
}

[Fact]
public void StreamName_And_ConsumerName_Can_Be_Set_And_Gotten()
{
Expand Down
27 changes: 27 additions & 0 deletions test/Savvyio.Extensions.RabbitMQ.Tests/RabbitMqMessageTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,33 @@ public async Task EnsureConnectivityAsync_InitializesConnectionAndChannel_Once()
factoryMock.Verify(f => f.CreateConnectionAsync(It.IsAny<CancellationToken>()), Times.Once);
}

[Fact]
public async Task EnsureConnectivityAsync_IsThreadSafe_WhenCalledConcurrently()
{
var marshaller = new Mock<IMarshaller>().Object;
var options = new RabbitMqMessageOptions { AmqpUrl = new Uri("amqp://localhost:5672") };

var connectionMock = new Mock<IConnection>();
var channelMock = new Mock<IChannel>();
connectionMock.Setup(c => c.CreateChannelAsync(It.IsAny<CreateChannelOptions?>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(channelMock.Object);

var factoryMock = new Mock<IConnectionFactory>();
factoryMock.Setup(f => f.CreateConnectionAsync(It.IsAny<CancellationToken>()))
.ReturnsAsync(connectionMock.Object);

var sut = new TestRabbitMqMessage(marshaller, options);
typeof(RabbitMqMessage)
.GetProperty("RabbitMqFactory", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
.SetValue(sut, factoryMock.Object);

await Task.WhenAll(
sut.CallEnsureConnectivityAsync(),
sut.CallEnsureConnectivityAsync());

factoryMock.Verify(f => f.CreateConnectionAsync(It.IsAny<CancellationToken>()), Times.Once);
}
Comment on lines +104 to +129
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Don’t mock IMarshaller; use a JsonMarshaller. Also verify CreateChannelAsync once.

Repo guideline: “Never mock IMarshaller; use a new JsonMarshaller instance instead.” Replace the mock and add a verify for channel creation.

Apply this diff:

         public async Task EnsureConnectivityAsync_IsThreadSafe_WhenCalledConcurrently()
         {
-            var marshaller = new Mock<IMarshaller>().Object;
+            var marshaller = new JsonMarshaller();
             var options = new RabbitMqMessageOptions { AmqpUrl = new Uri("amqp://localhost:5672") };
@@
             await Task.WhenAll(
                 sut.CallEnsureConnectivityAsync(),
                 sut.CallEnsureConnectivityAsync());
 
             factoryMock.Verify(f => f.CreateConnectionAsync(It.IsAny<CancellationToken>()), Times.Once);
+            connectionMock.Verify(c => c.CreateChannelAsync(It.IsAny<CreateChannelOptions?>(), It.IsAny<CancellationToken>()), Times.Once);
         }

Add the appropriate using for JsonMarshaller (adjust namespace to your concrete type if different):

using Savvyio.Serialization; // or the namespace where JsonMarshaller resides
🤖 Prompt for AI Agents
In test/Savvyio.Extensions.RabbitMQ.Tests/RabbitMqMessageTest.cs around lines
104 to 129, replace the mocked IMarshaller with a real JsonMarshaller instance
(add the appropriate using for its namespace), and after the concurrent
EnsureConnectivity calls add a Verify on the connectionMock.CreateChannelAsync
(or channelMock depending on method) to assert it was invoked once;
specifically: construct new JsonMarshaller() instead of
Mock<IMarshaller>().Object, add the using for Savvyio.Serialization (or the
correct namespace), and add factoryMock.Verify for
CreateConnectionAsync(Times.Once) is already present and add
channelMock.Verify(c => c.CreateChannelAsync(It.IsAny<CreateChannelOptions?>(),
It.IsAny<CancellationToken>()), Times.Once) to confirm single channel creation.


[Fact]
public async Task OnDisposeManagedResourcesAsync_DisposesChannelAndConnection()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,17 @@ public void PollingTimeout_ShouldClampValue_WhenOutsideRangeOfAllowedLimits()
sut.PollingTimeout = TimeSpan.MaxValue;
Assert.Equal(TimeSpan.FromSeconds(AmazonMessageOptions.MaxPollingWaitTimeInSeconds), sut.PollingTimeout);
}

[Fact]
public void VisibilityTimeout_ShouldClampValue_WhenOutsideRangeOfAllowedLimits()
{
var sut = new AmazonMessageReceiveOptions();

sut.VisibilityTimeout = TimeSpan.MinValue;
Assert.Equal(TimeSpan.Zero, sut.VisibilityTimeout);

sut.VisibilityTimeout = TimeSpan.FromSeconds(AmazonMessageOptions.MaxVisibilityTimeoutInSeconds + 1);
Assert.Equal(TimeSpan.FromSeconds(AmazonMessageOptions.MaxVisibilityTimeoutInSeconds), sut.VisibilityTimeout);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Amazon.Runtime;
using Amazon.SimpleNotificationService;
using Amazon.SQS;
using Codebelt.Extensions.Xunit;
using Xunit;
using Xunit.Abstractions;

namespace Savvyio.Extensions.SimpleQueueService
{
public class ClientConfigExtensionsTest : Test
{
public ClientConfigExtensionsTest(ITestOutputHelper output) : base(output) { }

[Fact]
public void IsValid_ShouldEvaluateConfigurations()
{
ClientConfig[] invalid = null;
Assert.False(invalid.IsValid());

var valid = new ClientConfig[] { new AmazonSQSConfig(), new AmazonSimpleNotificationServiceConfig() };
Assert.True(valid.IsValid());

var wrongLength = new ClientConfig[] { new AmazonSQSConfig() };
Assert.False(wrongLength.IsValid());
}

[Fact]
public void ShouldResolveSpecificConfigurations()
{
var sqs = new AmazonSQSConfig();
var sns = new AmazonSimpleNotificationServiceConfig();
ClientConfig[] configs = { sqs, sns };

Assert.Same(sqs, configs.SimpleQueueService());
Assert.Same(sns, configs.SimpleNotificationService());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using Codebelt.Extensions.Xunit;
using Xunit;
using Xunit.Abstractions;

namespace Savvyio.Extensions.SimpleQueueService.EventDriven
{
public class StringExtensionsTest : Test
{
public StringExtensionsTest(ITestOutputHelper output) : base(output) { }

[Fact]
public void ToSnsUri_ShouldBuildArnWithDefaults()
{
var uri = "sample-topic".ToSnsUri();

Assert.Equal(new Uri("arn:aws:sns:eu-west-1:000000000000:sample-topic"), uri);
}

[Fact]
public void ToSnsUri_ShouldRespectCustomOptions()
{
var uri = "topic".ToSnsUri(o =>
{
o.Partition = "aws";
o.Region = "us-east-1";
o.AccountId = "123456789012";
});

Assert.Equal(new Uri("arn:aws:sns:us-east-1:123456789012:topic"), uri);
}
}
}
Loading