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