Use latest language features (#2176)

* Make use of language features line AsyncDispose, init etc.

* Fix build errors

* Improve Unit Tests

* Improve Unit Tests

* Improve Unit Tests

* Update samples

* Remove obsolete benchmarks

* Code cleanup

* Improve unit tests

* Improve Unit Tests

* Fix Unit Tests

* Improve Unit Tests

* Improve Unit Tests

* Restore unit tests

* Exclude load tests from unit tests
This commit is contained in:
Christian
2025-08-30 10:52:59 +02:00
committed by GitHub
parent 247988e633
commit e43e7f8e36
330 changed files with 21417 additions and 22919 deletions
@@ -9,84 +9,82 @@ using MQTTnet.Formatter;
using MQTTnet.Internal;
using MQTTnet.Protocol;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class Assigned_Client_ID_Tests : BaseTestClass
{
[TestClass]
public sealed class Assigned_Client_ID_Tests : BaseTestClass
[TestMethod]
public Task Connect_With_No_Client_Id()
{
[TestMethod]
public Task Connect_With_No_Client_Id()
{
return Connect_With_Client_Id("test_123", "test_123", null, "test_123");
}
return Connect_With_Client_Id("test_123", "test_123", null, "test_123");
}
[TestMethod]
public Task Connect_With_Client_Id()
{
return Connect_With_Client_Id("Connect_With_Client_Id_test_456", null, "test_456", null);
}
[TestMethod]
public Task Connect_With_Client_Id()
{
return Connect_With_Client_Id("Connect_With_Client_Id_test_456", null, "test_456", null);
}
async Task Connect_With_Client_Id(string expectedClientId, string expectedReturnedClientId, string usedClientId, string assignedClientId)
async Task Connect_With_Client_Id(string expectedClientId, string expectedReturnedClientId, string usedClientId, string assignedClientId)
{
using var testEnvironment = CreateTestEnvironment();
string serverConnectedClientId = null;
string serverDisconnectedClientId = null;
string clientAssignedClientId = null;
// Arrange server
var disconnectedMre = new ManualResetEventSlim();
var server = await testEnvironment.StartServer();
server.ValidatingConnectionAsync += e =>
{
using (var testEnvironment = CreateTestEnvironment())
if (string.IsNullOrEmpty(e.ClientId))
{
string serverConnectedClientId = null;
string serverDisconnectedClientId = null;
string clientAssignedClientId = null;
// Arrange server
var disconnectedMre = new ManualResetEventSlim();
var server = await testEnvironment.StartServer();
server.ValidatingConnectionAsync += e =>
{
if (string.IsNullOrEmpty(e.ClientId))
{
e.AssignedClientIdentifier = assignedClientId;
e.ReasonCode = MqttConnectReasonCode.Success;
}
return CompletedTask.Instance;
};
testEnvironment.Server.ClientConnectedAsync += args =>
{
serverConnectedClientId = args.ClientId;
return CompletedTask.Instance;
};
testEnvironment.Server.ClientDisconnectedAsync += args =>
{
serverDisconnectedClientId = args.ClientId;
disconnectedMre.Set();
return CompletedTask.Instance;
};
// Arrange client
var client = testEnvironment.CreateClient();
client.ConnectedAsync += args =>
{
clientAssignedClientId = args.ConnectResult.AssignedClientIdentifier;
return CompletedTask.Instance;
};
// Act
await client.ConnectAsync(new MqttClientOptionsBuilder()
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
.WithProtocolVersion(MqttProtocolVersion.V500)
.WithClientId(usedClientId)
.Build());
await client.DisconnectAsync();
// Wait for ClientDisconnectedHandler to trigger
disconnectedMre.Wait(1000);
// Assert
Assert.AreEqual(expectedClientId, serverConnectedClientId);
Assert.AreEqual(expectedClientId, serverDisconnectedClientId);
Assert.AreEqual(expectedReturnedClientId, clientAssignedClientId);
e.AssignedClientIdentifier = assignedClientId;
e.ReasonCode = MqttConnectReasonCode.Success;
}
}
return CompletedTask.Instance;
};
testEnvironment.Server.ClientConnectedAsync += args =>
{
serverConnectedClientId = args.ClientId;
return CompletedTask.Instance;
};
testEnvironment.Server.ClientDisconnectedAsync += args =>
{
serverDisconnectedClientId = args.ClientId;
disconnectedMre.Set();
return CompletedTask.Instance;
};
// Arrange client
var client = testEnvironment.CreateClient();
client.ConnectedAsync += args =>
{
clientAssignedClientId = args.ConnectResult.AssignedClientIdentifier;
return CompletedTask.Instance;
};
// Act
await client.ConnectAsync(new MqttClientOptionsBuilder()
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
.WithProtocolVersion(MqttProtocolVersion.V500)
.WithClientId(usedClientId)
.Build());
await client.DisconnectAsync();
// Wait for ClientDisconnectedHandler to trigger
disconnectedMre.Wait(1000);
// Assert
Assert.AreEqual(expectedClientId, serverConnectedClientId);
Assert.AreEqual(expectedClientId, serverDisconnectedClientId);
Assert.AreEqual(expectedReturnedClientId, clientAssignedClientId);
}
}
+52 -57
View File
@@ -5,78 +5,73 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Implementations;
using MQTTnet.Server;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class Connection_Tests : BaseTestClass
{
[TestClass]
public sealed class Connection_Tests : BaseTestClass
[TestMethod]
public async Task Close_Idle_Connection_On_Connect()
{
[TestMethod]
public async Task Close_Idle_Connection_On_Connect()
using var testEnvironment = CreateTestEnvironment();
await testEnvironment.StartServer(new MqttServerOptionsBuilder().WithDefaultCommunicationTimeout(TimeSpan.FromSeconds(1)));
var client = new CrossPlatformSocket(AddressFamily.InterNetwork, ProtocolType.Tcp);
await client.ConnectAsync(new DnsEndPoint("localhost", testEnvironment.ServerPort), CancellationToken.None);
// Don't send anything. The server should close the connection.
await Task.Delay(TimeSpan.FromSeconds(3));
try
{
using (var testEnvironment = CreateTestEnvironment())
var receivedBytes = await client.ReceiveAsync(new ArraySegment<byte>(new byte[10]), SocketFlags.Partial);
if (receivedBytes == 0)
{
await testEnvironment.StartServer(new MqttServerOptionsBuilder().WithDefaultCommunicationTimeout(TimeSpan.FromSeconds(1)));
var client = new CrossPlatformSocket(AddressFamily.InterNetwork, ProtocolType.Tcp);
await client.ConnectAsync(new DnsEndPoint("localhost", testEnvironment.ServerPort), CancellationToken.None);
// Don't send anything. The server should close the connection.
await Task.Delay(TimeSpan.FromSeconds(3));
try
{
var receivedBytes = await client.ReceiveAsync(new ArraySegment<byte>(new byte[10]), SocketFlags.Partial);
if (receivedBytes == 0)
{
return;
}
Assert.Fail("Receive should throw an exception.");
}
catch (SocketException)
{
}
return;
}
Assert.Fail("Receive should throw an exception.");
}
[TestMethod]
public async Task Send_Garbage()
catch (SocketException)
{
using (var testEnvironment = CreateTestEnvironment())
}
}
[TestMethod]
public async Task Send_Garbage()
{
using var testEnvironment = CreateTestEnvironment();
await testEnvironment.StartServer(new MqttServerOptionsBuilder().WithDefaultCommunicationTimeout(TimeSpan.FromSeconds(1)));
// Send an invalid packet and ensure that the server will close the connection and stay in a waiting state
// forever. This is security related.
var client = new CrossPlatformSocket(AddressFamily.InterNetwork, ProtocolType.Tcp);
await client.ConnectAsync(new DnsEndPoint("localhost", testEnvironment.ServerPort), CancellationToken.None);
var buffer = "Garbage"u8.ToArray();
await client.SendAsync(new ArraySegment<byte>(buffer), SocketFlags.None);
await Task.Delay(TimeSpan.FromSeconds(3));
try
{
var receivedBytes = await client.ReceiveAsync(new ArraySegment<byte>(new byte[10]), SocketFlags.Partial);
if (receivedBytes == 0)
{
await testEnvironment.StartServer(new MqttServerOptionsBuilder().WithDefaultCommunicationTimeout(TimeSpan.FromSeconds(1)));
// Send an invalid packet and ensure that the server will close the connection and stay in a waiting state
// forever. This is security related.
var client = new CrossPlatformSocket(AddressFamily.InterNetwork, ProtocolType.Tcp);
await client.ConnectAsync(new DnsEndPoint("localhost", testEnvironment.ServerPort), CancellationToken.None);
var buffer = Encoding.UTF8.GetBytes("Garbage");
await client.SendAsync(new ArraySegment<byte>(buffer), SocketFlags.None);
await Task.Delay(TimeSpan.FromSeconds(3));
try
{
var receivedBytes = await client.ReceiveAsync(new ArraySegment<byte>(new byte[10]), SocketFlags.Partial);
if (receivedBytes == 0)
{
return;
}
Assert.Fail("Receive should throw an exception.");
}
catch (SocketException)
{
}
return;
}
Assert.Fail("Receive should throw an exception.");
}
catch (SocketException)
{
}
}
}
@@ -4,63 +4,59 @@ using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Formatter;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class Cross_Version_Tests : BaseTestClass
{
[TestClass]
public sealed class Cross_Version_Tests : BaseTestClass
[TestMethod]
public async Task Send_V311_Receive_V500()
{
[TestMethod]
public async Task Send_V311_Receive_V500()
{
using (var testEnvironment = CreateTestEnvironment())
{
await testEnvironment.StartServer();
using var testEnvironment = CreateTestEnvironment();
await testEnvironment.StartServer();
var receiver = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
var receivedApplicationMessages = testEnvironment.CreateApplicationMessageHandler(receiver);
await receiver.SubscribeAsync("#");
var receiver = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
var receivedApplicationMessages = testEnvironment.CreateApplicationMessageHandler(receiver);
await receiver.SubscribeAsync("#");
var sender = await testEnvironment.ConnectClient();
var sender = await testEnvironment.ConnectClient();
var applicationMessage = new MqttApplicationMessageBuilder().WithTopic("My/Message").WithPayload("My_Payload").Build();
await sender.PublishAsync(applicationMessage);
var applicationMessage = new MqttApplicationMessageBuilder().WithTopic("My/Message").WithPayload("My_Payload").Build();
await sender.PublishAsync(applicationMessage);
await LongTestDelay();
await LongTestDelay();
Assert.AreEqual(1, receivedApplicationMessages.ReceivedEventArgs.Count);
Assert.AreEqual("My/Message", receivedApplicationMessages.ReceivedEventArgs.First().ApplicationMessage.Topic);
Assert.AreEqual("My_Payload", receivedApplicationMessages.ReceivedEventArgs.First().ApplicationMessage.ConvertPayloadToString());
}
}
Assert.AreEqual(1, receivedApplicationMessages.ReceivedEventArgs.Count);
Assert.AreEqual("My/Message", receivedApplicationMessages.ReceivedEventArgs.First().ApplicationMessage.Topic);
Assert.AreEqual("My_Payload", receivedApplicationMessages.ReceivedEventArgs.First().ApplicationMessage.ConvertPayloadToString());
}
[TestMethod]
public async Task Send_V500_Receive_V311()
{
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
{
await testEnvironment.StartServer();
[TestMethod]
public async Task Send_V500_Receive_V311()
{
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
await testEnvironment.StartServer();
var receiver = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V311));
var receivedApplicationMessages = testEnvironment.CreateApplicationMessageHandler(receiver);
await receiver.SubscribeAsync("#");
var receiver = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V311));
var receivedApplicationMessages = testEnvironment.CreateApplicationMessageHandler(receiver);
await receiver.SubscribeAsync("#");
var sender = await testEnvironment.ConnectClient();
var sender = await testEnvironment.ConnectClient();
var applicationMessage = new MqttApplicationMessageBuilder().WithTopic("My/Message")
.WithPayload("My_Payload")
.WithUserProperty("A", "B")
.WithResponseTopic("Response")
.WithCorrelationData(Encoding.UTF8.GetBytes("Correlation"))
.Build();
var applicationMessage = new MqttApplicationMessageBuilder().WithTopic("My/Message")
.WithPayload("My_Payload")
.WithUserProperty("A", "B")
.WithResponseTopic("Response")
.WithCorrelationData(Encoding.UTF8.GetBytes("Correlation"))
.Build();
await sender.PublishAsync(applicationMessage);
await sender.PublishAsync(applicationMessage);
await LongTestDelay();
await LongTestDelay();
Assert.AreEqual(1, receivedApplicationMessages.ReceivedEventArgs.Count);
Assert.AreEqual("My/Message", receivedApplicationMessages.ReceivedEventArgs.First().ApplicationMessage.Topic);
Assert.AreEqual("My_Payload", receivedApplicationMessages.ReceivedEventArgs.First().ApplicationMessage.ConvertPayloadToString());
}
}
Assert.AreEqual(1, receivedApplicationMessages.ReceivedEventArgs.Count);
Assert.AreEqual("My/Message", receivedApplicationMessages.ReceivedEventArgs.First().ApplicationMessage.Topic);
Assert.AreEqual("My_Payload", receivedApplicationMessages.ReceivedEventArgs.First().ApplicationMessage.ConvertPayloadToString());
}
}
+127 -141
View File
@@ -10,194 +10,180 @@ using MQTTnet.Internal;
using MQTTnet.Protocol;
using MQTTnet.Server;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class Events_Tests : BaseTestClass
{
[TestClass]
public sealed class Events_Tests : BaseTestClass
[TestMethod]
public async Task Fire_Client_Connected_Event()
{
[TestMethod]
public async Task Fire_Client_Connected_Event()
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
ClientConnectedEventArgs eventArgs = null;
server.ClientConnectedAsync += e =>
{
using (var testEnvironment = CreateTestEnvironment())
{
var server = await testEnvironment.StartServer();
eventArgs = e;
return CompletedTask.Instance;
};
ClientConnectedEventArgs eventArgs = null;
server.ClientConnectedAsync += e =>
{
eventArgs = e;
return CompletedTask.Instance;
};
await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser", "ThePassword"));
await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser", "ThePassword"));
await LongTestDelay();
await LongTestDelay();
Assert.IsNotNull(eventArgs);
Assert.IsNotNull(eventArgs);
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Connected_Event)));
Assert.IsTrue(eventArgs.RemoteEndPoint.ToString().Contains("127.0.0.1"));
Assert.AreEqual(MqttProtocolVersion.V311, eventArgs.ProtocolVersion);
Assert.AreEqual("TheUser", eventArgs.UserName);
Assert.AreEqual("ThePassword", eventArgs.Password);
}
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Connected_Event)));
Assert.IsTrue(eventArgs.RemoteEndPoint.ToString().Contains("127.0.0.1"));
Assert.AreEqual(MqttProtocolVersion.V311, eventArgs.ProtocolVersion);
Assert.AreEqual("TheUser", eventArgs.UserName);
Assert.AreEqual("ThePassword", eventArgs.Password);
}
}
[TestMethod]
public async Task Fire_Client_Disconnected_Event()
{
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
[TestMethod]
public async Task Fire_Client_Disconnected_Event()
ClientDisconnectedEventArgs eventArgs = null;
server.ClientDisconnectedAsync += e =>
{
using (var testEnvironment = CreateTestEnvironment())
{
var server = await testEnvironment.StartServer();
eventArgs = e;
return CompletedTask.Instance;
};
ClientDisconnectedEventArgs eventArgs = null;
server.ClientDisconnectedAsync += e =>
{
eventArgs = e;
return CompletedTask.Instance;
};
var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser", "ThePassword"));
await client.DisconnectAsync();
var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser", "ThePassword"));
await client.DisconnectAsync();
await LongTestDelay();
await LongTestDelay();
Assert.IsNotNull(eventArgs);
Assert.IsNotNull(eventArgs);
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Disconnected_Event)));
Assert.IsTrue(eventArgs.RemoteEndPoint.ToString().Contains("127.0.0.1"));
Assert.AreEqual(MqttClientDisconnectType.Clean, eventArgs.DisconnectType);
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Disconnected_Event)));
Assert.IsTrue(eventArgs.RemoteEndPoint.ToString().Contains("127.0.0.1"));
Assert.AreEqual(MqttClientDisconnectType.Clean, eventArgs.DisconnectType);
Assert.AreEqual("TheUser", eventArgs.UserName);
Assert.AreEqual("ThePassword", eventArgs.Password);
}
Assert.AreEqual("TheUser", eventArgs.UserName);
Assert.AreEqual("ThePassword", eventArgs.Password);
}
}
[TestMethod]
public async Task Fire_Client_Subscribed_Event()
{
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
[TestMethod]
public async Task Fire_Client_Subscribed_Event()
ClientSubscribedTopicEventArgs eventArgs = null;
server.ClientSubscribedTopicAsync += e =>
{
using (var testEnvironment = CreateTestEnvironment())
{
var server = await testEnvironment.StartServer();
eventArgs = e;
return CompletedTask.Instance;
};
ClientSubscribedTopicEventArgs eventArgs = null;
server.ClientSubscribedTopicAsync += e =>
{
eventArgs = e;
return CompletedTask.Instance;
};
var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
await client.SubscribeAsync("The/Topic", MqttQualityOfServiceLevel.AtLeastOnce);
var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
await client.SubscribeAsync("The/Topic", MqttQualityOfServiceLevel.AtLeastOnce);
await LongTestDelay();
await LongTestDelay();
Assert.IsNotNull(eventArgs);
Assert.IsNotNull(eventArgs);
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Subscribed_Event)));
Assert.AreEqual("The/Topic", eventArgs.TopicFilter.Topic);
Assert.AreEqual(MqttQualityOfServiceLevel.AtLeastOnce, eventArgs.TopicFilter.QualityOfServiceLevel);
Assert.AreEqual("TheUser", eventArgs.UserName);
}
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Subscribed_Event)));
Assert.AreEqual("The/Topic", eventArgs.TopicFilter.Topic);
Assert.AreEqual(MqttQualityOfServiceLevel.AtLeastOnce, eventArgs.TopicFilter.QualityOfServiceLevel);
Assert.AreEqual("TheUser", eventArgs.UserName);
}
}
[TestMethod]
public async Task Fire_Client_Unsubscribed_Event()
{
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
[TestMethod]
public async Task Fire_Client_Unsubscribed_Event()
ClientUnsubscribedTopicEventArgs eventArgs = null;
server.ClientUnsubscribedTopicAsync += e =>
{
using (var testEnvironment = CreateTestEnvironment())
{
var server = await testEnvironment.StartServer();
eventArgs = e;
return CompletedTask.Instance;
};
ClientUnsubscribedTopicEventArgs eventArgs = null;
server.ClientUnsubscribedTopicAsync += e =>
{
eventArgs = e;
return CompletedTask.Instance;
};
var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
await client.UnsubscribeAsync("The/Topic");
var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
await client.UnsubscribeAsync("The/Topic");
await LongTestDelay();
await LongTestDelay();
Assert.IsNotNull(eventArgs);
Assert.IsNotNull(eventArgs);
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Unsubscribed_Event)));
Assert.AreEqual("The/Topic", eventArgs.TopicFilter);
Assert.AreEqual("TheUser", eventArgs.UserName);
}
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Unsubscribed_Event)));
Assert.AreEqual("The/Topic", eventArgs.TopicFilter);
Assert.AreEqual("TheUser", eventArgs.UserName);
}
}
[TestMethod]
public async Task Fire_Application_Message_Received_Event()
{
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
[TestMethod]
public async Task Fire_Application_Message_Received_Event()
InterceptingPublishEventArgs eventArgs = null;
server.InterceptingPublishAsync += e =>
{
using (var testEnvironment = CreateTestEnvironment())
{
var server = await testEnvironment.StartServer();
eventArgs = e;
return CompletedTask.Instance;
};
InterceptingPublishEventArgs eventArgs = null;
server.InterceptingPublishAsync += e =>
{
eventArgs = e;
return CompletedTask.Instance;
};
var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
await client.PublishStringAsync("The_Topic", "The_Payload");
var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
await client.PublishStringAsync("The_Topic", "The_Payload");
await LongTestDelay();
await LongTestDelay();
Assert.IsNotNull(eventArgs);
Assert.IsNotNull(eventArgs);
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Application_Message_Received_Event)));
Assert.AreEqual("The_Topic", eventArgs.ApplicationMessage.Topic);
Assert.AreEqual("The_Payload", eventArgs.ApplicationMessage.ConvertPayloadToString());
Assert.AreEqual("TheUser", eventArgs.UserName);
}
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Application_Message_Received_Event)));
Assert.AreEqual("The_Topic", eventArgs.ApplicationMessage.Topic);
Assert.AreEqual("The_Payload", eventArgs.ApplicationMessage.ConvertPayloadToString());
Assert.AreEqual("TheUser", eventArgs.UserName);
}
}
[TestMethod]
public async Task Fire_Started_Event()
{
using var testEnvironment = CreateTestEnvironment();
var server = testEnvironment.CreateServer(new MqttServerOptions());
[TestMethod]
public async Task Fire_Started_Event()
EventArgs eventArgs = null;
server.StartedAsync += e =>
{
using (var testEnvironment = CreateTestEnvironment())
{
var server = testEnvironment.CreateServer(new MqttServerOptions());
eventArgs = e;
return CompletedTask.Instance;
};
EventArgs eventArgs = null;
server.StartedAsync += e =>
{
eventArgs = e;
return CompletedTask.Instance;
};
await server.StartAsync();
await server.StartAsync();
await LongTestDelay();
await LongTestDelay();
Assert.IsNotNull(eventArgs);
}
Assert.IsNotNull(eventArgs);
}
}
[TestMethod]
public async Task Fire_Stopped_Event()
{
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
[TestMethod]
public async Task Fire_Stopped_Event()
EventArgs eventArgs = null;
server.StoppedAsync += e =>
{
using (var testEnvironment = CreateTestEnvironment())
{
var server = await testEnvironment.StartServer();
eventArgs = e;
return CompletedTask.Instance;
};
EventArgs eventArgs = null;
server.StoppedAsync += e =>
{
eventArgs = e;
return CompletedTask.Instance;
};
await server.StopAsync();
await server.StopAsync();
await LongTestDelay();
await LongTestDelay();
Assert.IsNotNull(eventArgs);
}
}
Assert.IsNotNull(eventArgs);
}
}
File diff suppressed because it is too large Load Diff
+339 -352
View File
@@ -15,403 +15,390 @@ using MQTTnet.Formatter;
using MQTTnet.Protocol;
using MQTTnet.Server;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// missing certificate builder api means tests won't work for older frameworks
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class HotSwapCerts_Tests
{
// missing certificate builder api means tests won't work for older frameworks
static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(10);
[TestClass]
public sealed class HotSwapCerts_Tests
[TestMethod]
public async Task ClientCertChangeWithoutServerUpdateFailsReconnect()
{
static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(10);
using var server = new ServerTestHarness();
using var client01 = new ClientTestHarness();
server.InstallNewClientCert(client01.GetCurrentClientCert());
client01.InstallNewServerCert(server.GetCurrentServerCert());
[TestMethod]
public async Task ClientCertChangeWithoutServerUpdateFailsReconnect()
await server.StartServer();
await client01.Connect();
client01.WaitForConnectOrFail(DefaultTimeout);
client01.HotSwapClientCert();
server.ForceDisconnectAsync(client01).Wait(DefaultTimeout);
client01.WaitForDisconnectOrFail(DefaultTimeout);
client01.WaitForConnectToFail(DefaultTimeout);
}
[TestMethod]
public async Task ClientCertChangeWithServerUpdateAcceptsReconnect()
{
using var server = new ServerTestHarness();
using var client01 = new ClientTestHarness();
server.InstallNewClientCert(client01.GetCurrentClientCert());
client01.InstallNewServerCert(server.GetCurrentServerCert());
await server.StartServer();
await client01.Connect();
client01.WaitForConnectOrFail(DefaultTimeout);
client01.HotSwapClientCert();
server.ForceDisconnectAsync(client01).Wait(DefaultTimeout);
client01.WaitForDisconnectOrFail(DefaultTimeout);
server.InstallNewClientCert(client01.GetCurrentClientCert());
client01.WaitForConnectOrFail(DefaultTimeout);
}
[TestMethod]
public async Task ServerCertChangeWithClientCertUpdateAllowsReconnect()
{
using var server = new ServerTestHarness();
using var client01 = new ClientTestHarness();
server.InstallNewClientCert(client01.GetCurrentClientCert());
client01.InstallNewServerCert(server.GetCurrentServerCert());
await server.StartServer();
await client01.Connect();
client01.WaitForConnectOrFail(DefaultTimeout);
server.HotSwapServerCert();
server.ForceDisconnectAsync(client01).Wait(DefaultTimeout);
client01.WaitForDisconnectOrFail(DefaultTimeout);
client01.InstallNewServerCert(server.GetCurrentServerCert());
client01.WaitForConnectOrFail(DefaultTimeout);
}
[TestMethod]
public async Task ServerCertChangeWithoutClientCertUpdateFailsReconnect()
{
using var server = new ServerTestHarness();
using var client01 = new ClientTestHarness();
server.InstallNewClientCert(client01.GetCurrentClientCert());
client01.InstallNewServerCert(server.GetCurrentServerCert());
await server.StartServer();
await client01.Connect();
client01.WaitForConnectOrFail(DefaultTimeout);
server.HotSwapServerCert();
server.ForceDisconnectAsync(client01).Wait(DefaultTimeout);
client01.WaitForDisconnectOrFail(DefaultTimeout);
client01.WaitForConnectToFail(DefaultTimeout);
}
static X509Certificate2 CreateSelfSignedCertificate(string oid)
{
var sanBuilder = new SubjectAlternativeNameBuilder();
sanBuilder.AddIpAddress(IPAddress.Loopback);
sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);
sanBuilder.AddDnsName("localhost");
using var rsa = RSA.Create();
var certRequest = new CertificateRequest("CN=localhost", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
certRequest.CertificateExtensions.Add(
new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
certRequest.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid(oid) }, false));
certRequest.CertificateExtensions.Add(sanBuilder.Build());
using var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now.AddMinutes(-10), DateTimeOffset.Now.AddMinutes(10));
var pfxCertificate = new X509Certificate2(
certificate.Export(X509ContentType.Pfx),
(string)null,
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
return pfxCertificate;
}
sealed class ClientTestHarness : IDisposable
{
readonly HotSwappableClientCertProvider _hotSwapClient = new HotSwappableClientCertProvider();
IMqttClient _client;
public string ClientId => _client.Options.ClientId;
public Task Connect()
{
using (var server = new ServerTestHarness())
using (var client01 = new ClientTestHarness())
{
server.InstallNewClientCert(client01.GetCurrentClientCert());
client01.InstallNewServerCert(server.GetCurrentServerCert());
await server.StartServer();
await client01.Connect();
client01.WaitForConnectOrFail(DefaultTimeout);
client01.HotSwapClientCert();
server.ForceDisconnectAsync(client01).Wait(DefaultTimeout);
client01.WaitForDisconnectOrFail(DefaultTimeout);
client01.WaitForConnectToFail(DefaultTimeout);
}
return Run_Client_Connection();
}
[TestMethod]
public async Task ClientCertChangeWithServerUpdateAcceptsReconnect()
public void Dispose()
{
using (var server = new ServerTestHarness())
using (var client01 = new ClientTestHarness())
{
server.InstallNewClientCert(client01.GetCurrentClientCert());
client01.InstallNewServerCert(server.GetCurrentServerCert());
await server.StartServer();
await client01.Connect();
client01.WaitForConnectOrFail(DefaultTimeout);
client01.HotSwapClientCert();
server.ForceDisconnectAsync(client01).Wait(DefaultTimeout);
client01.WaitForDisconnectOrFail(DefaultTimeout);
server.InstallNewClientCert(client01.GetCurrentClientCert());
client01.WaitForConnectOrFail(DefaultTimeout);
}
_client.Dispose();
_hotSwapClient.Dispose();
}
[TestMethod]
public async Task ServerCertChangeWithClientCertUpdateAllowsReconnect()
public X509Certificate2 GetCurrentClientCert()
{
using (var server = new ServerTestHarness())
using (var client01 = new ClientTestHarness())
{
server.InstallNewClientCert(client01.GetCurrentClientCert());
client01.InstallNewServerCert(server.GetCurrentServerCert());
await server.StartServer();
await client01.Connect();
client01.WaitForConnectOrFail(DefaultTimeout);
server.HotSwapServerCert();
server.ForceDisconnectAsync(client01).Wait(DefaultTimeout);
client01.WaitForDisconnectOrFail(DefaultTimeout);
client01.InstallNewServerCert(server.GetCurrentServerCert());
client01.WaitForConnectOrFail(DefaultTimeout);
}
var result = _hotSwapClient.GetCertificates()[0];
return new X509Certificate2(result);
}
[TestMethod]
public async Task ServerCertChangeWithoutClientCertUpdateFailsReconnect()
public void HotSwapClientCert()
{
using (var server = new ServerTestHarness())
using (var client01 = new ClientTestHarness())
{
server.InstallNewClientCert(client01.GetCurrentClientCert());
client01.InstallNewServerCert(server.GetCurrentServerCert());
await server.StartServer();
await client01.Connect();
client01.WaitForConnectOrFail(DefaultTimeout);
server.HotSwapServerCert();
server.ForceDisconnectAsync(client01).Wait(DefaultTimeout);
client01.WaitForDisconnectOrFail(DefaultTimeout);
client01.WaitForConnectToFail(DefaultTimeout);
}
_hotSwapClient.HotSwapCert();
}
static X509Certificate2 CreateSelfSignedCertificate(string oid)
public void InstallNewServerCert(X509Certificate2 serverCert)
{
var sanBuilder = new SubjectAlternativeNameBuilder();
sanBuilder.AddIpAddress(IPAddress.Loopback);
sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);
sanBuilder.AddDnsName("localhost");
using (var rsa = RSA.Create())
{
var certRequest = new CertificateRequest("CN=localhost", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
certRequest.CertificateExtensions.Add(
new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
certRequest.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid(oid) }, false));
certRequest.CertificateExtensions.Add(sanBuilder.Build());
using (var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now.AddMinutes(-10), DateTimeOffset.Now.AddMinutes(10)))
{
var pfxCertificate = new X509Certificate2(
certificate.Export(X509ContentType.Pfx),
(string)null,
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
return pfxCertificate;
}
}
_hotSwapClient.InstallNewServerCert(serverCert);
}
sealed class ClientTestHarness : IDisposable
public void WaitForConnectOrFail(TimeSpan timeout)
{
readonly HotSwappableClientCertProvider _hotSwapClient = new HotSwappableClientCertProvider();
Thread.Sleep(100);
IMqttClient _client;
public string ClientId => _client.Options.ClientId;
public Task Connect()
if (!_client.IsConnected)
{
return Run_Client_Connection();
_client.ReconnectAsync().Wait(timeout);
}
public void Dispose()
{
_client.Dispose();
_hotSwapClient.Dispose();
}
WaitForConnect(timeout);
public X509Certificate2 GetCurrentClientCert()
{
var result = _hotSwapClient.GetCertificates()[0];
return new X509Certificate2(result);
}
Assert.IsNotNull(_client, "Client was never initialized");
Assert.IsTrue(_client.IsConnected, $"Client connection failed after {timeout}");
}
public void HotSwapClientCert()
{
_hotSwapClient.HotSwapCert();
}
public void WaitForConnectToFail(TimeSpan timeout)
{
Assert.IsFalse(_client.IsConnected, "Client should be disconnected before waiting for connect.");
public void InstallNewServerCert(X509Certificate2 serverCert)
{
_hotSwapClient.InstallNewServerCert(serverCert);
}
WaitForConnect(timeout);
public void WaitForConnectOrFail(TimeSpan timeout)
Assert.IsNotNull(_client, "Client was never initialized");
Assert.IsFalse(_client.IsConnected, "Client connection success but test wanted fail");
}
void WaitForDisconnect(TimeSpan timeout)
{
var timer = Stopwatch.StartNew();
while ((_client == null || _client.IsConnected) && timer.Elapsed < timeout)
{
Thread.Sleep(100);
if (!_client.IsConnected)
{
_client.ReconnectAsync().Wait(timeout);
}
WaitForConnect(timeout);
Assert.IsNotNull(_client, "Client was never initialized");
Assert.IsTrue(_client.IsConnected, $"Client connection failed after {timeout}");
}
public void WaitForConnectToFail(TimeSpan timeout)
{
Assert.IsFalse(_client.IsConnected, "Client should be disconnected before waiting for connect.");
WaitForConnect(timeout);
Assert.IsNotNull(_client, "Client was never initialized");
Assert.IsFalse(_client.IsConnected, "Client connection success but test wanted fail");
}
void WaitForDisconnect(TimeSpan timeout)
{
var timer = Stopwatch.StartNew();
while ((_client == null || _client.IsConnected) && timer.Elapsed < timeout)
{
Thread.Sleep(100);
}
}
public void WaitForDisconnectOrFail(TimeSpan timeout)
{
WaitForDisconnect(timeout);
Assert.IsNotNull(_client, "Client was never initialized");
Assert.IsFalse(_client.IsConnected, $"Client connection should have disconnected after {timeout}");
}
async Task Run_Client_Connection()
{
var optionsBuilder = new MqttClientOptionsBuilder()
.WithTlsOptions(
o => o.WithClientCertificatesProvider(_hotSwapClient)
.WithCertificateValidationHandler(_hotSwapClient.OnCertificateValidation)
.WithSslProtocols(SslProtocols.Tls12))
.WithTcpServer("localhost")
.WithCleanSession()
.WithProtocolVersion(MqttProtocolVersion.V500);
var mqttClientOptions = optionsBuilder.Build();
var factory = new MqttClientFactory();
var mqttClient = factory.CreateMqttClient();
_client = mqttClient;
await mqttClient.ConnectAsync(mqttClientOptions);
}
void WaitForConnect(TimeSpan timeout)
{
var timer = Stopwatch.StartNew();
while ((_client == null || !_client.IsConnected) && timer.Elapsed < timeout)
{
Thread.Sleep(100);
}
}
}
sealed class ServerTestHarness : IDisposable
public void WaitForDisconnectOrFail(TimeSpan timeout)
{
readonly HotSwappableServerCertProvider _hotSwapServer = new HotSwappableServerCertProvider();
WaitForDisconnect(timeout);
MqttServer _server;
public void Dispose()
{
if (_server != null)
{
_server.StopAsync().Wait();
_server.Dispose();
}
_hotSwapServer?.Dispose();
}
public Task ForceDisconnectAsync(ClientTestHarness client)
{
return _server.DisconnectClientAsync(client.ClientId, MqttDisconnectReasonCode.UnspecifiedError);
}
public X509Certificate2 GetCurrentServerCert()
{
return _hotSwapServer.GetCertificate();
}
public void HotSwapServerCert()
{
_hotSwapServer.HotSwapCert();
}
public void InstallNewClientCert(X509Certificate2 serverCert)
{
_hotSwapServer.InstallNewClientCert(serverCert);
}
public Task StartServer()
{
var mqttServerFactory = new MqttServerFactory();
var mqttServerOptions = new MqttServerOptionsBuilder().WithEncryptionCertificate(_hotSwapServer)
.WithRemoteCertificateValidationCallback(_hotSwapServer.RemoteCertificateValidationCallback)
.WithEncryptedEndpoint()
.Build();
mqttServerOptions.TlsEndpointOptions.ClientCertificateRequired = true;
_server = mqttServerFactory.CreateMqttServer(mqttServerOptions);
return _server.StartAsync();
}
Assert.IsNotNull(_client, "Client was never initialized");
Assert.IsFalse(_client.IsConnected, $"Client connection should have disconnected after {timeout}");
}
class HotSwappableClientCertProvider : IMqttClientCertificatesProvider, IDisposable
async Task Run_Client_Connection()
{
X509Certificate2Collection _certificates;
ConcurrentBag<X509Certificate2> _serverCerts = new ConcurrentBag<X509Certificate2>();
var optionsBuilder = new MqttClientOptionsBuilder()
.WithTlsOptions(
o => o.WithClientCertificatesProvider(_hotSwapClient)
.WithCertificateValidationHandler(_hotSwapClient.OnCertificateValidation)
.WithSslProtocols(SslProtocols.Tls12))
.WithTcpServer("localhost")
.WithCleanSession()
.WithProtocolVersion(MqttProtocolVersion.V500);
public HotSwappableClientCertProvider()
{
_certificates = new X509Certificate2Collection(CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.2"));
}
var mqttClientOptions = optionsBuilder.Build();
public void Dispose()
{
if (_certificates != null)
{
foreach (var certs in _certificates)
{
certs.Dispose();
}
}
}
var factory = new MqttClientFactory();
var mqttClient = factory.CreateMqttClient();
_client = mqttClient;
public X509CertificateCollection GetCertificates()
{
return new X509Certificate2Collection(_certificates);
}
public void HotSwapCert()
{
_certificates = new X509Certificate2Collection(CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.2"));
}
public void InstallNewServerCert(X509Certificate2 serverCert)
{
_serverCerts.Add(serverCert);
}
public bool OnCertificateValidation(MqttClientCertificateValidationEventArgs certContext)
{
var serverCerts = _serverCerts.ToArray();
var providedCert = certContext.Certificate.GetRawCertData();
for (int i = 0, n = serverCerts.Length; i < n; i++)
{
var currentcert = serverCerts[i];
if (currentcert.RawData.SequenceEqual(providedCert))
{
return true;
}
}
return false;
}
await mqttClient.ConnectAsync(mqttClientOptions);
}
sealed class HotSwappableServerCertProvider : ICertificateProvider, IDisposable
void WaitForConnect(TimeSpan timeout)
{
readonly ConcurrentBag<X509Certificate2> _clientCerts = new ConcurrentBag<X509Certificate2>();
X509Certificate2 _certificate;
public HotSwappableServerCertProvider()
var timer = Stopwatch.StartNew();
while ((_client == null || !_client.IsConnected) && timer.Elapsed < timeout)
{
_certificate = CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.1");
}
public void Dispose()
{
_certificate.Dispose();
}
public X509Certificate2 GetCertificate()
{
return _certificate;
}
public void HotSwapCert()
{
var newCert = CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.1");
var oldCert = Interlocked.Exchange(ref _certificate, newCert);
oldCert.Dispose();
}
public void InstallNewClientCert(X509Certificate2 certificate)
{
_clientCerts.Add(certificate);
}
public bool RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
var serverCerts = _clientCerts.ToArray();
var providedCert = certificate.GetRawCertData();
for (int i = 0, n = serverCerts.Length; i < n; i++)
{
var currentCert = serverCerts[i];
if (currentCert.RawData.SequenceEqual(providedCert))
{
return true;
}
}
return false;
Thread.Sleep(100);
}
}
}
}
sealed class ServerTestHarness : IDisposable
{
readonly HotSwappableServerCertProvider _hotSwapServer = new HotSwappableServerCertProvider();
MqttServer _server;
public void Dispose()
{
if (_server != null)
{
_server.StopAsync().Wait();
_server.Dispose();
}
_hotSwapServer?.Dispose();
}
public Task ForceDisconnectAsync(ClientTestHarness client)
{
return _server.DisconnectClientAsync(client.ClientId, MqttDisconnectReasonCode.UnspecifiedError);
}
public X509Certificate2 GetCurrentServerCert()
{
return _hotSwapServer.GetCertificate();
}
public void HotSwapServerCert()
{
_hotSwapServer.HotSwapCert();
}
public void InstallNewClientCert(X509Certificate2 serverCert)
{
_hotSwapServer.InstallNewClientCert(serverCert);
}
public Task StartServer()
{
var mqttServerFactory = new MqttServerFactory();
var mqttServerOptions = new MqttServerOptionsBuilder().WithEncryptionCertificate(_hotSwapServer)
.WithRemoteCertificateValidationCallback(_hotSwapServer.RemoteCertificateValidationCallback)
.WithEncryptedEndpoint()
.Build();
mqttServerOptions.TlsEndpointOptions.ClientCertificateRequired = true;
_server = mqttServerFactory.CreateMqttServer(mqttServerOptions);
return _server.StartAsync();
}
}
class HotSwappableClientCertProvider : IMqttClientCertificatesProvider, IDisposable
{
X509Certificate2Collection _certificates;
ConcurrentBag<X509Certificate2> _serverCerts = new ConcurrentBag<X509Certificate2>();
public HotSwappableClientCertProvider()
{
_certificates = new X509Certificate2Collection(CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.2"));
}
public void Dispose()
{
if (_certificates != null)
{
foreach (var certs in _certificates)
{
certs.Dispose();
}
}
}
public X509CertificateCollection GetCertificates()
{
return new X509Certificate2Collection(_certificates);
}
public void HotSwapCert()
{
_certificates = new X509Certificate2Collection(CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.2"));
}
public void InstallNewServerCert(X509Certificate2 serverCert)
{
_serverCerts.Add(serverCert);
}
public bool OnCertificateValidation(MqttClientCertificateValidationEventArgs certContext)
{
var serverCerts = _serverCerts.ToArray();
var providedCert = certContext.Certificate.GetRawCertData();
for (int i = 0, n = serverCerts.Length; i < n; i++)
{
var currentcert = serverCerts[i];
if (currentcert.RawData.SequenceEqual(providedCert))
{
return true;
}
}
return false;
}
}
sealed class HotSwappableServerCertProvider : ICertificateProvider, IDisposable
{
readonly ConcurrentBag<X509Certificate2> _clientCerts = new ConcurrentBag<X509Certificate2>();
X509Certificate2 _certificate;
public HotSwappableServerCertProvider()
{
_certificate = CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.1");
}
public void Dispose()
{
_certificate.Dispose();
}
public X509Certificate2 GetCertificate()
{
return _certificate;
}
public void HotSwapCert()
{
var newCert = CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.1");
var oldCert = Interlocked.Exchange(ref _certificate, newCert);
oldCert.Dispose();
}
public void InstallNewClientCert(X509Certificate2 certificate)
{
_clientCerts.Add(certificate);
}
public bool RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
var serverCerts = _clientCerts.ToArray();
var providedCert = certificate.GetRawCertData();
for (int i = 0, n = serverCerts.Length; i < n; i++)
{
var currentCert = serverCerts[i];
if (currentCert.RawData.SequenceEqual(providedCert))
{
return true;
}
}
return false;
}
}
}
+395 -395
View File
@@ -7,449 +7,449 @@ using MQTTnet.Protocol;
using MQTTnet.Server;
using MQTTnet.Server.Exceptions;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class Injection_Tests : BaseTestClass
{
[TestClass]
public sealed class Injection_Tests : BaseTestClass
[TestMethod]
public async Task Enqueue_Application_Message_At_Session_Level()
{
[TestMethod]
public async Task Enqueue_Application_Message_At_Session_Level()
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
var receiver1 = await testEnvironment.ConnectClient();
var receiver2 = await testEnvironment.ConnectClient();
var messageReceivedHandler1 = testEnvironment.CreateApplicationMessageHandler(receiver1);
var messageReceivedHandler2 = testEnvironment.CreateApplicationMessageHandler(receiver2);
var status = await server.GetSessionsAsync();
var clientStatus = status[0];
await receiver1.SubscribeAsync("#");
await receiver2.SubscribeAsync("#");
var message = new MqttApplicationMessageBuilder()
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
.WithTopic("InjectedOne").Build();
var enqueued = clientStatus.TryEnqueueApplicationMessage(message, out var injectResult);
Assert.IsTrue(enqueued);
await LongTestDelay();
Assert.AreEqual(1, messageReceivedHandler1.ReceivedEventArgs.Count);
Assert.AreEqual(injectResult.PacketIdentifier, messageReceivedHandler1.ReceivedEventArgs[0].PacketIdentifier);
Assert.AreEqual("InjectedOne", messageReceivedHandler1.ReceivedEventArgs[0].ApplicationMessage.Topic);
// The second receiver should NOT receive the message.
Assert.AreEqual(0, messageReceivedHandler2.ReceivedEventArgs.Count);
}
[TestMethod]
public async Task Enqueue_Application_Message_At_Session_Level_QueueOverflow_DropNewMessageStrategy()
{
using var testEnvironment = CreateTestEnvironment(trackUnobservedTaskException: false);
var server = await testEnvironment.StartServer(
builder => builder
.WithMaxPendingMessagesPerClient(1)
.WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy.DropNewMessage));
var receiver = await testEnvironment.ConnectClient();
var firstMessageOutboundPacketInterceptedTcs = new TaskCompletionSource();
server.InterceptingOutboundPacketAsync += async args =>
{
using var testEnvironment = CreateTestEnvironment();
// - The first message is dequeued normally and calls this delay
// - The second message fills the outbound queue
// - The third message overflows the outbound queue
if (args.Packet is MqttPublishPacket)
{
firstMessageOutboundPacketInterceptedTcs.SetResult();
await Task.Delay(TimeSpan.FromDays(1), args.CancellationToken);
}
};
var server = await testEnvironment.StartServer();
var receiver1 = await testEnvironment.ConnectClient();
var receiver2 = await testEnvironment.ConnectClient();
var messageReceivedHandler1 = testEnvironment.CreateApplicationMessageHandler(receiver1);
var messageReceivedHandler2 = testEnvironment.CreateApplicationMessageHandler(receiver2);
var firstMessageEvicted = false;
var secondMessageEvicted = false;
var thirdMessageEvicted = false;
var status = await server.GetSessionsAsync();
var clientStatus = status[0];
await receiver1.SubscribeAsync("#");
await receiver2.SubscribeAsync("#");
var message = new MqttApplicationMessageBuilder()
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
.WithTopic("InjectedOne").Build();
var enqueued = clientStatus.TryEnqueueApplicationMessage(message, out var injectResult);
Assert.IsTrue(enqueued);
await LongTestDelay();
Assert.AreEqual(1, messageReceivedHandler1.ReceivedEventArgs.Count);
Assert.AreEqual(injectResult.PacketIdentifier, messageReceivedHandler1.ReceivedEventArgs[0].PacketIdentifier);
Assert.AreEqual("InjectedOne", messageReceivedHandler1.ReceivedEventArgs[0].ApplicationMessage.Topic);
// The second receiver should NOT receive the message.
Assert.AreEqual(0, messageReceivedHandler2.ReceivedEventArgs.Count);
}
[TestMethod]
public async Task Enqueue_Application_Message_At_Session_Level_QueueOverflow_DropNewMessageStrategy()
server.QueuedApplicationMessageOverwrittenAsync += args =>
{
using var testEnvironment = CreateTestEnvironment(trackUnobservedTaskException: false);
var server = await testEnvironment.StartServer(
builder => builder
.WithMaxPendingMessagesPerClient(1)
.WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy.DropNewMessage));
var receiver = await testEnvironment.ConnectClient();
var firstMessageOutboundPacketInterceptedTcs = new TaskCompletionSource();
server.InterceptingOutboundPacketAsync += async args =>
if (args.Packet is not MqttPublishPacket publishPacket)
{
// - The first message is dequeued normally and calls this delay
// - The second message fills the outbound queue
// - The third message overflows the outbound queue
if (args.Packet is MqttPublishPacket)
{
firstMessageOutboundPacketInterceptedTcs.SetResult();
await Task.Delay(TimeSpan.FromDays(1), args.CancellationToken);
}
};
var firstMessageEvicted = false;
var secondMessageEvicted = false;
var thirdMessageEvicted = false;
server.QueuedApplicationMessageOverwrittenAsync += args =>
{
if (args.Packet is not MqttPublishPacket publishPacket)
{
return Task.CompletedTask;
}
switch (publishPacket.Topic)
{
case "InjectedOne":
firstMessageEvicted = true;
break;
case "InjectedTwo":
secondMessageEvicted = true;
break;
case "InjectedThree":
thirdMessageEvicted = true;
break;
}
return Task.CompletedTask;
};
}
var status = await server.GetSessionsAsync();
var clientStatus = status[0];
await receiver.SubscribeAsync("#");
switch (publishPacket.Topic)
{
case "InjectedOne":
firstMessageEvicted = true;
break;
case "InjectedTwo":
secondMessageEvicted = true;
break;
case "InjectedThree":
thirdMessageEvicted = true;
break;
}
var firstMessageEnqueued = clientStatus.TryEnqueueApplicationMessage(
new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build(), out _);
await firstMessageOutboundPacketInterceptedTcs.Task;
return Task.CompletedTask;
};
var secondMessageEnqueued = clientStatus.TryEnqueueApplicationMessage(
new MqttApplicationMessageBuilder().WithTopic("InjectedTwo").Build(), out _);
var status = await server.GetSessionsAsync();
var clientStatus = status[0];
await receiver.SubscribeAsync("#");
var thirdMessageEnqueued = clientStatus.TryEnqueueApplicationMessage(
new MqttApplicationMessageBuilder().WithTopic("InjectedThree").Build(), out _);
var firstMessageEnqueued = clientStatus.TryEnqueueApplicationMessage(
new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build(), out _);
await firstMessageOutboundPacketInterceptedTcs.Task;
// Due to the DropNewMessage strategy the third message will not be enqueued.
// As a result, no existing messages in the queue will be dropped (evicted).
Assert.IsTrue(firstMessageEnqueued);
Assert.IsTrue(secondMessageEnqueued);
Assert.IsFalse(thirdMessageEnqueued);
var secondMessageEnqueued = clientStatus.TryEnqueueApplicationMessage(
new MqttApplicationMessageBuilder().WithTopic("InjectedTwo").Build(), out _);
Assert.IsFalse(firstMessageEvicted);
Assert.IsFalse(secondMessageEvicted);
Assert.IsFalse(thirdMessageEvicted);
}
var thirdMessageEnqueued = clientStatus.TryEnqueueApplicationMessage(
new MqttApplicationMessageBuilder().WithTopic("InjectedThree").Build(), out _);
// Due to the DropNewMessage strategy the third message will not be enqueued.
// As a result, no existing messages in the queue will be dropped (evicted).
Assert.IsTrue(firstMessageEnqueued);
Assert.IsTrue(secondMessageEnqueued);
Assert.IsFalse(thirdMessageEnqueued);
Assert.IsFalse(firstMessageEvicted);
Assert.IsFalse(secondMessageEvicted);
Assert.IsFalse(thirdMessageEvicted);
}
[TestMethod]
public async Task Enqueue_Application_Message_At_Session_Level_QueueOverflow_DropOldestQueuedMessageStrategy()
[TestMethod]
public async Task Enqueue_Application_Message_At_Session_Level_QueueOverflow_DropOldestQueuedMessageStrategy()
{
using var testEnvironment = CreateTestEnvironment(trackUnobservedTaskException: false);
var server = await testEnvironment.StartServer(
builder => builder
.WithMaxPendingMessagesPerClient(1)
.WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage));
var receiver = await testEnvironment.ConnectClient();
var firstMessageOutboundPacketInterceptedTcs = new TaskCompletionSource();
server.InterceptingOutboundPacketAsync += async args =>
{
using var testEnvironment = CreateTestEnvironment(trackUnobservedTaskException: false);
var server = await testEnvironment.StartServer(
builder => builder
.WithMaxPendingMessagesPerClient(1)
.WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage));
var receiver = await testEnvironment.ConnectClient();
var firstMessageOutboundPacketInterceptedTcs = new TaskCompletionSource();
server.InterceptingOutboundPacketAsync += async args =>
// - The first message is dequeued normally and calls this delay
// - The second message fills the outbound queue
// - The third message overflows the outbound queue
if (args.Packet is MqttPublishPacket)
{
// - The first message is dequeued normally and calls this delay
// - The second message fills the outbound queue
// - The third message overflows the outbound queue
if (args.Packet is MqttPublishPacket)
{
firstMessageOutboundPacketInterceptedTcs.SetResult();
await Task.Delay(TimeSpan.FromDays(1), args.CancellationToken);
}
};
firstMessageOutboundPacketInterceptedTcs.SetResult();
await Task.Delay(TimeSpan.FromDays(1), args.CancellationToken);
}
};
var firstMessageEvicted = false;
var secondMessageEvicted = false;
var thirdMessageEvicted = false;
var firstMessageEvicted = false;
var secondMessageEvicted = false;
var thirdMessageEvicted = false;
server.QueuedApplicationMessageOverwrittenAsync += args =>
server.QueuedApplicationMessageOverwrittenAsync += args =>
{
if (args.Packet is not MqttPublishPacket publishPacket)
{
if (args.Packet is not MqttPublishPacket publishPacket)
{
return Task.CompletedTask;
}
switch (publishPacket.Topic)
{
case "InjectedOne":
firstMessageEvicted = true;
break;
case "InjectedTwo":
secondMessageEvicted = true;
break;
case "InjectedThree":
thirdMessageEvicted = true;
break;
}
return Task.CompletedTask;
};
}
var status = await server.GetSessionsAsync();
var clientStatus = status[0];
await receiver.SubscribeAsync("#");
var firstMessageEnqueued = clientStatus.TryEnqueueApplicationMessage(
new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build(), out _);
await firstMessageOutboundPacketInterceptedTcs.Task;
var secondMessageEnqueued = clientStatus.TryEnqueueApplicationMessage(
new MqttApplicationMessageBuilder().WithTopic("InjectedTwo").Build(), out _);
var thirdMessageEnqueued = clientStatus.TryEnqueueApplicationMessage(
new MqttApplicationMessageBuilder().WithTopic("InjectedThree").Build(), out _);
// Due to the DropOldestQueuedMessage strategy, all messages will be enqueued initially.
// But the second message will eventually be dropped (evicted) to make room for the third one.
Assert.IsTrue(firstMessageEnqueued);
Assert.IsTrue(secondMessageEnqueued);
Assert.IsTrue(thirdMessageEnqueued);
Assert.IsFalse(firstMessageEvicted);
Assert.IsTrue(secondMessageEvicted);
Assert.IsFalse(thirdMessageEvicted);
}
[TestMethod]
public async Task Deliver_Application_Message_At_Session_Level()
{
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
var receiver1 = await testEnvironment.ConnectClient();
var receiver2 = await testEnvironment.ConnectClient();
var messageReceivedHandler1 = testEnvironment.CreateApplicationMessageHandler(receiver1);
var messageReceivedHandler2 = testEnvironment.CreateApplicationMessageHandler(receiver2);
var status = await server.GetSessionsAsync();
var clientStatus = status[0];
await receiver1.SubscribeAsync("#");
await receiver2.SubscribeAsync("#");
var mqttApplicationMessage = new MqttApplicationMessageBuilder()
.WithTopic("InjectedOne")
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
.Build();
var injectResult = await clientStatus.DeliverApplicationMessageAsync(mqttApplicationMessage);
await LongTestDelay();
Assert.AreEqual(1, messageReceivedHandler1.ReceivedEventArgs.Count);
Assert.AreEqual(injectResult.PacketIdentifier, messageReceivedHandler1.ReceivedEventArgs[0].PacketIdentifier);
Assert.AreEqual("InjectedOne", messageReceivedHandler1.ReceivedEventArgs[0].ApplicationMessage.Topic);
// The second receiver should NOT receive the message.
Assert.AreEqual(0, messageReceivedHandler2.ReceivedEventArgs.Count);
}
[TestMethod]
public async Task Deliver_Application_Message_At_Session_Level_QueueOverflow_DropNewMessageStrategy()
{
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer(
builder => builder
.WithMaxPendingMessagesPerClient(1)
.WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy.DropNewMessage));
var receiver = await testEnvironment.ConnectClient();
var firstMessageOutboundPacketInterceptedTcs = new TaskCompletionSource();
server.InterceptingOutboundPacketAsync += async args =>
switch (publishPacket.Topic)
{
// - The first message is dequeued normally and calls this delay
// - The second message fills the outbound queue
// - The third message overflows the outbound queue
if (args.Packet is MqttPublishPacket)
{
firstMessageOutboundPacketInterceptedTcs.SetResult();
await Task.Delay(TimeSpan.FromDays(1), args.CancellationToken);
}
};
case "InjectedOne":
firstMessageEvicted = true;
break;
case "InjectedTwo":
secondMessageEvicted = true;
break;
case "InjectedThree":
thirdMessageEvicted = true;
break;
}
var firstMessageEvicted = false;
var secondMessageEvicted = false;
var thirdMessageEvicted = false;
return Task.CompletedTask;
};
server.QueuedApplicationMessageOverwrittenAsync += args =>
var status = await server.GetSessionsAsync();
var clientStatus = status[0];
await receiver.SubscribeAsync("#");
var firstMessageEnqueued = clientStatus.TryEnqueueApplicationMessage(
new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build(), out _);
await firstMessageOutboundPacketInterceptedTcs.Task;
var secondMessageEnqueued = clientStatus.TryEnqueueApplicationMessage(
new MqttApplicationMessageBuilder().WithTopic("InjectedTwo").Build(), out _);
var thirdMessageEnqueued = clientStatus.TryEnqueueApplicationMessage(
new MqttApplicationMessageBuilder().WithTopic("InjectedThree").Build(), out _);
// Due to the DropOldestQueuedMessage strategy, all messages will be enqueued initially.
// But the second message will eventually be dropped (evicted) to make room for the third one.
Assert.IsTrue(firstMessageEnqueued);
Assert.IsTrue(secondMessageEnqueued);
Assert.IsTrue(thirdMessageEnqueued);
Assert.IsFalse(firstMessageEvicted);
Assert.IsTrue(secondMessageEvicted);
Assert.IsFalse(thirdMessageEvicted);
}
[TestMethod]
public async Task Deliver_Application_Message_At_Session_Level()
{
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
var receiver1 = await testEnvironment.ConnectClient();
var receiver2 = await testEnvironment.ConnectClient();
var messageReceivedHandler1 = testEnvironment.CreateApplicationMessageHandler(receiver1);
var messageReceivedHandler2 = testEnvironment.CreateApplicationMessageHandler(receiver2);
var status = await server.GetSessionsAsync();
var clientStatus = status[0];
await receiver1.SubscribeAsync("#");
await receiver2.SubscribeAsync("#");
var mqttApplicationMessage = new MqttApplicationMessageBuilder()
.WithTopic("InjectedOne")
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
.Build();
var injectResult = await clientStatus.DeliverApplicationMessageAsync(mqttApplicationMessage);
await LongTestDelay();
Assert.AreEqual(1, messageReceivedHandler1.ReceivedEventArgs.Count);
Assert.AreEqual(injectResult.PacketIdentifier, messageReceivedHandler1.ReceivedEventArgs[0].PacketIdentifier);
Assert.AreEqual("InjectedOne", messageReceivedHandler1.ReceivedEventArgs[0].ApplicationMessage.Topic);
// The second receiver should NOT receive the message.
Assert.AreEqual(0, messageReceivedHandler2.ReceivedEventArgs.Count);
}
[TestMethod]
public async Task Deliver_Application_Message_At_Session_Level_QueueOverflow_DropNewMessageStrategy()
{
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer(
builder => builder
.WithMaxPendingMessagesPerClient(1)
.WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy.DropNewMessage));
var receiver = await testEnvironment.ConnectClient();
var firstMessageOutboundPacketInterceptedTcs = new TaskCompletionSource();
server.InterceptingOutboundPacketAsync += async args =>
{
// - The first message is dequeued normally and calls this delay
// - The second message fills the outbound queue
// - The third message overflows the outbound queue
if (args.Packet is MqttPublishPacket)
{
if (args.Packet is not MqttPublishPacket publishPacket)
{
return Task.CompletedTask;
}
firstMessageOutboundPacketInterceptedTcs.SetResult();
await Task.Delay(TimeSpan.FromDays(1), args.CancellationToken);
}
};
switch (publishPacket.Topic)
{
case "InjectedOne":
firstMessageEvicted = true;
break;
case "InjectedTwo":
secondMessageEvicted = true;
break;
case "InjectedThree":
thirdMessageEvicted = true;
break;
}
var firstMessageEvicted = false;
var secondMessageEvicted = false;
var thirdMessageEvicted = false;
server.QueuedApplicationMessageOverwrittenAsync += args =>
{
if (args.Packet is not MqttPublishPacket publishPacket)
{
return Task.CompletedTask;
};
}
var status = await server.GetSessionsAsync();
var clientStatus = status[0];
await receiver.SubscribeAsync("#");
switch (publishPacket.Topic)
{
case "InjectedOne":
firstMessageEvicted = true;
break;
case "InjectedTwo":
secondMessageEvicted = true;
break;
case "InjectedThree":
thirdMessageEvicted = true;
break;
}
var firstMessageTask = Task.Run(
() => clientStatus.DeliverApplicationMessageAsync(
new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build()));
await LongTestDelay();
await firstMessageOutboundPacketInterceptedTcs.Task;
return Task.CompletedTask;
};
var secondMessageTask = Task.Run(
() => clientStatus.DeliverApplicationMessageAsync(
new MqttApplicationMessageBuilder().WithTopic("InjectedTwo").Build()));
await LongTestDelay();
var status = await server.GetSessionsAsync();
var clientStatus = status[0];
await receiver.SubscribeAsync("#");
var thirdMessageTask = Task.Run(
() => clientStatus.DeliverApplicationMessageAsync(
new MqttApplicationMessageBuilder().WithTopic("InjectedThree").Build()));
await LongTestDelay();
var firstMessageTask = Task.Run(
() => clientStatus.DeliverApplicationMessageAsync(
new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build()));
await LongTestDelay();
await firstMessageOutboundPacketInterceptedTcs.Task;
Task.WaitAny(firstMessageTask, secondMessageTask, thirdMessageTask);
var secondMessageTask = Task.Run(
() => clientStatus.DeliverApplicationMessageAsync(
new MqttApplicationMessageBuilder().WithTopic("InjectedTwo").Build()));
await LongTestDelay();
// Due to the DropNewMessage strategy the third message delivery will fail.
// As a result, no existing messages in the queue will be dropped (evicted).
Assert.AreEqual(firstMessageTask.Status, TaskStatus.WaitingForActivation);
Assert.AreEqual(secondMessageTask.Status, TaskStatus.WaitingForActivation);
Assert.AreEqual(thirdMessageTask.Status, TaskStatus.Faulted);
Assert.IsTrue(thirdMessageTask.Exception?.InnerException is MqttPendingMessagesOverflowException);
var thirdMessageTask = Task.Run(
() => clientStatus.DeliverApplicationMessageAsync(
new MqttApplicationMessageBuilder().WithTopic("InjectedThree").Build()));
await LongTestDelay();
Assert.IsFalse(firstMessageEvicted);
Assert.IsFalse(secondMessageEvicted);
Assert.IsFalse(thirdMessageEvicted);
}
Task.WaitAny(firstMessageTask, secondMessageTask, thirdMessageTask);
[TestMethod]
public async Task Deliver_Application_Message_At_Session_Level_QueueOverflow_DropOldestQueuedMessageStrategy()
// Due to the DropNewMessage strategy the third message delivery will fail.
// As a result, no existing messages in the queue will be dropped (evicted).
Assert.AreEqual(firstMessageTask.Status, TaskStatus.WaitingForActivation);
Assert.AreEqual(secondMessageTask.Status, TaskStatus.WaitingForActivation);
Assert.AreEqual(thirdMessageTask.Status, TaskStatus.Faulted);
Assert.IsTrue(thirdMessageTask.Exception?.InnerException is MqttPendingMessagesOverflowException);
Assert.IsFalse(firstMessageEvicted);
Assert.IsFalse(secondMessageEvicted);
Assert.IsFalse(thirdMessageEvicted);
}
[TestMethod]
public async Task Deliver_Application_Message_At_Session_Level_QueueOverflow_DropOldestQueuedMessageStrategy()
{
using var testEnvironment = CreateTestEnvironment(trackUnobservedTaskException: false);
var server = await testEnvironment.StartServer(
builder => builder
.WithMaxPendingMessagesPerClient(1)
.WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage));
var receiver = await testEnvironment.ConnectClient();
var firstMessageOutboundPacketInterceptedTcs = new TaskCompletionSource();
server.InterceptingOutboundPacketAsync += async args =>
{
using var testEnvironment = CreateTestEnvironment(trackUnobservedTaskException: false);
var server = await testEnvironment.StartServer(
builder => builder
.WithMaxPendingMessagesPerClient(1)
.WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage));
var receiver = await testEnvironment.ConnectClient();
var firstMessageOutboundPacketInterceptedTcs = new TaskCompletionSource();
server.InterceptingOutboundPacketAsync += async args =>
// - The first message is dequeued normally and calls this delay
// - The second message fills the outbound queue
// - The third message overflows the outbound queue
if (args.Packet is MqttPublishPacket)
{
// - The first message is dequeued normally and calls this delay
// - The second message fills the outbound queue
// - The third message overflows the outbound queue
if (args.Packet is MqttPublishPacket)
{
firstMessageOutboundPacketInterceptedTcs.SetResult();
await Task.Delay(TimeSpan.FromDays(1), args.CancellationToken);
}
};
firstMessageOutboundPacketInterceptedTcs.SetResult();
await Task.Delay(TimeSpan.FromDays(1), args.CancellationToken);
}
};
var firstMessageEvicted = false;
var secondMessageEvicted = false;
var thirdMessageEvicted = false;
var firstMessageEvicted = false;
var secondMessageEvicted = false;
var thirdMessageEvicted = false;
server.QueuedApplicationMessageOverwrittenAsync += args =>
server.QueuedApplicationMessageOverwrittenAsync += args =>
{
if (args.Packet is not MqttPublishPacket publishPacket)
{
if (args.Packet is not MqttPublishPacket publishPacket)
{
return Task.CompletedTask;
}
switch (publishPacket.Topic)
{
case "InjectedOne":
firstMessageEvicted = true;
break;
case "InjectedTwo":
secondMessageEvicted = true;
break;
case "InjectedThree":
thirdMessageEvicted = true;
break;
}
return Task.CompletedTask;
};
}
var status = await server.GetSessionsAsync();
var clientStatus = status[0];
await receiver.SubscribeAsync("#");
var firstMessageTask = Task.Run(
() => clientStatus.DeliverApplicationMessageAsync(
new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build()));
await LongTestDelay();
await firstMessageOutboundPacketInterceptedTcs.Task;
var secondMessageTask = Task.Run(
() => clientStatus.DeliverApplicationMessageAsync(
new MqttApplicationMessageBuilder().WithTopic("InjectedTwo").Build()));
await LongTestDelay();
var thirdMessageTask = Task.Run(
() => clientStatus.DeliverApplicationMessageAsync(
new MqttApplicationMessageBuilder().WithTopic("InjectedThree").Build()));
await LongTestDelay();
Task.WaitAny(firstMessageTask, secondMessageTask, thirdMessageTask);
// Due to the DropOldestQueuedMessage strategy, the second message delivery will fail
// to make room for the third one.
Assert.AreEqual(firstMessageTask.Status, TaskStatus.WaitingForActivation);
Assert.AreEqual(secondMessageTask.Status, TaskStatus.Faulted);
Assert.IsTrue(secondMessageTask.Exception?.InnerException is MqttPendingMessagesOverflowException);
Assert.AreEqual(thirdMessageTask.Status, TaskStatus.WaitingForActivation);
Assert.IsFalse(firstMessageEvicted);
Assert.IsTrue(secondMessageEvicted);
Assert.IsFalse(thirdMessageEvicted);
}
[TestMethod]
public async Task Inject_ApplicationMessage_At_Server_Level()
{
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
var receiver = await testEnvironment.ConnectClient();
var messageReceivedHandler = testEnvironment.CreateApplicationMessageHandler(receiver);
await receiver.SubscribeAsync("#");
var injectedApplicationMessage = new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build();
await server.InjectApplicationMessage(new InjectedMqttApplicationMessage(injectedApplicationMessage));
await LongTestDelay();
Assert.AreEqual(1, messageReceivedHandler.ReceivedEventArgs.Count);
Assert.AreEqual("InjectedOne", messageReceivedHandler.ReceivedEventArgs[0].ApplicationMessage.Topic);
}
[TestMethod]
public async Task Intercept_Injected_Application_Message()
{
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
MqttApplicationMessage interceptedMessage = null;
server.InterceptingPublishAsync += eventArgs =>
switch (publishPacket.Topic)
{
interceptedMessage = eventArgs.ApplicationMessage;
return CompletedTask.Instance;
};
case "InjectedOne":
firstMessageEvicted = true;
break;
case "InjectedTwo":
secondMessageEvicted = true;
break;
case "InjectedThree":
thirdMessageEvicted = true;
break;
}
var injectedApplicationMessage = new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build();
await server.InjectApplicationMessage(new InjectedMqttApplicationMessage(injectedApplicationMessage));
return Task.CompletedTask;
};
await LongTestDelay();
var status = await server.GetSessionsAsync();
var clientStatus = status[0];
await receiver.SubscribeAsync("#");
Assert.IsNotNull(interceptedMessage);
}
var firstMessageTask = Task.Run(
() => clientStatus.DeliverApplicationMessageAsync(
new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build()));
await LongTestDelay();
await firstMessageOutboundPacketInterceptedTcs.Task;
var secondMessageTask = Task.Run(
() => clientStatus.DeliverApplicationMessageAsync(
new MqttApplicationMessageBuilder().WithTopic("InjectedTwo").Build()));
await LongTestDelay();
var thirdMessageTask = Task.Run(
() => clientStatus.DeliverApplicationMessageAsync(
new MqttApplicationMessageBuilder().WithTopic("InjectedThree").Build()));
await LongTestDelay();
Task.WaitAny(firstMessageTask, secondMessageTask, thirdMessageTask);
// Due to the DropOldestQueuedMessage strategy, the second message delivery will fail
// to make room for the third one.
Assert.AreEqual(firstMessageTask.Status, TaskStatus.WaitingForActivation);
Assert.AreEqual(secondMessageTask.Status, TaskStatus.Faulted);
Assert.IsTrue(secondMessageTask.Exception?.InnerException is MqttPendingMessagesOverflowException);
Assert.AreEqual(thirdMessageTask.Status, TaskStatus.WaitingForActivation);
Assert.IsFalse(firstMessageEvicted);
Assert.IsTrue(secondMessageEvicted);
Assert.IsFalse(thirdMessageEvicted);
}
[TestMethod]
public async Task Inject_ApplicationMessage_At_Server_Level()
{
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
var receiver = await testEnvironment.ConnectClient();
var messageReceivedHandler = testEnvironment.CreateApplicationMessageHandler(receiver);
await receiver.SubscribeAsync("#");
var injectedApplicationMessage = new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build();
await server.InjectApplicationMessage(new InjectedMqttApplicationMessage(injectedApplicationMessage));
await LongTestDelay();
Assert.AreEqual(1, messageReceivedHandler.ReceivedEventArgs.Count);
Assert.AreEqual("InjectedOne", messageReceivedHandler.ReceivedEventArgs[0].ApplicationMessage.Topic);
}
[TestMethod]
public async Task Intercept_Injected_Application_Message()
{
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
MqttApplicationMessage interceptedMessage = null;
server.InterceptingPublishAsync += eventArgs =>
{
interceptedMessage = eventArgs.ApplicationMessage;
return CompletedTask.Instance;
};
var injectedApplicationMessage = new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build();
await server.InjectApplicationMessage(new InjectedMqttApplicationMessage(injectedApplicationMessage));
await LongTestDelay();
Assert.IsNotNull(interceptedMessage);
}
}
+45 -47
View File
@@ -10,59 +10,57 @@ using MQTTnet.Formatter;
using MQTTnet.Packets;
using MQTTnet.Protocol;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class KeepAlive_Tests : BaseTestClass
{
[TestClass]
public sealed class KeepAlive_Tests : BaseTestClass
[TestMethod]
public async Task Disconnect_Client_DueTo_KeepAlive()
{
[TestMethod]
public async Task Disconnect_Client_DueTo_KeepAlive()
using var testEnvironment = CreateTestEnvironment();
await testEnvironment.StartServer();
var client = await testEnvironment.ConnectLowLevelClient(o => o
.WithTimeout(TimeSpan.FromSeconds(1))
.WithTimeout(TimeSpan.Zero)
.WithProtocolVersion(MqttProtocolVersion.V500)).ConfigureAwait(false);
await client.SendAsync(new MqttConnectPacket
{
using (var testEnvironment = CreateTestEnvironment())
{
await testEnvironment.StartServer();
CleanSession = true,
ClientId = "Disconnect_Client_DueTo_KeepAlive",
KeepAlivePeriod = 1
}, CancellationToken.None).ConfigureAwait(false);
var client = await testEnvironment.ConnectLowLevelClient(o => o
.WithTimeout(TimeSpan.FromSeconds(1))
.WithTimeout(TimeSpan.Zero)
.WithProtocolVersion(MqttProtocolVersion.V500)).ConfigureAwait(false);
var responsePacket = await client.ReceiveAsync(CancellationToken.None).ConfigureAwait(false);
Assert.IsTrue(responsePacket is MqttConnAckPacket);
await client.SendAsync(new MqttConnectPacket
{
CleanSession = true,
ClientId = "Disconnect_Client_DueTo_KeepAlive",
KeepAlivePeriod = 1
}, CancellationToken.None).ConfigureAwait(false);
for (var i = 0; i < 6; i++)
{
await Task.Delay(500);
var responsePacket = await client.ReceiveAsync(CancellationToken.None).ConfigureAwait(false);
Assert.IsTrue(responsePacket is MqttConnAckPacket);
for (var i = 0; i < 6; i++)
{
await Task.Delay(500);
await client.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None);
responsePacket = await client.ReceiveAsync(CancellationToken.None);
Assert.IsTrue(responsePacket is MqttPingRespPacket);
}
// If we reach this point everything works as expected (server did not close the connection
// due to proper ping messages.
// Now we will wait 1.1 seconds because the server MUST wait 1.5 seconds in total (See spec).
await Task.Delay(1100);
await client.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None);
responsePacket = await client.ReceiveAsync(CancellationToken.None);
Assert.IsTrue(responsePacket is MqttPingRespPacket);
// Now we will wait longer than 1.5 so that the server will close the connection.
responsePacket = await client.ReceiveAsync(CancellationToken.None);
var disconnectPacket = responsePacket as MqttDisconnectPacket;
Assert.IsTrue(disconnectPacket != null);
Assert.AreEqual(disconnectPacket.ReasonCode, MqttDisconnectReasonCode.KeepAliveTimeout);
}
await client.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None);
responsePacket = await client.ReceiveAsync(CancellationToken.None);
Assert.IsTrue(responsePacket is MqttPingRespPacket);
}
// If we reach this point everything works as expected (server did not close the connection
// due to proper ping messages.
// Now we will wait 1.1 seconds because the server MUST wait 1.5 seconds in total (See spec).
await Task.Delay(1100);
await client.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None);
responsePacket = await client.ReceiveAsync(CancellationToken.None);
Assert.IsTrue(responsePacket is MqttPingRespPacket);
// Now we will wait longer than 1.5 so that the server will close the connection.
responsePacket = await client.ReceiveAsync(CancellationToken.None);
var disconnectPacket = responsePacket as MqttDisconnectPacket;
Assert.IsTrue(disconnectPacket != null);
Assert.AreEqual(disconnectPacket.ReasonCode, MqttDisconnectReasonCode.KeepAliveTimeout);
}
}
+126 -138
View File
@@ -6,166 +6,154 @@ using MQTTnet.Internal;
using MQTTnet.Packets;
using MQTTnet.Protocol;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
#if DEBUG
[TestClass]
#endif
public sealed class Load_Tests : BaseTestClass
{
[TestClass]
public sealed class Load_Tests : BaseTestClass
[TestMethod]
public async Task Handle_100_000_Messages_In_Low_Level_Client()
{
[TestMethod]
public async Task Handle_100_000_Messages_In_Receiving_Client()
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
var receivedMessages = 0;
server.InterceptingPublishAsync += _ =>
{
using (var testEnvironment = CreateTestEnvironment())
{
await testEnvironment.StartServer();
Interlocked.Increment(ref receivedMessages);
return CompletedTask.Instance;
};
var receivedMessages = 0;
using (var receiverClient = await testEnvironment.ConnectClient())
for (var i = 0; i < 100; i++)
{
_ = Task.Factory.StartNew(
async () =>
{
receiverClient.ApplicationMessageReceivedAsync += e =>
try
{
Interlocked.Increment(ref receivedMessages);
return CompletedTask.Instance;
};
using var client = await testEnvironment.ConnectLowLevelClient();
await receiverClient.SubscribeAsync("t/+");
for (var i = 0; i < 100; i++)
{
_ = Task.Run(
async () =>
await client.SendAsync(
new MqttConnectPacket
{
using (var client = await testEnvironment.ConnectClient())
{
var applicationMessageBuilder = new MqttApplicationMessageBuilder();
ClientId = "Handle_100_000_Messages_In_Low_Level_Client_" + Guid.NewGuid()
},
CancellationToken.None);
for (var j = 0; j < 1000; j++)
{
var message = applicationMessageBuilder.WithTopic("t/" + j)
.Build();
var packet = await client.ReceiveAsync(CancellationToken.None);
await client.PublishAsync(message)
.ConfigureAwait(false);
}
var connAckPacket = packet as MqttConnAckPacket;
await client.DisconnectAsync();
}
});
Assert.IsTrue(connAckPacket != null);
Assert.AreEqual(MqttConnectReasonCode.Success, connAckPacket.ReasonCode);
var publishPacket = new MqttPublishPacket();
for (var j = 0; j < 1000; j++)
{
publishPacket.Topic = j.ToString();
await client.SendAsync(publishPacket, CancellationToken.None).ConfigureAwait(false);
}
await client.DisconnectAsync(CancellationToken.None);
}
catch (Exception exception)
{
Console.WriteLine(exception);
}
},
TaskCreationOptions.LongRunning);
}
var result = SpinWait.SpinUntil(() => receivedMessages >= 100000, TimeSpan.FromMinutes(5));
Assert.IsTrue(result);
}
[TestMethod]
public async Task Handle_100_000_Messages_In_Receiving_Client()
{
using var testEnvironment = CreateTestEnvironment();
await testEnvironment.StartServer();
var receivedMessages = 0;
using var receiverClient = await testEnvironment.ConnectClient();
receiverClient.ApplicationMessageReceivedAsync += _ =>
{
Interlocked.Increment(ref receivedMessages);
return CompletedTask.Instance;
};
await receiverClient.SubscribeAsync("t/+");
for (var i = 0; i < 100; i++)
{
_ = Task.Factory.StartNew(
async () =>
{
using var client = await testEnvironment.ConnectClient();
var applicationMessageBuilder = testEnvironment.ClientFactory.CreateApplicationMessageBuilder();
for (var j = 0; j < 1000; j++)
{
var message = applicationMessageBuilder.WithTopic("t/" + j).Build();
await client.PublishAsync(message).ConfigureAwait(false);
}
SpinWait.SpinUntil(() => receivedMessages == 100000, TimeSpan.FromSeconds(120));
Assert.AreEqual(100000, receivedMessages);
}
}
await client.DisconnectAsync();
},
TaskCreationOptions.LongRunning);
}
[TestMethod]
public async Task Handle_100_000_Messages_In_Low_Level_Client()
var result = SpinWait.SpinUntil(() => receivedMessages >= 100000, TimeSpan.FromMinutes(5));
Assert.IsTrue(result);
}
[TestMethod]
public async Task Handle_100_000_Messages_In_Server()
{
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
var receivedMessages = 0;
server.InterceptingPublishAsync += _ =>
{
using (var testEnvironment = CreateTestEnvironment())
{
var server = await testEnvironment.StartServer();
Interlocked.Increment(ref receivedMessages);
return CompletedTask.Instance;
};
var receivedMessages = 0;
server.InterceptingPublishAsync += e =>
{
Interlocked.Increment(ref receivedMessages);
return CompletedTask.Instance;
};
for (var i = 0; i < 100; i++)
{
_ = Task.Run(
async () =>
{
try
{
using (var client = await testEnvironment.ConnectLowLevelClient())
{
await client.SendAsync(
new MqttConnectPacket
{
ClientId = "Handle_100_000_Messages_In_Low_Level_Client_" + Guid.NewGuid()
}, CancellationToken.None);
var packet = await client.ReceiveAsync(CancellationToken.None);
var connAckPacket = packet as MqttConnAckPacket;
Assert.IsTrue(connAckPacket != null);
Assert.AreEqual(MqttConnectReasonCode.Success, connAckPacket.ReasonCode);
var publishPacket = new MqttPublishPacket();
for (var j = 0; j < 1000; j++)
{
publishPacket.Topic = j.ToString();
await client.SendAsync(publishPacket, CancellationToken.None)
.ConfigureAwait(false);
}
await client.DisconnectAsync(CancellationToken.None);
}
}
catch (Exception exception)
{
Console.WriteLine(exception);
}
});
}
SpinWait.SpinUntil(() => receivedMessages == 100000, TimeSpan.FromSeconds(10));
Assert.AreEqual(100000, receivedMessages);
}
}
[TestMethod]
public async Task Handle_100_000_Messages_In_Server()
for (var i = 0; i < 100; i++)
{
using (var testEnvironment = CreateTestEnvironment())
{
var server = await testEnvironment.StartServer();
var receivedMessages = 0;
server.InterceptingPublishAsync += e =>
_ = Task.Factory.StartNew(
async () =>
{
Interlocked.Increment(ref receivedMessages);
return CompletedTask.Instance;
};
using var client = await testEnvironment.ConnectClient();
var applicationMessageBuilder = new MqttApplicationMessageBuilder();
for (var i = 0; i < 100; i++)
{
_ = Task.Run(
async () =>
{
using (var client = await testEnvironment.ConnectClient())
{
var applicationMessageBuilder = new MqttApplicationMessageBuilder();
for (var j = 0; j < 1000; j++)
{
var message = applicationMessageBuilder.WithTopic(j.ToString()).Build();
for (var j = 0; j < 1000; j++)
{
var message = applicationMessageBuilder.WithTopic(j.ToString())
.Build();
await client.PublishAsync(message).ConfigureAwait(false);
}
await client.PublishAsync(message)
.ConfigureAwait(false);
}
await client.DisconnectAsync();
}
});
}
SpinWait.SpinUntil(() => receivedMessages == 100000, TimeSpan.FromSeconds(10));
Assert.AreEqual(100000, receivedMessages);
}
await client.DisconnectAsync();
},
TaskCreationOptions.LongRunning);
}
var result = SpinWait.SpinUntil(() => receivedMessages >= 100000, TimeSpan.FromMinutes(5));
Assert.IsTrue(result);
}
}
@@ -1,26 +1,22 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MQTTnet.Server.Internal;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class MqttRetainedMessageManager_Tests
{
[TestClass]
public sealed class MqttRetainedMessageManager_Tests
[TestMethod]
public async Task MqttRetainedMessageManager_GetUndefinedTopic()
{
[TestMethod]
public async Task MqttRetainedMessageManager_GetUndefinedTopic()
{
var logger = new Mockups.TestLogger();
var eventContainer = new MqttServerEventContainer();
var retainedMessagesManager = new MqttRetainedMessagesManager(eventContainer, logger);
var task = retainedMessagesManager.GetMessage("undefined");
Assert.IsNotNull(task, "Task should not be null");
var result = await task;
Assert.IsNull(result, "Null result expected");
}
var logger = new Mockups.TestLogger();
var eventContainer = new MqttServerEventContainer();
var retainedMessagesManager = new MqttRetainedMessagesManager(eventContainer, logger);
var task = retainedMessagesManager.GetMessage("undefined");
Assert.IsNotNull(task, "Task should not be null");
var result = await task;
Assert.IsNull(result, "Null result expected");
}
}
}
@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -13,227 +12,218 @@ using MQTTnet.Server;
using MQTTnet.Server.Internal;
using MQTTnet.Tests.Mockups;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class MqttSubscriptionsManager_Tests : BaseTestClass
{
[TestClass]
public sealed class MqttSubscriptionsManager_Tests : BaseTestClass
MqttClientSubscriptionsManager _subscriptionsManager;
[TestMethod]
public async Task MqttSubscriptionsManager_SubscribeAndUnsubscribeSingle()
{
MqttClientSubscriptionsManager _subscriptionsManager;
[TestMethod]
public async Task MqttSubscriptionsManager_SubscribeAndUnsubscribeSingle()
var sp = new MqttSubscribePacket
{
var sp = new MqttSubscribePacket
{
TopicFilters = new List<MqttTopicFilter>
{
new MqttTopicFilterBuilder().WithTopic("A/B/C").Build()
}
};
TopicFilters = [new MqttTopicFilterBuilder().WithTopic("A/B/C").Build()]
};
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
Assert.IsTrue(CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.AtMostOnce, "").IsSubscribed);
Assert.IsTrue(CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.AtMostOnce, "").IsSubscribed);
var up = new MqttUnsubscribePacket();
up.TopicFilters.Add("A/B/C");
await _subscriptionsManager.Unsubscribe(up, CancellationToken.None);
var up = new MqttUnsubscribePacket();
up.TopicFilters.Add("A/B/C");
await _subscriptionsManager.Unsubscribe(up, CancellationToken.None);
Assert.IsFalse(CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.AtMostOnce, "").IsSubscribed);
}
Assert.IsFalse(CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.AtMostOnce, "").IsSubscribed);
}
[TestMethod]
public async Task MqttSubscriptionsManager_SubscribeDifferentQoSSuccess()
[TestMethod]
public async Task MqttSubscriptionsManager_SubscribeDifferentQoSSuccess()
{
var sp = new MqttSubscribePacket
{
var sp = new MqttSubscribePacket
{
TopicFilters = new List<MqttTopicFilter>
{
new MqttTopicFilter { Topic = "A/B/C", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce }
}
};
TopicFilters =
[
new() { Topic = "A/B/C", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce }
]
};
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
var result = CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.ExactlyOnce, "");
Assert.IsTrue(result.IsSubscribed);
Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtMostOnce);
}
var result = CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.ExactlyOnce, "");
Assert.IsTrue(result.IsSubscribed);
Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtMostOnce);
}
[TestMethod]
public async Task MqttSubscriptionsManager_SubscribeSingleNoSuccess()
[TestMethod]
public async Task MqttSubscriptionsManager_SubscribeSingleNoSuccess()
{
var sp = new MqttSubscribePacket
{
var sp = new MqttSubscribePacket
{
TopicFilters = new List<MqttTopicFilter>
{
new MqttTopicFilterBuilder().WithTopic("A/B/C").Build()
}
};
TopicFilters = [new MqttTopicFilterBuilder().WithTopic("A/B/C").Build()]
};
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
Assert.IsFalse(CheckSubscriptions("A/B/X", MqttQualityOfServiceLevel.AtMostOnce, "").IsSubscribed);
}
Assert.IsFalse(CheckSubscriptions("A/B/X", MqttQualityOfServiceLevel.AtMostOnce, "").IsSubscribed);
}
[TestMethod]
public async Task MqttSubscriptionsManager_SubscribeSingleSuccess()
[TestMethod]
public async Task MqttSubscriptionsManager_SubscribeSingleSuccess()
{
var sp = new MqttSubscribePacket
{
var sp = new MqttSubscribePacket
{
TopicFilters = new List<MqttTopicFilter>
{
new MqttTopicFilterBuilder().WithTopic("A/B/C").Build()
}
};
TopicFilters = [new MqttTopicFilterBuilder().WithTopic("A/B/C").Build()]
};
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
var result = CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.AtMostOnce, "");
var result = CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.AtMostOnce, "");
Assert.IsTrue(result.IsSubscribed);
Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtMostOnce);
}
Assert.IsTrue(result.IsSubscribed);
Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtMostOnce);
}
[TestMethod]
public async Task MqttSubscriptionsManager_SubscribeTwoTimesSuccess()
[TestMethod]
public async Task MqttSubscriptionsManager_SubscribeTwoTimesSuccess()
{
var sp = new MqttSubscribePacket
{
var sp = new MqttSubscribePacket
{
TopicFilters = new List<MqttTopicFilter>
{
new MqttTopicFilter { Topic = "#", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce },
new MqttTopicFilter { Topic = "A/B/C", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce }
}
};
TopicFilters =
[
new MqttTopicFilter { Topic = "#", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce },
new MqttTopicFilter { Topic = "A/B/C", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce }
]
};
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
var result = CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.ExactlyOnce, "");
var result = CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.ExactlyOnce, "");
Assert.IsTrue(result.IsSubscribed);
Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtLeastOnce);
}
Assert.IsTrue(result.IsSubscribed);
Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtLeastOnce);
}
[TestMethod]
public async Task MqttSubscriptionsManager_SubscribeWildcard1()
[TestMethod]
public async Task MqttSubscriptionsManager_SubscribeWildcard1()
{
await SubscribeToTopic("house/+/room");
await SubscribeToTopic("house/+/room/+");
CheckIsSubscribed("house/1/room");
CheckIsSubscribed("house/1/room/bed");
CheckIsSubscribed("house/1/room/chair");
CheckIsSubscribed("house/2/room/bed");
CheckIsSubscribed("house/2/room/chair");
CheckIsNotSubscribed("house/1/room/bed/cover");
CheckIsNotSubscribed("house/1/study/bed");
}
[TestMethod]
public async Task MqttSubscriptionsManager_SubscribeWildcard2()
{
await SubscribeToTopic("house/+/room");
await SubscribeToTopic("house/+/room/#");
CheckIsSubscribed("house/1/room");
CheckIsSubscribed("house/1/room/bed");
CheckIsSubscribed("house/2/room");
CheckIsSubscribed("house/2/room/bed");
CheckIsSubscribed("house/2/room/bed/cover");
CheckIsNotSubscribed("house/1/level");
CheckIsNotSubscribed("house/1/level/door");
}
[TestMethod]
public async Task MqttSubscriptionsManager_SubscribeWildcard3()
{
await SubscribeToTopic("house/1/room");
await SubscribeToTopic("house/1/room/+");
CheckIsSubscribed("house/1/room");
CheckIsSubscribed("house/1/room/bed");
CheckIsSubscribed("house/1/room/chair");
CheckIsNotSubscribed("house/2/room");
CheckIsNotSubscribed("house/2/room/bed/cover");
}
[TestMethod]
public async Task MqttSubscriptionsManager_SubscribeWildcard4()
{
await SubscribeToTopic("house/1/+/+");
CheckIsSubscribed("house/1/room/bed");
CheckIsSubscribed("house/1/room/chair");
CheckIsNotSubscribed("house/1/room");
CheckIsNotSubscribed("house/1/room/bed/cover");
}
[TestMethod]
public async Task MqttSubscriptionsManager_SubscribeWildcard5()
{
await SubscribeToTopic("house/1/+/#");
CheckIsSubscribed("house/1/room/bed");
CheckIsSubscribed("house/1/room/chair");
CheckIsSubscribed("house/1/room/chair/leg");
CheckIsSubscribed("house/1/level/window");
CheckIsSubscribed("house/1/level/door");
CheckIsNotSubscribed("house/2/room/bed");
}
async Task SubscribeToTopic(string topic)
{
var sp = new MqttSubscribePacket
{
await SubscribeToTopic("house/+/room");
await SubscribeToTopic("house/+/room/+");
TopicFilters =
[
new MqttTopicFilter { Topic = topic, QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce }
]
};
CheckIsSubscribed("house/1/room");
CheckIsSubscribed("house/1/room/bed");
CheckIsSubscribed("house/1/room/chair");
CheckIsSubscribed("house/2/room/bed");
CheckIsSubscribed("house/2/room/chair");
await _subscriptionsManager.Subscribe(sp, CancellationToken.None).ConfigureAwait(false);
}
CheckIsNotSubscribed("house/1/room/bed/cover");
CheckIsNotSubscribed("house/1/study/bed");
}
void CheckIsSubscribed(string topic)
{
var result = CheckSubscriptions(topic, MqttQualityOfServiceLevel.AtMostOnce, "");
Assert.IsTrue(result.IsSubscribed);
Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtMostOnce);
}
[TestMethod]
public async Task MqttSubscriptionsManager_SubscribeWildcard2()
void CheckIsNotSubscribed(string topic)
{
var result = CheckSubscriptions(topic, MqttQualityOfServiceLevel.AtMostOnce, "");
Assert.IsFalse(result.IsSubscribed);
}
[TestInitialize]
public void TestInitialize()
{
var logger = new TestLogger();
var options = new MqttServerOptions();
var retainedMessagesManager = new MqttRetainedMessagesManager(new MqttServerEventContainer(), logger);
var eventContainer = new MqttServerEventContainer();
var clientSessionManager = new MqttClientSessionsManager(options, retainedMessagesManager, eventContainer, logger);
var session = new MqttSession(new MqttConnectPacket
{
await SubscribeToTopic("house/+/room");
await SubscribeToTopic("house/+/room/#");
ClientId = ""
}, new ConcurrentDictionary<object, object>(), options, eventContainer, retainedMessagesManager, clientSessionManager);
CheckIsSubscribed("house/1/room");
CheckIsSubscribed("house/1/room/bed");
CheckIsSubscribed("house/2/room");
CheckIsSubscribed("house/2/room/bed");
CheckIsSubscribed("house/2/room/bed/cover");
_subscriptionsManager = new MqttClientSubscriptionsManager(session, new MqttServerEventContainer(), retainedMessagesManager, clientSessionManager);
}
CheckIsNotSubscribed("house/1/level");
CheckIsNotSubscribed("house/1/level/door");
}
[TestMethod]
public async Task MqttSubscriptionsManager_SubscribeWildcard3()
{
await SubscribeToTopic("house/1/room");
await SubscribeToTopic("house/1/room/+");
CheckIsSubscribed("house/1/room");
CheckIsSubscribed("house/1/room/bed");
CheckIsSubscribed("house/1/room/chair");
CheckIsNotSubscribed("house/2/room");
CheckIsNotSubscribed("house/2/room/bed/cover");
}
[TestMethod]
public async Task MqttSubscriptionsManager_SubscribeWildcard4()
{
await SubscribeToTopic("house/1/+/+");
CheckIsSubscribed("house/1/room/bed");
CheckIsSubscribed("house/1/room/chair");
CheckIsNotSubscribed("house/1/room");
CheckIsNotSubscribed("house/1/room/bed/cover");
}
[TestMethod]
public async Task MqttSubscriptionsManager_SubscribeWildcard5()
{
await SubscribeToTopic("house/1/+/#");
CheckIsSubscribed("house/1/room/bed");
CheckIsSubscribed("house/1/room/chair");
CheckIsSubscribed("house/1/room/chair/leg");
CheckIsSubscribed("house/1/level/window");
CheckIsSubscribed("house/1/level/door");
CheckIsNotSubscribed("house/2/room/bed");
}
async Task SubscribeToTopic(string topic)
{
var sp = new MqttSubscribePacket
{
TopicFilters = new List<MqttTopicFilter>
{
new MqttTopicFilter { Topic = topic, QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce },
}
};
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
}
void CheckIsSubscribed(string topic)
{
var result = CheckSubscriptions(topic, MqttQualityOfServiceLevel.AtMostOnce, "");
Assert.IsTrue(result.IsSubscribed);
Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtMostOnce);
}
void CheckIsNotSubscribed(string topic)
{
var result = CheckSubscriptions(topic, MqttQualityOfServiceLevel.AtMostOnce, "");
Assert.IsFalse(result.IsSubscribed);
}
[TestInitialize]
public void TestInitialize()
{
var logger = new TestLogger();
var options = new MqttServerOptions();
var retainedMessagesManager = new MqttRetainedMessagesManager(new MqttServerEventContainer(), logger);
var eventContainer = new MqttServerEventContainer();
var clientSessionManager = new MqttClientSessionsManager(options, retainedMessagesManager, eventContainer, logger);
var session = new MqttSession(new MqttConnectPacket
{
ClientId = ""
}, new ConcurrentDictionary<object, object>(), options, eventContainer, retainedMessagesManager, clientSessionManager);
_subscriptionsManager = new MqttClientSubscriptionsManager(session, new MqttServerEventContainer(), retainedMessagesManager, clientSessionManager);
}
CheckSubscriptionsResult CheckSubscriptions(string topic, MqttQualityOfServiceLevel applicationMessageQoSLevel, string senderClientId)
{
MqttTopicHash.Calculate(topic, out var topicHash, out _, out _);
return _subscriptionsManager.CheckSubscriptions(topic, topicHash, applicationMessageQoSLevel, senderClientId);
}
CheckSubscriptionsResult CheckSubscriptions(string topic, MqttQualityOfServiceLevel applicationMessageQoSLevel, string senderClientId)
{
MqttTopicHash.Calculate(topic, out var topicHash, out _, out _);
return _subscriptionsManager.CheckSubscriptions(topic, topicHash, applicationMessageQoSLevel, senderClientId);
}
}
+30 -32
View File
@@ -6,45 +6,43 @@ using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Formatter;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class No_Local_Tests : BaseTestClass
{
[TestClass]
public sealed class No_Local_Tests : BaseTestClass
[TestMethod]
public Task Subscribe_With_No_Local()
{
[TestMethod]
public Task Subscribe_With_No_Local()
{
return ExecuteTest(true, 0);
}
return ExecuteTest(true, 0);
}
[TestMethod]
public Task Subscribe_Without_No_Local()
{
return ExecuteTest(false, 1);
}
[TestMethod]
public Task Subscribe_Without_No_Local()
{
return ExecuteTest(false, 1);
}
async Task ExecuteTest(
bool noLocal,
int expectedCountAfterPublish)
{
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
{
await testEnvironment.StartServer();
async Task ExecuteTest(
bool noLocal,
int expectedCountAfterPublish)
{
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
await testEnvironment.StartServer();
var client1 = await testEnvironment.ConnectClient();
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client1);
var topicFilter = testEnvironment.ClientFactory.CreateTopicFilterBuilder().WithTopic("Topic").WithNoLocal(noLocal).Build();
await client1.SubscribeAsync(topicFilter);
await LongTestDelay();
var client1 = await testEnvironment.ConnectClient();
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client1);
var topicFilter = testEnvironment.ClientFactory.CreateTopicFilterBuilder().WithTopic("Topic").WithNoLocal(noLocal).Build();
await client1.SubscribeAsync(topicFilter);
await LongTestDelay();
applicationMessageHandler.AssertReceivedCountEquals(0);
applicationMessageHandler.AssertReceivedCountEquals(0);
// The client will publish a message where it is itself subscribing to.
await client1.PublishStringAsync("Topic", "Payload", retain: true);
await LongTestDelay();
// The client will publish a message where it is itself subscribing to.
await client1.PublishStringAsync("Topic", "Payload", retain: true);
await LongTestDelay();
applicationMessageHandler.AssertReceivedCountEquals(expectedCountAfterPublish);
}
}
applicationMessageHandler.AssertReceivedCountEquals(expectedCountAfterPublish);
}
}
+113 -123
View File
@@ -10,138 +10,128 @@ using MQTTnet.Internal;
using MQTTnet.Protocol;
using MQTTnet.Server;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class Publishing_Tests : BaseTestClass
{
[TestClass]
public sealed class Publishing_Tests : BaseTestClass
[TestMethod]
[ExpectedException(typeof(MqttClientDisconnectedException))]
public async Task Disconnect_While_Publishing()
{
[TestMethod]
[ExpectedException(typeof(MqttClientDisconnectedException))]
public async Task Disconnect_While_Publishing()
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
// The client will be disconnected directly after subscribing!
server.InterceptingPublishAsync += ev => server.DisconnectClientAsync(ev.ClientId);
var client = await testEnvironment.ConnectClient();
await client.PublishStringAsync("test", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
}
[TestMethod]
public async Task Return_NoMatchingSubscribers_When_Not_Subscribed()
{
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
await testEnvironment.StartServer();
var sender = await testEnvironment.ConnectClient();
var receiver = await testEnvironment.ConnectClient();
await receiver.SubscribeAsync("A");
// AtLeastOnce is required to get an ACK packet from the server.
var publishResult = await sender.PublishStringAsync("B", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
Assert.AreEqual(MqttClientPublishReasonCode.NoMatchingSubscribers, publishResult.ReasonCode);
Assert.AreEqual(true, publishResult.IsSuccess);
}
[TestMethod]
public async Task Return_Success_When_Subscribed()
{
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
await testEnvironment.StartServer();
var sender = await testEnvironment.ConnectClient();
var receiver = await testEnvironment.ConnectClient();
await receiver.SubscribeAsync("A");
// AtLeastOnce is required to get an ACK packet from the server.
var publishResult = await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
Assert.AreEqual(MqttClientPublishReasonCode.Success, publishResult.ReasonCode);
Assert.AreEqual(true, publishResult.IsSuccess);
}
[TestMethod]
public async Task Intercept_Client_Enqueue()
{
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
var sender = await testEnvironment.ConnectClient();
var receiver = await testEnvironment.ConnectClient();
await receiver.SubscribeAsync("A");
var receivedMessages = testEnvironment.CreateApplicationMessageHandler(receiver);
await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
await LongTestDelay();
receivedMessages.AssertReceivedCountEquals(1);
server.InterceptingClientEnqueueAsync += e =>
{
using (var testEnvironment = CreateTestEnvironment())
{
var server = await testEnvironment.StartServer();
e.AcceptEnqueue = false;
return CompletedTask.Instance;
};
// The client will be disconnect directly after subscribing!
server.InterceptingPublishAsync += ev => server.DisconnectClientAsync(ev.ClientId, MqttDisconnectReasonCode.NormalDisconnection);
await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
var client = await testEnvironment.ConnectClient();
await client.PublishStringAsync("test", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
}
}
await LongTestDelay();
[TestMethod]
public async Task Return_NoMatchingSubscribers_When_Not_Subscribed()
// Do not increase because the internal enqueue to the target client is not accepted!
receivedMessages.AssertReceivedCountEquals(1);
}
[TestMethod]
public async Task Intercept_Client_Enqueue_Multiple_Clients_Subscribed_Messages_Are_Filtered()
{
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
var sender = await testEnvironment.ConnectClient();
var receiverOne = await testEnvironment.ConnectClient(o => o.WithClientId("One"));
await receiverOne.SubscribeAsync("A");
var receivedMessagesOne = testEnvironment.CreateApplicationMessageHandler(receiverOne);
var receiverTwo = await testEnvironment.ConnectClient(o => o.WithClientId("Two"));
await receiverTwo.SubscribeAsync("A");
var receivedMessagesTwo = testEnvironment.CreateApplicationMessageHandler(receiverTwo);
var receiverThree = await testEnvironment.ConnectClient(o => o.WithClientId("Three"));
await receiverThree.SubscribeAsync("A");
var receivedMessagesThree = testEnvironment.CreateApplicationMessageHandler(receiverThree);
server.InterceptingClientEnqueueAsync += e =>
{
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
{
await testEnvironment.StartServer();
if (e.ReceiverClientId.Contains("Two")) e.AcceptEnqueue = false;
return CompletedTask.Instance;
};
var sender = await testEnvironment.ConnectClient();
var receiver = await testEnvironment.ConnectClient();
await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
await receiver.SubscribeAsync("A");
await LongTestDelay();
// AtLeastOnce is required to get an ACK packet from the server.
var publishResult = await sender.PublishStringAsync("B", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
Assert.AreEqual(MqttClientPublishReasonCode.NoMatchingSubscribers, publishResult.ReasonCode);
Assert.AreEqual(true, publishResult.IsSuccess);
}
}
[TestMethod]
public async Task Return_Success_When_Subscribed()
{
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
{
await testEnvironment.StartServer();
var sender = await testEnvironment.ConnectClient();
var receiver = await testEnvironment.ConnectClient();
await receiver.SubscribeAsync("A");
// AtLeastOnce is required to get an ACK packet from the server.
var publishResult = await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
Assert.AreEqual(MqttClientPublishReasonCode.Success, publishResult.ReasonCode);
Assert.AreEqual(true, publishResult.IsSuccess);
}
}
[TestMethod]
public async Task Intercept_Client_Enqueue()
{
using (var testEnvironment = CreateTestEnvironment())
{
var server = await testEnvironment.StartServer();
var sender = await testEnvironment.ConnectClient();
var receiver = await testEnvironment.ConnectClient();
await receiver.SubscribeAsync("A");
var receivedMessages = testEnvironment.CreateApplicationMessageHandler(receiver);
await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
await LongTestDelay();
receivedMessages.AssertReceivedCountEquals(1);
server.InterceptingClientEnqueueAsync += e =>
{
e.AcceptEnqueue = false;
return CompletedTask.Instance;
};
await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
await LongTestDelay();
// Do not increase because the internal enqueue to the target client is not accepted!
receivedMessages.AssertReceivedCountEquals(1);
}
}
[TestMethod]
public async Task Intercept_Client_Enqueue_Multiple_Clients_Subscribed_Messages_Are_Filtered()
{
using (var testEnvironment = CreateTestEnvironment())
{
var server = await testEnvironment.StartServer();
var sender = await testEnvironment.ConnectClient();
var receiverOne = await testEnvironment.ConnectClient(o => o.WithClientId("One"));
await receiverOne.SubscribeAsync("A");
var receivedMessagesOne = testEnvironment.CreateApplicationMessageHandler(receiverOne);
var receiverTwo = await testEnvironment.ConnectClient(o => o.WithClientId("Two"));
await receiverTwo.SubscribeAsync("A");
var receivedMessagesTwo = testEnvironment.CreateApplicationMessageHandler(receiverTwo);
var receiverThree = await testEnvironment.ConnectClient(o => o.WithClientId("Three"));
await receiverThree.SubscribeAsync("A");
var receivedMessagesThree = testEnvironment.CreateApplicationMessageHandler(receiverThree);
server.InterceptingClientEnqueueAsync += e =>
{
if (e.ReceiverClientId.Contains("Two")) e.AcceptEnqueue = false;
return CompletedTask.Instance;
};
await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
await LongTestDelay();
receivedMessagesOne.AssertReceivedCountEquals(1);
receivedMessagesTwo.AssertReceivedCountEquals(0);
receivedMessagesThree.AssertReceivedCountEquals(1);
}
}
receivedMessagesOne.AssertReceivedCountEquals(1);
receivedMessagesTwo.AssertReceivedCountEquals(0);
receivedMessagesThree.AssertReceivedCountEquals(1);
}
}
+89 -96
View File
@@ -11,157 +11,150 @@ using MQTTnet.Server;
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class QoS_Tests : BaseTestClass
{
[TestMethod]
public async Task Fire_Event_On_Client_Acknowledges_QoS_0()
{
using (var testEnvironment = CreateTestEnvironment())
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
ClientAcknowledgedPublishPacketEventArgs eventArgs = null;
server.ClientAcknowledgedPublishPacketAsync += args =>
{
var server = await testEnvironment.StartServer();
eventArgs = args;
return CompletedTask.Instance;
};
ClientAcknowledgedPublishPacketEventArgs eventArgs = null;
server.ClientAcknowledgedPublishPacketAsync += args =>
{
eventArgs = args;
return CompletedTask.Instance;
};
var client1 = await testEnvironment.ConnectClient();
await client1.SubscribeAsync("A");
var client1 = await testEnvironment.ConnectClient();
await client1.SubscribeAsync("A");
var client2 = await testEnvironment.ConnectClient();
await client2.PublishStringAsync("A");
var client2 = await testEnvironment.ConnectClient();
await client2.PublishStringAsync("A");
await LongTestDelay();
await LongTestDelay();
// Must be null because no event should be fired for QoS 0.
Assert.IsNull(eventArgs);
}
// Must be null because no event should be fired for QoS 0.
Assert.IsNull(eventArgs);
}
[TestMethod]
public async Task Fire_Event_On_Client_Acknowledges_QoS_1()
{
using (var testEnvironment = CreateTestEnvironment())
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
ClientAcknowledgedPublishPacketEventArgs eventArgs = null;
server.ClientAcknowledgedPublishPacketAsync += args =>
{
var server = await testEnvironment.StartServer();
eventArgs = args;
return CompletedTask.Instance;
};
ClientAcknowledgedPublishPacketEventArgs eventArgs = null;
server.ClientAcknowledgedPublishPacketAsync += args =>
{
eventArgs = args;
return CompletedTask.Instance;
};
var client1 = await testEnvironment.ConnectClient();
await client1.SubscribeAsync("A", MqttQualityOfServiceLevel.AtLeastOnce);
var client1 = await testEnvironment.ConnectClient();
await client1.SubscribeAsync("A", MqttQualityOfServiceLevel.AtLeastOnce);
var client2 = await testEnvironment.ConnectClient();
await client2.PublishStringAsync("A", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
var client2 = await testEnvironment.ConnectClient();
await client2.PublishStringAsync("A", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
await LongTestDelay();
await LongTestDelay();
Assert.IsNotNull(eventArgs);
Assert.IsNotNull(eventArgs.PublishPacket);
Assert.IsNotNull(eventArgs.AcknowledgePacket);
Assert.IsTrue(eventArgs.IsCompleted);
Assert.IsNotNull(eventArgs);
Assert.IsNotNull(eventArgs.PublishPacket);
Assert.IsNotNull(eventArgs.AcknowledgePacket);
Assert.IsTrue(eventArgs.IsCompleted);
Assert.AreEqual("A", eventArgs.PublishPacket.Topic);
}
Assert.AreEqual("A", eventArgs.PublishPacket.Topic);
}
[TestMethod]
public async Task Fire_Event_On_Client_Acknowledges_QoS_2()
{
using (var testEnvironment = CreateTestEnvironment())
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
var eventArgs = new List<ClientAcknowledgedPublishPacketEventArgs>();
server.ClientAcknowledgedPublishPacketAsync += args =>
{
var server = await testEnvironment.StartServer();
var eventArgs = new List<ClientAcknowledgedPublishPacketEventArgs>();
server.ClientAcknowledgedPublishPacketAsync += args =>
lock (eventArgs)
{
lock (eventArgs)
{
eventArgs.Add(args);
}
eventArgs.Add(args);
}
return CompletedTask.Instance;
};
return CompletedTask.Instance;
};
var client1 = await testEnvironment.ConnectClient();
await client1.SubscribeAsync("A", MqttQualityOfServiceLevel.ExactlyOnce);
var client1 = await testEnvironment.ConnectClient();
await client1.SubscribeAsync("A", MqttQualityOfServiceLevel.ExactlyOnce);
var client2 = await testEnvironment.ConnectClient();
await client2.PublishStringAsync("A", qualityOfServiceLevel: MqttQualityOfServiceLevel.ExactlyOnce);
var client2 = await testEnvironment.ConnectClient();
await client2.PublishStringAsync("A", qualityOfServiceLevel: MqttQualityOfServiceLevel.ExactlyOnce);
await LongTestDelay();
await LongTestDelay();
Assert.AreEqual(1, eventArgs.Count);
Assert.AreEqual(1, eventArgs.Count);
var firstEvent = eventArgs[0];
var firstEvent = eventArgs[0];
Assert.IsNotNull(firstEvent);
Assert.IsNotNull(firstEvent.PublishPacket);
Assert.IsNotNull(firstEvent.AcknowledgePacket);
Assert.IsTrue(firstEvent.IsCompleted);
Assert.IsNotNull(firstEvent);
Assert.IsNotNull(firstEvent.PublishPacket);
Assert.IsNotNull(firstEvent.AcknowledgePacket);
Assert.IsTrue(firstEvent.IsCompleted);
Assert.AreEqual("A", firstEvent.PublishPacket.Topic);
}
Assert.AreEqual("A", firstEvent.PublishPacket.Topic);
}
[TestMethod]
public async Task Preserve_Message_Order_For_Queued_Messages()
{
using (var testEnvironment = CreateTestEnvironment())
{
var server = await testEnvironment.StartServer(o => o.WithPersistentSessions());
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer(o => o.WithPersistentSessions());
// Create a session which will contain the messages.
var dummyClient = await testEnvironment.ConnectClient(o => o.WithClientId("A").WithCleanSession(false));
await dummyClient.SubscribeAsync("#", MqttQualityOfServiceLevel.AtLeastOnce);
dummyClient.Dispose();
// Create a session which will contain the messages.
var dummyClient = await testEnvironment.ConnectClient(o => o.WithClientId("A").WithCleanSession(false));
await dummyClient.SubscribeAsync("#", MqttQualityOfServiceLevel.AtLeastOnce);
dummyClient.Dispose();
await LongTestDelay();
await LongTestDelay();
await LongTestDelay();
await LongTestDelay();
// Now inject messages which are appended to the queue of the client.
await server.InjectApplicationMessage("T", "0", MqttQualityOfServiceLevel.AtLeastOnce);
// Now inject messages which are appended to the queue of the client.
await server.InjectApplicationMessage("T", "0", MqttQualityOfServiceLevel.AtLeastOnce);
await server.InjectApplicationMessage("T", "2", MqttQualityOfServiceLevel.AtLeastOnce);
await server.InjectApplicationMessage("T", "1", MqttQualityOfServiceLevel.AtLeastOnce);
await server.InjectApplicationMessage("T", "2", MqttQualityOfServiceLevel.AtLeastOnce);
await server.InjectApplicationMessage("T", "1", MqttQualityOfServiceLevel.AtLeastOnce);
await server.InjectApplicationMessage("T", "4", MqttQualityOfServiceLevel.AtLeastOnce);
await server.InjectApplicationMessage("T", "3", MqttQualityOfServiceLevel.AtLeastOnce);
await server.InjectApplicationMessage("T", "4", MqttQualityOfServiceLevel.AtLeastOnce);
await server.InjectApplicationMessage("T", "3", MqttQualityOfServiceLevel.AtLeastOnce);
await server.InjectApplicationMessage("T", "6", MqttQualityOfServiceLevel.AtLeastOnce);
await server.InjectApplicationMessage("T", "5", MqttQualityOfServiceLevel.AtLeastOnce);
await server.InjectApplicationMessage("T", "6", MqttQualityOfServiceLevel.AtLeastOnce);
await server.InjectApplicationMessage("T", "5", MqttQualityOfServiceLevel.AtLeastOnce);
await server.InjectApplicationMessage("T", "8", MqttQualityOfServiceLevel.AtLeastOnce);
await server.InjectApplicationMessage("T", "7", MqttQualityOfServiceLevel.AtLeastOnce);
await server.InjectApplicationMessage("T", "8", MqttQualityOfServiceLevel.AtLeastOnce);
await server.InjectApplicationMessage("T", "7", MqttQualityOfServiceLevel.AtLeastOnce);
await server.InjectApplicationMessage("T", "9", MqttQualityOfServiceLevel.AtLeastOnce);
await server.InjectApplicationMessage("T", "9", MqttQualityOfServiceLevel.AtLeastOnce);
await LongTestDelay();
await LongTestDelay();
// Create a new client for the existing message.
var client = await testEnvironment.ConnectClient(o => o.WithClientId("A").WithCleanSession(false));
var messages = testEnvironment.CreateApplicationMessageHandler(client);
// Create a new client for the existing message.
var client = await testEnvironment.ConnectClient(o => o.WithClientId("A").WithCleanSession(false));
var messages = testEnvironment.CreateApplicationMessageHandler(client);
await LongTestDelay();
await LongTestDelay();
var payloadSequence = messages.GeneratePayloadSequence();
Assert.AreEqual("0214365879", payloadSequence);
var payloadSequence = messages.GeneratePayloadSequence();
Assert.AreEqual("0214365879", payloadSequence);
// Disconnect and reconnect to make sure that the server will not send the messages twice.
await client.DisconnectAsync();
await LongTestDelay();
await client.ReconnectAsync();
await LongTestDelay();
// Disconnect and reconnect to make sure that the server will not send the messages twice.
await client.DisconnectAsync();
await LongTestDelay();
await client.ReconnectAsync();
await LongTestDelay();
payloadSequence = messages.GeneratePayloadSequence();
Assert.AreEqual("0214365879", payloadSequence);
}
payloadSequence = messages.GeneratePayloadSequence();
Assert.AreEqual("0214365879", payloadSequence);
}
}
@@ -6,42 +6,40 @@ using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Formatter;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class Retain_As_Published_Tests : BaseTestClass
{
[TestClass]
public sealed class Retain_As_Published_Tests : BaseTestClass
[TestMethod]
public Task Subscribe_With_Retain_As_Published()
{
[TestMethod]
public Task Subscribe_With_Retain_As_Published()
{
return ExecuteTest(true);
}
return ExecuteTest(true);
}
[TestMethod]
public Task Subscribe_Without_Retain_As_Published()
{
return ExecuteTest(false);
}
[TestMethod]
public Task Subscribe_Without_Retain_As_Published()
{
return ExecuteTest(false);
}
async Task ExecuteTest(bool retainAsPublished)
{
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
{
await testEnvironment.StartServer();
async Task ExecuteTest(bool retainAsPublished)
{
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
await testEnvironment.StartServer();
var client1 = await testEnvironment.ConnectClient();
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client1);
var topicFilter = testEnvironment.ClientFactory.CreateTopicFilterBuilder().WithTopic("Topic").WithRetainAsPublished(retainAsPublished).Build();
await client1.SubscribeAsync(topicFilter);
await LongTestDelay();
var client1 = await testEnvironment.ConnectClient();
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client1);
var topicFilter = testEnvironment.ClientFactory.CreateTopicFilterBuilder().WithTopic("Topic").WithRetainAsPublished(retainAsPublished).Build();
await client1.SubscribeAsync(topicFilter);
await LongTestDelay();
// The client will publish a message where it is itself subscribing to.
await client1.PublishStringAsync("Topic", "Payload", retain: true);
await LongTestDelay();
// The client will publish a message where it is itself subscribing to.
await client1.PublishStringAsync("Topic", "Payload", retain: true);
await LongTestDelay();
applicationMessageHandler.AssertReceivedCountEquals(1);
Assert.AreEqual(retainAsPublished, applicationMessageHandler.ReceivedEventArgs[0].ApplicationMessage.Retain);
}
}
applicationMessageHandler.AssertReceivedCountEquals(1);
Assert.AreEqual(retainAsPublished, applicationMessageHandler.ReceivedEventArgs[0].ApplicationMessage.Retain);
}
}
@@ -7,63 +7,61 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Formatter;
using MQTTnet.Protocol;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class Retain_Handling_Tests : BaseTestClass
{
[TestClass]
public sealed class Retain_Handling_Tests : BaseTestClass
[TestMethod]
public Task Send_At_Subscribe()
{
[TestMethod]
public Task Send_At_Subscribe()
{
return ExecuteTest(MqttRetainHandling.SendAtSubscribe, 1, 2, 3);
}
return ExecuteTest(MqttRetainHandling.SendAtSubscribe, 1, 2, 3);
}
[TestMethod]
public Task Do_Not_Send_On_Subscribe()
{
return ExecuteTest(MqttRetainHandling.DoNotSendOnSubscribe, 0, 1, 1);
}
[TestMethod]
public Task Do_Not_Send_On_Subscribe()
{
return ExecuteTest(MqttRetainHandling.DoNotSendOnSubscribe, 0, 1, 1);
}
[TestMethod]
public Task Send_At_Subscribe_If_New_Subscription_Only()
{
return ExecuteTest(MqttRetainHandling.SendAtSubscribeIfNewSubscriptionOnly, 1, 2, 2);
}
[TestMethod]
public Task Send_At_Subscribe_If_New_Subscription_Only()
{
return ExecuteTest(MqttRetainHandling.SendAtSubscribeIfNewSubscriptionOnly, 1, 2, 2);
}
async Task ExecuteTest(
MqttRetainHandling retainHandling,
int expectedCountAfterSubscribe,
int expectedCountAfterSecondPublish,
int expectedCountAfterSecondSubscribe)
{
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
{
await testEnvironment.StartServer();
async Task ExecuteTest(
MqttRetainHandling retainHandling,
int expectedCountAfterSubscribe,
int expectedCountAfterSecondPublish,
int expectedCountAfterSecondSubscribe)
{
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
await testEnvironment.StartServer();
var client1 = await testEnvironment.ConnectClient();
await client1.PublishStringAsync("Topic", "Payload", retain: true);
var client1 = await testEnvironment.ConnectClient();
await client1.PublishStringAsync("Topic", "Payload", retain: true);
await LongTestDelay();
await LongTestDelay();
var client2 = await testEnvironment.ConnectClient();
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client2);
var client2 = await testEnvironment.ConnectClient();
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client2);
var topicFilter = testEnvironment.ClientFactory.CreateTopicFilterBuilder().WithTopic("Topic").WithRetainHandling(retainHandling).Build();
await client2.SubscribeAsync(topicFilter);
await LongTestDelay();
var topicFilter = testEnvironment.ClientFactory.CreateTopicFilterBuilder().WithTopic("Topic").WithRetainHandling(retainHandling).Build();
await client2.SubscribeAsync(topicFilter);
await LongTestDelay();
applicationMessageHandler.AssertReceivedCountEquals(expectedCountAfterSubscribe);
applicationMessageHandler.AssertReceivedCountEquals(expectedCountAfterSubscribe);
await client1.PublishStringAsync("Topic", "Payload", retain: true);
await LongTestDelay();
await client1.PublishStringAsync("Topic", "Payload", retain: true);
await LongTestDelay();
applicationMessageHandler.AssertReceivedCountEquals(expectedCountAfterSecondPublish);
applicationMessageHandler.AssertReceivedCountEquals(expectedCountAfterSecondPublish);
await client2.SubscribeAsync(topicFilter);
await LongTestDelay();
await client2.SubscribeAsync(topicFilter);
await LongTestDelay();
applicationMessageHandler.AssertReceivedCountEquals(expectedCountAfterSecondSubscribe);
}
}
applicationMessageHandler.AssertReceivedCountEquals(expectedCountAfterSecondSubscribe);
}
}
@@ -9,263 +9,243 @@ using MQTTnet.Formatter;
using MQTTnet.Packets;
using MQTTnet.Protocol;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class Retained_Messages_Tests : BaseTestClass
{
[TestClass]
public sealed class Retained_Messages_Tests : BaseTestClass
[TestMethod]
public async Task Clear_Retained_Message_With_Empty_Payload()
{
[TestMethod]
public async Task Clear_Retained_Message_With_Empty_Payload()
using var testEnvironment = CreateTestEnvironment();
await testEnvironment.StartServer();
var c1 = await testEnvironment.ConnectClient();
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[0]).WithRetainFlag().Build());
await c1.DisconnectAsync();
var c2 = await testEnvironment.ConnectClient();
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
await Task.Delay(200);
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce });
await Task.Delay(500);
messageHandler.AssertReceivedCountEquals(0);
}
[TestMethod]
public async Task Clear_Retained_Message_With_Null_Payload()
{
using var testEnvironment = CreateTestEnvironment();
await testEnvironment.StartServer();
var c1 = await testEnvironment.ConnectClient();
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload((byte[])null).WithRetainFlag().Build());
await c1.DisconnectAsync();
var c2 = await testEnvironment.ConnectClient();
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
await Task.Delay(200);
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce });
await Task.Delay(500);
messageHandler.AssertReceivedCountEquals(0);
}
[TestMethod]
public async Task Downgrade_QoS_Level()
{
using var testEnvironment = CreateTestEnvironment();
await testEnvironment.StartServer();
var c1 = await testEnvironment.ConnectClient();
// Add the retained message with QoS 2!
await c1.PublishAsync(
new MqttApplicationMessageBuilder().WithTopic("retained")
.WithPayload(new byte[3])
.WithRetainFlag()
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.ExactlyOnce)
.Build());
// The second client uses QoS 1 so a downgrade is required.
var c2 = await testEnvironment.ConnectClient();
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce });
await LongTestDelay();
messageHandler.AssertReceivedCountEquals(1);
Assert.AreEqual(MqttQualityOfServiceLevel.AtLeastOnce, messageHandler.ReceivedEventArgs.First().ApplicationMessage.QualityOfServiceLevel);
}
[TestMethod]
public async Task No_Upgrade_QoS_Level()
{
using var testEnvironment = CreateTestEnvironment();
await testEnvironment.StartServer();
var c1 = await testEnvironment.ConnectClient();
// Add the retained message with QoS 1!
await c1.PublishAsync(
new MqttApplicationMessageBuilder().WithTopic("retained")
.WithPayload(new byte[3])
.WithRetainFlag()
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
.Build());
// The second client uses QoS 2 so an upgrade is expected but according to the MQTT spec this is not supported!
var c2 = await testEnvironment.ConnectClient();
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce });
await LongTestDelay();
messageHandler.AssertReceivedCountEquals(1);
Assert.AreEqual(MqttQualityOfServiceLevel.AtLeastOnce, messageHandler.ReceivedEventArgs.First().ApplicationMessage.QualityOfServiceLevel);
}
[TestMethod]
public async Task Receive_No_Retained_Message_After_Subscribe()
{
using var testEnvironment = CreateTestEnvironment();
await testEnvironment.StartServer();
var c1 = await testEnvironment.ConnectClient();
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
await c1.DisconnectAsync();
var c2 = await testEnvironment.ConnectClient();
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
await c2.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("retained_other").Build());
await Task.Delay(500);
messageHandler.AssertReceivedCountEquals(0);
}
[TestMethod]
public async Task Receive_Retained_Message_After_Subscribe()
{
using var testEnvironment = CreateTestEnvironment();
await testEnvironment.StartServer();
var c1 = await testEnvironment.ConnectClient();
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
await c1.DisconnectAsync();
var c2 = await testEnvironment.ConnectClient();
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
await c2.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("retained").Build());
await Task.Delay(500);
messageHandler.AssertReceivedCountEquals(1);
Assert.IsTrue(messageHandler.ReceivedEventArgs.First().ApplicationMessage.Retain);
}
[TestMethod]
public async Task Receive_Retained_Messages_From_Higher_Qos_Level()
{
using var testEnvironment = CreateTestEnvironment();
await testEnvironment.StartServer();
// Upload retained message.
var c1 = await testEnvironment.ConnectClient();
await c1.PublishAsync(
new MqttApplicationMessageBuilder().WithTopic("retained")
.WithPayload(new byte[1])
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
.WithRetainFlag()
.Build());
await c1.DisconnectAsync();
// Subscribe using a new client.
var c2 = await testEnvironment.ConnectClient();
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
await Task.Delay(200);
// Using QoS 2 will lead to 1 instead because the publish was made with QoS level 1 (see 3.8.4 SUBSCRIBE Actions)!
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce });
await Task.Delay(500);
messageHandler.AssertReceivedCountEquals(1);
}
[TestMethod]
public async Task Retained_Messages_Flow()
{
using var testEnvironment = CreateTestEnvironment();
var retainedMessage = new MqttApplicationMessageBuilder().WithTopic("r").WithPayload("r").WithRetainFlag().Build();
await testEnvironment.StartServer();
var c1 = await testEnvironment.ConnectClient();
var c2 = await testEnvironment.ConnectClient();
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
await c1.PublishAsync(retainedMessage);
await c1.DisconnectAsync();
await LongTestDelay();
for (var i = 0; i < 5; i++)
{
using (var testEnvironment = CreateTestEnvironment())
{
await testEnvironment.StartServer();
await c2.UnsubscribeAsync("r");
await Task.Delay(100);
messageHandler.AssertReceivedCountEquals(i);
var c1 = await testEnvironment.ConnectClient();
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[0]).WithRetainFlag().Build());
await c1.DisconnectAsync();
var c2 = await testEnvironment.ConnectClient();
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
await Task.Delay(200);
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce });
await Task.Delay(500);
messageHandler.AssertReceivedCountEquals(0);
}
await c2.SubscribeAsync("r");
await Task.Delay(100);
messageHandler.AssertReceivedCountEquals(i + 1);
}
[TestMethod]
public async Task Clear_Retained_Message_With_Null_Payload()
{
using (var testEnvironment = CreateTestEnvironment())
{
await testEnvironment.StartServer();
await c2.DisconnectAsync();
}
var c1 = await testEnvironment.ConnectClient();
[TestMethod]
public async Task Server_Reports_Retained_Messages_Supported_V3()
{
using var testEnvironment = CreateTestEnvironment();
await testEnvironment.StartServer();
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload((byte[])null).WithRetainFlag().Build());
var client = testEnvironment.CreateClient();
await c1.DisconnectAsync();
var connectResult = await client.ConnectAsync(
testEnvironment.ClientFactory.CreateClientOptionsBuilder()
.WithProtocolVersion(MqttProtocolVersion.V311)
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
.Build());
var c2 = await testEnvironment.ConnectClient();
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
Assert.IsTrue(connectResult.RetainAvailable);
}
await Task.Delay(200);
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce });
await Task.Delay(500);
[TestMethod]
public async Task Server_Reports_Retained_Messages_Supported_V5()
{
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
await testEnvironment.StartServer();
messageHandler.AssertReceivedCountEquals(0);
}
}
var client = testEnvironment.CreateClient();
var connectResult = await client.ConnectAsync(
testEnvironment.ClientFactory.CreateClientOptionsBuilder()
.WithProtocolVersion(MqttProtocolVersion.V500)
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
.Build());
[TestMethod]
public async Task Downgrade_QoS_Level()
{
using (var testEnvironment = CreateTestEnvironment())
{
await testEnvironment.StartServer();
var c1 = await testEnvironment.ConnectClient();
// Add the retained message with QoS 2!
await c1.PublishAsync(
new MqttApplicationMessageBuilder().WithTopic("retained")
.WithPayload(new byte[3])
.WithRetainFlag()
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.ExactlyOnce)
.Build());
// The second client uses QoS 1 so a downgrade is required.
var c2 = await testEnvironment.ConnectClient();
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce });
await LongTestDelay();
messageHandler.AssertReceivedCountEquals(1);
Assert.AreEqual(MqttQualityOfServiceLevel.AtLeastOnce, messageHandler.ReceivedEventArgs.First().ApplicationMessage.QualityOfServiceLevel);
}
}
[TestMethod]
public async Task No_Upgrade_QoS_Level()
{
using (var testEnvironment = CreateTestEnvironment())
{
await testEnvironment.StartServer();
var c1 = await testEnvironment.ConnectClient();
// Add the retained message with QoS 1!
await c1.PublishAsync(
new MqttApplicationMessageBuilder().WithTopic("retained")
.WithPayload(new byte[3])
.WithRetainFlag()
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
.Build());
// The second client uses QoS 2 so an upgrade is expected but according to the MQTT spec this is not supported!
var c2 = await testEnvironment.ConnectClient();
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce });
await LongTestDelay();
messageHandler.AssertReceivedCountEquals(1);
Assert.AreEqual(MqttQualityOfServiceLevel.AtLeastOnce, messageHandler.ReceivedEventArgs.First().ApplicationMessage.QualityOfServiceLevel);
}
}
[TestMethod]
public async Task Receive_No_Retained_Message_After_Subscribe()
{
using (var testEnvironment = CreateTestEnvironment())
{
await testEnvironment.StartServer();
var c1 = await testEnvironment.ConnectClient();
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
await c1.DisconnectAsync();
var c2 = await testEnvironment.ConnectClient();
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
await c2.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("retained_other").Build());
await Task.Delay(500);
messageHandler.AssertReceivedCountEquals(0);
}
}
[TestMethod]
public async Task Receive_Retained_Message_After_Subscribe()
{
using (var testEnvironment = CreateTestEnvironment())
{
await testEnvironment.StartServer();
var c1 = await testEnvironment.ConnectClient();
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
await c1.DisconnectAsync();
var c2 = await testEnvironment.ConnectClient();
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
await c2.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("retained").Build());
await Task.Delay(500);
messageHandler.AssertReceivedCountEquals(1);
Assert.IsTrue(messageHandler.ReceivedEventArgs.First().ApplicationMessage.Retain);
}
}
[TestMethod]
public async Task Receive_Retained_Messages_From_Higher_Qos_Level()
{
using (var testEnvironment = CreateTestEnvironment())
{
await testEnvironment.StartServer();
// Upload retained message.
var c1 = await testEnvironment.ConnectClient();
await c1.PublishAsync(
new MqttApplicationMessageBuilder().WithTopic("retained")
.WithPayload(new byte[1])
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
.WithRetainFlag()
.Build());
await c1.DisconnectAsync();
// Subscribe using a new client.
var c2 = await testEnvironment.ConnectClient();
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
await Task.Delay(200);
// Using QoS 2 will lead to 1 instead because the publish was made with QoS level 1 (see 3.8.4 SUBSCRIBE Actions)!
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce });
await Task.Delay(500);
messageHandler.AssertReceivedCountEquals(1);
}
}
[TestMethod]
public async Task Retained_Messages_Flow()
{
using (var testEnvironment = CreateTestEnvironment())
{
var retainedMessage = new MqttApplicationMessageBuilder().WithTopic("r").WithPayload("r").WithRetainFlag().Build();
await testEnvironment.StartServer();
var c1 = await testEnvironment.ConnectClient();
var c2 = await testEnvironment.ConnectClient();
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
await c1.PublishAsync(retainedMessage);
await c1.DisconnectAsync();
await LongTestDelay();
for (var i = 0; i < 5; i++)
{
await c2.UnsubscribeAsync("r");
await Task.Delay(100);
messageHandler.AssertReceivedCountEquals(i);
await c2.SubscribeAsync("r");
await Task.Delay(100);
messageHandler.AssertReceivedCountEquals(i + 1);
}
await c2.DisconnectAsync();
}
}
[TestMethod]
public async Task Server_Reports_Retained_Messages_Supported_V3()
{
using (var testEnvironment = CreateTestEnvironment())
{
await testEnvironment.StartServer();
var client = testEnvironment.CreateClient();
var connectResult = await client.ConnectAsync(
testEnvironment.ClientFactory.CreateClientOptionsBuilder()
.WithProtocolVersion(MqttProtocolVersion.V311)
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
.Build());
Assert.IsTrue(connectResult.RetainAvailable);
}
}
[TestMethod]
public async Task Server_Reports_Retained_Messages_Supported_V5()
{
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
{
await testEnvironment.StartServer();
var client = testEnvironment.CreateClient();
var connectResult = await client.ConnectAsync(
testEnvironment.ClientFactory.CreateClientOptionsBuilder()
.WithProtocolVersion(MqttProtocolVersion.V500)
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
.Build());
Assert.IsTrue(connectResult.RetainAvailable);
}
}
Assert.IsTrue(connectResult.RetainAvailable);
}
}
+134 -144
View File
@@ -10,161 +10,151 @@ using MQTTnet.Exceptions;
using MQTTnet.Internal;
using MQTTnet.Protocol;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class Security_Tests : BaseTestClass
{
[TestClass]
public sealed class Security_Tests : BaseTestClass
[TestMethod]
public async Task Do_Not_Affect_Authorized_Clients()
{
[TestMethod]
public async Task Do_Not_Affect_Authorized_Clients()
using var testEnvironment = CreateTestEnvironment();
testEnvironment.IgnoreClientLogErrors = true;
await testEnvironment.StartServer();
var publishedApplicationMessages = new List<MqttApplicationMessage>();
testEnvironment.Server.InterceptingPublishAsync += eventArgs =>
{
using (var testEnvironment = CreateTestEnvironment())
lock (publishedApplicationMessages)
{
testEnvironment.IgnoreClientLogErrors = true;
await testEnvironment.StartServer();
var publishedApplicationMessages = new List<MqttApplicationMessage>();
testEnvironment.Server.InterceptingPublishAsync += eventArgs =>
{
lock (publishedApplicationMessages)
{
publishedApplicationMessages.Add(eventArgs.ApplicationMessage);
}
return CompletedTask.Instance;
};
testEnvironment.Server.ValidatingConnectionAsync += eventArgs =>
{
if (eventArgs.UserName == "SECRET")
{
eventArgs.ReasonCode = MqttConnectReasonCode.Success;
}
else
{
eventArgs.ReasonCode = MqttConnectReasonCode.NotAuthorized;
}
return CompletedTask.Instance;
};
using (var validClient = testEnvironment.CreateClient())
{
await validClient.ConnectAsync(
testEnvironment.ClientFactory.CreateClientOptionsBuilder()
.WithTcpServer("localhost", testEnvironment.ServerPort)
.WithCredentials("SECRET")
.WithClientId("CLIENT")
.Build());
await validClient.PublishStringAsync("HELLO 1");
// The following code tries to connect a new client with the same client ID but invalid
// credentials. This should block the second client but keep the first one up and running.
try
{
using (var invalidClient = testEnvironment.CreateClient())
{
await invalidClient.ConnectAsync(
testEnvironment.ClientFactory.CreateClientOptionsBuilder()
.WithTcpServer("localhost", testEnvironment.ServerPort)
.WithCredentials("???")
.WithClientId("CLIENT")
.Build());
}
}
catch
{
// Ignore errors from the second client.
}
await LongTestDelay();
await validClient.PublishStringAsync("HELLO 2");
await LongTestDelay();
await validClient.PublishStringAsync("HELLO 3");
await LongTestDelay();
Assert.AreEqual(3, publishedApplicationMessages.Count);
Assert.AreEqual(1, testEnvironment.Server.GetClientsAsync().GetAwaiter().GetResult().Count);
}
publishedApplicationMessages.Add(eventArgs.ApplicationMessage);
}
}
[TestMethod]
public Task Handle_Wrong_Password()
return CompletedTask.Instance;
};
testEnvironment.Server.ValidatingConnectionAsync += eventArgs =>
{
return TestCredentials("UserName", "x");
}
[TestMethod]
public Task Handle_Wrong_UserName()
{
return TestCredentials("x", "Password1");
}
[TestMethod]
public Task Handle_Wrong_UserName_And_Password()
{
return TestCredentials("x", "x");
}
[TestMethod]
public async Task Use_Username_Null_Password_Empty()
{
string username = null;
var password = string.Empty;
using (var testEnvironment = CreateTestEnvironment())
if (eventArgs.UserName == "SECRET")
{
testEnvironment.IgnoreClientLogErrors = true;
await testEnvironment.StartServer();
var client = testEnvironment.CreateClient();
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).WithCredentials(username, password).Build();
var ex = await Assert.ThrowsExceptionAsync<MqttConnectingFailedException>(async () => await client.ConnectAsync(clientOptions));
Assert.IsInstanceOfType(ex.InnerException, typeof(MqttProtocolViolationException));
Assert.AreEqual("Error while authenticating. If the User Name Flag is set to 0, the Password Flag MUST be set to 0 [MQTT-3.1.2-22].", ex.Message, false);
eventArgs.ReasonCode = MqttConnectReasonCode.Success;
}
}
async Task TestCredentials(string userName, string password)
{
using (var testEnvironment = CreateTestEnvironment())
else
{
testEnvironment.IgnoreClientLogErrors = true;
var server = await testEnvironment.StartServer();
server.ValidatingConnectionAsync += e =>
{
if (e.UserName != "UserName1")
{
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
}
if (e.Password != "Password1")
{
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
}
return CompletedTask.Instance;
};
var client = testEnvironment.CreateClient();
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).WithCredentials(userName, password).Build();
var response = await client.ConnectAsync(clientOptions);
Assert.AreEqual(MqttClientConnectResultCode.BadUserNameOrPassword, response.ResultCode);
eventArgs.ReasonCode = MqttConnectReasonCode.NotAuthorized;
}
return CompletedTask.Instance;
};
using var validClient = testEnvironment.CreateClient();
await validClient.ConnectAsync(
testEnvironment.ClientFactory.CreateClientOptionsBuilder()
.WithTcpServer("localhost", testEnvironment.ServerPort)
.WithCredentials("SECRET")
.WithClientId("CLIENT")
.Build());
await validClient.PublishStringAsync("HELLO 1");
// The following code tries to connect a new client with the same client ID but invalid
// credentials. This should block the second client but keep the first one up and running.
try
{
using var invalidClient = testEnvironment.CreateClient();
await invalidClient.ConnectAsync(
testEnvironment.ClientFactory.CreateClientOptionsBuilder()
.WithTcpServer("localhost", testEnvironment.ServerPort)
.WithCredentials("???")
.WithClientId("CLIENT")
.Build());
}
catch
{
// Ignore errors from the second client.
}
await LongTestDelay();
await validClient.PublishStringAsync("HELLO 2");
await LongTestDelay();
await validClient.PublishStringAsync("HELLO 3");
await LongTestDelay();
Assert.AreEqual(3, publishedApplicationMessages.Count);
Assert.AreEqual(1, testEnvironment.Server.GetClientsAsync().GetAwaiter().GetResult().Count);
}
[TestMethod]
public Task Handle_Wrong_Password()
{
return TestCredentials("UserName", "x");
}
[TestMethod]
public Task Handle_Wrong_UserName()
{
return TestCredentials("x", "Password1");
}
[TestMethod]
public Task Handle_Wrong_UserName_And_Password()
{
return TestCredentials("x", "x");
}
[TestMethod]
public async Task Use_Username_Null_Password_Empty()
{
string username = null;
var password = string.Empty;
using var testEnvironment = CreateTestEnvironment();
testEnvironment.IgnoreClientLogErrors = true;
await testEnvironment.StartServer();
var client = testEnvironment.CreateClient();
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).WithCredentials(username, password).Build();
var ex = await Assert.ThrowsExceptionAsync<MqttConnectingFailedException>(async () => await client.ConnectAsync(clientOptions).ConfigureAwait(false));
Assert.IsInstanceOfType(ex.InnerException, typeof(MqttProtocolViolationException));
Assert.AreEqual("Error while authenticating. If the User Name Flag is set to 0, the Password Flag MUST be set to 0 [MQTT-3.1.2-22].", ex.Message, false);
}
async Task TestCredentials(string userName, string password)
{
using var testEnvironment = CreateTestEnvironment();
testEnvironment.IgnoreClientLogErrors = true;
var server = await testEnvironment.StartServer();
server.ValidatingConnectionAsync += e =>
{
if (e.UserName != "UserName1")
{
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
}
if (e.Password != "Password1")
{
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
}
return CompletedTask.Instance;
};
var client = testEnvironment.CreateClient();
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).WithCredentials(userName, password).Build();
var response = await client.ConnectAsync(clientOptions);
Assert.AreEqual(MqttClientConnectResultCode.BadUserNameOrPassword, response.ResultCode);
}
}
@@ -10,32 +10,31 @@ using MQTTnet.Protocol;
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class Server_Reference_Tests : BaseTestClass
{
[TestMethod]
public async Task Server_Reports_With_Reference_Server()
{
using (var testEnvironment = CreateTestEnvironment())
using var testEnvironment = CreateTestEnvironment();
testEnvironment.IgnoreClientLogErrors = true;
var server = await testEnvironment.StartServer();
server.ValidatingConnectionAsync += e =>
{
testEnvironment.IgnoreClientLogErrors = true;
e.ReasonCode = MqttConnectReasonCode.ServerMoved;
e.ServerReference = "new_server";
return CompletedTask.Instance;
};
var server = await testEnvironment.StartServer();
var client = testEnvironment.CreateClient();
server.ValidatingConnectionAsync += e =>
{
e.ReasonCode = MqttConnectReasonCode.ServerMoved;
e.ServerReference = "new_server";
return CompletedTask.Instance;
};
var response = await client.ConnectAsync(
new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500).WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
var client = testEnvironment.CreateClient();
var response = await client.ConnectAsync(
new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500).WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
Assert.AreEqual(MqttClientConnectResultCode.ServerMoved, response.ResultCode);
Assert.AreEqual("new_server", response.ServerReference);
}
Assert.AreEqual(MqttClientConnectResultCode.ServerMoved, response.ResultCode);
Assert.AreEqual("new_server", response.ServerReference);
}
}
+293 -313
View File
@@ -13,377 +13,357 @@ using MQTTnet.Internal;
using MQTTnet.Protocol;
using MQTTnet.Tests.Mockups;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class Session_Tests : BaseTestClass
{
[TestClass]
public sealed class Session_Tests : BaseTestClass
[TestMethod]
public async Task Clean_Session_Persistence()
{
[TestMethod]
public async Task Clean_Session_Persistence()
using var testEnvironment = new TestEnvironment(TestContext);
// Create server with persistent sessions enabled
await testEnvironment.StartServer(o => o.WithPersistentSessions());
const string clientId = "Client1";
// Create client with clean session and long session expiry interval
var client1 = await testEnvironment.ConnectClient(
o => o.WithProtocolVersion(MqttProtocolVersion.V311)
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
.WithSessionExpiryInterval(9999) // not relevant for v311 but testing impact
.WithCleanSession() // start and end with clean session
.WithClientId(clientId)
.Build());
// Disconnect; empty session should be removed from server
await client1.DisconnectAsync();
// Simulate some time delay between connections
await Task.Delay(1000);
// Reconnect the same client ID without clean session
var client2 = testEnvironment.CreateClient();
var options = testEnvironment.ClientFactory.CreateClientOptionsBuilder()
.WithProtocolVersion(MqttProtocolVersion.V311)
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
.WithSessionExpiryInterval(9999) // not relevant for v311 but testing impact
.WithCleanSession(false) // see if there is a session
.WithClientId(clientId)
.Build();
var result = await client2.ConnectAsync(options).ConfigureAwait(false);
await client2.DisconnectAsync();
// Session should NOT be present for MQTT v311 and initial CleanSession == true
Assert.IsTrue(!result.IsSessionPresent, "Session present");
}
[TestMethod]
public async Task Do_Not_Use_Expired_Session()
{
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
await testEnvironment.StartServer(o => o.WithPersistentSessions());
var c1 = await testEnvironment.ConnectClient(o => o.WithClientId("Client1").WithCleanSession(false).WithSessionExpiryInterval(3));
// Kill the client connection and ensure that the session will stay there but is expired after 3 seconds!
c1.Dispose();
await Task.Delay(TimeSpan.FromSeconds(6));
c1 = testEnvironment.CreateClient();
var options = testEnvironment.CreateDefaultClientOptionsBuilder().WithClientId("Client1").WithCleanSession(false).WithSessionExpiryInterval(3).Build();
var connectResult = await c1.ConnectAsync(options);
Assert.AreEqual(false, connectResult.IsSessionPresent);
}
[TestMethod]
public async Task Fire_Deleted_Event()
{
using var testEnvironment = CreateTestEnvironment();
// Arrange client and server.
var server = await testEnvironment.StartServer(o => o.WithPersistentSessions(false));
var deletedEventFired = false;
server.SessionDeletedAsync += _ =>
{
using (var testEnvironment = new TestEnvironment(TestContext))
{
// Create server with persistent sessions enabled
deletedEventFired = true;
return CompletedTask.Instance;
};
await testEnvironment.StartServer(o => o.WithPersistentSessions());
var client = await testEnvironment.ConnectClient();
const string clientId = "Client1";
// Act: Disconnect the client -> Event must be fired.
await client.DisconnectAsync();
// Create client with clean session and long session expiry interval
await LongTestDelay();
var client1 = await testEnvironment.ConnectClient(
o => o.WithProtocolVersion(MqttProtocolVersion.V311)
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
.WithSessionExpiryInterval(9999) // not relevant for v311 but testing impact
.WithCleanSession() // start and end with clean session
.WithClientId(clientId)
.Build());
// Assert that the event was fired properly.
Assert.IsTrue(deletedEventFired);
}
// Disconnect; empty session should be removed from server
[TestMethod]
public async Task Get_Session_Items_In_Status()
{
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
await client1.DisconnectAsync();
server.ValidatingConnectionAsync += e =>
{
// Don't validate anything. Just set some session items.
e.SessionItems["can_subscribe_x"] = true;
e.SessionItems["default_payload"] = "Hello World";
// Simulate some time delay between connections
return CompletedTask.Instance;
};
await Task.Delay(1000);
await testEnvironment.ConnectClient();
// Reconnect the same client ID without clean session
var sessionStatus = await testEnvironment.Server.GetSessionsAsync();
var session = sessionStatus.First();
var client2 = testEnvironment.CreateClient();
var options = testEnvironment.ClientFactory.CreateClientOptionsBuilder()
.WithProtocolVersion(MqttProtocolVersion.V311)
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
.WithSessionExpiryInterval(9999) // not relevant for v311 but testing impact
.WithCleanSession(false) // see if there is a session
.WithClientId(clientId)
.Build();
Assert.AreEqual(true, session.Items["can_subscribe_x"]);
}
[TestMethod]
[DataRow(MqttProtocolVersion.V310)]
[DataRow(MqttProtocolVersion.V311)]
[DataRow(MqttProtocolVersion.V500)]
public async Task Handle_Parallel_Connection_Attempts(MqttProtocolVersion protocolVersion)
{
using var testEnvironment = CreateTestEnvironment();
testEnvironment.IgnoreClientLogErrors = true;
var result = await client2.ConnectAsync(options).ConfigureAwait(false);
await testEnvironment.StartServer();
await client2.DisconnectAsync();
var options = new MqttClientOptionsBuilder().WithClientId("1").WithTimeout(TimeSpan.FromSeconds(10)).WithProtocolVersion(protocolVersion);
// Session should NOT be present for MQTT v311 and initial CleanSession == true
var hasReceive = false;
Assert.IsTrue(!result.IsSessionPresent, "Session present");
}
// Try to connect 50 clients at the same time.
var clients = await Task.WhenAll(Enumerable.Range(0, 50).Select(_ => ConnectAndSubscribe(testEnvironment, options, OnReceive)));
var connectedClients = clients.Where(c => c?.TryPingAsync().GetAwaiter().GetResult() == true).ToList();
await LongTestDelay();
Assert.AreEqual(1, connectedClients.Count);
var option2 = new MqttClientOptionsBuilder().WithClientId("2").WithKeepAlivePeriod(TimeSpan.FromSeconds(10));
var sendClient = await testEnvironment.ConnectClient(option2);
await sendClient.PublishStringAsync("aaa", "1");
await LongTestDelay();
Assert.AreEqual(true, hasReceive);
return;
void OnReceive()
{
hasReceive = true;
}
}
[TestMethod]
public async Task Do_Not_Use_Expired_Session()
[TestMethod]
[DataRow(MqttQualityOfServiceLevel.ExactlyOnce)]
[DataRow(MqttQualityOfServiceLevel.AtLeastOnce)]
public async Task Retry_If_Not_PubAck(MqttQualityOfServiceLevel qos)
{
long count = 0;
using var testEnvironment = CreateTestEnvironment();
await testEnvironment.StartServer(o => o.WithPersistentSessions());
var publisher = await testEnvironment.ConnectClient();
var subscriber = await testEnvironment.ConnectClient(o => o.WithClientId(qos.ToString()).WithCleanSession(false));
subscriber.ApplicationMessageReceivedAsync += c =>
{
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
{
await testEnvironment.StartServer(o => o.WithPersistentSessions());
c.AutoAcknowledge = false;
++count;
Console.WriteLine("process");
return CompletedTask.Instance;
};
var c1 = await testEnvironment.ConnectClient(o => o.WithClientId("Client1").WithCleanSession(false).WithSessionExpiryInterval(3));
await subscriber.SubscribeAsync("#", qos);
// Kill the client connection and ensure that the session will stay there but is expired after 3 seconds!
c1.Dispose();
var pub = publisher.PublishStringAsync("a", null, qos);
await Task.Delay(TimeSpan.FromSeconds(6));
await Task.Delay(100);
await subscriber.DisconnectAsync();
await subscriber.ConnectAsync(subscriber.Options);
await Task.Delay(100);
c1 = testEnvironment.CreateClient();
var options = testEnvironment.CreateDefaultClientOptionsBuilder().WithClientId("Client1").WithCleanSession(false).WithSessionExpiryInterval(3).Build();
var res = await pub;
var connectResult = await c1.ConnectAsync(options);
Assert.AreEqual(false, connectResult.IsSessionPresent);
}
}
Assert.AreEqual(MqttClientPublishReasonCode.Success, res.ReasonCode);
Assert.AreEqual(2, count);
}
[TestMethod]
public async Task Fire_Deleted_Event()
[TestMethod]
public async Task Session_Takeover()
{
using var testEnvironment = CreateTestEnvironment();
await testEnvironment.StartServer();
var options = new MqttClientOptionsBuilder().WithCleanSession(false)
.WithProtocolVersion(MqttProtocolVersion.V500) // Disconnect reason is only available in MQTT 5+
.WithClientId("a");
var client1 = await testEnvironment.ConnectClient(options);
await Task.Delay(500);
var disconnectReason = MqttClientDisconnectReason.NormalDisconnection;
client1.DisconnectedAsync += c =>
{
using (var testEnvironment = CreateTestEnvironment())
{
// Arrange client and server.
var server = await testEnvironment.StartServer(o => o.WithPersistentSessions(false));
disconnectReason = c.Reason;
return CompletedTask.Instance;
};
var deletedEventFired = false;
server.SessionDeletedAsync += e =>
var client2 = await testEnvironment.ConnectClient(options);
await Task.Delay(500);
Assert.IsFalse(client1.IsConnected);
Assert.IsTrue(client2.IsConnected);
Assert.AreEqual(MqttClientDisconnectReason.SessionTakenOver, disconnectReason);
}
[TestMethod]
public async Task Set_Session_Item()
{
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
server.ValidatingConnectionAsync += e =>
{
// Don't validate anything. Just set some session items.
e.SessionItems["can_subscribe_x"] = true;
e.SessionItems["default_payload"] = "Hello World";
return CompletedTask.Instance;
};
server.InterceptingSubscriptionAsync += e =>
{
if (e.TopicFilter.Topic == "x")
{
if (e.SessionItems["can_subscribe_x"] as bool? == false)
{
deletedEventFired = true;
return CompletedTask.Instance;
};
var client = await testEnvironment.ConnectClient();
// Act: Disconnect the client -> Event must be fired.
await client.DisconnectAsync();
await LongTestDelay();
// Assert that the event was fired properly.
Assert.IsTrue(deletedEventFired);
}
}
[TestMethod]
public async Task Get_Session_Items_In_Status()
{
using (var testEnvironment = CreateTestEnvironment())
{
var server = await testEnvironment.StartServer();
server.ValidatingConnectionAsync += e =>
{
// Don't validate anything. Just set some session items.
e.SessionItems["can_subscribe_x"] = true;
e.SessionItems["default_payload"] = "Hello World";
return CompletedTask.Instance;
};
await testEnvironment.ConnectClient();
var sessionStatus = await testEnvironment.Server.GetSessionsAsync();
var session = sessionStatus.First();
Assert.AreEqual(true, session.Items["can_subscribe_x"]);
}
}
[TestMethod]
[DataRow(MqttProtocolVersion.V310)]
[DataRow(MqttProtocolVersion.V311)]
[DataRow(MqttProtocolVersion.V500)]
public async Task Handle_Parallel_Connection_Attempts(MqttProtocolVersion protocolVersion)
{
using (var testEnvironment = CreateTestEnvironment())
{
testEnvironment.IgnoreClientLogErrors = true;
await testEnvironment.StartServer();
var options = new MqttClientOptionsBuilder().WithClientId("1").WithTimeout(TimeSpan.FromSeconds(10)).WithProtocolVersion(protocolVersion);
var hasReceive = false;
void OnReceive()
{
hasReceive = true;
e.Response.ReasonCode = MqttSubscribeReasonCode.ImplementationSpecificError;
}
// Try to connect 50 clients at the same time.
var clients = await Task.WhenAll(Enumerable.Range(0, 50).Select(i => ConnectAndSubscribe(testEnvironment, options, OnReceive)));
var connectedClients = clients.Where(c => c?.TryPingAsync().GetAwaiter().GetResult() == true).ToList();
await LongTestDelay();
Assert.AreEqual(1, connectedClients.Count);
var option2 = new MqttClientOptionsBuilder().WithClientId("2").WithKeepAlivePeriod(TimeSpan.FromSeconds(10));
var sendClient = await testEnvironment.ConnectClient(option2);
await sendClient.PublishStringAsync("aaa", "1");
await LongTestDelay();
Assert.AreEqual(true, hasReceive);
}
}
[TestMethod]
[DataRow(MqttQualityOfServiceLevel.ExactlyOnce)]
[DataRow(MqttQualityOfServiceLevel.AtLeastOnce)]
public async Task Retry_If_Not_PubAck(MqttQualityOfServiceLevel qos)
return CompletedTask.Instance;
};
server.InterceptingPublishAsync += e =>
{
long count = 0;
using (var testEnvironment = CreateTestEnvironment())
{
await testEnvironment.StartServer(o => o.WithPersistentSessions());
e.ApplicationMessage.PayloadSegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes(e.SessionItems["default_payload"] as string ?? string.Empty));
return CompletedTask.Instance;
};
var publisher = await testEnvironment.ConnectClient();
string receivedPayload = null;
var subscriber = await testEnvironment.ConnectClient(o => o.WithClientId(qos.ToString()).WithCleanSession(false));
subscriber.ApplicationMessageReceivedAsync += c =>
{
c.AutoAcknowledge = false;
++count;
Console.WriteLine("process");
return CompletedTask.Instance;
};
await subscriber.SubscribeAsync("#", qos);
var pub = publisher.PublishStringAsync("a", null, qos);
await Task.Delay(100);
await subscriber.DisconnectAsync();
await subscriber.ConnectAsync(subscriber.Options);
await Task.Delay(100);
var res = await pub;
Assert.AreEqual(MqttClientPublishReasonCode.Success, res.ReasonCode);
Assert.AreEqual(2, count);
}
}
[TestMethod]
public async Task Session_Takeover()
var client = await testEnvironment.ConnectClient();
client.ApplicationMessageReceivedAsync += e =>
{
using (var testEnvironment = CreateTestEnvironment())
{
await testEnvironment.StartServer();
receivedPayload = e.ApplicationMessage.ConvertPayloadToString();
return CompletedTask.Instance;
};
var options = new MqttClientOptionsBuilder().WithCleanSession(false)
.WithProtocolVersion(MqttProtocolVersion.V500) // Disconnect reason is only available in MQTT 5+
.WithClientId("a");
var subscribeResult = await client.SubscribeAsync("x");
var client1 = await testEnvironment.ConnectClient(options);
await Task.Delay(500);
Assert.AreEqual(MqttClientSubscribeResultCode.GrantedQoS0, subscribeResult.Items.First().ResultCode);
var disconnectReason = MqttClientDisconnectReason.NormalDisconnection;
client1.DisconnectedAsync += c =>
{
disconnectReason = c.Reason;
return CompletedTask.Instance;
};
var client2 = await testEnvironment.ConnectClient();
await client2.PublishStringAsync("x");
var client2 = await testEnvironment.ConnectClient(options);
await Task.Delay(500);
await Task.Delay(1000);
Assert.IsFalse(client1.IsConnected);
Assert.IsTrue(client2.IsConnected);
Assert.AreEqual("Hello World", receivedPayload);
}
Assert.AreEqual(MqttClientDisconnectReason.SessionTakenOver, disconnectReason);
}
}
[TestMethod]
public async Task Use_Clean_Session()
{
using var testEnvironment = CreateTestEnvironment();
await testEnvironment.StartServer();
[TestMethod]
public async Task Set_Session_Item()
var client = testEnvironment.CreateClient();
var connectResult = await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort).WithCleanSession().Build());
// Create the session including the subscription.
var client1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("a").WithCleanSession(false));
await client1.SubscribeAsync("x");
await client1.DisconnectAsync();
await Task.Delay(500);
Assert.IsFalse(connectResult.IsSessionPresent);
}
[TestMethod]
public async Task Will_Message_Do_Not_Send_On_Takeover()
{
using var testEnvironment = CreateTestEnvironment();
var receivedMessagesCount = 0;
await testEnvironment.StartServer();
// C1 will receive the last will!
var c1 = await testEnvironment.ConnectClient();
c1.ApplicationMessageReceivedAsync += _ =>
{
using (var testEnvironment = CreateTestEnvironment())
{
var server = await testEnvironment.StartServer();
Interlocked.Increment(ref receivedMessagesCount);
return CompletedTask.Instance;
};
server.ValidatingConnectionAsync += e =>
{
// Don't validate anything. Just set some session items.
e.SessionItems["can_subscribe_x"] = true;
e.SessionItems["default_payload"] = "Hello World";
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
return CompletedTask.Instance;
};
// C2 has the last will defined.
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will").WithClientId("WillOwner");
server.InterceptingSubscriptionAsync += e =>
{
if (e.TopicFilter.Topic == "x")
{
if (e.SessionItems["can_subscribe_x"] as bool? == false)
{
e.Response.ReasonCode = MqttSubscribeReasonCode.ImplementationSpecificError;
}
}
await testEnvironment.ConnectClient(clientOptions);
return CompletedTask.Instance;
};
// C3 will do the connection takeover.
await testEnvironment.ConnectClient(clientOptions);
server.InterceptingPublishAsync += e =>
{
e.ApplicationMessage.PayloadSegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes(e.SessionItems["default_payload"] as string ?? string.Empty));
return CompletedTask.Instance;
};
await Task.Delay(1000);
string receivedPayload = null;
Assert.AreEqual(0, receivedMessagesCount);
}
var client = await testEnvironment.ConnectClient();
client.ApplicationMessageReceivedAsync += e =>
{
receivedPayload = e.ApplicationMessage.ConvertPayloadToString();
return CompletedTask.Instance;
};
var subscribeResult = await client.SubscribeAsync("x");
Assert.AreEqual(MqttClientSubscribeResultCode.GrantedQoS0, subscribeResult.Items.First().ResultCode);
var client2 = await testEnvironment.ConnectClient();
await client2.PublishStringAsync("x");
await Task.Delay(1000);
Assert.AreEqual("Hello World", receivedPayload);
}
}
[TestMethod]
public async Task Use_Clean_Session()
static async Task<IMqttClient> ConnectAndSubscribe(TestEnvironment testEnvironment, MqttClientOptionsBuilder options, Action onReceive)
{
try
{
using (var testEnvironment = CreateTestEnvironment())
var client = await testEnvironment.ConnectClient(options).ConfigureAwait(false);
client.ApplicationMessageReceivedAsync += _ =>
{
await testEnvironment.StartServer();
onReceive();
return CompletedTask.Instance;
};
var client = testEnvironment.CreateClient();
var connectResult = await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort).WithCleanSession().Build());
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(30));
await client.SubscribeAsync("aaa", MqttQualityOfServiceLevel.AtMostOnce, timeout.Token).ConfigureAwait(false);
// Create the session including the subscription.
var client1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("a").WithCleanSession(false));
await client1.SubscribeAsync("x");
await client1.DisconnectAsync();
await Task.Delay(500);
Assert.IsFalse(connectResult.IsSessionPresent);
}
return client;
}
[TestMethod]
public async Task Will_Message_Do_Not_Send_On_Takeover()
catch
{
using (var testEnvironment = CreateTestEnvironment())
{
var receivedMessagesCount = 0;
await testEnvironment.StartServer();
// C1 will receive the last will!
var c1 = await testEnvironment.ConnectClient();
c1.ApplicationMessageReceivedAsync += e =>
{
Interlocked.Increment(ref receivedMessagesCount);
return CompletedTask.Instance;
};
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
// C2 has the last will defined.
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will").WithClientId("WillOwner");
await testEnvironment.ConnectClient(clientOptions);
// C3 will do the connection takeover.
await testEnvironment.ConnectClient(clientOptions);
await Task.Delay(1000);
Assert.AreEqual(0, receivedMessagesCount);
}
}
static async Task<IMqttClient> ConnectAndSubscribe(TestEnvironment testEnvironment, MqttClientOptionsBuilder options, Action onReceive)
{
try
{
var client = await testEnvironment.ConnectClient(options).ConfigureAwait(false);
client.ApplicationMessageReceivedAsync += e =>
{
onReceive();
return CompletedTask.Instance;
};
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(30)))
{
await client.SubscribeAsync("aaa", MqttQualityOfServiceLevel.AtMostOnce, timeout.Token).ConfigureAwait(false);
}
return client;
}
catch (Exception)
{
return null;
}
return null;
}
}
}
@@ -7,43 +7,39 @@ using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Formatter;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class Shared_Subscriptions_Tests : BaseTestClass
{
[TestClass]
public sealed class Shared_Subscriptions_Tests : BaseTestClass
[TestMethod]
public async Task Server_Reports_Shared_Subscriptions_Not_Supported()
{
[TestMethod]
public async Task Server_Reports_Shared_Subscriptions_Not_Supported()
{
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
{
await testEnvironment.StartServer();
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
await testEnvironment.StartServer();
var client = testEnvironment.CreateClient();
var connectResult = await client.ConnectAsync(testEnvironment.ClientFactory.CreateClientOptionsBuilder()
.WithProtocolVersion(MqttProtocolVersion.V500)
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
var client = testEnvironment.CreateClient();
var connectResult = await client.ConnectAsync(testEnvironment.ClientFactory.CreateClientOptionsBuilder()
.WithProtocolVersion(MqttProtocolVersion.V500)
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
Assert.IsFalse(connectResult.SharedSubscriptionAvailable);
}
}
Assert.IsFalse(connectResult.SharedSubscriptionAvailable);
}
[TestMethod]
public async Task Subscription_Of_Shared_Subscription_Is_Denied()
{
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
{
await testEnvironment.StartServer();
[TestMethod]
public async Task Subscription_Of_Shared_Subscription_Is_Denied()
{
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
await testEnvironment.StartServer();
var client = testEnvironment.CreateClient();
await client.ConnectAsync(testEnvironment.ClientFactory.CreateClientOptionsBuilder()
.WithProtocolVersion(MqttProtocolVersion.V500)
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
var client = testEnvironment.CreateClient();
await client.ConnectAsync(testEnvironment.ClientFactory.CreateClientOptionsBuilder()
.WithProtocolVersion(MqttProtocolVersion.V500)
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
var subscribeResult = await client.SubscribeAsync("$share/A");
var subscribeResult = await client.SubscribeAsync("$share/A");
Assert.AreEqual(MqttClientSubscribeResultCode.SharedSubscriptionsNotSupported, subscribeResult.Items.First().ResultCode);
}
}
Assert.AreEqual(MqttClientSubscribeResultCode.SharedSubscriptionsNotSupported, subscribeResult.Items.First().ResultCode);
}
}
+96 -107
View File
@@ -12,189 +12,178 @@ using MQTTnet.Tests.Mockups;
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class Status_Tests : BaseTestClass
{
[TestMethod]
public async Task Disconnect_Client()
{
using (var testEnvironment = new TestEnvironment(TestContext))
{
var server = await testEnvironment.StartServer();
using var testEnvironment = new TestEnvironment(TestContext);
var server = await testEnvironment.StartServer();
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("client1"));
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("client1"));
await Task.Delay(1000);
await Task.Delay(1000);
var clientStatus = await server.GetClientsAsync();
var clientStatus = await server.GetClientsAsync();
Assert.AreEqual(1, clientStatus.Count);
Assert.IsTrue(clientStatus.Any(s => s.Id == c1.Options.ClientId));
Assert.AreEqual(1, clientStatus.Count);
Assert.IsTrue(clientStatus.Any(s => s.Id == c1.Options.ClientId));
await clientStatus.First().DisconnectAsync();
await clientStatus[0].DisconnectAsync();
await Task.Delay(500);
await Task.Delay(500);
Assert.IsFalse(c1.IsConnected);
Assert.IsFalse(c1.IsConnected);
clientStatus = await server.GetClientsAsync();
clientStatus = await server.GetClientsAsync();
Assert.AreEqual(0, clientStatus.Count);
}
Assert.AreEqual(0, clientStatus.Count);
}
[TestMethod]
public async Task Keep_Persistent_Session_Version311()
{
using (var testEnvironment = new TestEnvironment(TestContext))
{
var server = await testEnvironment.StartServer(o => o.WithPersistentSessions());
using var testEnvironment = new TestEnvironment(TestContext);
var server = await testEnvironment.StartServer(o => o.WithPersistentSessions());
var c1 = await testEnvironment.ConnectClient(
new MqttClientOptionsBuilder().WithClientId("client1").WithCleanSession(false).WithProtocolVersion(MqttProtocolVersion.V311));
var c2 = await testEnvironment.ConnectClient(
new MqttClientOptionsBuilder().WithClientId("client2").WithCleanSession(false).WithProtocolVersion(MqttProtocolVersion.V311));
var c1 = await testEnvironment.ConnectClient(
new MqttClientOptionsBuilder().WithClientId("client1").WithCleanSession(false).WithProtocolVersion(MqttProtocolVersion.V311));
var c2 = await testEnvironment.ConnectClient(
new MqttClientOptionsBuilder().WithClientId("client2").WithCleanSession(false).WithProtocolVersion(MqttProtocolVersion.V311));
await c1.DisconnectAsync();
await c1.DisconnectAsync();
await LongTestDelay();
await LongTestDelay();
var clientStatus = await server.GetClientsAsync();
var sessionStatus = await server.GetSessionsAsync();
var clientStatus = await server.GetClientsAsync();
var sessionStatus = await server.GetSessionsAsync();
Assert.AreEqual(1, clientStatus.Count);
Assert.AreEqual(2, sessionStatus.Count);
Assert.AreEqual(1, clientStatus.Count);
Assert.AreEqual(2, sessionStatus.Count);
await c2.DisconnectAsync();
await c2.DisconnectAsync();
await LongTestDelay();
await LongTestDelay();
clientStatus = await server.GetClientsAsync();
sessionStatus = await server.GetSessionsAsync();
clientStatus = await server.GetClientsAsync();
sessionStatus = await server.GetSessionsAsync();
Assert.AreEqual(0, clientStatus.Count);
Assert.AreEqual(2, sessionStatus.Count);
}
Assert.AreEqual(0, clientStatus.Count);
Assert.AreEqual(2, sessionStatus.Count);
}
[TestMethod]
public async Task Keep_Persistent_Session_Version500()
{
using (var testEnvironment = new TestEnvironment(TestContext))
{
var server = await testEnvironment.StartServer(o => o.WithPersistentSessions());
using var testEnvironment = new TestEnvironment(TestContext);
var server = await testEnvironment.StartServer(o => o.WithPersistentSessions());
var c1 = await testEnvironment.ConnectClient(
new MqttClientOptionsBuilder().WithClientId("client1").WithCleanSession(false).WithProtocolVersion(MqttProtocolVersion.V500));
var c2 = await testEnvironment.ConnectClient(
new MqttClientOptionsBuilder().WithClientId("client2").WithCleanSession(false).WithProtocolVersion(MqttProtocolVersion.V500));
var c1 = await testEnvironment.ConnectClient(
new MqttClientOptionsBuilder().WithClientId("client1").WithCleanSession(false).WithProtocolVersion(MqttProtocolVersion.V500));
var c2 = await testEnvironment.ConnectClient(
new MqttClientOptionsBuilder().WithClientId("client2").WithCleanSession(false).WithProtocolVersion(MqttProtocolVersion.V500));
// The session expiry interval is mandatory for MQTT5.0.0 in order keep session!
await c1.DisconnectAsync(sessionExpiryInterval: 60);
// The session expiry interval is mandatory for MQTT5.0.0 in order keep session!
await c1.DisconnectAsync(sessionExpiryInterval: 60);
await LongTestDelay();
await LongTestDelay();
var clientStatus = await server.GetClientsAsync();
var sessionStatus = await server.GetSessionsAsync();
var clientStatus = await server.GetClientsAsync();
var sessionStatus = await server.GetSessionsAsync();
Assert.AreEqual(1, clientStatus.Count);
Assert.AreEqual(2, sessionStatus.Count);
Assert.AreEqual(1, clientStatus.Count);
Assert.AreEqual(2, sessionStatus.Count);
// The session expiry interval is mandatory for MQTT5.0.0 in order keep session!
await c2.DisconnectAsync(sessionExpiryInterval: 60);
// The session expiry interval is mandatory for MQTT5.0.0 in order keep session!
await c2.DisconnectAsync(sessionExpiryInterval: 60);
await LongTestDelay();
await LongTestDelay();
clientStatus = await server.GetClientsAsync();
sessionStatus = await server.GetSessionsAsync();
clientStatus = await server.GetClientsAsync();
sessionStatus = await server.GetSessionsAsync();
Assert.AreEqual(0, clientStatus.Count);
Assert.AreEqual(2, sessionStatus.Count);
}
Assert.AreEqual(0, clientStatus.Count);
Assert.AreEqual(2, sessionStatus.Count);
}
[TestMethod]
public async Task Show_Client_And_Session_Statistics()
{
using (var testEnvironment = new TestEnvironment(TestContext))
{
var server = await testEnvironment.StartServer();
using var testEnvironment = new TestEnvironment(TestContext);
var server = await testEnvironment.StartServer();
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("client1"));
var c2 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("client2"));
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("client1"));
var c2 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("client2"));
await Task.Delay(500);
await Task.Delay(500);
var clientStatus = await server.GetClientsAsync();
var sessionStatus = await server.GetSessionsAsync();
var clientStatus = await server.GetClientsAsync();
var sessionStatus = await server.GetSessionsAsync();
Assert.AreEqual(2, clientStatus.Count);
Assert.AreEqual(2, sessionStatus.Count);
Assert.AreEqual(2, clientStatus.Count);
Assert.AreEqual(2, sessionStatus.Count);
Assert.IsTrue(clientStatus.Any(s => s.Id == c1.Options.ClientId));
Assert.IsTrue(clientStatus.Any(s => s.Id == c2.Options.ClientId));
Assert.IsTrue(clientStatus.Any(s => s.Id == c1.Options.ClientId));
Assert.IsTrue(clientStatus.Any(s => s.Id == c2.Options.ClientId));
await c1.DisconnectAsync();
await c2.DisconnectAsync();
await c1.DisconnectAsync();
await c2.DisconnectAsync();
await Task.Delay(500);
await Task.Delay(500);
clientStatus = await server.GetClientsAsync();
sessionStatus = await server.GetSessionsAsync();
clientStatus = await server.GetClientsAsync();
sessionStatus = await server.GetSessionsAsync();
Assert.AreEqual(0, clientStatus.Count);
Assert.AreEqual(0, sessionStatus.Count);
}
Assert.AreEqual(0, clientStatus.Count);
Assert.AreEqual(0, sessionStatus.Count);
}
[TestMethod]
public async Task Track_Sent_Application_Messages()
{
using (var testEnvironment = new TestEnvironment(TestContext))
using var testEnvironment = new TestEnvironment(TestContext);
var server = await testEnvironment.StartServer(new MqttServerOptionsBuilder().WithPersistentSessions());
var c1 = await testEnvironment.ConnectClient();
for (var i = 1; i < 25; i++)
{
var server = await testEnvironment.StartServer(new MqttServerOptionsBuilder().WithPersistentSessions());
await c1.PublishStringAsync("a");
await Task.Delay(50);
var c1 = await testEnvironment.ConnectClient();
for (var i = 1; i < 25; i++)
{
await c1.PublishStringAsync("a");
await Task.Delay(50);
var clientStatus = await server.GetClientsAsync();
Assert.AreEqual(i, clientStatus.First().SentApplicationMessagesCount);
Assert.AreEqual(0, clientStatus.First().ReceivedApplicationMessagesCount);
}
var clientStatus = await server.GetClientsAsync();
Assert.AreEqual(i, clientStatus[0].SentApplicationMessagesCount);
Assert.AreEqual(0, clientStatus[0].ReceivedApplicationMessagesCount);
}
}
[TestMethod]
public async Task Track_Sent_Packets()
{
using (var testEnvironment = new TestEnvironment(TestContext))
using var testEnvironment = new TestEnvironment(TestContext);
var server = await testEnvironment.StartServer(new MqttServerOptionsBuilder().WithPersistentSessions());
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithNoKeepAlive());
for (var i = 1; i < 25; i++)
{
var server = await testEnvironment.StartServer(new MqttServerOptionsBuilder().WithPersistentSessions());
// At most once will send one packet to the client and the server will reply
// with an additional ACK packet.
await c1.PublishStringAsync("a", string.Empty, MqttQualityOfServiceLevel.AtLeastOnce);
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithNoKeepAlive());
await Task.Delay(500);
for (var i = 1; i < 25; i++)
{
// At most once will send one packet to the client and the server will reply
// with an additional ACK packet.
await c1.PublishStringAsync("a", string.Empty, MqttQualityOfServiceLevel.AtLeastOnce);
var clientStatus = await server.GetClientsAsync();
await Task.Delay(500);
Assert.AreEqual(i, clientStatus[0].SentApplicationMessagesCount, "SAMC invalid!");
var clientStatus = await server.GetClientsAsync();
// + 1 because CONNECT is also counted.
Assert.AreEqual(i + 1, clientStatus[0].SentPacketsCount, "SPC invalid!");
Assert.AreEqual(i, clientStatus.First().SentApplicationMessagesCount, "SAMC invalid!");
// + 1 because CONNECT is also counted.
Assert.AreEqual(i + 1, clientStatus.First().SentPacketsCount, "SPC invalid!");
// +2 because ConnACK + PubAck package is already counted.
Assert.AreEqual(i + 2, clientStatus.First().ReceivedPacketsCount, "RPC invalid!");
}
// +2 because ConnACK + PubAck package is already counted.
Assert.AreEqual(i + 2, clientStatus[0].ReceivedPacketsCount, "RPC invalid!");
}
}
}
+385 -409
View File
@@ -15,420 +15,396 @@ using MQTTnet.Protocol;
using MQTTnet.Server;
using MQTTnet.Tests.Helpers;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class Subscribe_Tests : BaseTestClass
{
[TestClass]
public sealed class Subscribe_Tests : BaseTestClass
[TestMethod]
[DataRow("A", "A", true)]
[DataRow("A", "B", false)]
[DataRow("A", "#", true)]
[DataRow("A", "+", true)]
[DataRow("A/B", "A/B", true)]
[DataRow("A/B", "A/+", true)]
[DataRow("A/B", "A/#", true)]
[DataRow("A/B/C", "A/B/C", true)]
[DataRow("A/B/C", "A/+/C", true)]
[DataRow("A/B/C", "A/+/+", true)]
[DataRow("A/B/C", "A/+/#", true)]
[DataRow("A/B/C/D", "A/B/C/D", true)]
[DataRow("A/B/C/D", "A/+/C/+", true)]
[DataRow("A/B/C/D", "A/+/C/#", true)]
[DataRow("A/B/C", "A/B/+", true)]
[DataRow("A/B1/B2/C", "A/+/C", false)]
public async Task Subscription_Roundtrip(string topic, string filter, bool shouldWork)
{
[TestMethod]
[DataRow("A", "A", true)]
[DataRow("A", "B", false)]
[DataRow("A", "#", true)]
[DataRow("A", "+", true)]
[DataRow("A/B", "A/B", true)]
[DataRow("A/B", "A/+", true)]
[DataRow("A/B", "A/#", true)]
[DataRow("A/B/C", "A/B/C", true)]
[DataRow("A/B/C", "A/+/C", true)]
[DataRow("A/B/C", "A/+/+", true)]
[DataRow("A/B/C", "A/+/#", true)]
[DataRow("A/B/C/D", "A/B/C/D", true)]
[DataRow("A/B/C/D", "A/+/C/+", true)]
[DataRow("A/B/C/D", "A/+/C/#", true)]
[DataRow("A/B/C", "A/B/+", true)]
[DataRow("A/B1/B2/C", "A/+/C", false)]
public async Task Subscription_Roundtrip(string topic, string filter, bool shouldWork)
using var testEnvironment = CreateTestEnvironment();
await testEnvironment.StartServer().ConfigureAwait(false);
var receiver = await testEnvironment.ConnectClient();
await receiver.SubscribeAsync(filter).ConfigureAwait(false);
var receivedMessages = receiver.TrackReceivedMessages();
var sender = await testEnvironment.ConnectClient();
await sender.PublishStringAsync(topic, "PAYLOAD").ConfigureAwait(false);
await LongTestDelay().ConfigureAwait(false);
if (shouldWork)
{
using (var testEnvironment = CreateTestEnvironment())
{
await testEnvironment.StartServer().ConfigureAwait(false);
var receiver = await testEnvironment.ConnectClient();
await receiver.SubscribeAsync(filter).ConfigureAwait(false);
var receivedMessages = receiver.TrackReceivedMessages();
var sender = await testEnvironment.ConnectClient();
await sender.PublishStringAsync(topic, "PAYLOAD").ConfigureAwait(false);
await LongTestDelay().ConfigureAwait(false);
if (shouldWork)
{
Assert.AreEqual(1, receivedMessages.Count, message: "The filter should work!");
}
else
{
Assert.AreEqual(0, receivedMessages.Count, message: "The filter should not work!");
}
}
Assert.AreEqual(1, receivedMessages.Count, message: "The filter should work!");
}
[TestMethod]
public async Task Deny_Invalid_Topic()
else
{
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
{
var server = await testEnvironment.StartServer();
server.InterceptingSubscriptionAsync += e =>
{
if (e.TopicFilter.Topic == "not_allowed_topic")
{
e.Response.ReasonCode = MqttSubscribeReasonCode.TopicFilterInvalid;
}
return CompletedTask.Instance;
};
var client = await testEnvironment.ConnectClient();
var subscribeResult = await client.SubscribeAsync("allowed_topic");
Assert.AreEqual(MqttClientSubscribeResultCode.GrantedQoS0, subscribeResult.Items.First().ResultCode);
subscribeResult = await client.SubscribeAsync("not_allowed_topic");
Assert.AreEqual(MqttClientSubscribeResultCode.TopicFilterInvalid, subscribeResult.Items.First().ResultCode);
}
}
[TestMethod]
public async Task Intercept_Subscribe_With_User_Properties()
{
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
{
var server = await testEnvironment.StartServer();
InterceptingSubscriptionEventArgs eventArgs = null;
server.InterceptingSubscriptionAsync += e =>
{
eventArgs = e;
return CompletedTask.Instance;
};
var client = await testEnvironment.ConnectClient();
var subscribeOptions = testEnvironment.ClientFactory.CreateSubscribeOptionsBuilder().WithTopicFilter("X").WithUserProperty("A", "1").Build();
await client.SubscribeAsync(subscribeOptions);
CollectionAssert.AreEqual(subscribeOptions.UserProperties.ToList(), eventArgs.UserProperties);
}
}
[TestMethod]
[ExpectedException(typeof(MqttClientDisconnectedException))]
public async Task Disconnect_While_Subscribing()
{
using (var testEnvironment = CreateTestEnvironment())
{
var server = await testEnvironment.StartServer();
// The client will be disconnect directly after subscribing!
server.ClientSubscribedTopicAsync += ev => server.DisconnectClientAsync(ev.ClientId, MqttDisconnectReasonCode.NormalDisconnection);
var client = await testEnvironment.ConnectClient();
await client.SubscribeAsync("#");
}
}
[TestMethod]
public async Task Enqueue_Message_After_Subscription()
{
using (var testEnvironment = CreateTestEnvironment())
{
var server = await testEnvironment.StartServer();
server.ClientSubscribedTopicAsync += e =>
{
server.InjectApplicationMessage(new InjectedMqttApplicationMessage(new MqttApplicationMessageBuilder().WithTopic("test_topic").Build()));
return CompletedTask.Instance;
};
var client = await testEnvironment.ConnectClient();
var receivedMessages = testEnvironment.CreateApplicationMessageHandler(client);
await client.SubscribeAsync("test_topic");
await LongTestDelay();
Assert.AreEqual(1, receivedMessages.ReceivedEventArgs.Count);
}
}
[TestMethod]
public async Task Intercept_Subscription()
{
using (var testEnvironment = CreateTestEnvironment())
{
var server = await testEnvironment.StartServer();
server.InterceptingSubscriptionAsync += e =>
{
// Set the topic to "a" regards what the client wants to subscribe.
e.TopicFilter.Topic = "a";
return CompletedTask.Instance;
};
var topicAReceived = false;
var topicBReceived = false;
var client = await testEnvironment.ConnectClient();
client.ApplicationMessageReceivedAsync += e =>
{
if (e.ApplicationMessage.Topic == "a")
{
topicAReceived = true;
}
else if (e.ApplicationMessage.Topic == "b")
{
topicBReceived = true;
}
return CompletedTask.Instance;
};
await client.SubscribeAsync("b");
await client.PublishStringAsync("a");
await Task.Delay(500);
Assert.IsTrue(topicAReceived);
Assert.IsFalse(topicBReceived);
}
}
[TestMethod]
public async Task Response_Contains_Equal_Reason_Codes()
{
using (var testEnvironment = CreateTestEnvironment())
{
await testEnvironment.StartServer();
var client = await testEnvironment.ConnectClient();
var subscribeOptions = new MqttClientSubscribeOptionsBuilder().WithTopicFilter("a")
.WithTopicFilter("b", MqttQualityOfServiceLevel.AtLeastOnce)
.WithTopicFilter("c", MqttQualityOfServiceLevel.ExactlyOnce)
.WithTopicFilter("d")
.Build();
var response = await client.SubscribeAsync(subscribeOptions);
Assert.AreEqual(subscribeOptions.TopicFilters.Count, response.Items.Count);
}
}
[TestMethod]
public async Task Subscribe_Lots_In_Multiple_Requests()
{
using (var testEnvironment = CreateTestEnvironment())
{
var receivedMessagesCount = 0;
await testEnvironment.StartServer();
var c1 = await testEnvironment.ConnectClient();
c1.ApplicationMessageReceivedAsync += e =>
{
Interlocked.Increment(ref receivedMessagesCount);
return CompletedTask.Instance;
};
for (var i = 0; i < 500; i++)
{
var so = new MqttClientSubscribeOptionsBuilder().WithTopicFilter(i.ToString()).Build();
await c1.SubscribeAsync(so).ConfigureAwait(false);
await Task.Delay(10);
}
var c2 = await testEnvironment.ConnectClient();
var messageBuilder = new MqttApplicationMessageBuilder();
for (var i = 0; i < 500; i++)
{
messageBuilder.WithTopic(i.ToString());
await c2.PublishAsync(messageBuilder.Build()).ConfigureAwait(false);
await Task.Delay(10);
}
SpinWait.SpinUntil(() => receivedMessagesCount == 500, 5000);
Assert.AreEqual(500, receivedMessagesCount);
}
}
[TestMethod]
public async Task Subscribe_Lots_In_Single_Request()
{
using (var testEnvironment = CreateTestEnvironment())
{
var receivedMessagesCount = 0;
await testEnvironment.StartServer();
var c1 = await testEnvironment.ConnectClient();
c1.ApplicationMessageReceivedAsync += e =>
{
Interlocked.Increment(ref receivedMessagesCount);
return CompletedTask.Instance;
};
var optionsBuilder = new MqttClientSubscribeOptionsBuilder();
for (var i = 0; i < 500; i++)
{
optionsBuilder.WithTopicFilter(i.ToString());
}
await c1.SubscribeAsync(optionsBuilder.Build()).ConfigureAwait(false);
var c2 = await testEnvironment.ConnectClient();
var messageBuilder = new MqttApplicationMessageBuilder();
for (var i = 0; i < 500; i++)
{
messageBuilder.WithTopic(i.ToString());
await c2.PublishAsync(messageBuilder.Build()).ConfigureAwait(false);
}
SpinWait.SpinUntil(() => receivedMessagesCount == 500, TimeSpan.FromSeconds(20));
Assert.AreEqual(500, receivedMessagesCount);
}
}
[TestMethod]
public async Task Subscribe_Multiple_In_Multiple_Request()
{
using (var testEnvironment = CreateTestEnvironment())
{
var receivedMessagesCount = 0;
await testEnvironment.StartServer();
var c1 = await testEnvironment.ConnectClient();
c1.ApplicationMessageReceivedAsync += e =>
{
Interlocked.Increment(ref receivedMessagesCount);
return CompletedTask.Instance;
};
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("a").Build());
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("b").Build());
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("c").Build());
var c2 = await testEnvironment.ConnectClient();
await c2.PublishStringAsync("a");
await Task.Delay(100);
Assert.AreEqual(receivedMessagesCount, 1);
await c2.PublishStringAsync("b");
await Task.Delay(100);
Assert.AreEqual(receivedMessagesCount, 2);
await c2.PublishStringAsync("c");
await Task.Delay(100);
Assert.AreEqual(receivedMessagesCount, 3);
}
}
[TestMethod]
public async Task Subscribe_Multiple_In_Single_Request()
{
using (var testEnvironment = CreateTestEnvironment())
{
var receivedMessagesCount = 0;
await testEnvironment.StartServer();
var c1 = await testEnvironment.ConnectClient();
c1.ApplicationMessageReceivedAsync += e =>
{
Interlocked.Increment(ref receivedMessagesCount);
return CompletedTask.Instance;
};
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("a").WithTopicFilter("b").WithTopicFilter("c").Build());
var c2 = await testEnvironment.ConnectClient();
await c2.PublishStringAsync("a");
await Task.Delay(100);
Assert.AreEqual(receivedMessagesCount, 1);
await c2.PublishStringAsync("b");
await Task.Delay(100);
Assert.AreEqual(receivedMessagesCount, 2);
await c2.PublishStringAsync("c");
await Task.Delay(100);
Assert.AreEqual(receivedMessagesCount, 3);
}
}
[TestMethod]
public async Task Subscribe_Unsubscribe()
{
using (var testEnvironment = CreateTestEnvironment())
{
var receivedMessagesCount = 0;
var server = await testEnvironment.StartServer();
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("c1"));
c1.ApplicationMessageReceivedAsync += e =>
{
Interlocked.Increment(ref receivedMessagesCount);
return CompletedTask.Instance;
};
var c2 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("c2"));
var message = new MqttApplicationMessageBuilder().WithTopic("a").WithQualityOfServiceLevel(MqttQualityOfServiceLevel.ExactlyOnce).Build();
await c2.PublishAsync(message);
await Task.Delay(500);
Assert.AreEqual(0, receivedMessagesCount);
var subscribeEventCalled = false;
server.ClientSubscribedTopicAsync += e =>
{
subscribeEventCalled = e.TopicFilter.Topic == "a" && e.ClientId == c1.Options.ClientId;
return CompletedTask.Instance;
};
await c1.SubscribeAsync(new MqttTopicFilter { Topic = "a", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce });
await Task.Delay(250);
Assert.IsTrue(subscribeEventCalled, "Subscribe event not called.");
await c2.PublishAsync(message);
await Task.Delay(250);
Assert.AreEqual(1, receivedMessagesCount);
var unsubscribeEventCalled = false;
server.ClientUnsubscribedTopicAsync += e =>
{
unsubscribeEventCalled = e.TopicFilter == "a" && e.ClientId == c1.Options.ClientId;
return CompletedTask.Instance;
};
await c1.UnsubscribeAsync("a");
await Task.Delay(250);
Assert.IsTrue(unsubscribeEventCalled, "Unsubscribe event not called.");
await c2.PublishAsync(message);
await Task.Delay(500);
Assert.AreEqual(1, receivedMessagesCount);
await Task.Delay(500);
Assert.AreEqual(1, receivedMessagesCount);
}
Assert.AreEqual(0, receivedMessages.Count, message: "The filter should not work!");
}
}
[TestMethod]
public async Task Deny_Invalid_Topic()
{
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
var server = await testEnvironment.StartServer();
server.InterceptingSubscriptionAsync += e =>
{
if (e.TopicFilter.Topic == "not_allowed_topic")
{
e.Response.ReasonCode = MqttSubscribeReasonCode.TopicFilterInvalid;
}
return CompletedTask.Instance;
};
var client = await testEnvironment.ConnectClient();
var subscribeResult = await client.SubscribeAsync("allowed_topic");
Assert.AreEqual(MqttClientSubscribeResultCode.GrantedQoS0, subscribeResult.Items.First().ResultCode);
subscribeResult = await client.SubscribeAsync("not_allowed_topic");
Assert.AreEqual(MqttClientSubscribeResultCode.TopicFilterInvalid, subscribeResult.Items.First().ResultCode);
}
[TestMethod]
public async Task Intercept_Subscribe_With_User_Properties()
{
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
var server = await testEnvironment.StartServer();
InterceptingSubscriptionEventArgs eventArgs = null;
server.InterceptingSubscriptionAsync += e =>
{
eventArgs = e;
return CompletedTask.Instance;
};
var client = await testEnvironment.ConnectClient();
var subscribeOptions = testEnvironment.ClientFactory.CreateSubscribeOptionsBuilder().WithTopicFilter("X").WithUserProperty("A", "1").Build();
await client.SubscribeAsync(subscribeOptions);
CollectionAssert.AreEqual(subscribeOptions.UserProperties.ToList(), eventArgs.UserProperties);
}
[TestMethod]
[ExpectedException(typeof(MqttClientDisconnectedException))]
public async Task Disconnect_While_Subscribing()
{
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
// The client will be disconnected directly after subscribing!
server.ClientSubscribedTopicAsync += ev => server.DisconnectClientAsync(ev.ClientId);
var client = await testEnvironment.ConnectClient();
await client.SubscribeAsync("#");
}
[TestMethod]
public async Task Enqueue_Message_After_Subscription()
{
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
server.ClientSubscribedTopicAsync += _ =>
{
server.InjectApplicationMessage(new InjectedMqttApplicationMessage(new MqttApplicationMessageBuilder().WithTopic("test_topic").Build()));
return CompletedTask.Instance;
};
var client = await testEnvironment.ConnectClient();
var receivedMessages = testEnvironment.CreateApplicationMessageHandler(client);
await client.SubscribeAsync("test_topic");
await LongTestDelay();
Assert.AreEqual(1, receivedMessages.ReceivedEventArgs.Count);
}
[TestMethod]
public async Task Intercept_Subscription()
{
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
server.InterceptingSubscriptionAsync += e =>
{
// Set the topic to "a" regards what the client wants to subscribe.
e.TopicFilter.Topic = "a";
return CompletedTask.Instance;
};
var topicAReceived = false;
var topicBReceived = false;
var client = await testEnvironment.ConnectClient();
client.ApplicationMessageReceivedAsync += e =>
{
if (e.ApplicationMessage.Topic == "a")
{
topicAReceived = true;
}
else if (e.ApplicationMessage.Topic == "b")
{
topicBReceived = true;
}
return CompletedTask.Instance;
};
await client.SubscribeAsync("b");
await client.PublishStringAsync("a");
await Task.Delay(500);
Assert.IsTrue(topicAReceived);
Assert.IsFalse(topicBReceived);
}
[TestMethod]
public async Task Response_Contains_Equal_Reason_Codes()
{
using var testEnvironment = CreateTestEnvironment();
await testEnvironment.StartServer();
var client = await testEnvironment.ConnectClient();
var subscribeOptions = new MqttClientSubscribeOptionsBuilder().WithTopicFilter("a")
.WithTopicFilter("b", MqttQualityOfServiceLevel.AtLeastOnce)
.WithTopicFilter("c", MqttQualityOfServiceLevel.ExactlyOnce)
.WithTopicFilter("d")
.Build();
var response = await client.SubscribeAsync(subscribeOptions);
Assert.AreEqual(subscribeOptions.TopicFilters.Count, response.Items.Count);
}
[TestMethod]
public async Task Subscribe_Lots_In_Multiple_Requests()
{
using var testEnvironment = CreateTestEnvironment();
var receivedMessagesCount = 0;
await testEnvironment.StartServer();
var c1 = await testEnvironment.ConnectClient();
c1.ApplicationMessageReceivedAsync += _ =>
{
Interlocked.Increment(ref receivedMessagesCount);
return CompletedTask.Instance;
};
for (var i = 0; i < 500; i++)
{
var so = new MqttClientSubscribeOptionsBuilder().WithTopicFilter(i.ToString()).Build();
await c1.SubscribeAsync(so).ConfigureAwait(false);
await Task.Delay(10);
}
var c2 = await testEnvironment.ConnectClient();
var messageBuilder = new MqttApplicationMessageBuilder();
for (var i = 0; i < 500; i++)
{
messageBuilder.WithTopic(i.ToString());
await c2.PublishAsync(messageBuilder.Build()).ConfigureAwait(false);
await Task.Delay(10);
}
SpinWait.SpinUntil(() => receivedMessagesCount == 500, 5000);
Assert.AreEqual(500, receivedMessagesCount);
}
[TestMethod]
public async Task Subscribe_Lots_In_Single_Request()
{
using var testEnvironment = CreateTestEnvironment();
var receivedMessagesCount = 0;
await testEnvironment.StartServer();
var c1 = await testEnvironment.ConnectClient();
c1.ApplicationMessageReceivedAsync += _ =>
{
Interlocked.Increment(ref receivedMessagesCount);
return CompletedTask.Instance;
};
var optionsBuilder = new MqttClientSubscribeOptionsBuilder();
for (var i = 0; i < 500; i++)
{
optionsBuilder.WithTopicFilter(i.ToString());
}
await c1.SubscribeAsync(optionsBuilder.Build()).ConfigureAwait(false);
var c2 = await testEnvironment.ConnectClient();
var messageBuilder = new MqttApplicationMessageBuilder();
for (var i = 0; i < 500; i++)
{
messageBuilder.WithTopic(i.ToString());
await c2.PublishAsync(messageBuilder.Build()).ConfigureAwait(false);
}
SpinWait.SpinUntil(() => receivedMessagesCount == 500, TimeSpan.FromSeconds(20));
Assert.AreEqual(500, receivedMessagesCount);
}
[TestMethod]
public async Task Subscribe_Multiple_In_Multiple_Request()
{
using var testEnvironment = CreateTestEnvironment();
var receivedMessagesCount = 0;
await testEnvironment.StartServer();
var c1 = await testEnvironment.ConnectClient();
c1.ApplicationMessageReceivedAsync += _ =>
{
Interlocked.Increment(ref receivedMessagesCount);
return CompletedTask.Instance;
};
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("a").Build());
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("b").Build());
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("c").Build());
var c2 = await testEnvironment.ConnectClient();
await c2.PublishStringAsync("a");
await Task.Delay(100);
Assert.AreEqual(receivedMessagesCount, 1);
await c2.PublishStringAsync("b");
await Task.Delay(100);
Assert.AreEqual(receivedMessagesCount, 2);
await c2.PublishStringAsync("c");
await Task.Delay(100);
Assert.AreEqual(receivedMessagesCount, 3);
}
[TestMethod]
public async Task Subscribe_Multiple_In_Single_Request()
{
using var testEnvironment = CreateTestEnvironment();
var receivedMessagesCount = 0;
await testEnvironment.StartServer();
var c1 = await testEnvironment.ConnectClient();
c1.ApplicationMessageReceivedAsync += _ =>
{
Interlocked.Increment(ref receivedMessagesCount);
return CompletedTask.Instance;
};
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("a").WithTopicFilter("b").WithTopicFilter("c").Build());
var c2 = await testEnvironment.ConnectClient();
await c2.PublishStringAsync("a");
await Task.Delay(100);
Assert.AreEqual(receivedMessagesCount, 1);
await c2.PublishStringAsync("b");
await Task.Delay(100);
Assert.AreEqual(receivedMessagesCount, 2);
await c2.PublishStringAsync("c");
await Task.Delay(100);
Assert.AreEqual(receivedMessagesCount, 3);
}
[TestMethod]
public async Task Subscribe_Unsubscribe()
{
using var testEnvironment = CreateTestEnvironment();
var receivedMessagesCount = 0;
var server = await testEnvironment.StartServer();
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("c1"));
c1.ApplicationMessageReceivedAsync += _ =>
{
Interlocked.Increment(ref receivedMessagesCount);
return CompletedTask.Instance;
};
var c2 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("c2"));
var message = new MqttApplicationMessageBuilder().WithTopic("a").WithQualityOfServiceLevel(MqttQualityOfServiceLevel.ExactlyOnce).Build();
await c2.PublishAsync(message);
await Task.Delay(500);
Assert.AreEqual(0, receivedMessagesCount);
var subscribeEventCalled = false;
server.ClientSubscribedTopicAsync += e =>
{
subscribeEventCalled = e.TopicFilter.Topic == "a" && e.ClientId == c1.Options.ClientId;
return CompletedTask.Instance;
};
await c1.SubscribeAsync(new MqttTopicFilter { Topic = "a", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce });
await Task.Delay(250);
Assert.IsTrue(subscribeEventCalled, "Subscribe event not called.");
await c2.PublishAsync(message);
await Task.Delay(250);
Assert.AreEqual(1, receivedMessagesCount);
var unsubscribeEventCalled = false;
server.ClientUnsubscribedTopicAsync += e =>
{
unsubscribeEventCalled = e.TopicFilter == "a" && e.ClientId == c1.Options.ClientId;
return CompletedTask.Instance;
};
await c1.UnsubscribeAsync("a");
await Task.Delay(250);
Assert.IsTrue(unsubscribeEventCalled, "Unsubscribe event not called.");
await c2.PublishAsync(message);
await Task.Delay(500);
Assert.AreEqual(1, receivedMessagesCount);
await Task.Delay(500);
Assert.AreEqual(1, receivedMessagesCount);
}
}
@@ -6,87 +6,81 @@ using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Formatter;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class Subscription_Identifier_Tests : BaseTestClass
{
[TestClass]
public sealed class Subscription_Identifier_Tests : BaseTestClass
[TestMethod]
public async Task Server_Reports_Subscription_Identifiers_Supported()
{
[TestMethod]
public async Task Server_Reports_Subscription_Identifiers_Supported()
{
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
{
await testEnvironment.StartServer();
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
await testEnvironment.StartServer();
var client = testEnvironment.CreateClient();
var connectResult = await client.ConnectAsync(testEnvironment.ClientFactory.CreateClientOptionsBuilder()
.WithProtocolVersion(MqttProtocolVersion.V500)
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
var client = testEnvironment.CreateClient();
var connectResult = await client.ConnectAsync(testEnvironment.ClientFactory.CreateClientOptionsBuilder()
.WithProtocolVersion(MqttProtocolVersion.V500)
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
Assert.IsTrue(connectResult.SubscriptionIdentifiersAvailable);
}
}
Assert.IsTrue(connectResult.SubscriptionIdentifiersAvailable);
}
[TestMethod]
public async Task Subscribe_With_Subscription_Identifier()
{
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
{
await testEnvironment.StartServer();
[TestMethod]
public async Task Subscribe_With_Subscription_Identifier()
{
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
await testEnvironment.StartServer();
var client1 = await testEnvironment.ConnectClient();
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client1);
var topicFilter = testEnvironment.ClientFactory.CreateTopicFilterBuilder().WithTopic("Topic").Build();
var subscribeOptions = testEnvironment.ClientFactory.CreateSubscribeOptionsBuilder().WithSubscriptionIdentifier(456).WithTopicFilter(topicFilter).Build();
var client1 = await testEnvironment.ConnectClient();
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client1);
var topicFilter = testEnvironment.ClientFactory.CreateTopicFilterBuilder().WithTopic("Topic").Build();
var subscribeOptions = testEnvironment.ClientFactory.CreateSubscribeOptionsBuilder().WithSubscriptionIdentifier(456).WithTopicFilter(topicFilter).Build();
await client1.SubscribeAsync(subscribeOptions);
await LongTestDelay();
await client1.SubscribeAsync(subscribeOptions);
await LongTestDelay();
applicationMessageHandler.AssertReceivedCountEquals(0);
applicationMessageHandler.AssertReceivedCountEquals(0);
// The client will publish a message where it is itself subscribing to.
await client1.PublishStringAsync("Topic", "Payload", retain: true);
await LongTestDelay();
// The client will publish a message where it is itself subscribing to.
await client1.PublishStringAsync("Topic", "Payload", retain: true);
await LongTestDelay();
applicationMessageHandler.AssertReceivedCountEquals(1);
applicationMessageHandler.AssertReceivedCountEquals(1);
applicationMessageHandler.ReceivedEventArgs[0].ApplicationMessage.SubscriptionIdentifiers.Contains(456);
}
}
Assert.IsTrue(applicationMessageHandler.ReceivedEventArgs[0].ApplicationMessage.SubscriptionIdentifiers.Contains(456));
}
[TestMethod]
public async Task Subscribe_With_Multiple_Subscription_Identifiers()
{
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
{
await testEnvironment.StartServer();
[TestMethod]
public async Task Subscribe_With_Multiple_Subscription_Identifiers()
{
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
await testEnvironment.StartServer();
var client1 = await testEnvironment.ConnectClient();
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client1);
var client1 = await testEnvironment.ConnectClient();
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client1);
var topicFilter = testEnvironment.ClientFactory.CreateTopicFilterBuilder().WithTopic("Topic/A").Build();
var subscribeOptions = testEnvironment.ClientFactory.CreateSubscribeOptionsBuilder().WithSubscriptionIdentifier(456).WithTopicFilter(topicFilter).Build();
await client1.SubscribeAsync(subscribeOptions);
var topicFilter = testEnvironment.ClientFactory.CreateTopicFilterBuilder().WithTopic("Topic/A").Build();
var subscribeOptions = testEnvironment.ClientFactory.CreateSubscribeOptionsBuilder().WithSubscriptionIdentifier(456).WithTopicFilter(topicFilter).Build();
await client1.SubscribeAsync(subscribeOptions);
await LongTestDelay();
await LongTestDelay();
topicFilter = testEnvironment.ClientFactory.CreateTopicFilterBuilder().WithTopic("Topic/+").Build();
subscribeOptions = testEnvironment.ClientFactory.CreateSubscribeOptionsBuilder().WithSubscriptionIdentifier(789).WithTopicFilter(topicFilter).Build();
await client1.SubscribeAsync(subscribeOptions);
topicFilter = testEnvironment.ClientFactory.CreateTopicFilterBuilder().WithTopic("Topic/+").Build();
subscribeOptions = testEnvironment.ClientFactory.CreateSubscribeOptionsBuilder().WithSubscriptionIdentifier(789).WithTopicFilter(topicFilter).Build();
await client1.SubscribeAsync(subscribeOptions);
await LongTestDelay();
await LongTestDelay();
applicationMessageHandler.AssertReceivedCountEquals(0);
applicationMessageHandler.AssertReceivedCountEquals(0);
// The client will publish a message where it is itself subscribing to.
await client1.PublishStringAsync("Topic/A", "Payload", retain: true);
await LongTestDelay();
// The client will publish a message where it is itself subscribing to.
await client1.PublishStringAsync("Topic/A", "Payload", retain: true);
await LongTestDelay();
applicationMessageHandler.AssertReceivedCountEquals(1);
applicationMessageHandler.AssertReceivedCountEquals(1);
applicationMessageHandler.ReceivedEventArgs[0].ApplicationMessage.SubscriptionIdentifiers.Contains(456);
applicationMessageHandler.ReceivedEventArgs[0].ApplicationMessage.SubscriptionIdentifiers.Contains(789);
}
}
Assert.IsTrue(applicationMessageHandler.ReceivedEventArgs[0].ApplicationMessage.SubscriptionIdentifiers.Contains(456));
Assert.IsTrue(applicationMessageHandler.ReceivedEventArgs[0].ApplicationMessage.SubscriptionIdentifiers.Contains(789));
}
}
File diff suppressed because it is too large Load Diff
+140 -144
View File
@@ -12,188 +12,184 @@ using MQTTnet.Formatter;
using MQTTnet.Server;
using MQTTnet.Tests.Mockups;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class Tls_Tests : BaseTestClass
{
[TestClass]
public sealed class Tls_Tests : BaseTestClass
static X509Certificate2 CreateCertificate(string oid)
{
static X509Certificate2 CreateCertificate(string oid)
var sanBuilder = new SubjectAlternativeNameBuilder();
sanBuilder.AddIpAddress(IPAddress.Loopback);
sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);
sanBuilder.AddDnsName("localhost");
using var rsa = RSA.Create();
var certRequest = new CertificateRequest("CN=localhost", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
certRequest.CertificateExtensions.Add(
new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
certRequest.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid(oid) }, false));
certRequest.CertificateExtensions.Add(sanBuilder.Build());
using var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now.AddMinutes(-10), DateTimeOffset.Now.AddMinutes(10));
var pfxCertificate = new X509Certificate2(
certificate.Export(X509ContentType.Pfx),
(string)null,
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
return pfxCertificate;
}
[TestMethod]
public async Task Tls_Swap_Test()
{
var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
var serverOptionsBuilder = testEnvironment.ServerFactory.CreateServerOptionsBuilder();
var firstOid = "1.3.6.1.5.5.7.3.1";
var secondOid = "1.3.6.1.5.5.7.3.2";
var certificateProvider = new CertificateProvider
{
var sanBuilder = new SubjectAlternativeNameBuilder();
sanBuilder.AddIpAddress(IPAddress.Loopback);
sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);
sanBuilder.AddDnsName("localhost");
CurrentCertificate = CreateCertificate(firstOid)
};
using (var rsa = RSA.Create())
{
var certRequest = new CertificateRequest("CN=localhost", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
serverOptionsBuilder.WithoutDefaultEndpoint().WithEncryptedEndpoint().WithEncryptionSslProtocol(SslProtocols.Tls12).WithEncryptionCertificate(certificateProvider);
certRequest.CertificateExtensions.Add(
new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
var serverOptions = serverOptionsBuilder.Build();
certRequest.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid(oid) }, false));
var server = testEnvironment.CreateServer(serverOptions);
certRequest.CertificateExtensions.Add(sanBuilder.Build());
using (var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now.AddMinutes(-10), DateTimeOffset.Now.AddMinutes(10)))
{
var pfxCertificate = new X509Certificate2(
certificate.Export(X509ContentType.Pfx),
(string)null,
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
return pfxCertificate;
}
}
}
[TestMethod]
public async Task Tls_Swap_Test()
var publishedCount = 0;
server.InterceptingPublishAsync += _ =>
{
var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
var serverOptionsBuilder = testEnvironment.ServerFactory.CreateServerOptionsBuilder();
Interlocked.Increment(ref publishedCount);
var firstOid = "1.3.6.1.5.5.7.3.1";
var secondOid = "1.3.6.1.5.5.7.3.2";
return Task.CompletedTask;
};
var certificateProvider = new CertificateProvider
await server.StartAsync();
var firstClient = await ConnectClientAsync(
testEnvironment,
args =>
{
CurrentCertificate = CreateCertificate(firstOid)
};
Assert.AreEqual(firstOid, ((X509Certificate2)args.Certificate).Extensions.OfType<X509EnhancedKeyUsageExtension>().First().EnhancedKeyUsages[0].Value);
return true;
});
serverOptionsBuilder.WithoutDefaultEndpoint().WithEncryptedEndpoint().WithEncryptionSslProtocol(SslProtocols.Tls12).WithEncryptionCertificate(certificateProvider);
var firstClientReceivedCount = 0;
firstClient.ApplicationMessageReceivedAsync += _ =>
{
Interlocked.Increment(ref firstClientReceivedCount);
var serverOptions = serverOptionsBuilder.Build();
return Task.CompletedTask;
};
var server = testEnvironment.CreateServer(serverOptions);
await firstClient.SubscribeAsync("TestTopic1");
var publishedCount = 0;
server.InterceptingPublishAsync += args =>
await firstClient.PublishAsync(
new MqttApplicationMessage
{
Interlocked.Increment(ref publishedCount);
Topic = "TestTopic1",
PayloadSegment = new ArraySegment<byte>([1, 2, 3, 4])
});
return Task.CompletedTask;
};
await server.StartAsync();
var firstClient = await ConnectClientAsync(
testEnvironment,
args =>
{
Assert.AreEqual(firstOid, ((X509Certificate2)args.Certificate).Extensions.OfType<X509EnhancedKeyUsageExtension>().First().EnhancedKeyUsages[0].Value);
return true;
});
var firstClientReceivedCount = 0;
firstClient.ApplicationMessageReceivedAsync += args =>
{
Interlocked.Increment(ref firstClientReceivedCount);
return Task.CompletedTask;
};
await firstClient.SubscribeAsync("TestTopic1");
await firstClient.PublishAsync(
await testEnvironment.Server.InjectApplicationMessage(
new InjectedMqttApplicationMessage(
new MqttApplicationMessage
{
Topic = "TestTopic1",
PayloadSegment = new ArraySegment<byte>(new byte[] { 1, 2, 3, 4 })
});
PayloadSegment = new ArraySegment<byte>([1, 2, 3, 4])
}));
await testEnvironment.Server.InjectApplicationMessage(
new InjectedMqttApplicationMessage(
new MqttApplicationMessage
{
Topic = "TestTopic1",
PayloadSegment = new ArraySegment<byte>(new byte[] { 1, 2, 3, 4 })
}));
certificateProvider.CurrentCertificate = CreateCertificate(secondOid);
certificateProvider.CurrentCertificate = CreateCertificate(secondOid);
// Validate that the certificate was switched
var secondClient = await ConnectClientAsync(
testEnvironment,
args =>
{
Assert.AreEqual(secondOid, ((X509Certificate2)args.Certificate).Extensions.OfType<X509EnhancedKeyUsageExtension>().First().EnhancedKeyUsages[0].Value);
return true;
});
var secondClientReceivedCount = 0;
secondClient.ApplicationMessageReceivedAsync += args =>
// Validate that the certificate was switched
var secondClient = await ConnectClientAsync(
testEnvironment,
args =>
{
Interlocked.Increment(ref secondClientReceivedCount);
Assert.AreEqual(secondOid, ((X509Certificate2)args.Certificate).Extensions.OfType<X509EnhancedKeyUsageExtension>().First().EnhancedKeyUsages[0].Value);
return true;
});
return Task.CompletedTask;
};
var secondClientReceivedCount = 0;
secondClient.ApplicationMessageReceivedAsync += _ =>
{
Interlocked.Increment(ref secondClientReceivedCount);
await secondClient.SubscribeAsync("TestTopic2");
return Task.CompletedTask;
};
await firstClient.PublishAsync(
await secondClient.SubscribeAsync("TestTopic2");
await firstClient.PublishAsync(
new MqttApplicationMessage
{
Topic = "TestTopic2",
PayloadSegment = new ArraySegment<byte>([1, 2, 3, 4])
});
await testEnvironment.Server.InjectApplicationMessage(
new InjectedMqttApplicationMessage(
new MqttApplicationMessage
{
Topic = "TestTopic2",
PayloadSegment = new ArraySegment<byte>(new byte[] { 1, 2, 3, 4 })
});
PayloadSegment = new ArraySegment<byte>([1, 2, 3, 4])
}));
await testEnvironment.Server.InjectApplicationMessage(
new InjectedMqttApplicationMessage(
new MqttApplicationMessage
{
Topic = "TestTopic2",
PayloadSegment = new ArraySegment<byte>(new byte[] { 1, 2, 3, 4 })
}));
// Ensure first client still works
await firstClient.PublishAsync(
new MqttApplicationMessage
{
Topic = "TestTopic1",
PayloadSegment = new ArraySegment<byte>([1, 2, 3, 4])
});
// Ensure first client still works
await firstClient.PublishAsync(
await testEnvironment.Server.InjectApplicationMessage(
new InjectedMqttApplicationMessage(
new MqttApplicationMessage
{
Topic = "TestTopic1",
PayloadSegment = new ArraySegment<byte>(new byte[] { 1, 2, 3, 4 })
PayloadSegment = new ArraySegment<byte>([1, 2, 3, 4])
}));
await Task.Delay(1000);
Assert.AreEqual(6, publishedCount);
Assert.AreEqual(4, firstClientReceivedCount);
Assert.AreEqual(2, secondClientReceivedCount);
await server.StopAsync().ConfigureAwait(false);
}
static async Task<IMqttClient> ConnectClientAsync(TestEnvironment testEnvironment, Func<MqttClientCertificateValidationEventArgs, bool> certValidator)
{
var clientOptionsBuilder = testEnvironment.ClientFactory.CreateClientOptionsBuilder();
clientOptionsBuilder.WithClientId(Guid.NewGuid().ToString())
.WithTcpServer("localhost", 8883)
.WithTlsOptions(
o =>
{
o.WithSslProtocols(SslProtocols.Tls12).WithCertificateValidationHandler(certValidator);
});
await testEnvironment.Server.InjectApplicationMessage(
new InjectedMqttApplicationMessage(
new MqttApplicationMessage
{
Topic = "TestTopic1",
PayloadSegment = new ArraySegment<byte>(new byte[] { 1, 2, 3, 4 })
}));
var clientOptions = clientOptionsBuilder.Build();
return await testEnvironment.ConnectClient(clientOptions).ConfigureAwait(false);
}
await Task.Delay(1000);
sealed class CertificateProvider : ICertificateProvider
{
public X509Certificate2 CurrentCertificate { get; set; }
Assert.AreEqual(6, publishedCount);
Assert.AreEqual(4, firstClientReceivedCount);
Assert.AreEqual(2, secondClientReceivedCount);
await server.StopAsync();
}
static async Task<IMqttClient> ConnectClientAsync(TestEnvironment testEnvironment, Func<MqttClientCertificateValidationEventArgs, bool> certValidator)
public X509Certificate2 GetCertificate()
{
var clientOptionsBuilder = testEnvironment.ClientFactory.CreateClientOptionsBuilder();
clientOptionsBuilder.WithClientId(Guid.NewGuid().ToString())
.WithTcpServer("localhost", 8883)
.WithTlsOptions(
o =>
{
o.WithSslProtocols(SslProtocols.Tls12).WithCertificateValidationHandler(certValidator);
});
var clientOptions = clientOptionsBuilder.Build();
return await testEnvironment.ConnectClient(clientOptions);
}
sealed class CertificateProvider : ICertificateProvider
{
public X509Certificate2 CurrentCertificate { get; set; }
public X509Certificate2 GetCertificate()
{
return CurrentCertificate;
}
return CurrentCertificate;
}
}
}
}
@@ -9,72 +9,68 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Formatter;
using MQTTnet.Internal;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class Topic_Alias_Tests : BaseTestClass
{
[TestClass]
public sealed class Topic_Alias_Tests : BaseTestClass
[TestMethod]
public async Task Server_Reports_Topic_Alias_Supported()
{
[TestMethod]
public async Task Server_Reports_Topic_Alias_Supported()
{
using (var testEnvironment = CreateTestEnvironment())
{
await testEnvironment.StartServer();
using var testEnvironment = CreateTestEnvironment();
await testEnvironment.StartServer();
var client = testEnvironment.CreateClient();
var client = testEnvironment.CreateClient();
var connectResult = await client.ConnectAsync(new MqttClientOptionsBuilder()
.WithProtocolVersion(MqttProtocolVersion.V500)
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
.Build());
var connectResult = await client.ConnectAsync(new MqttClientOptionsBuilder()
.WithProtocolVersion(MqttProtocolVersion.V500)
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
.Build());
Assert.AreEqual(connectResult.TopicAliasMaximum, ushort.MaxValue);
}
}
[TestMethod]
public async Task Publish_With_Topic_Alias()
{
using (var testEnvironment = CreateTestEnvironment())
{
await testEnvironment.StartServer();
var receivedTopics = new List<string>();
var c1 = await testEnvironment.ConnectClient(options => options.WithProtocolVersion(MqttProtocolVersion.V500));
c1.ApplicationMessageReceivedAsync += e =>
{
lock (receivedTopics)
{
receivedTopics.Add(e.ApplicationMessage.Topic);
}
return CompletedTask.Instance;
};
await c1.SubscribeAsync("#");
var c2 = await testEnvironment.ConnectClient(options => options.WithProtocolVersion(MqttProtocolVersion.V500));
var message = new MqttApplicationMessage
{
Topic = "this_is_the_topic",
TopicAlias = 22
};
await c2.PublishAsync(message);
message.Topic = null;
await c2.PublishAsync(message);
await c2.PublishAsync(message);
await Task.Delay(500);
Assert.AreEqual(3, receivedTopics.Count);
CollectionAssert.AllItemsAreNotNull(receivedTopics);
Assert.IsTrue(receivedTopics.All(t => t.Equals("this_is_the_topic")));
}
}
Assert.AreEqual(connectResult.TopicAliasMaximum, ushort.MaxValue);
}
}
[TestMethod]
public async Task Publish_With_Topic_Alias()
{
using var testEnvironment = CreateTestEnvironment();
await testEnvironment.StartServer();
var receivedTopics = new List<string>();
var c1 = await testEnvironment.ConnectClient(options => options.WithProtocolVersion(MqttProtocolVersion.V500));
c1.ApplicationMessageReceivedAsync += e =>
{
lock (receivedTopics)
{
receivedTopics.Add(e.ApplicationMessage.Topic);
}
return CompletedTask.Instance;
};
await c1.SubscribeAsync("#");
var c2 = await testEnvironment.ConnectClient(options => options.WithProtocolVersion(MqttProtocolVersion.V500));
var message = new MqttApplicationMessage
{
Topic = "this_is_the_topic",
TopicAlias = 22
};
await c2.PublishAsync(message);
message.Topic = null;
await c2.PublishAsync(message);
await c2.PublishAsync(message);
await Task.Delay(500);
Assert.AreEqual(3, receivedTopics.Count);
CollectionAssert.AllItemsAreNotNull(receivedTopics);
Assert.IsTrue(receivedTopics.All(t => t.Equals("this_is_the_topic")));
}
}
@@ -8,52 +8,47 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
using MQTTnet.Internal;
using MQTTnet.Protocol;
using MQTTnet.Server;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class Unsubscribe_Tests : BaseTestClass
{
[TestClass]
public sealed class Unsubscribe_Tests : BaseTestClass
[TestMethod]
[ExpectedException(typeof(MqttClientDisconnectedException))]
public async Task Disconnect_While_Unsubscribing()
{
[TestMethod]
[ExpectedException(typeof(MqttClientDisconnectedException))]
public async Task Disconnect_While_Unsubscribing()
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer();
// The client will be disconnected directly after subscribing!
server.ClientUnsubscribedTopicAsync += ev => server.DisconnectClientAsync(ev.ClientId);
var client = await testEnvironment.ConnectClient();
await client.SubscribeAsync("#");
await client.UnsubscribeAsync("#");
}
[TestMethod]
public async Task Intercept_Unsubscribe_With_User_Properties()
{
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
var server = await testEnvironment.StartServer();
InterceptingUnsubscriptionEventArgs eventArgs = null;
server.InterceptingUnsubscriptionAsync += e =>
{
using (var testEnvironment = CreateTestEnvironment())
{
var server = await testEnvironment.StartServer();
eventArgs = e;
return CompletedTask.Instance;
};
// The client will be disconnect directly after subscribing!
server.ClientUnsubscribedTopicAsync += ev => server.DisconnectClientAsync(ev.ClientId, MqttDisconnectReasonCode.NormalDisconnection);
var client = await testEnvironment.ConnectClient();
var client = await testEnvironment.ConnectClient();
await client.SubscribeAsync("#");
await client.UnsubscribeAsync("#");
}
}
var unsubscribeOptions = testEnvironment.ClientFactory.CreateUnsubscribeOptionsBuilder().WithTopicFilter("X").WithUserProperty("A", "1").Build();
await client.UnsubscribeAsync(unsubscribeOptions);
[TestMethod]
public async Task Intercept_Unsubscribe_With_User_Properties()
{
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
{
var server = await testEnvironment.StartServer();
InterceptingUnsubscriptionEventArgs eventArgs = null;
server.InterceptingUnsubscriptionAsync += e =>
{
eventArgs = e;
return CompletedTask.Instance;
};
var client = await testEnvironment.ConnectClient();
var unsubscribeOptions = testEnvironment.ClientFactory.CreateUnsubscribeOptionsBuilder().WithTopicFilter("X").WithUserProperty("A", "1").Build();
await client.UnsubscribeAsync(unsubscribeOptions);
CollectionAssert.AreEqual(unsubscribeOptions.UserProperties.ToList(), eventArgs.UserProperties);
}
}
CollectionAssert.AreEqual(unsubscribeOptions.UserProperties.ToList(), eventArgs.UserProperties);
}
}
@@ -2,64 +2,56 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Formatter;
using MQTTnet.Implementations;
using MQTTnet.Internal;
using MQTTnet.Packets;
using MQTTnet.Tests.Mockups;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public class Feature_Tests
{
[TestClass]
public class Feature_Tests
public TestContext TestContext { get; set; }
[TestMethod]
public async Task Use_User_Properties()
{
public TestContext TestContext { get; set; }
using var testEnvironment = new TestEnvironment(TestContext);
await testEnvironment.StartServer();
[TestMethod]
public async Task Use_User_Properties()
var sender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
var receiver = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
var message = new MqttApplicationMessageBuilder()
.WithTopic("A")
.WithUserProperty("x", "1")
.WithUserProperty("y", "2")
.WithUserProperty("z", "3")
.WithUserProperty("z", "4"); // z is here two times to test list of items
await receiver.SubscribeAsync(new MqttClientSubscribeOptions
{
using (var testEnvironment = new TestEnvironment(TestContext))
{
await testEnvironment.StartServer();
TopicFilters = [new MqttTopicFilter { Topic = "#" }]
}, CancellationToken.None);
var sender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
var receiver = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
MqttApplicationMessage receivedMessage = null;
receiver.ApplicationMessageReceivedAsync += e =>
{
receivedMessage = e.ApplicationMessage;
return CompletedTask.Instance;
};
var message = new MqttApplicationMessageBuilder()
.WithTopic("A")
.WithUserProperty("x", "1")
.WithUserProperty("y", "2")
.WithUserProperty("z", "3")
.WithUserProperty("z", "4"); // z is here two times to test list of items
await sender.PublishAsync(message.Build(), CancellationToken.None);
await receiver.SubscribeAsync(new MqttClientSubscribeOptions
{
TopicFilters = new List<MqttTopicFilter>
{
new MqttTopicFilter { Topic = "#" }
}
}, CancellationToken.None);
await Task.Delay(1000);
MqttApplicationMessage receivedMessage = null;
receiver.ApplicationMessageReceivedAsync += e =>
{
receivedMessage = e.ApplicationMessage;
return CompletedTask.Instance;
};
await sender.PublishAsync(message.Build(), CancellationToken.None);
await Task.Delay(1000);
Assert.IsNotNull(receivedMessage);
Assert.AreEqual("A", receivedMessage.Topic);
Assert.AreEqual(4, receivedMessage.UserProperties.Count);
}
}
Assert.IsNotNull(receivedMessage);
Assert.AreEqual("A", receivedMessage.Topic);
Assert.AreEqual(4, receivedMessage.UserProperties.Count);
}
}
}
@@ -6,41 +6,37 @@ using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Formatter;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class Wildcard_Subscription_Available_Tests : BaseTestClass
{
[TestClass]
public sealed class Wildcard_Subscription_Available_Tests : BaseTestClass
[TestMethod]
public async Task Server_Reports_Wildcard_Subscription_Available_Tests_Supported_V3()
{
[TestMethod]
public async Task Server_Reports_Wildcard_Subscription_Available_Tests_Supported_V3()
{
using (var testEnvironment = CreateTestEnvironment())
{
await testEnvironment.StartServer();
using var testEnvironment = CreateTestEnvironment();
await testEnvironment.StartServer();
var client = testEnvironment.CreateClient();
var connectResult = await client.ConnectAsync(testEnvironment.ClientFactory.CreateClientOptionsBuilder()
.WithProtocolVersion(MqttProtocolVersion.V311)
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
var client = testEnvironment.CreateClient();
var connectResult = await client.ConnectAsync(testEnvironment.ClientFactory.CreateClientOptionsBuilder()
.WithProtocolVersion(MqttProtocolVersion.V311)
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
Assert.IsTrue(connectResult.WildcardSubscriptionAvailable);
}
}
Assert.IsTrue(connectResult.WildcardSubscriptionAvailable);
}
[TestMethod]
public async Task Server_Reports_Wildcard_Subscription_Available_Tests_Supported_V5()
{
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
{
await testEnvironment.StartServer();
[TestMethod]
public async Task Server_Reports_Wildcard_Subscription_Available_Tests_Supported_V5()
{
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
await testEnvironment.StartServer();
var client = testEnvironment.CreateClient();
var connectResult = await client.ConnectAsync(testEnvironment.ClientFactory.CreateClientOptionsBuilder()
.WithProtocolVersion(MqttProtocolVersion.V500)
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
var client = testEnvironment.CreateClient();
var connectResult = await client.ConnectAsync(testEnvironment.ClientFactory.CreateClientOptionsBuilder()
.WithProtocolVersion(MqttProtocolVersion.V500)
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
Assert.IsTrue(connectResult.WildcardSubscriptionAvailable);
}
}
Assert.IsTrue(connectResult.WildcardSubscriptionAvailable);
}
}
+49 -55
View File
@@ -3,78 +3,72 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Internal;
using MQTTnet.Protocol;
namespace MQTTnet.Tests.Server
namespace MQTTnet.Tests.Server;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class Will_Tests : BaseTestClass
{
[TestClass]
public sealed class Will_Tests : BaseTestClass
[TestMethod]
public async Task Intercept_Will_Message()
{
[TestMethod]
public async Task Intercept_Will_Message()
using var testEnvironment = CreateTestEnvironment();
var server = await testEnvironment.StartServer().ConfigureAwait(false);
MqttApplicationMessage willMessage = null;
server.InterceptingPublishAsync += eventArgs =>
{
using (var testEnvironment = CreateTestEnvironment())
{
var server = await testEnvironment.StartServer().ConfigureAwait(false);
willMessage = eventArgs.ApplicationMessage;
return CompletedTask.Instance;
};
MqttApplicationMessage willMessage = null;
server.InterceptingPublishAsync += eventArgs =>
{
willMessage = eventArgs.ApplicationMessage;
return CompletedTask.Instance;
};
await testEnvironment.ConnectClient(new MqttClientOptionsBuilder()).ConfigureAwait(false);
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will").WithWillQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce);
var takeOverClient = await testEnvironment.ConnectClient(clientOptions).ConfigureAwait(false);
takeOverClient.Dispose(); // Dispose will not send a DISCONNECT pattern first so the will message must be sent.
await testEnvironment.ConnectClient(new MqttClientOptionsBuilder()).ConfigureAwait(false);
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will").WithWillQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce);
var takeOverClient = await testEnvironment.ConnectClient(clientOptions).ConfigureAwait(false);
takeOverClient.Dispose(); // Dispose will not send a DISCONNECT pattern first so the will message must be sent.
await LongTestDelay().ConfigureAwait(false);
await LongTestDelay().ConfigureAwait(false);
Assert.IsNotNull(willMessage);
}
Assert.IsNotNull(willMessage);
}
}
[TestMethod]
public async Task Will_Message_Do_Not_Send_On_Clean_Disconnect()
{
using var testEnvironment = CreateTestEnvironment();
await testEnvironment.StartServer();
[TestMethod]
public async Task Will_Message_Do_Not_Send_On_Clean_Disconnect()
{
using (var testEnvironment = CreateTestEnvironment())
{
await testEnvironment.StartServer();
var receiver = await testEnvironment.ConnectClient().ConfigureAwait(false);
var receiver = await testEnvironment.ConnectClient().ConfigureAwait(false);
var receivedMessages = testEnvironment.CreateApplicationMessageHandler(receiver);
var receivedMessages = testEnvironment.CreateApplicationMessageHandler(receiver);
await receiver.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
await receiver.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will");
var sender = await testEnvironment.ConnectClient(clientOptions).ConfigureAwait(false);
await sender.DisconnectAsync().ConfigureAwait(false);
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will");
var sender = await testEnvironment.ConnectClient(clientOptions).ConfigureAwait(false);
await sender.DisconnectAsync().ConfigureAwait(false);
await LongTestDelay().ConfigureAwait(false);
await LongTestDelay().ConfigureAwait(false);
Assert.AreEqual(0, receivedMessages.ReceivedEventArgs.Count);
}
Assert.AreEqual(0, receivedMessages.ReceivedEventArgs.Count);
}
}
[TestMethod]
public async Task Will_Message_Send()
{
using var testEnvironment = CreateTestEnvironment();
await testEnvironment.StartServer();
[TestMethod]
public async Task Will_Message_Send()
{
using (var testEnvironment = CreateTestEnvironment())
{
await testEnvironment.StartServer();
var receiver = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder()).ConfigureAwait(false);
var receivedMessages = testEnvironment.CreateApplicationMessageHandler(receiver);
await receiver.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
var receiver = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder()).ConfigureAwait(false);
var receivedMessages = testEnvironment.CreateApplicationMessageHandler(receiver);
await receiver.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will").WithWillQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce);
var takeOverClient = await testEnvironment.ConnectClient(clientOptions).ConfigureAwait(false);
takeOverClient.Dispose(); // Dispose will not send a DISCONNECT pattern first so the will message must be sent.
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will").WithWillQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce);
var takeOverClient = await testEnvironment.ConnectClient(clientOptions).ConfigureAwait(false);
takeOverClient.Dispose(); // Dispose will not send a DISCONNECT pattern first so the will message must be sent.
await LongTestDelay().ConfigureAwait(false);
await LongTestDelay().ConfigureAwait(false);
Assert.AreEqual(1, receivedMessages.ReceivedEventArgs.Count);
}
}
Assert.AreEqual(1, receivedMessages.ReceivedEventArgs.Count);
}
}