1 // Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. 2 3 using System.IO; 4 using System.Net; 5 using System.Net.Http; 6 using System.Net.Http.Formatting; 7 using System.Net.Http.Headers; 8 using System.Text; 9 using System.Threading.Tasks; 10 using Moq; 11 using Xunit; 12 using Xunit.Extensions; 13 using Assert = Microsoft.TestCommon.AssertEx; 14 15 namespace System.Web.Http.Tracing.Tracers 16 { 17 public class MediaTypeFormatterTracerTest 18 { 19 [Fact] OnReadFromStreamAsync_Traces()20 public void OnReadFromStreamAsync_Traces() 21 { 22 // Arrange 23 Mock<MediaTypeFormatter> mockFormatter = new Mock<MediaTypeFormatter>() { CallBase = true }; 24 mockFormatter.Setup( 25 f => f.ReadFromStreamAsync(It.IsAny<Type>(), It.IsAny<Stream>(), It.IsAny<HttpContentHeaders>(), It.IsAny<IFormatterLogger>())). 26 Returns(TaskHelpers.FromResult<object>("sampleValue")); 27 TestTraceWriter traceWriter = new TestTraceWriter(); 28 HttpRequestMessage request = new HttpRequestMessage(); 29 request.Content = new StringContent(""); 30 MediaTypeFormatterTracer tracer = new MediaTypeFormatterTracer(mockFormatter.Object, traceWriter, request); 31 TraceRecord[] expectedTraces = new TraceRecord[] 32 { 33 new TraceRecord(request, TraceCategories.FormattingCategory, TraceLevel.Info) { Kind = TraceKind.Begin, Operation = "ReadFromStreamAsync" }, 34 new TraceRecord(request, TraceCategories.FormattingCategory, TraceLevel.Info) { Kind = TraceKind.End, Operation = "ReadFromStreamAsync" } 35 }; 36 37 // Act 38 Task<object> task = tracer.ReadFromStreamAsync(typeof(string), new MemoryStream(), request.Content.Headers, null); 39 string result = task.Result as string; 40 41 // Assert 42 Assert.Equal<TraceRecord>(expectedTraces, traceWriter.Traces, new TraceRecordComparer()); 43 Assert.Equal("sampleValue", result); 44 } 45 46 [Fact] OnReadFromStreamAsync_Traces_And_Throws_When_Inner_Throws()47 public void OnReadFromStreamAsync_Traces_And_Throws_When_Inner_Throws() 48 { 49 // Arrange 50 InvalidOperationException exception = new InvalidOperationException("test"); 51 Mock<MediaTypeFormatter> mockFormatter = new Mock<MediaTypeFormatter>() { CallBase = true }; 52 mockFormatter.Setup( 53 f => f.ReadFromStreamAsync(It.IsAny<Type>(), It.IsAny<Stream>(), It.IsAny<HttpContentHeaders>(), It.IsAny<IFormatterLogger>())).Throws(exception); 54 55 TestTraceWriter traceWriter = new TestTraceWriter(); 56 HttpRequestMessage request = new HttpRequestMessage(); 57 request.Content = new StringContent(""); 58 MediaTypeFormatterTracer tracer = new MediaTypeFormatterTracer(mockFormatter.Object, traceWriter, request); 59 TraceRecord[] expectedTraces = new TraceRecord[] 60 { 61 new TraceRecord(request, TraceCategories.FormattingCategory, TraceLevel.Info) { Kind = TraceKind.Begin, Operation = "ReadFromStreamAsync" }, 62 new TraceRecord(request, TraceCategories.FormattingCategory, TraceLevel.Error) { Kind = TraceKind.End, Operation = "ReadFromStreamAsync" } 63 }; 64 65 // Act 66 Exception thrown = Assert.Throws<InvalidOperationException>(() => tracer.ReadFromStreamAsync(typeof(string), new MemoryStream(), request.Content.Headers, null)); 67 68 // Assert 69 Assert.Equal<TraceRecord>(expectedTraces, traceWriter.Traces, new TraceRecordComparer()); 70 Assert.Same(exception, thrown); 71 Assert.Same(exception, traceWriter.Traces[1].Exception); 72 } 73 74 [Fact] OnReadFromStreamAsync_Traces_And_Faults_When_Inner_Faults()75 public void OnReadFromStreamAsync_Traces_And_Faults_When_Inner_Faults() 76 { 77 // Arrange 78 InvalidOperationException exception = new InvalidOperationException("test"); 79 Mock<MediaTypeFormatter> mockFormatter = new Mock<MediaTypeFormatter>() { CallBase = true }; 80 TaskCompletionSource<object> tcs = new TaskCompletionSource<object>(); 81 tcs.TrySetException(exception); 82 83 mockFormatter.Setup( 84 f => f.ReadFromStreamAsync(It.IsAny<Type>(), It.IsAny<Stream>(), It.IsAny<HttpContentHeaders>(), It.IsAny<IFormatterLogger>())). 85 Returns(tcs.Task); 86 TestTraceWriter traceWriter = new TestTraceWriter(); 87 HttpRequestMessage request = new HttpRequestMessage(); 88 request.Content = new StringContent(""); 89 MediaTypeFormatterTracer tracer = new MediaTypeFormatterTracer(mockFormatter.Object, traceWriter, request); 90 TraceRecord[] expectedTraces = new TraceRecord[] 91 { 92 new TraceRecord(request, TraceCategories.FormattingCategory, TraceLevel.Info) { Kind = TraceKind.Begin, Operation = "ReadFromStreamAsync" }, 93 new TraceRecord(request, TraceCategories.FormattingCategory, TraceLevel.Error) { Kind = TraceKind.End, Operation = "ReadFromStreamAsync" } 94 }; 95 96 // Act 97 Task<object> task = tracer.ReadFromStreamAsync(typeof(string), new MemoryStream(), request.Content.Headers, null); 98 99 // Assert 100 Exception thrown = Assert.Throws<InvalidOperationException>(() => task.Wait()); 101 Assert.Equal<TraceRecord>(expectedTraces, traceWriter.Traces, new TraceRecordComparer()); 102 Assert.Same(exception, thrown); 103 Assert.Same(exception, traceWriter.Traces[1].Exception); 104 } 105 106 [Fact] OnWriteToStreamAsync_Traces()107 public void OnWriteToStreamAsync_Traces() 108 { 109 // Arrange 110 Mock<MediaTypeFormatter> mockFormatter = new Mock<MediaTypeFormatter>() { CallBase = true }; 111 mockFormatter.Setup( 112 f => f.WriteToStreamAsync(It.IsAny<Type>(), It.IsAny<Object>(), It.IsAny<Stream>(), It.IsAny<HttpContentHeaders>(), It.IsAny<TransportContext>())). 113 Returns(TaskHelpers.Completed()); 114 TestTraceWriter traceWriter = new TestTraceWriter(); 115 HttpRequestMessage request = new HttpRequestMessage(); 116 request.Content = new StringContent(""); 117 MediaTypeFormatterTracer tracer = new MediaTypeFormatterTracer(mockFormatter.Object, traceWriter, request); 118 TraceRecord[] expectedTraces = new TraceRecord[] 119 { 120 new TraceRecord(request, TraceCategories.FormattingCategory, TraceLevel.Info) { Kind = TraceKind.Begin, Operation = "WriteToStreamAsync" }, 121 new TraceRecord(request, TraceCategories.FormattingCategory, TraceLevel.Info) { Kind = TraceKind.End, Operation = "WriteToStreamAsync" } 122 }; 123 124 // Act 125 Task task = tracer.WriteToStreamAsync(typeof(string), "sampleValue", new MemoryStream(), request.Content.Headers, transportContext: null); 126 task.Wait(); 127 128 // Assert 129 Assert.Equal<TraceRecord>(expectedTraces, traceWriter.Traces, new TraceRecordComparer()); 130 } 131 132 [Fact] OnWriteToStreamAsync_Traces_And_Throws_When_Inner_Throws()133 public void OnWriteToStreamAsync_Traces_And_Throws_When_Inner_Throws() 134 { 135 // Arrange 136 InvalidOperationException exception = new InvalidOperationException("test"); 137 Mock<MediaTypeFormatter> mockFormatter = new Mock<MediaTypeFormatter>() { CallBase = true }; 138 mockFormatter.Setup( 139 f => 140 f.WriteToStreamAsync(It.IsAny<Type>(), It.IsAny<Object>(), It.IsAny<Stream>(), 141 It.IsAny<HttpContentHeaders>(), It.IsAny<TransportContext>())). 142 Throws(exception); 143 144 TestTraceWriter traceWriter = new TestTraceWriter(); 145 HttpRequestMessage request = new HttpRequestMessage(); 146 request.Content = new StringContent(""); 147 MediaTypeFormatterTracer tracer = new MediaTypeFormatterTracer(mockFormatter.Object, traceWriter, request); 148 TraceRecord[] expectedTraces = new TraceRecord[] 149 { 150 new TraceRecord(request, TraceCategories.FormattingCategory, TraceLevel.Info) { Kind = TraceKind.Begin, Operation = "WriteToStreamAsync" }, 151 new TraceRecord(request, TraceCategories.FormattingCategory, TraceLevel.Error) { Kind = TraceKind.End, Operation = "WriteToStreamAsync" } 152 }; 153 154 // Act 155 Exception thrown = Assert.Throws<InvalidOperationException>(() => tracer.WriteToStreamAsync(typeof(string), "sampleValue", new MemoryStream(), request.Content.Headers, transportContext: null)); 156 157 // Assert 158 Assert.Equal<TraceRecord>(expectedTraces, traceWriter.Traces, new TraceRecordComparer()); 159 Assert.Same(exception, thrown); 160 Assert.Same(exception, traceWriter.Traces[1].Exception); 161 } 162 163 [Fact] OnWriteToStreamAsync_Traces_And_Faults_When_Inner_Faults()164 public void OnWriteToStreamAsync_Traces_And_Faults_When_Inner_Faults() 165 { 166 // Arrange 167 InvalidOperationException exception = new InvalidOperationException("test"); 168 Mock<MediaTypeFormatter> mockFormatter = new Mock<MediaTypeFormatter>() { CallBase = true }; 169 TaskCompletionSource<object> tcs = new TaskCompletionSource<object>(); 170 tcs.TrySetException(exception); 171 172 mockFormatter.Setup( 173 f => f.WriteToStreamAsync(It.IsAny<Type>(), It.IsAny<Object>(), It.IsAny<Stream>(), It.IsAny<HttpContentHeaders>(), It.IsAny<TransportContext>())). 174 Returns(tcs.Task); 175 176 TestTraceWriter traceWriter = new TestTraceWriter(); 177 HttpRequestMessage request = new HttpRequestMessage(); 178 request.Content = new StringContent(""); 179 MediaTypeFormatterTracer tracer = new MediaTypeFormatterTracer(mockFormatter.Object, traceWriter, request); 180 TraceRecord[] expectedTraces = new TraceRecord[] 181 { 182 new TraceRecord(request, TraceCategories.FormattingCategory, TraceLevel.Info) { Kind = TraceKind.Begin, Operation = "WriteToStreamAsync" }, 183 new TraceRecord(request, TraceCategories.FormattingCategory, TraceLevel.Error) { Kind = TraceKind.End, Operation = "WriteToStreamAsync" } 184 }; 185 186 // Act 187 Task task = tracer.WriteToStreamAsync(typeof(string), "sampleValue", new MemoryStream(), request.Content.Headers, transportContext: null); 188 189 // Assert 190 Exception thrown = Assert.Throws<InvalidOperationException>(() => task.Wait()); 191 Assert.Equal<TraceRecord>(expectedTraces, traceWriter.Traces, new TraceRecordComparer()); 192 Assert.Same(exception, thrown); 193 Assert.Same(exception, traceWriter.Traces[1].Exception); 194 } 195 196 [Fact] GetPerRequestFormatterInstance_Returns_Tracing_MediaTypeFormatter()197 public void GetPerRequestFormatterInstance_Returns_Tracing_MediaTypeFormatter() 198 { 199 // Arrange 200 Mock<MediaTypeFormatter> mockReturnFormatter = new Mock<MediaTypeFormatter>() { CallBase = true }; 201 Mock<MediaTypeFormatter> mockFormatter = new Mock<MediaTypeFormatter>() { CallBase = true }; 202 mockFormatter.Setup( 203 f => 204 f.GetPerRequestFormatterInstance(It.IsAny<Type>(), It.IsAny<HttpRequestMessage>(), 205 It.IsAny<MediaTypeHeaderValue>())).Returns(mockReturnFormatter.Object); 206 207 TestTraceWriter traceWriter = new TestTraceWriter(); 208 HttpRequestMessage request = new HttpRequestMessage(); 209 MediaTypeFormatterTracer tracer = new MediaTypeFormatterTracer(mockFormatter.Object, traceWriter, request); 210 211 // Act 212 MediaTypeFormatter actualFormatter = tracer.GetPerRequestFormatterInstance(typeof(string), request, new MediaTypeHeaderValue("application/json")); 213 214 // Assert 215 Assert.IsAssignableFrom<IFormatterTracer>(actualFormatter); 216 } 217 218 [Theory] 219 [InlineDataAttribute(typeof(XmlMediaTypeFormatter))] 220 [InlineDataAttribute(typeof(JsonMediaTypeFormatter))] 221 [InlineDataAttribute(typeof(FormUrlEncodedMediaTypeFormatter))] CreateTracer_Returns_Tracing_Formatter(Type formatterType)222 public void CreateTracer_Returns_Tracing_Formatter(Type formatterType) 223 { 224 // Arrange 225 HttpRequestMessage request = new HttpRequestMessage(); 226 MediaTypeFormatter formatter = (MediaTypeFormatter)Activator.CreateInstance(formatterType); 227 228 // Act 229 MediaTypeFormatter tracingFormatter = MediaTypeFormatterTracer.CreateTracer(formatter, new TestTraceWriter(), request); 230 231 // Assert 232 Assert.IsAssignableFrom<IFormatterTracer>(tracingFormatter); 233 Assert.IsAssignableFrom(formatterType, tracingFormatter); 234 } 235 236 [Fact] CreateTracer_Returns_Tracing_BufferedFormatter()237 public void CreateTracer_Returns_Tracing_BufferedFormatter() 238 { 239 // Arrange 240 HttpRequestMessage request = new HttpRequestMessage(); 241 MediaTypeFormatter formatter = new Mock<BufferedMediaTypeFormatter>() { CallBase = true }.Object; 242 243 // Act 244 MediaTypeFormatter tracingFormatter = MediaTypeFormatterTracer.CreateTracer(formatter, new TestTraceWriter(), request); 245 246 // Assert 247 Assert.IsAssignableFrom<IFormatterTracer>(tracingFormatter); 248 Assert.IsAssignableFrom<BufferedMediaTypeFormatter>(tracingFormatter); 249 } 250 251 [Theory] 252 [InlineDataAttribute(typeof(XmlMediaTypeFormatter))] 253 [InlineDataAttribute(typeof(JsonMediaTypeFormatter))] 254 [InlineDataAttribute(typeof(FormUrlEncodedMediaTypeFormatter))] CreateTracer_Loads_SupportedEncodings_From_InnerFormatter(Type formatterType)255 public void CreateTracer_Loads_SupportedEncodings_From_InnerFormatter(Type formatterType) 256 { 257 // Arrange 258 HttpRequestMessage request = new HttpRequestMessage(); 259 MediaTypeFormatter formatter = (MediaTypeFormatter)Activator.CreateInstance(formatterType); 260 Mock<Encoding> encoding = new Mock<Encoding>(); 261 formatter.SupportedEncodings.Clear(); 262 formatter.SupportedEncodings.Add(encoding.Object); 263 264 // Act 265 MediaTypeFormatter tracingFormatter = MediaTypeFormatterTracer.CreateTracer(formatter, new TestTraceWriter(), request); 266 267 // Assert 268 Assert.Equal(formatter.SupportedEncodings, tracingFormatter.SupportedEncodings); 269 } 270 271 [Theory] 272 [InlineDataAttribute(typeof(XmlMediaTypeFormatter))] 273 [InlineDataAttribute(typeof(JsonMediaTypeFormatter))] 274 [InlineDataAttribute(typeof(FormUrlEncodedMediaTypeFormatter))] CreateTracer_Loads_SupportedMediaTypes_From_InnerFormatter(Type formatterType)275 public void CreateTracer_Loads_SupportedMediaTypes_From_InnerFormatter(Type formatterType) 276 { 277 // Arrange 278 HttpRequestMessage request = new HttpRequestMessage(); 279 MediaTypeFormatter formatter = (MediaTypeFormatter)Activator.CreateInstance(formatterType); 280 281 MediaTypeHeaderValue mediaTypeHeaderValue = new MediaTypeHeaderValue("application/dummy"); 282 formatter.SupportedMediaTypes.Clear(); 283 formatter.SupportedMediaTypes.Add(mediaTypeHeaderValue); 284 285 // Act 286 MediaTypeFormatter tracingFormatter = MediaTypeFormatterTracer.CreateTracer(formatter, new TestTraceWriter(), request); 287 288 // Assert 289 Assert.Equal(formatter.SupportedMediaTypes, tracingFormatter.SupportedMediaTypes); 290 } 291 292 [Theory] 293 [InlineDataAttribute(typeof(XmlMediaTypeFormatter))] 294 [InlineDataAttribute(typeof(JsonMediaTypeFormatter))] 295 [InlineDataAttribute(typeof(FormUrlEncodedMediaTypeFormatter))] CreateTracer_Loads_MediaTypeMappings_From_InnerFormatter(Type formatterType)296 public void CreateTracer_Loads_MediaTypeMappings_From_InnerFormatter(Type formatterType) 297 { 298 // Arrange 299 HttpRequestMessage request = new HttpRequestMessage(); 300 MediaTypeFormatter formatter = (MediaTypeFormatter)Activator.CreateInstance(formatterType); 301 302 Mock<MediaTypeMapping> mediaTypeMapping = new Mock<MediaTypeMapping>(new MediaTypeHeaderValue("application/dummy")); 303 formatter.MediaTypeMappings.Clear(); 304 formatter.MediaTypeMappings.Add(mediaTypeMapping.Object); 305 306 // Act 307 MediaTypeFormatter tracingFormatter = MediaTypeFormatterTracer.CreateTracer(formatter, new TestTraceWriter(), request); 308 309 // Assert 310 Assert.Equal(formatter.MediaTypeMappings, tracingFormatter.MediaTypeMappings); 311 } 312 313 [Theory] 314 [InlineDataAttribute(typeof(XmlMediaTypeFormatter))] 315 [InlineDataAttribute(typeof(JsonMediaTypeFormatter))] 316 [InlineDataAttribute(typeof(FormUrlEncodedMediaTypeFormatter))] CreateTracer_Loads_RequiredMemberSelector_From_InnerFormatter(Type formatterType)317 public void CreateTracer_Loads_RequiredMemberSelector_From_InnerFormatter(Type formatterType) 318 { 319 // Arrange 320 HttpRequestMessage request = new HttpRequestMessage(); 321 MediaTypeFormatter formatter = (MediaTypeFormatter)Activator.CreateInstance(formatterType); 322 323 Mock<IRequiredMemberSelector> requiredMemberSelector = new Mock<IRequiredMemberSelector>(); 324 formatter.RequiredMemberSelector = requiredMemberSelector.Object; 325 326 // Act 327 MediaTypeFormatter tracingFormatter = MediaTypeFormatterTracer.CreateTracer(formatter, new TestTraceWriter(), request); 328 329 // Assert 330 Assert.Equal(formatter.RequiredMemberSelector, tracingFormatter.RequiredMemberSelector); 331 } 332 } 333 } 334