mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-16 09:13:02 +08:00
* Add MqttTopicTemplate
The topic template class is a way to deal with dynamic topics that
is less cumbersome and error-prone than String.Format or
other string fu one might use to handle dynamic topics.
Example:
MyCity/{street}/{crossing}/lights
'street' and 'crossing' would be parameters.
The class and its extension methods are intended to support
dynamic subscription/publication, message-topic matching and
routing. The template syntax is modelled after the well-known
moustache syntax, which conincidentally is also published
as the 'AsyncAPI channel address expressions'.
* Add MQTTnet.Extensions.TopicTemplate to solution
* Apply code style from team shared resharper settings.
Add missing file headers.
* Update release notes
---------
Co-authored-by: christian <6939810+chkr1011@users.noreply.github.com>
101 lines
3.9 KiB
C#
101 lines
3.9 KiB
C#
// Licensed to the .NET Foundation under one or more agreements.
|
|
// 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;
|
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
using MQTTnet.Extensions.TopicTemplate;
|
|
|
|
namespace MQTTnet.Tests.Extensions
|
|
{
|
|
[TestClass]
|
|
public sealed class MqttTopicTemplate_Tests
|
|
{
|
|
[TestMethod]
|
|
public void BasicOps()
|
|
{
|
|
var template = new MqttTopicTemplate("A/B/{param}/D");
|
|
Assert.AreEqual("A/B/C/D", template.WithParameter("param", "D"));
|
|
Assert.AreEqual("A/B/+/D", template.WithoutParameter("param"));
|
|
Assert.AreEqual("A/B/+/D", template.TopicFilter);
|
|
Assert.AreEqual("A/B/+/D/#", template.TopicTreeRootFilter);
|
|
|
|
// corner cases
|
|
Assert.AreEqual("", new MqttTopicTemplate("").TopicFilter);
|
|
Assert.AreEqual("#", new MqttTopicTemplate("#").TopicFilter);
|
|
Assert.AreEqual("+", new MqttTopicTemplate("+").TopicFilter);
|
|
Assert.AreEqual("/", new MqttTopicTemplate("/").TopicFilter);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void BasicOpsWithHash()
|
|
{
|
|
var template = new MqttTopicTemplate("A/B/{param}/D/#");
|
|
Assert.AreEqual("A/B/C/D/#", template.WithParameter("param", "D"));
|
|
Assert.AreEqual("A/B/+/D/#", template.WithoutParameter("param"));
|
|
Assert.AreEqual("A/B/+/D/#", template.TopicFilter);
|
|
Assert.AreEqual("A/B/+/D/#", template.TopicTreeRootFilter);
|
|
}
|
|
|
|
[TestMethod]
|
|
[ExpectedException(typeof(ArgumentException))]
|
|
public void RejectsReservedChars1()
|
|
{
|
|
var template = new MqttTopicTemplate("A/B/{foo}/D");
|
|
template.WithParameter("foo", "a#");
|
|
}
|
|
|
|
[TestMethod]
|
|
[ExpectedException(typeof(ArgumentException))]
|
|
public void RejectsReservedChars2()
|
|
{
|
|
var template = new MqttTopicTemplate("A/B/{foo}/D");
|
|
template.WithParameter("foo", "e/f");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void DynamicRoutingSupport()
|
|
{
|
|
var v1template = new MqttTopicTemplate("A/v1/{id}/F");
|
|
var v2template = new MqttTopicTemplate("A/v2/foo/{id}/{bar}");
|
|
var incoming_v1_topic = "A/v1/32/F";
|
|
|
|
var compat_v2_topic = v2template
|
|
.WithParameterValuesFrom(
|
|
v1template.ParseParameterValues(incoming_v1_topic))
|
|
.WithParameter("bar", "unknown");
|
|
|
|
// id: 32 was relocated and bar: unknown added
|
|
Assert.AreEqual("A/v2/foo/32/unknown", compat_v2_topic.Template);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void SubscriptionSupport()
|
|
{
|
|
var template = new MqttTopicTemplate("A/v1/{param}/F");
|
|
var filter = template.BuildFilter()
|
|
.WithAtLeastOnceQoS()
|
|
.WithNoLocal()
|
|
.Build();
|
|
Assert.AreEqual("A/v1/+/F", filter.Topic);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void CanonicalPrefixFilter()
|
|
{
|
|
var template1 = new MqttTopicTemplate("A/v1/{param}/F");
|
|
var template2 = new MqttTopicTemplate("A/v1/E/F");
|
|
var template3 = new MqttTopicTemplate("A/v1/E/F/G/H");
|
|
var canonicalFilter = MqttTopicTemplate.FindCanonicalPrefix(new[] { template1, template2, template3 });
|
|
Assert.AreEqual("A/v1/+/+", canonicalFilter.TopicFilter);
|
|
Assert.AreEqual("A/v1/+/+/#", canonicalFilter.TopicTreeRootFilter);
|
|
|
|
var template4 = new MqttTopicTemplate("A/v2/{prefix}");
|
|
var canonicalFilter2 = MqttTopicTemplate.FindCanonicalPrefix(new[] { template1, template2, template3, template4 });
|
|
Assert.AreEqual("A/+/+", canonicalFilter2.TopicFilter);
|
|
Assert.AreEqual("A/+/#", canonicalFilter2.TopicTreeRootFilter);
|
|
}
|
|
|
|
|
|
}
|
|
} |