From 5db2963bb06c587c53da742ed1d4a485ed948866 Mon Sep 17 00:00:00 2001 From: Vijaya Gopal Yarramneni Date: Mon, 2 Nov 2020 16:18:10 -0800 Subject: [PATCH] Fixing a bug in management client that is sending atom xml elements out of order (#16488) * Fixing a bug in management client that is sending atom xml elements in enity description out of order in an update call. Because of this bug, deserializer on the service ignores these out of order elements, and persists elements with default values in the righr order resulting in these out of order elements getting duplicated in the resource xml. This fix removes duplicate elements only if they are present from the xml, and sends xml elements in the correct order in update call. * Removing the part that is added to remove duplicate elements, as I realize it is not required. * Correcting the order of IsAnonymousAccessbile and AuthorizationRules in QueueDescription. --- .../src/Management/QueueDescription.cs | 23 +++++++- .../Management/QueueDescriptionExtensions.cs | 57 ++++++++++++------- .../SubscriptionDescriptionExtensions.cs | 29 +++++----- .../src/Management/TopicDescription.cs | 15 +++++ .../Management/TopicDescriptionExtensions.cs | 50 +++++++++++----- .../tests/Management/QueueDescriptionTests.cs | 55 ++++++++++++++++++ .../SubscriptionDescriptionTests.cs | 47 +++++++++++++++ 7 files changed, 226 insertions(+), 50 deletions(-) diff --git a/sdk/servicebus/Microsoft.Azure.ServiceBus/src/Management/QueueDescription.cs b/sdk/servicebus/Microsoft.Azure.ServiceBus/src/Management/QueueDescription.cs index be5fd4badea5a..7c2de4fbbb09a 100644 --- a/sdk/servicebus/Microsoft.Azure.ServiceBus/src/Management/QueueDescription.cs +++ b/sdk/servicebus/Microsoft.Azure.ServiceBus/src/Management/QueueDescription.cs @@ -5,6 +5,7 @@ namespace Microsoft.Azure.ServiceBus.Management { using System; using System.Collections.Generic; + using System.Xml.Linq; using Microsoft.Azure.ServiceBus.Primitives; /// @@ -14,6 +15,7 @@ public class QueueDescription : IEquatable { internal TimeSpan duplicateDetectionHistoryTimeWindow = TimeSpan.FromMinutes(1); internal string path; + internal bool? internalSupportOrdering = null; TimeSpan lockDuration = TimeSpan.FromSeconds(60); TimeSpan defaultMessageTimeToLive = TimeSpan.MaxValue; TimeSpan autoDeleteOnIdle = TimeSpan.MaxValue; @@ -273,11 +275,27 @@ public string UserMetadata } } + internal bool IsAnonymousAccessible { get; set; } = false; + + internal bool SupportOrdering + { + get + { + return this.internalSupportOrdering ?? !this.EnablePartitioning; + } + set + { + this.internalSupportOrdering = value; + } + } + + internal bool EnableExpress { get; set; } = false; + /// /// List of properties that were retrieved using GetQueue but are not understood by this version of client is stored here. /// The list will be sent back when an already retrieved QueueDescription will be used in UpdateQueue call. /// - internal List UnknownProperties { get; set; } + internal List UnknownProperties { get; set; } public override int GetHashCode() { @@ -308,6 +326,9 @@ public bool Equals(QueueDescription otherDescription) && this.RequiresDuplicateDetection.Equals(other.RequiresDuplicateDetection) && this.RequiresSession.Equals(other.RequiresSession) && this.Status.Equals(other.Status) + && this.internalSupportOrdering.Equals(other.SupportOrdering) + && this.EnableExpress == other.EnableExpress + && this.IsAnonymousAccessible == other.IsAnonymousAccessible && string.Equals(this.userMetadata, other.userMetadata, StringComparison.OrdinalIgnoreCase) && (this.AuthorizationRules != null && other.AuthorizationRules != null || this.AuthorizationRules == null && other.AuthorizationRules == null) diff --git a/sdk/servicebus/Microsoft.Azure.ServiceBus/src/Management/QueueDescriptionExtensions.cs b/sdk/servicebus/Microsoft.Azure.ServiceBus/src/Management/QueueDescriptionExtensions.cs index 84dcb77acedc4..555967c5ef574 100644 --- a/sdk/servicebus/Microsoft.Azure.ServiceBus/src/Management/QueueDescriptionExtensions.cs +++ b/sdk/servicebus/Microsoft.Azure.ServiceBus/src/Management/QueueDescriptionExtensions.cs @@ -12,7 +12,7 @@ internal static class QueueDescriptionExtensions { public static XDocument Serialize(this QueueDescription description) { - var queueDescriptionElements = new List() + var queueDescriptionElements = new List() { new XElement(XName.Get("LockDuration", ManagementClientConstants.ServiceBusNamespace), XmlConvert.ToString(description.LockDuration)), new XElement(XName.Get("MaxSizeInMegabytes", ManagementClientConstants.ServiceBusNamespace), XmlConvert.ToString(description.MaxSizeInMB)), @@ -25,15 +25,20 @@ public static XDocument Serialize(this QueueDescription description) : null, new XElement(XName.Get("MaxDeliveryCount", ManagementClientConstants.ServiceBusNamespace), XmlConvert.ToString(description.MaxDeliveryCount)), new XElement(XName.Get("EnableBatchedOperations", ManagementClientConstants.ServiceBusNamespace), XmlConvert.ToString(description.EnableBatchedOperations)), + new XElement(XName.Get("IsAnonymousAccessible", ManagementClientConstants.ServiceBusNamespace), XmlConvert.ToString(description.IsAnonymousAccessible)), description.AuthorizationRules?.Serialize(), new XElement(XName.Get("Status", ManagementClientConstants.ServiceBusNamespace), description.Status.ToString()), description.ForwardTo != null ? new XElement(XName.Get("ForwardTo", ManagementClientConstants.ServiceBusNamespace), description.ForwardTo) : null, description.UserMetadata != null ? new XElement(XName.Get("UserMetadata", ManagementClientConstants.ServiceBusNamespace), description.UserMetadata) : null, + description.internalSupportOrdering.HasValue ? new XElement(XName.Get("SupportOrdering", ManagementClientConstants.ServiceBusNamespace), XmlConvert.ToString(description.internalSupportOrdering.Value)) : null, description.AutoDeleteOnIdle != TimeSpan.MaxValue ? new XElement(XName.Get("AutoDeleteOnIdle", ManagementClientConstants.ServiceBusNamespace), XmlConvert.ToString(description.AutoDeleteOnIdle)) : null, new XElement(XName.Get("EnablePartitioning", ManagementClientConstants.ServiceBusNamespace), XmlConvert.ToString(description.EnablePartitioning)), - description.ForwardDeadLetteredMessagesTo != null ? new XElement(XName.Get("ForwardDeadLetteredMessagesTo", ManagementClientConstants.ServiceBusNamespace), description.ForwardDeadLetteredMessagesTo) : null + description.ForwardDeadLetteredMessagesTo != null ? new XElement(XName.Get("ForwardDeadLetteredMessagesTo", ManagementClientConstants.ServiceBusNamespace), description.ForwardDeadLetteredMessagesTo) : null, + new XElement(XName.Get("EnableExpress", ManagementClientConstants.ServiceBusNamespace), XmlConvert.ToString(description.EnableExpress)) }; + // Insert unknown properties in the exact order they were in the received xml. + // Expectation is that servicebus will add any new elements only at the bottom of the xml tree. if (description.UnknownProperties != null) { queueDescriptionElements.AddRange(description.UnknownProperties); @@ -71,8 +76,6 @@ public static QueueDescription ParseFromContent(string xml) private static QueueDescription ParseFromEntryElement(XElement xEntry) { var name = xEntry.Element(XName.Get("title", ManagementClientConstants.AtomNamespace)).Value; - var qd = new QueueDescription(name); - var qdXml = xEntry.Element(XName.Get("content", ManagementClientConstants.AtomNamespace))? .Element(XName.Get("QueueDescription", ManagementClientConstants.ServiceBusNamespace)); @@ -81,10 +84,14 @@ private static QueueDescription ParseFromEntryElement(XElement xEntry) throw new MessagingEntityNotFoundException("Queue was not found"); } + var qd = new QueueDescription(name); foreach (var element in qdXml.Elements()) { switch (element.Name.LocalName) { + case "LockDuration": + qd.LockDuration = XmlConvert.ToTimeSpan(element.Value); + break; case "MaxSizeInMegabytes": qd.MaxSizeInMB = Int64.Parse(element.Value); break; @@ -94,35 +101,29 @@ private static QueueDescription ParseFromEntryElement(XElement xEntry) case "RequiresSession": qd.RequiresSession = Boolean.Parse(element.Value); break; + case "DefaultMessageTimeToLive": + qd.DefaultMessageTimeToLive = XmlConvert.ToTimeSpan(element.Value); + break; case "DeadLetteringOnMessageExpiration": qd.EnableDeadLetteringOnMessageExpiration = Boolean.Parse(element.Value); break; case "DuplicateDetectionHistoryTimeWindow": qd.duplicateDetectionHistoryTimeWindow = XmlConvert.ToTimeSpan(element.Value); break; - case "LockDuration": - qd.LockDuration = XmlConvert.ToTimeSpan(element.Value); - break; - case "DefaultMessageTimeToLive": - qd.DefaultMessageTimeToLive = XmlConvert.ToTimeSpan(element.Value); - break; case "MaxDeliveryCount": qd.MaxDeliveryCount = Int32.Parse(element.Value); break; case "EnableBatchedOperations": qd.EnableBatchedOperations = Boolean.Parse(element.Value); break; - case "Status": - qd.Status = (EntityStatus)Enum.Parse(typeof(EntityStatus), element.Value); - break; - case "AutoDeleteOnIdle": - qd.AutoDeleteOnIdle = XmlConvert.ToTimeSpan(element.Value); + case "IsAnonymousAccessible": + qd.IsAnonymousAccessible = Boolean.Parse(element.Value); break; - case "EnablePartitioning": - qd.EnablePartitioning = bool.Parse(element.Value); + case "AuthorizationRules": + qd.AuthorizationRules = AuthorizationRules.ParseFromXElement(element); break; - case "UserMetadata": - qd.UserMetadata = element.Value; + case "Status": + qd.Status = (EntityStatus)Enum.Parse(typeof(EntityStatus), element.Value); break; case "ForwardTo": if (!string.IsNullOrWhiteSpace(element.Value)) @@ -130,14 +131,26 @@ private static QueueDescription ParseFromEntryElement(XElement xEntry) qd.ForwardTo = element.Value; } break; + case "UserMetadata": + qd.UserMetadata = element.Value; + break; + case "SupportOrdering": + qd.SupportOrdering = Boolean.Parse(element.Value); + break; + case "AutoDeleteOnIdle": + qd.AutoDeleteOnIdle = XmlConvert.ToTimeSpan(element.Value); + break; + case "EnablePartitioning": + qd.EnablePartitioning = bool.Parse(element.Value); + break; case "ForwardDeadLetteredMessagesTo": if (!string.IsNullOrWhiteSpace(element.Value)) { qd.ForwardDeadLetteredMessagesTo = element.Value; } break; - case "AuthorizationRules": - qd.AuthorizationRules = AuthorizationRules.ParseFromXElement(element); + case "EnableExpress": + qd.EnableExpress = bool.Parse(element.Value); break; case "AccessedAt": case "CreatedAt": @@ -152,7 +165,7 @@ private static QueueDescription ParseFromEntryElement(XElement xEntry) // For unknown properties, keep them as-is for forward proof. if (qd.UnknownProperties == null) { - qd.UnknownProperties = new List(); + qd.UnknownProperties = new List(); } qd.UnknownProperties.Add(element); diff --git a/sdk/servicebus/Microsoft.Azure.ServiceBus/src/Management/SubscriptionDescriptionExtensions.cs b/sdk/servicebus/Microsoft.Azure.ServiceBus/src/Management/SubscriptionDescriptionExtensions.cs index 40ac2ad514cca..9901bf4c4b93e 100644 --- a/sdk/servicebus/Microsoft.Azure.ServiceBus/src/Management/SubscriptionDescriptionExtensions.cs +++ b/sdk/servicebus/Microsoft.Azure.ServiceBus/src/Management/SubscriptionDescriptionExtensions.cs @@ -105,35 +105,29 @@ private static SubscriptionDescription ParseFromEntryElement(string topicName, X { switch (element.Name.LocalName) { + case "LockDuration": + subscriptionDesc.LockDuration = XmlConvert.ToTimeSpan(element.Value); + break; case "RequiresSession": subscriptionDesc.RequiresSession = bool.Parse(element.Value); break; + case "DefaultMessageTimeToLive": + subscriptionDesc.DefaultMessageTimeToLive = XmlConvert.ToTimeSpan(element.Value); + break; case "DeadLetteringOnMessageExpiration": subscriptionDesc.EnableDeadLetteringOnMessageExpiration = bool.Parse(element.Value); break; case "DeadLetteringOnFilterEvaluationExceptions": subscriptionDesc.EnableDeadLetteringOnFilterEvaluationExceptions = bool.Parse(element.Value); break; - case "LockDuration": - subscriptionDesc.LockDuration = XmlConvert.ToTimeSpan(element.Value); - break; - case "DefaultMessageTimeToLive": - subscriptionDesc.DefaultMessageTimeToLive = XmlConvert.ToTimeSpan(element.Value); - break; case "MaxDeliveryCount": subscriptionDesc.MaxDeliveryCount = int.Parse(element.Value); break; - case "Status": - subscriptionDesc.Status = (EntityStatus)Enum.Parse(typeof(EntityStatus), element.Value); - break; case "EnableBatchedOperations": subscriptionDesc.EnableBatchedOperations = bool.Parse(element.Value); break; - case "UserMetadata": - subscriptionDesc.UserMetadata = element.Value; - break; - case "AutoDeleteOnIdle": - subscriptionDesc.AutoDeleteOnIdle = XmlConvert.ToTimeSpan(element.Value); + case "Status": + subscriptionDesc.Status = (EntityStatus)Enum.Parse(typeof(EntityStatus), element.Value); break; case "ForwardTo": if (!string.IsNullOrWhiteSpace(element.Value)) @@ -141,18 +135,25 @@ private static SubscriptionDescription ParseFromEntryElement(string topicName, X subscriptionDesc.ForwardTo = element.Value; } break; + case "UserMetadata": + subscriptionDesc.UserMetadata = element.Value; + break; case "ForwardDeadLetteredMessagesTo": if (!string.IsNullOrWhiteSpace(element.Value)) { subscriptionDesc.ForwardDeadLetteredMessagesTo = element.Value; } break; + case "AutoDeleteOnIdle": + subscriptionDesc.AutoDeleteOnIdle = XmlConvert.ToTimeSpan(element.Value); + break; case "AccessedAt": case "CreatedAt": case "MessageCount": case "SizeInBytes": case "UpdatedAt": case "CountDetails": + case "DefaultRuleDescription": // Ignore known properties // Do nothing break; diff --git a/sdk/servicebus/Microsoft.Azure.ServiceBus/src/Management/TopicDescription.cs b/sdk/servicebus/Microsoft.Azure.ServiceBus/src/Management/TopicDescription.cs index cfa5aa59a0bd6..218c8b7aab3a2 100644 --- a/sdk/servicebus/Microsoft.Azure.ServiceBus/src/Management/TopicDescription.cs +++ b/sdk/servicebus/Microsoft.Azure.ServiceBus/src/Management/TopicDescription.cs @@ -172,6 +172,16 @@ public string UserMetadata } } + internal bool IsAnonymousAccessible { get; set; } = false; + + internal bool FilteringMessagesBeforePublishing { get; set; } = false; + + internal string ForwardTo { get; set; } = null; + + internal bool EnableExpress { get; set; } = false; + + internal bool EnableSubscriptionPartitioning { get; set; } = false; + /// /// List of properties that were retrieved using GetTopic but are not understood by this version of client is stored here. /// The list will be sent back when an already retrieved TopicDescription will be used in UpdateTopic call. @@ -202,6 +212,11 @@ public bool Equals(TopicDescription otherDescription) && this.RequiresDuplicateDetection.Equals(other.RequiresDuplicateDetection) && this.Status.Equals(other.Status) && string.Equals(this.userMetadata, other.userMetadata, StringComparison.OrdinalIgnoreCase) + && string.Equals(this.ForwardTo, other.ForwardTo, StringComparison.OrdinalIgnoreCase) + && this.EnableExpress == other.EnableExpress + && this.IsAnonymousAccessible == other.IsAnonymousAccessible + && this.FilteringMessagesBeforePublishing == other.FilteringMessagesBeforePublishing + && this.EnableSubscriptionPartitioning == other.EnableSubscriptionPartitioning && (this.AuthorizationRules != null && other.AuthorizationRules != null || this.AuthorizationRules == null && other.AuthorizationRules == null) && (this.AuthorizationRules == null || this.AuthorizationRules.Equals(other.AuthorizationRules))) diff --git a/sdk/servicebus/Microsoft.Azure.ServiceBus/src/Management/TopicDescriptionExtensions.cs b/sdk/servicebus/Microsoft.Azure.ServiceBus/src/Management/TopicDescriptionExtensions.cs index 2aca66feca67b..efa0fb4881a55 100644 --- a/sdk/servicebus/Microsoft.Azure.ServiceBus/src/Management/TopicDescriptionExtensions.cs +++ b/sdk/servicebus/Microsoft.Azure.ServiceBus/src/Management/TopicDescriptionExtensions.cs @@ -63,20 +63,22 @@ public static IList ParseCollectionFromContent(string xml) private static TopicDescription ParseFromEntryElement(XElement xEntry) { var name = xEntry.Element(XName.Get("title", ManagementClientConstants.AtomNamespace)).Value; - var topicDesc = new TopicDescription(name); - - var qdXml = xEntry.Element(XName.Get("content", ManagementClientConstants.AtomNamespace))? + var tdXml = xEntry.Element(XName.Get("content", ManagementClientConstants.AtomNamespace))? .Element(XName.Get("TopicDescription", ManagementClientConstants.ServiceBusNamespace)); - if (qdXml == null) + if (tdXml == null) { throw new MessagingEntityNotFoundException("Topic was not found"); } - foreach (var element in qdXml.Elements()) + var topicDesc = new TopicDescription(name); + foreach (var element in tdXml.Elements()) { switch (element.Name.LocalName) { + case "DefaultMessageTimeToLive": + topicDesc.DefaultMessageTimeToLive = XmlConvert.ToTimeSpan(element.Value); + break; case "MaxSizeInMegabytes": topicDesc.MaxSizeInMB = long.Parse(element.Value); break; @@ -86,29 +88,44 @@ private static TopicDescription ParseFromEntryElement(XElement xEntry) case "DuplicateDetectionHistoryTimeWindow": topicDesc.duplicateDetectionHistoryTimeWindow = XmlConvert.ToTimeSpan(element.Value); break; - case "DefaultMessageTimeToLive": - topicDesc.DefaultMessageTimeToLive = XmlConvert.ToTimeSpan(element.Value); - break; case "EnableBatchedOperations": topicDesc.EnableBatchedOperations = bool.Parse(element.Value); break; + case "FilteringMessagesBeforePublishing": + topicDesc.FilteringMessagesBeforePublishing = bool.Parse(element.Value); + break; + case "IsAnonymousAccessible": + topicDesc.IsAnonymousAccessible = bool.Parse(element.Value); + break; + case "AuthorizationRules": + topicDesc.AuthorizationRules = AuthorizationRules.ParseFromXElement(element); + break; case "Status": topicDesc.Status = (EntityStatus)Enum.Parse(typeof(EntityStatus), element.Value); break; + case "ForwardTo": + if (!string.IsNullOrWhiteSpace(element.Value)) + { + topicDesc.ForwardTo = element.Value; + } + break; case "UserMetadata": topicDesc.UserMetadata = element.Value; break; + case "SupportOrdering": + topicDesc.SupportOrdering = bool.Parse(element.Value); + break; case "AutoDeleteOnIdle": topicDesc.AutoDeleteOnIdle = XmlConvert.ToTimeSpan(element.Value); break; case "EnablePartitioning": topicDesc.EnablePartitioning = bool.Parse(element.Value); break; - case "SupportOrdering": - topicDesc.SupportOrdering = bool.Parse(element.Value); + case "EnableSubscriptionPartitioning": + topicDesc.EnableSubscriptionPartitioning = bool.Parse(element.Value); break; - case "AuthorizationRules": - topicDesc.AuthorizationRules = AuthorizationRules.ParseFromXElement(element); + case "EnableExpress": + topicDesc.EnableExpress = bool.Parse(element.Value); break; case "AccessedAt": case "CreatedAt": @@ -146,14 +163,21 @@ public static XDocument Serialize(this TopicDescription description) new XElement(XName.Get("DuplicateDetectionHistoryTimeWindow", ManagementClientConstants.ServiceBusNamespace), XmlConvert.ToString(description.DuplicateDetectionHistoryTimeWindow)) : null, new XElement(XName.Get("EnableBatchedOperations", ManagementClientConstants.ServiceBusNamespace), XmlConvert.ToString(description.EnableBatchedOperations)), + new XElement(XName.Get("FilteringMessagesBeforePublishing", ManagementClientConstants.ServiceBusNamespace), XmlConvert.ToString(description.FilteringMessagesBeforePublishing)), + new XElement(XName.Get("IsAnonymousAccessible", ManagementClientConstants.ServiceBusNamespace), XmlConvert.ToString(description.IsAnonymousAccessible)), description.AuthorizationRules?.Serialize(), new XElement(XName.Get("Status", ManagementClientConstants.ServiceBusNamespace), description.Status.ToString()), + description.ForwardTo != null ? new XElement(XName.Get("ForwardTo", ManagementClientConstants.ServiceBusNamespace), description.ForwardTo) : null, description.UserMetadata != null ? new XElement(XName.Get("UserMetadata", ManagementClientConstants.ServiceBusNamespace), description.UserMetadata) : null, new XElement(XName.Get("SupportOrdering", ManagementClientConstants.ServiceBusNamespace), XmlConvert.ToString(description.SupportOrdering)), description.AutoDeleteOnIdle != TimeSpan.MaxValue ? new XElement(XName.Get("AutoDeleteOnIdle", ManagementClientConstants.ServiceBusNamespace), XmlConvert.ToString(description.AutoDeleteOnIdle)) : null, - new XElement(XName.Get("EnablePartitioning", ManagementClientConstants.ServiceBusNamespace), XmlConvert.ToString(description.EnablePartitioning)) + new XElement(XName.Get("EnablePartitioning", ManagementClientConstants.ServiceBusNamespace), XmlConvert.ToString(description.EnablePartitioning)), + new XElement(XName.Get("EnableSubscriptionPartitioning", ManagementClientConstants.ServiceBusNamespace), XmlConvert.ToString(description.EnableSubscriptionPartitioning)), + new XElement(XName.Get("EnableExpress", ManagementClientConstants.ServiceBusNamespace), XmlConvert.ToString(description.EnableExpress)) }; + // Insert unknown properties in the exact order they were in the received xml. + // Expectation is that servicebus will add any new elements only at the bottom of the xml tree. if (description.UnknownProperties != null) { topicDescriptionElements.AddRange(description.UnknownProperties); diff --git a/sdk/servicebus/Microsoft.Azure.ServiceBus/tests/Management/QueueDescriptionTests.cs b/sdk/servicebus/Microsoft.Azure.ServiceBus/tests/Management/QueueDescriptionTests.cs index 730b3a9910e71..22e46a081a96b 100644 --- a/sdk/servicebus/Microsoft.Azure.ServiceBus/tests/Management/QueueDescriptionTests.cs +++ b/sdk/servicebus/Microsoft.Azure.ServiceBus/tests/Management/QueueDescriptionTests.cs @@ -1,5 +1,7 @@ using System; using System.Linq; +using System.Xml; +using System.Xml.Linq; using Microsoft.Azure.ServiceBus.Management; using Microsoft.Azure.ServiceBus.UnitTests; using Xunit; @@ -89,4 +91,57 @@ public void PathAllowsMaxLengthMinusBaseUrl(string baseUrl, int lengthOfName) sub.Path = $"{baseUrl}{longName}"; Assert.Equal($"{baseUrl}{longName}", sub.Path); } + + [Fact] + [DisplayTestMethodName] + public void UnknownElementsInAtomXmlHanldedRight() + { + string queueDescriptionXml = $@"" + + $@"testqueue1" + + $@"" + + $@"" + + $"{XmlConvert.ToString(TimeSpan.FromMinutes(1))}" + + $"1024" + + $"true" + + $"true" + + $"{XmlConvert.ToString(TimeSpan.FromMinutes(60))}" + + $"false" + + $"{XmlConvert.ToString(TimeSpan.FromMinutes(2))}" + + $"10" + + $"true" + + $"false" + + $"" + + $"Active" + + $"fq1" + + $"" + + $"true" + + $"{XmlConvert.ToString(TimeSpan.FromMinutes(60))}" + + $"false" + + $"false" + + $"prop1" + + $"prop2" + + $"prop3" + + $"prop4" + + $"prop5" + + $"" + + $"" + + $""; + + QueueDescription queueDesc = QueueDescriptionExtensions.ParseFromContent(queueDescriptionXml); + Assert.NotNull(queueDesc.UnknownProperties); + XDocument doc = QueueDescriptionExtensions.Serialize(queueDesc); + + XName queueDescriptionElementName = XName.Get("QueueDescription", ManagementClientConstants.ServiceBusNamespace); + XElement expectedQueueDecriptionElement = XElement.Parse(queueDescriptionXml).Descendants(queueDescriptionElementName).FirstOrDefault(); + XElement serializedQueueDescritionElement = doc.Descendants(queueDescriptionElementName).FirstOrDefault(); + XNode expectedChildNode = expectedQueueDecriptionElement.FirstNode; + XNode actualChildNode = serializedQueueDescritionElement.FirstNode; + while (expectedChildNode != null) + { + Assert.NotNull(actualChildNode); + Assert.True(XNode.DeepEquals(expectedChildNode, actualChildNode), $"QueueDescrition parsing and serialization combo didn't work as expected. {expectedChildNode.ToString()}"); + expectedChildNode = expectedChildNode.NextNode; + actualChildNode = actualChildNode.NextNode; + } + } } \ No newline at end of file diff --git a/sdk/servicebus/Microsoft.Azure.ServiceBus/tests/Management/SubscriptionDescriptionTests.cs b/sdk/servicebus/Microsoft.Azure.ServiceBus/tests/Management/SubscriptionDescriptionTests.cs index 1fc920420ad28..b9af5499ca6aa 100644 --- a/sdk/servicebus/Microsoft.Azure.ServiceBus/tests/Management/SubscriptionDescriptionTests.cs +++ b/sdk/servicebus/Microsoft.Azure.ServiceBus/tests/Management/SubscriptionDescriptionTests.cs @@ -1,5 +1,7 @@ using System; using System.Linq; +using System.Xml; +using System.Xml.Linq; using Microsoft.Azure.ServiceBus.Management; using Microsoft.Azure.ServiceBus.UnitTests; using Xunit; @@ -61,4 +63,49 @@ public void ForwardDeadLetteredMessagesToAllowsMaxLengthMinusBaseUrl(string base sub.ForwardDeadLetteredMessagesTo = $"{baseUrl}{longName}"; Assert.Equal($"{baseUrl}{longName}", sub.ForwardDeadLetteredMessagesTo); } + + [Fact] + [DisplayTestMethodName] + public void UnknownElementsInAtomXmlHanldedRight() + { + string subscriptionDescriptionXml = $@"" + + $@"testqueue1" + + $@"" + + $@"" + + $"{XmlConvert.ToString(TimeSpan.FromMinutes(1))}" + + $"true" + + $"{XmlConvert.ToString(TimeSpan.FromMinutes(60))}" + + $"false" + + $"false" + + $"10" + + $"true" + + $"Active" + + $"fq1" + + $"" + + $"{XmlConvert.ToString(TimeSpan.FromMinutes(60))}" + + $"prop1" + + $"xyzfalsetrue" + + $"prop3" + + $"prop4" + + $"" + + $"" + + $""; + + SubscriptionDescription subscriptionDesc = SubscriptionDescriptionExtensions.ParseFromContent("abcd", subscriptionDescriptionXml); + Assert.NotNull(subscriptionDesc.UnknownProperties); + XDocument doc = SubscriptionDescriptionExtensions.Serialize(subscriptionDesc); + + XName subscriptionDescriptionElementName = XName.Get("SubscriptionDescription", ManagementClientConstants.ServiceBusNamespace); + XElement expectedSubscriptionDecriptionElement = XElement.Parse(subscriptionDescriptionXml).Descendants(subscriptionDescriptionElementName).FirstOrDefault(); + XElement serializedSubscriptionDescritionElement = doc.Descendants(subscriptionDescriptionElementName).FirstOrDefault(); + XNode expectedChildNode = expectedSubscriptionDecriptionElement.FirstNode; + XNode actualChildNode = serializedSubscriptionDescritionElement.FirstNode; + while (expectedChildNode != null) + { + Assert.NotNull(actualChildNode); + Assert.True(XNode.DeepEquals(expectedChildNode, actualChildNode), $"SubscriptionDescrition parsing and serialization combo didn't work as expected. {expectedChildNode.ToString()}"); + expectedChildNode = expectedChildNode.NextNode; + actualChildNode = actualChildNode.NextNode; + } + } } \ No newline at end of file