// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. #if NET45 using System; using System.IO; using System.Linq; using System.Runtime.Serialization; using System.Text; using System.Threading.Tasks; using System.Xml; using Microsoft.AspNet.Http; using Moq; using Xunit; namespace Microsoft.AspNet.Mvc.ModelBinding { public class DataContractSerializerInputFormatterTests { [DataContract(Name = "DummyClass", Namespace = "")] public class DummyClass { [DataMember] public int SampleInt { get; set; } } [DataContract(Name = "TestLevelOne", Namespace = "")] public class TestLevelOne { [DataMember] public int SampleInt { get; set; } [DataMember] public string sampleString; public DateTime SampleDate { get; set; } } [DataContract(Name = "TestLevelTwo", Namespace = "")] public class TestLevelTwo { [DataMember] public string SampleString { get; set; } [DataMember] public TestLevelOne TestOne { get; set; } } [Fact] public void XmlDataContractSerializerFormatterHasProperSuppportedMediaTypes() { // Arrange & Act var formatter = new XmlDataContractSerializerInputFormatter(); // Assert Assert.True(formatter.SupportedMediaTypes .Select(content => content.RawValue) .Contains("application/xml")); Assert.True(formatter.SupportedMediaTypes .Select(content => content.RawValue) .Contains("text/xml")); } [Fact] public void XmlDataContractSerializerFormatterHasProperSuppportedEncodings() { // Arrange & Act var formatter = new XmlDataContractSerializerInputFormatter(); // Assert Assert.True(formatter.SupportedEncodings.Any(i => i.WebName == "utf-8")); Assert.True(formatter.SupportedEncodings.Any(i => i.WebName == "utf-16")); } [Fact] public async Task XmlDataContractSerializerFormatterReadsSimpleTypes() { // Arrange var expectedInt = 10; var expectedString = "TestString"; var input = "" + "" + expectedInt + "" + "" + expectedString + ""; var formatter = new XmlDataContractSerializerInputFormatter(); var contentBytes = Encoding.UTF8.GetBytes(input); var context = GetInputFormatterContext(contentBytes, typeof(TestLevelOne)); // Act await formatter.ReadAsync(context); // Assert Assert.NotNull(context.Model); Assert.IsType(context.Model); var model = context.Model as TestLevelOne; Assert.Equal(expectedInt, model.SampleInt); Assert.Equal(expectedString, model.sampleString); } [Fact] public async Task XmlDataContractSerializerFormatterReadsComplexTypes() { // Arrange var expectedInt = 10; var expectedString = "TestString"; var expectedLevelTwoString = "102"; var input = "" + "" + expectedLevelTwoString + "" + "" + expectedInt + "" + "" + expectedString + ""; var formatter = new XmlDataContractSerializerInputFormatter(); var contentBytes = Encoding.UTF8.GetBytes(input); var context = GetInputFormatterContext(contentBytes, typeof(TestLevelTwo)); // Act await formatter.ReadAsync(context); // Assert Assert.NotNull(context.Model); Assert.IsType(context.Model); var model = context.Model as TestLevelTwo; Assert.Equal(expectedLevelTwoString, model.SampleString); Assert.Equal(expectedInt, model.TestOne.SampleInt); Assert.Equal(expectedString, model.TestOne.sampleString); } [Fact] public async Task XmlDataContractSerializerFormatterReadsWhenMaxDepthIsModified() { // Arrange var expectedInt = 10; var input = "" + "" + expectedInt + ""; var formatter = new XmlDataContractSerializerInputFormatter(); formatter.MaxDepth = 10; var contentBytes = Encoding.UTF8.GetBytes(input); var context = GetInputFormatterContext(contentBytes, typeof(DummyClass)); // Act await formatter.ReadAsync(context); // Assert Assert.NotNull(context.Model); Assert.IsType(context.Model); var model = context.Model as DummyClass; Assert.Equal(expectedInt, model.SampleInt); } [Fact] public async Task XmlDataContractSerializerFormatterThrowsOnExceededMaxDepth() { // Arrange var input = "" + "test" + "10" + "test"; var formatter = new XmlDataContractSerializerInputFormatter(); formatter.MaxDepth = 1; var contentBytes = Encoding.UTF8.GetBytes(input); var context = GetInputFormatterContext(contentBytes, typeof(TestLevelTwo)); // Act & Assert await Assert.ThrowsAsync(typeof(SerializationException), async () => await formatter.ReadAsync(context)); } [Fact] public async Task XmlDataContractSerializerFormatterThrowsWhenReaderQuotasAreChanged() { // Arrange var input = "" + "test" + "10" + "test"; var formatter = new XmlDataContractSerializerInputFormatter(); formatter.XmlDictionaryReaderQuotas.MaxStringContentLength = 2; var contentBytes = Encoding.UTF8.GetBytes(input); var context = GetInputFormatterContext(contentBytes, typeof(TestLevelTwo)); // Act & Assert await Assert.ThrowsAsync(typeof(SerializationException), async () => await formatter.ReadAsync(context)); } [Fact] public void XmlDataContractSerializerFormatterThrowsWhenMaxDepthIsBelowOne() { // Arrange var formatter = new XmlDataContractSerializerInputFormatter(); // Act & Assert Assert.Throws(typeof(ArgumentException), () => formatter.MaxDepth = 0); } [Fact] public async Task VerifyStreamIsOpenAfterRead() { // Arrange var input = "" + "10"; var formatter = new XmlDataContractSerializerInputFormatter(); var contentBytes = Encoding.UTF8.GetBytes(input); var context = GetInputFormatterContext(contentBytes, typeof(DummyClass)); // Act await formatter.ReadAsync(context); // Assert Assert.NotNull(context.Model); Assert.True(context.HttpContext.Request.Body.CanRead); } [Fact] public async Task XmlDataContractSerializerFormatterThrowsOnInvalidCharacters() { // Arrange var inpStart = Encodings.UTF16EncodingLittleEndian.GetBytes("" + ""); byte[] inp = { 192, 193 }; var inpEnd = Encodings.UTF16EncodingLittleEndian.GetBytes(""); var contentBytes = new byte[inpStart.Length + inp.Length + inpEnd.Length]; Buffer.BlockCopy(inpStart, 0, contentBytes, 0, inpStart.Length); Buffer.BlockCopy(inp, 0, contentBytes, inpStart.Length, inp.Length); Buffer.BlockCopy(inpEnd, 0, contentBytes, inpStart.Length + inp.Length, inpEnd.Length); var formatter = new XmlDataContractSerializerInputFormatter(); var context = GetInputFormatterContext(contentBytes, typeof(TestLevelTwo)); // Act await Assert.ThrowsAsync(typeof(XmlException), async () => await formatter.ReadAsync(context)); } [Fact] public async Task XmlDataContractSerializerFormatterIgnoresBOMCharacters() { // Arrange var sampleString = "Test"; var sampleStringBytes = Encoding.UTF8.GetBytes(sampleString); var inputStart = Encoding.UTF8.GetBytes("" + Environment.NewLine + "" + sampleString); byte[] bom = { 0xef, 0xbb, 0xbf }; var inputEnd = Encoding.UTF8.GetBytes(""); var expectedBytes = new byte[sampleString.Length + bom.Length]; var contentBytes = new byte[inputStart.Length + bom.Length + inputEnd.Length]; Buffer.BlockCopy(inputStart, 0, contentBytes, 0, inputStart.Length); Buffer.BlockCopy(bom, 0, contentBytes, inputStart.Length, bom.Length); Buffer.BlockCopy(inputEnd, 0, contentBytes, inputStart.Length + bom.Length, inputEnd.Length); var formatter = new XmlDataContractSerializerInputFormatter(); var context = GetInputFormatterContext(contentBytes, typeof(TestLevelTwo)); // Act await formatter.ReadAsync(context); // Assert Assert.NotNull(context.Model); var model = context.Model as TestLevelTwo; Buffer.BlockCopy(sampleStringBytes, 0, expectedBytes, 0, sampleStringBytes.Length); Buffer.BlockCopy(bom, 0, expectedBytes, sampleStringBytes.Length, bom.Length); Assert.Equal(expectedBytes, Encoding.UTF8.GetBytes(model.SampleString)); } [Fact] public async Task XmlDataContractSerializerAcceptsUTF16Characters() { // Arrange var expectedInt = 10; var expectedString = "TestString"; var input = "" + "" + expectedInt + "" + "" + expectedString + ""; var formatter = new XmlDataContractSerializerInputFormatter(); var contentBytes = Encodings.UTF16EncodingLittleEndian.GetBytes(input); var context = GetInputFormatterContext(contentBytes, typeof(TestLevelOne)); // Act await formatter.ReadAsync(context); // Assert Assert.NotNull(context.Model); Assert.IsType(context.Model); var model = context.Model as TestLevelOne; Assert.Equal(expectedInt, model.SampleInt); Assert.Equal(expectedString, model.sampleString); } private InputFormatterContext GetInputFormatterContext(byte[] contentBytes, Type modelType) { var httpContext = GetHttpContext(contentBytes); var modelState = new ModelStateDictionary(); var metadata = new EmptyModelMetadataProvider().GetMetadataForType(null, modelType); return new InputFormatterContext(httpContext, metadata, modelState); } private static HttpContext GetHttpContext(byte[] contentBytes, string contentType = "application/xml") { var request = new Mock(); var headers = new Mock(); headers.SetupGet(h => h["Content-Type"]).Returns(contentType); request.SetupGet(r => r.Headers).Returns(headers.Object); request.SetupGet(f => f.Body).Returns(new MemoryStream(contentBytes)); var httpContext = new Mock(); httpContext.SetupGet(c => c.Request).Returns(request.Object); return httpContext.Object; } } } #endif