public void ReadXmlWithoutProperties()
        {
            var xml = $"<san:Properties xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}' />";

            using (var reader = XmlReader.Create(new StringReader(xml)))
            {
                var sut = new PropertyExtractorCollection();
                sut.ReadXml(reader);
                sut.Should().BeEmpty();
            }
        }
        public void ReadXmlForConstantExtractorThrowsWhenModeAttributeIsInvalid()
        {
            var xml = $"<san:Properties xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}'>"
                      + "<s0:Property1 mode='demote' value='constant'/>"
                      + "</san:Properties>";

            using (var reader = XmlReader.Create(new StringReader(xml)))
            {
                var sut = new PropertyExtractorCollection();
                Invoking(() => sut.ReadXml(reader)).Should().Throw <ArgumentException>().WithMessage("ExtractionMode 'Demote' is not supported by ConstantExtractor.*");
            }
        }
        public void ReadXmlForConstantExtractorThrowsWhenValueIsEmpty()
        {
            var xml = $"<san:Properties xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}'>"
                      + "<s0:Property1 value=''/>"
                      + "</san:Properties>";

            using (var reader = XmlReader.Create(new StringReader(xml)))
            {
                var sut = new PropertyExtractorCollection();
                Invoking(() => sut.ReadXml(reader)).Should().Throw <ArgumentNullException>().Where(e => e.ParamName == "value");
            }
        }
        public void ReadXmlForConstantExtractor()
        {
            var xml = $"<san:Properties xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}'>"
                      + "<s0:Property1 value='constant'/>"
                      + "</san:Properties>";

            using (var reader = XmlReader.Create(new StringReader(xml)))
            {
                var sut = new PropertyExtractorCollection();
                sut.ReadXml(reader);
                sut.Should().BeEquivalentTo(new ConstantExtractor(new XmlQualifiedName("Property1", "urn"), "constant", ExtractionMode.Write));
            }
        }
        public void ReadXmlThrowsWhenRootElementNamespaceIsInvalid()
        {
            const string xml = "<san:Properties xmlns:s0='urn' xmlns:san='urn:schemas.stateless.be:biztalk:2012:12:extractors'><s0:PropertyName xpath='*'/></san:Properties>";

            using (var reader = XmlReader.Create(new StringReader(xml)))
            {
                var sut = new PropertyExtractorCollection();
                Invoking(() => sut.ReadXml(reader))
                .Should().Throw <ConfigurationErrorsException>()
                .WithInnerException <XmlException>()
                .WithMessage($"Element 'Properties' with namespace name '{SchemaAnnotation.NAMESPACE}' was not found. Line 1, position 2.");
            }
        }
        public void ReadXmlForXPathExtractorWithPromotedAttribute()
        {
            var xml = $"<san:Properties xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}'>"
                      + "<s0:Property2 promoted='true' xpath='*/other-node'/>"
                      + "</san:Properties>";

            using (var reader = XmlReader.Create(new StringReader(xml)))
            {
                var sut = new PropertyExtractorCollection();
                sut.ReadXml(reader);
                sut.Should().BeEquivalentTo(new XPathExtractor(new XmlQualifiedName("Property2", "urn"), "*/other-node", ExtractionMode.Promote));
            }
        }
        public void ReadXmlForQNameValueExtractorFallsBackOnXPathExtractorWhenQNameValueExtractionModeIsDefault()
        {
            var xml = $"<san:Properties xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}'>"
                      + "<s0:Property3 mode='promote' qnameValue='name' xpath='*/extra-node'/>"
                      + "</san:Properties>";

            using (var reader = XmlReader.Create(new StringReader(xml)))
            {
                var sut = new PropertyExtractorCollection();
                sut.ReadXml(reader);
                sut.Should().BeEquivalentTo(new XPathExtractor(new XmlQualifiedName("Property3", "urn"), "*/extra-node", ExtractionMode.Promote));
            }
        }
        public void ReadXmlForPropertyExtractorThrowsWhenPromotedAttributeIsPresent()
        {
            var xml = $"<san:Properties xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}'>"
                      + "<s0:Property1 promoted='true'/>"
                      + "</san:Properties>";

            using (var reader = XmlReader.Create(new StringReader(xml)))
            {
                var sut = new PropertyExtractorCollection();
                Invoking(() => sut.ReadXml(reader))
                .Should().Throw <ConfigurationErrorsException>().WithMessage("ExtractionMode is missing for PropertyExtractor without a Value or an XPath.*");
            }
        }
        public void ReadXmlForExtractorPrecedence()
        {
            var xml = $"<san:Properties precedence='schemaOnly' xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}'>"
                      + "<s0:Property1 mode='clear'/>"
                      + "</san:Properties>";

            using (var reader = XmlReader.Create(new StringReader(xml)))
            {
                var sut = new PropertyExtractorCollection();
                sut.ReadXml(reader);
                sut.Precedence.Should().Be(ExtractorPrecedence.SchemaOnly);
            }
        }
        public void ReadXmlForPropertyExtractorThrowsWhenModeAttributeIsInvalid()
        {
            var xml = $"<san:Properties xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}'>"
                      + "<s0:Property1 mode='promote'/>"
                      + "</san:Properties>";

            using (var reader = XmlReader.Create(new StringReader(xml)))
            {
                var sut = new PropertyExtractorCollection();
                Invoking(() => sut.ReadXml(reader))
                .Should().Throw <ArgumentException>()
                .WithMessage("Invalid ExtractionMode, only Clear and Ignore are supported for PropertyExtractor without a Value or an XPath.*");
            }
        }
        public void ReadXmlThrowsWhenPropertyToExtractHasNoNamespace()
        {
            var xml = $"<san:Properties xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}'>"
                      + "<PropertyName xpath='*'/>"
                      + "</san:Properties>";

            using (var reader = XmlReader.Create(new StringReader(xml)))
            {
                var sut = new PropertyExtractorCollection();
                Invoking(() => sut.ReadXml(reader))
                .Should().Throw <ConfigurationErrorsException>()
                .WithInnerException <XmlException>()
                .WithMessage("The following properties are not associated with the target namespace URI of some property schema: [PropertyName].");
            }
        }
        public void ReadXmlThrowsWhenDuplicatePropertyToExtract()
        {
            var xml = $"<san:Properties xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}'>"
                      + "<s0:PropertyName xpath='*'/><s0:PropertyName xpath='*'/>"
                      + "</san:Properties>";

            using (var reader = XmlReader.Create(new StringReader(xml)))
            {
                var sut = new PropertyExtractorCollection();
                Invoking(() => sut.ReadXml(reader))
                .Should().Throw <ConfigurationErrorsException>()
                .WithInnerException <XmlException>()
                .WithMessage("The following properties are declared multiple times: [urn:PropertyName].");
            }
        }
 public static PropertyExtractorCollection Deserialize(string xml)
 {
     if (xml.IsNullOrEmpty())
     {
         return(PropertyExtractorCollection.Empty);
     }
     using (var reader = XmlReader.Create(new StringReader(xml), new XmlReaderSettings {
         IgnoreWhitespace = true, IgnoreComments = true
     }))
     {
         var collection = new PropertyExtractorCollection();
         collection.ReadXml(reader);
         return(collection);
     }
 }
        public void ReadXmlForXPathExtractorThrowsWhenXPathIsEmpty()
        {
            var xml = $"<san:Properties xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}'>"
                      + "<s0:PropertyName xpath=''/>"
                      + "</san:Properties>";

            using (var reader = XmlReader.Create(new StringReader(xml)))
            {
                var sut = new PropertyExtractorCollection();
                Invoking(() => sut.ReadXml(reader))
                .Should().Throw <ConfigurationErrorsException>()
                .WithInnerException <XPathException>()
                // ReSharper disable once StringLiteralTypo
                .WithMessage("Bad Query string encoundered in XPath:*");
            }
        }
        public void ReadXml()
        {
            var xml = $"<san:Properties xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}'>"
                      + "<s0:Property1 xpath='*/some-node'/>"
                      + "<s0:Property2 promoted='true' xpath='*/other-node'/>"
                      + "<s0:Property3 mode='write' value='constant'/>"
                      + "<s0:Property4 mode='clear'/>"
                      + "</san:Properties>";

            using (var reader = XmlReader.Create(new StringReader(xml)))
            {
                var sut = new PropertyExtractorCollection();
                sut.ReadXml(reader);
                sut.Should().BeEquivalentTo(
                    new XPathExtractor(new XmlQualifiedName("Property1", "urn"), "*/some-node", ExtractionMode.Write),
                    new XPathExtractor(new XmlQualifiedName("Property2", "urn"), "*/other-node", ExtractionMode.Promote),
                    new ConstantExtractor(new XmlQualifiedName("Property3", "urn"), "constant", ExtractionMode.Write),
                    new PropertyExtractor(new XmlQualifiedName("Property4", "urn"), ExtractionMode.Clear)
                    );
            }
        }