PageRenderTime 55ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/test/System.Web.Http.WebHost.Test/HttpControllerHandlerTest.cs

https://github.com/huyq2002/aspnetwebstack
C# | 2119 lines | 1600 code | 325 blank | 194 comment | 84 complexity | b4eab9694b6c24e48ca5e40a68bb6289 MD5 | raw file
Possible License(s): Apache-2.0

Large files files are truncated, but you can click here to view the full file

  1. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Collections.Specialized;
  5. using System.Diagnostics.Contracts;
  6. using System.IO;
  7. using System.Linq;
  8. using System.Net;
  9. using System.Net.Http;
  10. using System.Net.Http.Formatting;
  11. using System.Net.Http.Headers;
  12. using System.Reflection;
  13. using System.Text;
  14. using System.Threading;
  15. using System.Threading.Tasks;
  16. using System.Web.Http.Controllers;
  17. using System.Web.Http.ExceptionHandling;
  18. using System.Web.Http.Hosting;
  19. using System.Web.Http.Results;
  20. using System.Web.Http.WebHost.Routing;
  21. using System.Web.Routing;
  22. using Microsoft.TestCommon;
  23. using Moq;
  24. using Newtonsoft.Json.Linq;
  25. namespace System.Web.Http.WebHost
  26. {
  27. public class HttpControllerHandlerTest
  28. {
  29. public static TheoryDataSet<HttpMethod> AllHttpMethods
  30. {
  31. get
  32. {
  33. return new TheoryDataSet<HttpMethod>
  34. {
  35. HttpMethod.Get,
  36. HttpMethod.Post,
  37. HttpMethod.Put,
  38. HttpMethod.Delete,
  39. HttpMethod.Head,
  40. HttpMethod.Options,
  41. HttpMethod.Trace
  42. };
  43. }
  44. }
  45. public static TheoryDataSet<HttpMethod> HttpMethodsWithContent
  46. {
  47. get
  48. {
  49. return new TheoryDataSet<HttpMethod>
  50. {
  51. HttpMethod.Post,
  52. HttpMethod.Put,
  53. HttpMethod.Delete,
  54. };
  55. }
  56. }
  57. [Theory]
  58. [PropertyData("AllHttpMethods")]
  59. public void ConvertRequest_Creates_HttpRequestMessage_For_All_HttpMethods(HttpMethod httpMethod)
  60. {
  61. // Arrange
  62. HttpContextBase contextBase = CreateStubContextBase(httpMethod.Method, new MemoryStream());
  63. // Act
  64. HttpRequestMessage request = HttpControllerHandler.ConvertRequest(contextBase);
  65. // Assert
  66. Assert.Equal(httpMethod, request.Method);
  67. }
  68. [Fact]
  69. public void ConvertRequest_Copies_Headers_And_Content_Headers()
  70. {
  71. // Arrange
  72. HttpContextBase contextBase = CreateStubContextBase("Get", new MemoryStream());
  73. HttpRequestBase requestBase = contextBase.Request;
  74. NameValueCollection nameValues = requestBase.Headers;
  75. nameValues["myHeader"] = "myValue";
  76. nameValues["Content-Type"] = "application/mine";
  77. // Act
  78. HttpRequestMessage request = HttpControllerHandler.ConvertRequest(contextBase);
  79. string[] headerValues = request.Headers.GetValues("myHeader").ToArray();
  80. // Assert
  81. Assert.Equal("myValue", headerValues[0]);
  82. Assert.Equal("application/mine", request.Content.Headers.ContentType.MediaType);
  83. }
  84. [Theory]
  85. [PropertyData("HttpMethodsWithContent")]
  86. public void ConvertRequest_Creates_Request_With_Content_For_Content_Methods(HttpMethod httpMethod)
  87. {
  88. // Arrange
  89. HttpContextBase contextBase = CreateStubContextBase(httpMethod.Method, new MemoryStream());
  90. // Act
  91. HttpRequestMessage actualRequest = HttpControllerHandler.ConvertRequest(contextBase);
  92. // Assert
  93. Assert.NotNull(actualRequest.Content);
  94. }
  95. [Fact]
  96. public void ConvertRequest_Uses_HostBufferPolicySelector_To_Select_Buffered_Stream()
  97. {
  98. // Arrange
  99. HttpContextBase contextMock = CreateStubContextBase("Post", new MemoryStream(new byte[] { 5 }));
  100. MemoryStream memoryStream = new MemoryStream();
  101. // Act
  102. HttpRequestMessage actualRequest = HttpControllerHandler.ConvertRequest(contextMock);
  103. actualRequest.Content.CopyToAsync(memoryStream).Wait();
  104. byte[] actualBuffer = memoryStream.GetBuffer();
  105. // Assert
  106. Assert.Equal(5, actualBuffer[0]);
  107. }
  108. [Fact]
  109. public void ConvertRequest_AddsOwinEnvironment_WhenPresentInHttpContext()
  110. {
  111. // Arrange
  112. using (MemoryStream ignoreStream = new MemoryStream())
  113. {
  114. HttpRequestBase stubRequest = CreateStubRequestBase("IgnoreMethod", ignoreStream);
  115. IDictionary<string, object> expectedEnvironment = new Dictionary<string, object>();
  116. IDictionary items = new Hashtable();
  117. items.Add(HttpControllerHandler.OwinEnvironmentHttpContextKey, expectedEnvironment);
  118. HttpContextBase context = CreateStubContextBase(stubRequest, items);
  119. // Act
  120. using (HttpRequestMessage actualRequest = HttpControllerHandler.ConvertRequest(context))
  121. {
  122. IDictionary<string, object> environment = actualRequest.GetOwinEnvironment();
  123. // Assert
  124. Assert.Same(expectedEnvironment, environment);
  125. }
  126. }
  127. }
  128. [Fact]
  129. public void ConvertRequest_DoesNotAddOwinEnvironment_WhenNotPresentInHttpContext()
  130. {
  131. // Arrange
  132. using (MemoryStream ignoreStream = new MemoryStream())
  133. {
  134. HttpRequestBase stubRequest = CreateStubRequestBase("IgnoreMethod", ignoreStream);
  135. IDictionary items = new Hashtable();
  136. HttpContextBase context = CreateStubContextBase(stubRequest, items);
  137. // Act
  138. using (HttpRequestMessage actualRequest = HttpControllerHandler.ConvertRequest(context))
  139. {
  140. // Assert
  141. object ignore;
  142. bool found = actualRequest.Properties.TryGetValue(HttpControllerHandler.OwinEnvironmentKey,
  143. out ignore);
  144. Assert.False(found);
  145. }
  146. }
  147. }
  148. [Fact]
  149. public void ConvertRequest_DoesNotAddOwinEnvironment_WhenItemsIsNull()
  150. {
  151. // Arrange
  152. using (MemoryStream ignoreStream = new MemoryStream())
  153. {
  154. HttpRequestBase stubRequest = CreateStubRequestBase("IgnoreMethod", ignoreStream);
  155. IDictionary items = null;
  156. HttpContextBase context = CreateStubContextBase(stubRequest, items);
  157. // Act
  158. using (HttpRequestMessage actualRequest = HttpControllerHandler.ConvertRequest(context))
  159. {
  160. // Assert
  161. object ignore;
  162. bool found = actualRequest.Properties.TryGetValue(HttpControllerHandler.OwinEnvironmentKey,
  163. out ignore);
  164. Assert.False(found);
  165. }
  166. }
  167. }
  168. [Fact]
  169. public void ConvertRequest_DoesLazyGetInputStream()
  170. {
  171. bool inputStreamCalled = false;
  172. HttpRequestBase stubRequest = CreateFakeRequestBase(() =>
  173. {
  174. inputStreamCalled = true;
  175. return new MemoryStream();
  176. }, buffered: true);
  177. HttpContextBase context = CreateStubContextBase(request: stubRequest, items: null);
  178. HttpRequestMessage actualRequest = HttpControllerHandler.ConvertRequest(context);
  179. Assert.False(inputStreamCalled);
  180. var contentStream = actualRequest.Content.ReadAsStreamAsync().Result;
  181. Assert.True(inputStreamCalled);
  182. }
  183. [Fact]
  184. public void ConvertRequest_UsesRequestInputStream_InClassicMode()
  185. {
  186. // Arrange
  187. string input = "Hello world";
  188. var stream = new MemoryStream(Encoding.UTF8.GetBytes(input));
  189. HttpRequestBase fakeRequest = CreateFakeRequestBase(() => stream, buffered: true);
  190. HttpContextBase context = CreateStubContextBase(request: fakeRequest, items: null);
  191. Mock<HttpRequestBase> mockRequest = Mock.Get<HttpRequestBase>(fakeRequest);
  192. // Act
  193. fakeRequest.InputStream.Position = 10;
  194. HttpRequestMessage actualRequest = HttpControllerHandler.ConvertRequest(context);
  195. string result = actualRequest.Content.ReadAsStringAsync().Result;
  196. // Assert
  197. // Verify that the InputStream was reset when reading the content.
  198. Assert.Equal(input, result);
  199. mockRequest.Verify(r => r.InputStream, Times.AtLeastOnce());
  200. mockRequest.Verify(r => r.GetBufferedInputStream(), Times.Never());
  201. }
  202. [Fact]
  203. public void ConvertRequest_UsesBufferedInputStream_IfReadEntityBodyModeIsNone()
  204. {
  205. // Arrange
  206. HttpRequestBase stubRequest = CreateFakeRequestBase(() => new MemoryStream(), buffered: true);
  207. HttpContextBase context = CreateStubContextBase(request: stubRequest, items: null);
  208. Mock<HttpRequestBase> mockRequest = Mock.Get<HttpRequestBase>(stubRequest);
  209. // Act
  210. HttpRequestMessage actualRequest = HttpControllerHandler.ConvertRequest(context);
  211. actualRequest.Content.ReadAsStreamAsync().Wait();
  212. // Assert
  213. mockRequest.Verify(r => r.InputStream, Times.Never());
  214. mockRequest.Verify(r => r.GetBufferedInputStream(), Times.AtLeastOnce());
  215. }
  216. [Fact]
  217. public void ConvertRequest_WithBufferedPolicy_ThrowsIfRequestHasBeenPartiallyRead()
  218. {
  219. // Arrange
  220. var stream = new MemoryStream(new byte[16]);
  221. HttpRequestBase fakeRequest = CreateFakeRequestBase(() => stream, buffered: true);
  222. HttpContextBase context = CreateStubContextBase(request: fakeRequest, items: null);
  223. // Act
  224. fakeRequest.GetBufferedInputStream().Position = 4;
  225. HttpRequestMessage actualRequest = HttpControllerHandler.ConvertRequest(context);
  226. // Assert
  227. Assert.Throws<InvalidOperationException>(() => actualRequest.Content.ReadAsStringAsync().Result);
  228. }
  229. [Fact]
  230. public void ConvertRequest_WithBufferedPolicy_ReturnsInputStreamIfBufferedStreamWasFullyRead()
  231. {
  232. // Arrange
  233. string inputStreamMessage = "This is from input stream";
  234. var bufferedStream = new MemoryStream(new byte[16]);
  235. var inputStream = new MemoryStream(Encoding.UTF8.GetBytes(inputStreamMessage));
  236. HttpRequestBase fakeRequest = CreateFakeRequestBase(() => bufferedStream, buffered: true);
  237. HttpContextBase context = CreateStubContextBase(request: fakeRequest, items: null);
  238. Mock<HttpRequestBase> mockRequest = Mock.Get<HttpRequestBase>(fakeRequest);
  239. mockRequest.SetupGet(f => f.InputStream)
  240. .Returns(inputStream)
  241. .Verifiable();
  242. // Act
  243. bufferedStream.Seek(0, SeekOrigin.End);
  244. new StreamReader(fakeRequest.GetBufferedInputStream()).ReadToEnd();
  245. HttpRequestMessage actualRequest = HttpControllerHandler.ConvertRequest(context);
  246. string result = actualRequest.Content.ReadAsStringAsync().Result;
  247. // Assert
  248. Assert.Equal(inputStreamMessage, result);
  249. }
  250. [Theory]
  251. [InlineData(ReadEntityBodyMode.None)]
  252. [InlineData(ReadEntityBodyMode.Bufferless)]
  253. public void ConvertRequest_DoesLazyGetBufferlessInputStream_IfRequestStreamHasNotBeenRead(ReadEntityBodyMode readEntityBodyMode)
  254. {
  255. // Arrange
  256. bool inputStreamCalled = false;
  257. var hostBufferPolicy = new Mock<IHostBufferPolicySelector>();
  258. hostBufferPolicy.Setup(c => c.UseBufferedInputStream(It.IsAny<object>()))
  259. .Returns(false);
  260. hostBufferPolicy.Setup(c => c.UseBufferedOutputStream(It.IsAny<HttpResponseMessage>()))
  261. .Returns(true);
  262. HttpRequestBase stubRequest = CreateFakeRequestBase(() =>
  263. {
  264. inputStreamCalled = true;
  265. return new MemoryStream();
  266. }, buffered: false);
  267. HttpContextBase context = HttpControllerHandlerTest.CreateStubContextBase(request: stubRequest, items: null);
  268. // Act
  269. HttpRequestMessage actualRequest = HttpControllerHandler.ConvertRequest(context, hostBufferPolicy.Object);
  270. // Assert
  271. Assert.False(inputStreamCalled);
  272. Stream contentStream = actualRequest.Content.ReadAsStreamAsync().Result;
  273. Assert.True(inputStreamCalled);
  274. }
  275. [Fact]
  276. public void ConvertRequest_WithBufferlessPolicy_ThrowsIfRequestStreamHasBeenReadInClassicMode()
  277. {
  278. // Arrange
  279. var hostBufferPolicy = new Mock<IHostBufferPolicySelector>();
  280. hostBufferPolicy.Setup(c => c.UseBufferedInputStream(It.IsAny<object>()))
  281. .Returns(false);
  282. hostBufferPolicy.Setup(c => c.UseBufferedOutputStream(It.IsAny<HttpResponseMessage>()))
  283. .Returns(true);
  284. var stream = new MemoryStream(8);
  285. HttpRequestBase stubRequest = CreateFakeRequestBase(() => stream, buffered: false);
  286. HttpContextBase context = HttpControllerHandlerTest.CreateStubContextBase(request: stubRequest, items: null);
  287. // Act
  288. context.Request.InputStream.Position = 2;
  289. HttpRequestMessage actualRequest = HttpControllerHandler.ConvertRequest(context, hostBufferPolicy.Object);
  290. // Assert
  291. Assert.Throws<InvalidOperationException>(() => actualRequest.Content.ReadAsStreamAsync().Result,
  292. "Unable to read the entity body in Bufferless mode. The request stream has already been buffered.");
  293. }
  294. [Fact]
  295. public void ConvertRequest_WithBufferlessPolicy_ThrowsIfRequestStreamHasBeenReadInBufferedMode()
  296. {
  297. // Arrange
  298. var hostBufferPolicy = new Mock<IHostBufferPolicySelector>();
  299. hostBufferPolicy.Setup(c => c.UseBufferedInputStream(It.IsAny<object>()))
  300. .Returns(false);
  301. hostBufferPolicy.Setup(c => c.UseBufferedOutputStream(It.IsAny<HttpResponseMessage>()))
  302. .Returns(true);
  303. var stream = new MemoryStream(8);
  304. HttpRequestBase stubRequest = CreateFakeRequestBase(() => stream, buffered: false);
  305. HttpContextBase context = HttpControllerHandlerTest.CreateStubContextBase(request: stubRequest, items: null);
  306. // Act
  307. context.Request.GetBufferedInputStream();
  308. HttpRequestMessage message = HttpControllerHandler.ConvertRequest(context, hostBufferPolicy.Object);
  309. // Assert
  310. Assert.Throws<InvalidOperationException>(() => message.Content.ReadAsStringAsync().Result,
  311. "Unable to read the entity body. The request stream has already been read in 'Buffered' mode.");
  312. }
  313. [Fact]
  314. public void ConvertRequest_WithBufferlessPolicy_ThrowsIfRequestStreamHasBeenRead()
  315. {
  316. // Arrange
  317. var hostBufferPolicy = new Mock<IHostBufferPolicySelector>();
  318. hostBufferPolicy.Setup(c => c.UseBufferedInputStream(It.IsAny<object>()))
  319. .Returns(false);
  320. hostBufferPolicy.Setup(c => c.UseBufferedOutputStream(It.IsAny<HttpResponseMessage>()))
  321. .Returns(true);
  322. var stream = new MemoryStream(new byte[16]);
  323. HttpRequestBase stubRequest = CreateFakeRequestBase(() => stream, buffered: false);
  324. HttpContextBase context = HttpControllerHandlerTest.CreateStubContextBase(request: stubRequest, items: null);
  325. // Act
  326. context.Request.GetBufferlessInputStream().Position = 4;
  327. HttpRequestMessage message = HttpControllerHandler.ConvertRequest(context, hostBufferPolicy.Object);
  328. // Assert
  329. Assert.Throws<InvalidOperationException>(() => message.Content.ReadAsStringAsync().Result,
  330. "Unable to read the entity body. A portion of the request stream has already been read.");
  331. }
  332. [Fact]
  333. public void ConvertRequest_AddsWebHostHttpRequestContext()
  334. {
  335. // Arrange
  336. Mock<HttpRequestBase> requestBaseMock = new Mock<HttpRequestBase>(MockBehavior.Strict);
  337. requestBaseMock.Setup(r => r.HttpMethod).Returns("IGNORED");
  338. requestBaseMock.Setup(r => r.Url).Returns(new Uri("http://ignore"));
  339. requestBaseMock.Setup(r => r.Headers).Returns(new NameValueCollection());
  340. requestBaseMock.Setup(r => r.ReadEntityBodyMode).Returns(ReadEntityBodyMode.None);
  341. HttpRequestBase requestBase = requestBaseMock.Object;
  342. Mock<HttpContextBase> contextBaseMock = new Mock<HttpContextBase>(MockBehavior.Strict);
  343. contextBaseMock.Setup(c => c.Request).Returns(requestBase);
  344. contextBaseMock.Setup(c => c.Items).Returns((IDictionary)null);
  345. HttpContextBase contextBase = contextBaseMock.Object;
  346. // Act
  347. using (HttpRequestMessage expectedRequest = HttpControllerHandler.ConvertRequest(contextBase))
  348. {
  349. // Assert
  350. HttpRequestContext context = expectedRequest.GetRequestContext();
  351. Assert.IsType<WebHostHttpRequestContext>(context);
  352. WebHostHttpRequestContext typedContext = (WebHostHttpRequestContext)context;
  353. Assert.Same(contextBase, typedContext.Context);
  354. Assert.Same(requestBase, typedContext.WebRequest);
  355. Assert.Same(expectedRequest, typedContext.Request);
  356. }
  357. }
  358. [Fact]
  359. public void CopyResponseAsync_IfResponseHasNoCacheControlDefined_SetsNoCacheCacheabilityOnAspNetResponse()
  360. {
  361. // Arrange
  362. Mock<HttpContextBase> contextMock = new Mock<HttpContextBase>() { DefaultValue = DefaultValue.Mock };
  363. HttpRequestMessage request = new HttpRequestMessage();
  364. HttpResponseMessage response = new HttpResponseMessage();
  365. // Act
  366. CopyResponseAsync(contextMock.Object, request, response).Wait();
  367. // Assert
  368. contextMock.Verify(c => c.Response.Cache.SetCacheability(HttpCacheability.NoCache));
  369. }
  370. [Fact]
  371. public void CopyResponseAsync_IfResponseHasCacheControlDefined_DoesNotSetCacheCacheabilityOnAspNetResponse()
  372. {
  373. // Arrange
  374. Mock<HttpContextBase> contextMock = new Mock<HttpContextBase>() { DefaultValue = DefaultValue.Mock };
  375. HttpRequestMessage request = new HttpRequestMessage();
  376. HttpResponseMessage response = new HttpResponseMessage();
  377. response.Headers.CacheControl = new CacheControlHeaderValue { Public = true };
  378. // Act
  379. CopyResponseAsync(contextMock.Object, request, response).Wait();
  380. // Assert
  381. contextMock.Verify(c => c.Response.Cache.SetCacheability(HttpCacheability.NoCache), Times.Never());
  382. }
  383. [Fact]
  384. public Task ProcessRequestAsync_DisposesRequestAndResponse()
  385. {
  386. // Arrange
  387. Mock<HttpContextBase> contextMock = new Mock<HttpContextBase>() { DefaultValue = DefaultValue.Mock };
  388. contextMock.SetupGet((hcb) => hcb.Response.OutputStream).Returns(Stream.Null);
  389. IDictionary items = new Dictionary<object, object>();
  390. contextMock.SetupGet((hcb) => hcb.Items).Returns(items);
  391. HttpContextBase context = contextMock.Object;
  392. HttpRequestMessage request = new HttpRequestMessage();
  393. context.SetHttpRequestMessage(request);
  394. SpyDisposable spy = new SpyDisposable();
  395. request.RegisterForDispose(spy);
  396. HttpResponseMessage response = new HttpResponseMessage();
  397. Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> sendAsync = (r, c) =>
  398. Task.FromResult(response);
  399. using (HttpMessageHandler handler = new LambdaHttpMessageHandler(sendAsync))
  400. {
  401. HttpControllerHandler product = new HttpControllerHandler(
  402. new Mock<RouteData>(MockBehavior.Strict).Object, handler);
  403. // Act
  404. return product.ProcessRequestAsyncCore(context).ContinueWith(
  405. _ =>
  406. {
  407. // Assert
  408. Assert.True(spy.Disposed);
  409. Assert.ThrowsObjectDisposed(() => request.Method = HttpMethod.Get, typeof(HttpRequestMessage).FullName);
  410. Assert.ThrowsObjectDisposed(() => response.StatusCode = HttpStatusCode.OK, typeof(HttpResponseMessage).FullName);
  411. });
  412. }
  413. }
  414. [Fact]
  415. public Task ProcessRequestAsync_DisposesRequestAndResponseWithContent()
  416. {
  417. // Arrange
  418. Mock<HttpContextBase> contextMock = new Mock<HttpContextBase>() { DefaultValue = DefaultValue.Mock };
  419. contextMock.SetupGet((hcb) => hcb.Response.OutputStream).Returns(Stream.Null);
  420. IDictionary items = new Dictionary<object, object>();
  421. contextMock.SetupGet((hcb) => hcb.Items).Returns(items);
  422. HttpContextBase context = contextMock.Object;
  423. HttpRequestMessage request = new HttpRequestMessage() { Content = new StringContent("request") };
  424. context.SetHttpRequestMessage(request);
  425. SpyDisposable spy = new SpyDisposable();
  426. request.RegisterForDispose(spy);
  427. HttpResponseMessage response = new HttpResponseMessage() { Content = new StringContent("response") };
  428. Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> sendAsync = (r, c) =>
  429. Task.FromResult(response);
  430. using (HttpMessageHandler handler = new LambdaHttpMessageHandler(sendAsync))
  431. {
  432. HttpControllerHandler product = new HttpControllerHandler(
  433. new Mock<RouteData>(MockBehavior.Strict).Object, handler);
  434. // Act
  435. return product.ProcessRequestAsyncCore(context).ContinueWith(
  436. _ =>
  437. {
  438. // Assert
  439. Assert.True(spy.Disposed);
  440. Assert.ThrowsObjectDisposed(() => request.Method = HttpMethod.Get, typeof(HttpRequestMessage).FullName);
  441. Assert.ThrowsObjectDisposed(() => response.StatusCode = HttpStatusCode.OK, typeof(HttpResponseMessage).FullName);
  442. });
  443. }
  444. }
  445. [Fact]
  446. public Task ProcessRequestAsync_IfHandlerFaults_DisposesRequest()
  447. {
  448. // Arrange
  449. Mock<HttpContextBase> contextMock = new Mock<HttpContextBase>() { DefaultValue = DefaultValue.Mock };
  450. contextMock.SetupGet((hcb) => hcb.Response.OutputStream).Returns(Stream.Null);
  451. IDictionary items = new Dictionary<object, object>();
  452. contextMock.SetupGet((hcb) => hcb.Items).Returns(items);
  453. HttpContextBase context = contextMock.Object;
  454. HttpRequestMessage request = new HttpRequestMessage();
  455. context.SetHttpRequestMessage(request);
  456. SpyDisposable spy = new SpyDisposable();
  457. request.RegisterForDispose(spy);
  458. Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> sendAsync = (r, c) =>
  459. CreateFaultedTask<HttpResponseMessage>(CreateException());
  460. using (HttpMessageHandler handler = new LambdaHttpMessageHandler(sendAsync))
  461. {
  462. HttpControllerHandler product = new HttpControllerHandler(
  463. new Mock<RouteData>(MockBehavior.Strict).Object, handler);
  464. // Act
  465. return product.ProcessRequestAsyncCore(context).ContinueWith(
  466. _ =>
  467. {
  468. // Assert
  469. Assert.True(spy.Disposed);
  470. Assert.ThrowsObjectDisposed(() => request.Method = HttpMethod.Get, typeof(HttpRequestMessage).FullName);
  471. });
  472. }
  473. }
  474. [Fact]
  475. public void SuppressFormsAuthenticationRedirect_DoesntRequireSuppressRedirect()
  476. {
  477. // Arrange
  478. Mock<HttpContextBase> contextMock = new Mock<HttpContextBase>() { DefaultValue = DefaultValue.Mock };
  479. IDictionary contextItems = new Hashtable();
  480. contextMock.SetupGet(hcb => hcb.Response.StatusCode).Returns(200);
  481. contextMock.SetupGet(hcb => hcb.Items).Returns(contextItems);
  482. // Act
  483. HttpControllerHandler.EnsureSuppressFormsAuthenticationRedirect(contextMock.Object);
  484. // Assert
  485. Assert.False(contextMock.Object.Response.SuppressFormsAuthenticationRedirect);
  486. }
  487. [Fact]
  488. public void SuppressFormsAuthenticationRedirect_RequireSuppressRedirect()
  489. {
  490. // Arrange
  491. Mock<HttpContextBase> contextMock = new Mock<HttpContextBase>() { DefaultValue = DefaultValue.Mock };
  492. IDictionary contextItems = new Hashtable();
  493. contextMock.SetupGet(hcb => hcb.Response.StatusCode).Returns(401);
  494. contextMock.SetupGet(hcb => hcb.Items).Returns(contextItems);
  495. // Act
  496. HttpControllerHandler.EnsureSuppressFormsAuthenticationRedirect(contextMock.Object);
  497. // Assert
  498. Assert.True(contextMock.Object.Response.SuppressFormsAuthenticationRedirect);
  499. }
  500. [Fact]
  501. public void CopyResponseAsync_Creates_Correct_HttpResponseBase()
  502. {
  503. // Arrange
  504. MemoryStream memoryStream = new MemoryStream();
  505. Mock<HttpContextBase> contextMock = CreateMockHttpContextBaseForResponse(memoryStream);
  506. HttpResponseBase responseBase = contextMock.Object.Response;
  507. HttpRequestMessage request = new HttpRequestMessage();
  508. HttpResponseMessage response = new HttpResponseMessage() { RequestMessage = request };
  509. response.Content = new ObjectContent<string>("hello", new JsonMediaTypeFormatter());
  510. // Act
  511. Task task = CopyResponseAsync(contextMock.Object, request, response);
  512. task.Wait();
  513. // Assert preparation -- deserialize the response
  514. memoryStream.Seek(0L, SeekOrigin.Begin);
  515. string responseString = null;
  516. using (var streamReader = new StreamReader(memoryStream))
  517. {
  518. responseString = streamReader.ReadToEnd();
  519. }
  520. // Assert
  521. Assert.Equal<int>((int)HttpStatusCode.OK, responseBase.StatusCode);
  522. Assert.True(responseBase.Headers["Content-Type"].StartsWith(JsonMediaTypeFormatter.DefaultMediaType.MediaType));
  523. Assert.Equal("\"hello\"", responseString);
  524. }
  525. [Fact]
  526. public void CopyResponseAsync_IfTransferEncodingChunkedAndContentLengthAreBothSet_IgnoresContentLength()
  527. {
  528. // Arrange
  529. HttpResponseBase responseBase = CreateMockHttpResponseBaseForResponse(Stream.Null).Object;
  530. HttpContextBase contextBase = CreateStubContextBase(responseBase);
  531. using (HttpRequestMessage request = new HttpRequestMessage())
  532. using (HttpResponseMessage response = new HttpResponseMessage() { RequestMessage = request })
  533. {
  534. response.Headers.TransferEncodingChunked = true;
  535. response.Content = new StringContent("SomeContent");
  536. Assert.NotNull(response.Content.Headers.ContentLength); // Guard; added by System.Net.Http.
  537. // Act
  538. Task task = CopyResponseAsync(contextBase, request, response);
  539. // Assert
  540. Assert.NotNull(task);
  541. task.WaitUntilCompleted();
  542. task.ThrowIfFaulted();
  543. Assert.DoesNotContain("Content-Length", responseBase.Headers.OfType<string>());
  544. }
  545. }
  546. [Fact]
  547. public void CopyResponseAsync_IfTransferEncodingIsJustChunked_DoesNotCopyHeaderToHost()
  548. {
  549. // Arrange
  550. HttpResponseBase responseBase = CreateMockHttpResponseBaseForResponse(Stream.Null).Object;
  551. HttpContextBase contextBase = CreateStubContextBase(responseBase);
  552. using (HttpRequestMessage request = new HttpRequestMessage())
  553. using (HttpResponseMessage response = new HttpResponseMessage() { RequestMessage = request })
  554. {
  555. response.Headers.TransferEncodingChunked = true;
  556. // Act
  557. Task task = CopyResponseAsync(contextBase, request, response);
  558. // Assert
  559. Assert.NotNull(task);
  560. task.WaitUntilCompleted();
  561. task.ThrowIfFaulted();
  562. Assert.DoesNotContain("Transfer-Encoding", responseBase.Headers.OfType<string>());
  563. }
  564. }
  565. [Fact]
  566. public void CopyResponseAsync_IfTransferEncodingIsIdentity_CopiesHeaderToHost()
  567. {
  568. // Arrange
  569. HttpResponseBase responseBase = CreateMockHttpResponseBaseForResponse(Stream.Null).Object;
  570. HttpContextBase contextBase = CreateStubContextBase(responseBase);
  571. using (HttpRequestMessage request = new HttpRequestMessage())
  572. using (HttpResponseMessage response = new HttpResponseMessage() { RequestMessage = request })
  573. {
  574. response.Headers.TransferEncoding.Add(new TransferCodingHeaderValue("identity"));
  575. // Act
  576. Task task = CopyResponseAsync(contextBase, request, response);
  577. // Assert
  578. Assert.NotNull(task);
  579. task.WaitUntilCompleted();
  580. task.ThrowIfFaulted();
  581. Assert.Contains("Transfer-Encoding", responseBase.Headers.OfType<string>());
  582. Assert.Equal(new string[] { "identity" }, responseBase.Headers.GetValues("Transfer-Encoding"));
  583. }
  584. }
  585. [Fact]
  586. public void CopyResponseAsync_IfTransferEncodingIsIdentityChunked_CopiesHeaderToHost()
  587. {
  588. // Arrange
  589. HttpResponseBase responseBase = CreateMockHttpResponseBaseForResponse(Stream.Null).Object;
  590. HttpContextBase contextBase = CreateStubContextBase(responseBase);
  591. using (HttpRequestMessage request = new HttpRequestMessage())
  592. using (HttpResponseMessage response = new HttpResponseMessage() { RequestMessage = request })
  593. {
  594. response.Headers.TransferEncoding.Add(new TransferCodingHeaderValue("identity"));
  595. response.Headers.TransferEncodingChunked = true;
  596. Assert.Equal("identity, chunked", response.Headers.TransferEncoding.ToString()); // Guard
  597. // Act
  598. Task task = CopyResponseAsync(contextBase, request, response);
  599. // Assert
  600. Assert.NotNull(task);
  601. task.WaitUntilCompleted();
  602. task.ThrowIfFaulted();
  603. Assert.Contains("Transfer-Encoding", responseBase.Headers.OfType<string>());
  604. Assert.Equal(new string[] { "identity", "chunked" },
  605. responseBase.Headers.GetValues("Transfer-Encoding"));
  606. }
  607. }
  608. [Fact]
  609. public void CopyResponseAsync_IfTransferEncodingIsChunked_DisablesResponseBuffering()
  610. {
  611. // Arrange
  612. HttpResponseBase responseBase = CreateMockHttpResponseBaseForResponse(Stream.Null).Object;
  613. HttpContextBase contextBase = CreateStubContextBase(responseBase);
  614. using (HttpRequestMessage request = new HttpRequestMessage())
  615. using (HttpResponseMessage response = new HttpResponseMessage() { RequestMessage = request })
  616. {
  617. response.Headers.TransferEncodingChunked = true;
  618. response.Content = new ObjectContent(typeof(string), String.Empty, new JsonMediaTypeFormatter());
  619. // Act
  620. Task task = CopyResponseAsync(contextBase, request, response);
  621. // Assert
  622. Assert.NotNull(task);
  623. task.WaitUntilCompleted();
  624. task.ThrowIfFaulted();
  625. Assert.False(responseBase.BufferOutput);
  626. }
  627. }
  628. [Fact]
  629. public void CopyResponseAsync_IfHandlerIsDefault_Returns_Error_Response_When_Formatter_Write_Task_Faults()
  630. {
  631. // Arrange
  632. Mock<JsonMediaTypeFormatter> formatterMock = new Mock<JsonMediaTypeFormatter>() { CallBase = true };
  633. TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
  634. try
  635. {
  636. // to capture stack trace inside this method
  637. throw new NotSupportedException("Expected error");
  638. }
  639. catch (Exception ex)
  640. {
  641. tcs.SetException(ex);
  642. }
  643. formatterMock.Setup(m => m.WriteToStreamAsync(It.IsAny<Type>(),
  644. It.IsAny<object>(),
  645. It.IsAny<Stream>(),
  646. It.IsAny<HttpContent>(),
  647. It.IsAny<TransportContext>())).Returns(tcs.Task);
  648. MemoryStream memoryStream = new MemoryStream();
  649. Mock<HttpContextBase> contextMock = CreateMockHttpContextBaseForResponse(memoryStream);
  650. HttpResponseBase responseBase = contextMock.Object.Response;
  651. HttpRequestMessage request = new HttpRequestMessage();
  652. request.SetIsLocal(new Lazy<bool>(() => true));
  653. HttpResponseMessage response = new HttpResponseMessage() { RequestMessage = request };
  654. response.Content = new ObjectContent<string>("hello", formatterMock.Object);
  655. IExceptionLogger exceptionLogger = CreateStubExceptionLogger();
  656. IExceptionHandler exceptionHandler = ExceptionServices.GetHandler(GlobalConfiguration.Configuration);
  657. // Act
  658. Task task = HttpControllerHandler.CopyResponseAsync(contextMock.Object, request, response, exceptionLogger,
  659. exceptionHandler, CancellationToken.None);
  660. task.Wait();
  661. // Assert preparation -- deserialize the HttpError response
  662. HttpError httpError = null;
  663. memoryStream.Seek(0L, SeekOrigin.Begin);
  664. using (StreamContent content = new StreamContent(memoryStream))
  665. {
  666. content.Headers.ContentType = JsonMediaTypeFormatter.DefaultMediaType;
  667. httpError = content.ReadAsAsync<HttpError>().Result;
  668. }
  669. // Assert
  670. Assert.Equal<int>((int)HttpStatusCode.InternalServerError, responseBase.StatusCode);
  671. Assert.True(responseBase.Headers["Content-Type"].StartsWith(JsonMediaTypeFormatter.DefaultMediaType.MediaType));
  672. Assert.Equal("An error has occurred.", httpError["Message"]);
  673. Assert.Equal("The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'.", httpError["ExceptionMessage"]);
  674. Assert.Equal(typeof(InvalidOperationException).FullName, httpError["ExceptionType"]);
  675. Assert.True(httpError.ContainsKey("StackTrace"));
  676. HttpError innerError = (httpError["InnerException"] as JObject).ToObject<HttpError>();
  677. Assert.NotNull(innerError);
  678. Assert.Equal(typeof(NotSupportedException).FullName, innerError["ExceptionType"].ToString());
  679. Assert.Equal("Expected error", innerError["ExceptionMessage"]);
  680. Assert.Contains(MethodInfo.GetCurrentMethod().Name, innerError["StackTrace"].ToString());
  681. }
  682. [Fact]
  683. public void CopyResponseAsync_IfHandlerIsDefault_Returns_Error_Response_When_Formatter_Write_Throws_Immediately()
  684. {
  685. // Arrange
  686. Mock<JsonMediaTypeFormatter> formatterMock = new Mock<JsonMediaTypeFormatter>() { CallBase = true };
  687. formatterMock.Setup(m => m.WriteToStreamAsync(It.IsAny<Type>(),
  688. It.IsAny<object>(),
  689. It.IsAny<Stream>(),
  690. It.IsAny<HttpContent>(),
  691. It.IsAny<TransportContext>())).Throws(new NotSupportedException("Expected error"));
  692. MemoryStream memoryStream = new MemoryStream();
  693. Mock<HttpContextBase> contextMock = CreateMockHttpContextBaseForResponse(memoryStream);
  694. HttpResponseBase responseBase = contextMock.Object.Response;
  695. HttpRequestMessage request = new HttpRequestMessage();
  696. request.SetIsLocal(new Lazy<bool>(() => true));
  697. HttpResponseMessage response = new HttpResponseMessage() { RequestMessage = request };
  698. response.Content = new ObjectContent<string>("hello", formatterMock.Object);
  699. IExceptionLogger exceptionLogger = CreateStubExceptionLogger();
  700. IExceptionHandler exceptionHandler = ExceptionServices.GetHandler(GlobalConfiguration.Configuration);
  701. // Act
  702. Task task = HttpControllerHandler.CopyResponseAsync(contextMock.Object, request, response, exceptionLogger, exceptionHandler, CancellationToken.None);
  703. task.Wait();
  704. // Assert preparation -- deserialize the HttpError response
  705. HttpError httpError = null;
  706. memoryStream.Seek(0L, SeekOrigin.Begin);
  707. using (StreamContent content = new StreamContent(memoryStream))
  708. {
  709. content.Headers.ContentType = JsonMediaTypeFormatter.DefaultMediaType;
  710. httpError = content.ReadAsAsync<HttpError>().Result;
  711. }
  712. // Assert
  713. Assert.Equal<int>((int)HttpStatusCode.InternalServerError, responseBase.StatusCode);
  714. Assert.True(responseBase.Headers["Content-Type"].StartsWith(JsonMediaTypeFormatter.DefaultMediaType.MediaType));
  715. Assert.Equal("An error has occurred.", httpError["Message"]);
  716. Assert.Equal("The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'.", httpError["ExceptionMessage"]);
  717. Assert.Equal(typeof(InvalidOperationException).FullName, httpError["ExceptionType"]);
  718. Assert.True(httpError.ContainsKey("StackTrace"));
  719. HttpError innerError = (httpError["InnerException"] as JObject).ToObject<HttpError>();
  720. Assert.NotNull(innerError);
  721. Assert.Equal(typeof(NotSupportedException).FullName, innerError["ExceptionType"].ToString());
  722. Assert.Equal("Expected error", innerError["ExceptionMessage"]);
  723. Assert.Contains("System.Net.Http.HttpContent.CopyToAsync", innerError["StackTrace"].ToString());
  724. }
  725. [Fact]
  726. public void CopyResponseAsync_Returns_User_Response_When_Formatter_Write_Throws_HttpResponseException_With_No_Content()
  727. {
  728. // Arrange
  729. HttpResponseMessage errorResponse = new HttpResponseMessage(HttpStatusCode.MethodNotAllowed);
  730. errorResponse.Headers.Add("myHeader", "myValue");
  731. Mock<JsonMediaTypeFormatter> formatterMock = new Mock<JsonMediaTypeFormatter>() { CallBase = true };
  732. formatterMock.Setup(m => m.WriteToStreamAsync(It.IsAny<Type>(),
  733. It.IsAny<object>(),
  734. It.IsAny<Stream>(),
  735. It.IsAny<HttpContent>(),
  736. It.IsAny<TransportContext>())).Throws(new HttpResponseException(errorResponse));
  737. MemoryStream memoryStream = new MemoryStream();
  738. Mock<HttpContextBase> contextMock = CreateMockHttpContextBaseForResponse(memoryStream);
  739. HttpResponseBase responseBase = contextMock.Object.Response;
  740. HttpRequestMessage request = new HttpRequestMessage();
  741. request.SetIsLocal(new Lazy<bool>(() => true));
  742. HttpResponseMessage response = new HttpResponseMessage() { RequestMessage = request };
  743. response.Content = new ObjectContent<string>("hello", formatterMock.Object);
  744. // Act
  745. Task task = CopyResponseAsync(contextMock.Object, request, response);
  746. task.Wait();
  747. memoryStream.Seek(0L, SeekOrigin.Begin);
  748. // Assert
  749. Assert.Equal<int>((int)errorResponse.StatusCode, responseBase.StatusCode);
  750. Assert.Equal(0, memoryStream.Length);
  751. Assert.Equal("myValue", responseBase.Headers["myHeader"]);
  752. Assert.Null(responseBase.Headers["Content-Type"]);
  753. }
  754. [Fact]
  755. public void CopyResponseAsync_Returns_User_Response_When_Formatter_Write_Throws_HttpResponseException_With_Content()
  756. {
  757. // Arrange
  758. HttpResponseMessage errorResponse = new HttpResponseMessage(HttpStatusCode.MethodNotAllowed);
  759. errorResponse.Headers.Add("myHeader", "myValue");
  760. errorResponse.Content = new StringContent("user message", Encoding.UTF8, "application/fake");
  761. Mock<JsonMediaTypeFormatter> formatterMock = new Mock<JsonMediaTypeFormatter>() { CallBase = true };
  762. formatterMock.Setup(m => m.WriteToStreamAsync(It.IsAny<Type>(),
  763. It.IsAny<object>(),
  764. It.IsAny<Stream>(),
  765. It.IsAny<HttpContent>(),
  766. It.IsAny<TransportContext>())).Throws(new HttpResponseException(errorResponse));
  767. MemoryStream memoryStream = new MemoryStream();
  768. Mock<HttpContextBase> contextMock = CreateMockHttpContextBaseForResponse(memoryStream);
  769. HttpResponseBase responseBase = contextMock.Object.Response;
  770. HttpRequestMessage request = new HttpRequestMessage();
  771. request.SetIsLocal(new Lazy<bool>(() => true));
  772. HttpResponseMessage response = new HttpResponseMessage() { RequestMessage = request };
  773. response.Content = new ObjectContent<string>("hello", formatterMock.Object);
  774. // Act
  775. Task task = CopyResponseAsync(contextMock.Object, request, response);
  776. task.Wait();
  777. // Assert preparation -- deserialize the response
  778. memoryStream.Seek(0L, SeekOrigin.Begin);
  779. string responseContent = null;
  780. using (var streamReader = new StreamReader(memoryStream))
  781. {
  782. responseContent = streamReader.ReadToEnd();
  783. }
  784. // Assert
  785. Assert.Equal<int>((int)errorResponse.StatusCode, responseBase.StatusCode);
  786. Assert.True(responseBase.Headers["Content-Type"].StartsWith("application/fake"));
  787. Assert.Equal("user message", responseContent);
  788. Assert.Equal("myValue", responseBase.Headers["myHeader"]);
  789. }
  790. [Fact]
  791. public void CopyResponseAsync_Returns_InternalServerError_And_No_Content_When_Formatter_Write_Task_Faults_During_Error_Response()
  792. {
  793. // Arrange
  794. Mock<JsonMediaTypeFormatter> formatterMock = new Mock<JsonMediaTypeFormatter>() { CallBase = true };
  795. TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
  796. tcs.SetException(new NotSupportedException("Expected error"));
  797. // This formatter throws on any write attempt
  798. formatterMock.Setup(m => m.WriteToStreamAsync(It.IsAny<Type>(),
  799. It.IsAny<object>(),
  800. It.IsAny<Stream>(),
  801. It.IsAny<HttpContent>(),
  802. It.IsAny<TransportContext>())).Returns(tcs.Task);
  803. // Create a local config to hook to the request to condition
  804. // the formatter selection for the error response
  805. HttpConfiguration config = new HttpConfiguration();
  806. config.Formatters.Clear();
  807. config.Formatters.Add(formatterMock.Object);
  808. MemoryStream memoryStream = new MemoryStream();
  809. Mock<HttpContextBase> contextMock = CreateMockHttpContextBaseForResponse(memoryStream);
  810. HttpResponseBase responseBase = contextMock.Object.Response;
  811. HttpRequestMessage request = new HttpRequestMessage();
  812. request.SetIsLocal(new Lazy<bool>(() => true));
  813. request.SetConfiguration(config);
  814. HttpResponseMessage response = new HttpResponseMessage() { RequestMessage = request };
  815. response.Content = new ObjectContent<string>("hello", formatterMock.Object);
  816. IExceptionLogger exceptionLogger = CreateStubExceptionLogger();
  817. IExceptionHandler exceptionHandler = ExceptionServices.GetHandler(GlobalConfiguration.Configuration);
  818. // Act
  819. Task task = HttpControllerHandler.CopyResponseAsync(contextMock.Object, request, response, exceptionLogger,
  820. exceptionHandler, CancellationToken.None);
  821. task.Wait();
  822. // Assert
  823. Assert.Equal<int>((int)HttpStatusCode.InternalServerError, responseBase.StatusCode);
  824. Assert.Equal(0, memoryStream.Length);
  825. Assert.Null(responseBase.Headers["Content-Type"]);
  826. }
  827. [Fact]
  828. public void CopyResponseAsync_Returns_InternalServerError_And_No_Content_When_Formatter_Write_Throws_Immediately_During_Error_Response()
  829. {
  830. // Arrange
  831. Mock<JsonMediaTypeFormatter> formatterMock = new Mock<JsonMediaTypeFormatter>() { CallBase = true };
  832. // This formatter throws on any write attempt
  833. formatterMock.Setup(m => m.WriteToStreamAsync(It.IsAny<Type>(),
  834. It.IsAny<object>(),
  835. It.IsAny<Stream>(),
  836. It.IsAny<HttpContent>(),
  837. It.IsAny<TransportContext>())).Throws(new NotSupportedException("Expected error"));
  838. // Create a local config to hook to the request to condition
  839. // the formatter selection for the error response
  840. HttpConfiguration config = new HttpConfiguration();
  841. config.Formatters.Clear();
  842. config.Formatters.Add(formatterMock.Object);
  843. MemoryStream memoryStream = new MemoryStream();
  844. Mock<HttpContextBase> contextMock = CreateMockHttpContextBaseForResponse(memoryStream);
  845. HttpResponseBase responseBase = contextMock.Object.Response;
  846. HttpRequestMessage request = new HttpRequestMessage();
  847. request.SetIsLocal(new Lazy<bool>(() => true));
  848. request.SetConfiguration(config);
  849. HttpResponseMessage response = new HttpResponseMessage() { RequestMessage = request };
  850. response.Content = new ObjectContent<string>("hello", formatterMock.Object);
  851. IExceptionLogger exceptionLogger = CreateStubExceptionLogger();
  852. IExceptionHandler exceptionHandler = ExceptionServices.GetHandler(GlobalConfiguration.Configuration);
  853. // Act
  854. Task task = HttpControllerHandler.CopyResponseAsync(contextMock.Object, request, response, exceptionLogger,
  855. exceptionHandler, CancellationToken.None);
  856. task.Wait();
  857. // Assert
  858. Assert.Equal<int>((int)HttpStatusCode.InternalServerError, responseBase.StatusCode);
  859. Assert.Equal(0, memoryStream.Length);
  860. Assert.Null(responseBase.Headers["Content-Type"]);
  861. }
  862. [Fact]
  863. public void CopyResponseAsync_Returns_InternalServerError_And_No_Content_When_Content_Negotiation_Cannot_Find_Formatter_For_Error_Response()
  864. {
  865. // Create a content negotiator that works attempting a normal response but fails when creating the error response.
  866. Mock<IContentNegotiator> negotiatorMock = new Mock<IContentNegotiator>() { CallBase = true };
  867. negotiatorMock.Setup(m => m.Negotiate(It.IsAny<Type>(), It.IsAny<HttpRequestMessage>(), It.IsAny<IEnumerable<MediaTypeFormatter>>()))
  868. .Returns((Type t, HttpRequestMessage r, IEnumerable<MediaTypeFormatter> f) =>
  869. {
  870. ContentNegotiationResult result = t == typeof(HttpError)
  871. ? null
  872. : new ContentNegotiationResult(f.First(), JsonMediaTypeFormatter.DefaultMediaType);
  873. return result;
  874. });
  875. // Arrange
  876. Mock<JsonMediaTypeFormatter> formatterMock = new Mock<JsonMediaTyp

Large files files are truncated, but you can click here to view the full file