// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System.Collections.Generic; using System.Net.Http.Formatting; using System.Net.Http.Formatting.Mocks; using System.Net.Http.Headers; using System.Threading; using System.Web.Http; using System.Web.Http.Hosting; using System.Web.Http.Routing; using System.Web.Http.Services; using Moq; using Xunit; using Assert = Microsoft.TestCommon.AssertEx; namespace System.Net.Http { public class HttpRequestMessageExtensionsTest { private readonly HttpRequestMessage _request = new HttpRequestMessage(); private readonly HttpConfiguration _config = new HttpConfiguration(); private readonly object _value = new object(); private readonly Mock _disposableMock = new Mock(); private readonly Mock _negotiatorMock = new Mock(); private readonly IDisposable _disposable; public HttpRequestMessageExtensionsTest() { _disposable = _disposableMock.Object; _request.Properties[HttpPropertyKeys.HttpConfigurationKey] = _config; } [Fact] public void GetConfigurationThrowsOnNull() { HttpRequestMessage request = null; Assert.ThrowsArgumentNull(() => request.GetConfiguration(), "request"); } [Fact] public void GetConfiguration() { // Arrange _request.Properties[HttpPropertyKeys.HttpConfigurationKey] = _config; // Act HttpConfiguration afterConfig = _request.GetConfiguration(); // Assert Assert.Same(_config, afterConfig); } [Fact] public void GetSynchronizationContextThrowsOnNull() { HttpRequestMessage request = null; Assert.ThrowsArgumentNull(() => request.GetSynchronizationContext(), "request"); } [Fact] public void GetSynchronizationContext() { // Arrange Mock syncContextMock = new Mock(); SynchronizationContext beforeSyncContext = syncContextMock.Object; _request.Properties.Add(HttpPropertyKeys.SynchronizationContextKey, beforeSyncContext); // Act SynchronizationContext afterSyncContext = _request.GetSynchronizationContext(); // Assert Assert.Same(beforeSyncContext, afterSyncContext); } [Fact] public void GetRouteData() { // Arrange IHttpRouteData routeData = new Mock().Object; _request.Properties.Add(HttpPropertyKeys.HttpRouteDataKey, routeData); // Act var httpRouteData = _request.GetRouteData(); // Assert Assert.Same(routeData, httpRouteData); } [Fact] public void GetRouteData_WhenRequestIsNull_Throws() { // Arrange HttpRequestMessage request = null; // Act Assert.ThrowsArgumentNull(() => request.GetRouteData(), "request"); } [Fact] public void CreateResponse_DoingConneg_OnNullRequest_ThrowsException() { HttpRequestMessage request = null; Assert.ThrowsArgumentNull(() => { request.CreateResponse(HttpStatusCode.OK, _value); }, "request"); Assert.ThrowsArgumentNull(() => { request.CreateResponse(HttpStatusCode.OK, _value, configuration: null); }, "request"); } [Fact] public void CreateResponse_DoingConneg_OnNullConfiguration_ThrowsException() { _request.Properties[HttpPropertyKeys.HttpConfigurationKey] = null; Assert.Throws(() => { _request.CreateResponse(HttpStatusCode.OK, _value, configuration: null); }, "The request does not have an associated configuration object or the provided configuration was null."); } [Fact] public void CreateResponse_DoingConneg_RetrievesContentNegotiatorFromServiceResolver() { // Arrange Mock servicesMock = new Mock { CallBase = true }; servicesMock.Setup(s => s.GetService(typeof(IContentNegotiator))) .Returns(new Mock().Object) .Verifiable(); _config.Services = servicesMock.Object; // Act _request.CreateResponse(HttpStatusCode.OK, _value, _config); // Assert servicesMock.Verify(); } [Fact] public void CreateResponse_DoingConneg_WhenNoContentNegotiatorInstanceRegistered_Throws() { // Arrange _config.Services.Clear(typeof(IContentNegotiator)); // Act & Assert Assert.Throws(() => _request.CreateResponse(HttpStatusCode.OK, _value, _config), "The provided configuration does not have an instance of the 'System.Net.Http.Formatting.IContentNegotiator' service registered."); } [Fact] public void CreateResponse_DoingConneg_WhenContentNegotiatorReturnsNullResult_Throws() { // Arrange _negotiatorMock.Setup(r => r.Negotiate(typeof(string), _request, _config.Formatters)).Returns(value: null); _config.Services.Replace(typeof(IContentNegotiator), _negotiatorMock.Object); // Act var response = _request.CreateResponse(HttpStatusCode.OK, "", _config); // Assert Assert.Equal(HttpStatusCode.NotAcceptable, response.StatusCode); Assert.Same(_request, response.RequestMessage); } [Fact] public void CreateResponse_DoingConneg_PerformsContentNegotiationAndCreatesContentUsingResults() { // Arrange XmlMediaTypeFormatter formatter = new XmlMediaTypeFormatter(); _negotiatorMock.Setup(r => r.Negotiate(typeof(string), _request, _config.Formatters)) .Returns(new ContentNegotiationResult(formatter, null)); _config.Services.Replace(typeof(IContentNegotiator), _negotiatorMock.Object); // Act var response = _request.CreateResponse(HttpStatusCode.NoContent, "42", _config); // Assert Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); Assert.Same(_request, response.RequestMessage); var objectContent = Assert.IsType>(response.Content); Assert.Equal("42", objectContent.Value); Assert.Same(formatter, objectContent.Formatter); } [Fact] public void CreateResponse_MatchingMediaType_WhenRequestIsNull_Throws() { // Arrange HttpRequestMessage request = null; // Act Assert.ThrowsArgumentNull(() => request.CreateResponse(HttpStatusCode.OK, _value, "foo/bar"), "request"); } [Fact] public void CreateResponse_MatchingMediaType_WhenMediaTypeHeaderIsNull_Throws() { Assert.ThrowsArgumentNull(() => _request.CreateResponse(HttpStatusCode.OK, _value, (MediaTypeHeaderValue)null), "mediaType"); } [Fact] public void CreateResponse_MatchingMediaType_WhenMediaTypeStringIsNull_Throws() { Assert.Throws(() => _request.CreateResponse(HttpStatusCode.OK, _value, (string)null), "The value cannot be null or empty.\r\nParameter name: mediaType"); } [Fact] public void CreateResponse_MatchingMediaType_WhenMediaTypeStringIsEmpty_Throws() { Assert.ThrowsArgumentNull(() => _request.CreateResponse(HttpStatusCode.OK, _value, (MediaTypeHeaderValue)null), "mediaType"); } [Fact] public void CreateResponse_MatchingMediaType_WhenMediaTypeStringIsInvalidFormat_Throws() { Assert.Throws(() => _request.CreateResponse(HttpStatusCode.OK, _value, "foo/bar; param=value"), "The format of value 'foo/bar; param=value' is invalid."); } [Fact] public void CreateResponse_MatchingMediaType_WhenRequestDoesNotHaveConfiguration_Throws() { // Arrange _request.Properties[HttpPropertyKeys.HttpConfigurationKey] = null; // Act Assert.Throws(() => _request.CreateResponse(HttpStatusCode.OK, _value, mediaType: "foo/bar"), "The request does not have an associated configuration object or the provided configuration was null."); } [Fact] public void CreateResponse_MatchingMediaType_WhenMediaTypeDoesNotMatch_Throws() { // Arrange _request.Properties[HttpPropertyKeys.HttpConfigurationKey] = new HttpConfiguration(); // Act Assert.Throws(() => _request.CreateResponse(HttpStatusCode.OK, _value, mediaType: "foo/bar"), "Could not find a formatter matching the media type 'foo/bar' that can write an instance of 'Object'."); } [Fact] public void CreateResponse_MatchingMediaType_FindsMatchingFormatterAndCreatesResponse() { // Arrange var config = new HttpConfiguration(); _request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config; config.Formatters.Clear(); Mock formatterMock = new Mock { CallBase = true }; var formatter = formatterMock.Object; formatterMock.Setup(f => f.CanWriteType(typeof(object))).Returns(true).Verifiable(); formatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("foo/bar")); config.Formatters.Add(formatter); // Act var response = _request.CreateResponse(HttpStatusCode.Gone, _value, mediaType: "foo/bar"); // Assert Assert.Equal(HttpStatusCode.Gone, response.StatusCode); var content = Assert.IsType>(response.Content); Assert.Same(_value, content.Value); Assert.Same(formatter, content.Formatter); Assert.Equal("foo/bar", content.Headers.ContentType.MediaType); formatterMock.Verify(); } [Fact] public void CreateResponse_AcceptingFormatter_WhenRequestIsNull_Throws() { // Arrange HttpRequestMessage request = null; // Act Assert.ThrowsArgumentNull(() => request.CreateResponse(HttpStatusCode.OK, _value, new MockMediaTypeFormatter()), "request"); } [Fact] public void CreateResponse_AcceptingFormatter_WhenFormatterIsNull_Throws() { // Act Assert.ThrowsArgumentNull(() => _request.CreateResponse(HttpStatusCode.OK, _value, formatter: null), "formatter"); } [Fact] public void CreateResponse_AcceptingFormatter_CreatesResponseWithDefaultMediaType() { // Arrange var formatter = new MockMediaTypeFormatter { CallBase = true }; formatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("foo/bar")); // Act var response = _request.CreateResponse(HttpStatusCode.MultipleChoices, _value, formatter, mediaType: (string)null); // Assert Assert.Equal(HttpStatusCode.MultipleChoices, response.StatusCode); var content = Assert.IsType>(response.Content); Assert.Same(_value, content.Value); Assert.Same(formatter, content.Formatter); Assert.Equal("foo/bar", content.Headers.ContentType.MediaType); } [Fact] public void CreateResponse_AcceptingFormatter_WithOverridenMediaTypeString_CreatesResponse() { // Arrange var formatter = new MockMediaTypeFormatter { CallBase = true }; formatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("foo/bar")); // Act var response = _request.CreateResponse(HttpStatusCode.MultipleChoices, _value, formatter, mediaType: "bin/baz"); // Assert Assert.Equal("bin/baz", response.Content.Headers.ContentType.MediaType); } [Fact] public void CreateResponse_AcceptingFormatter_WithOverridenMediaTypeHeader_CreatesResponse() { // Arrange var formatter = new MockMediaTypeFormatter { CallBase = true }; formatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("foo/bar")); // Act var response = _request.CreateResponse(HttpStatusCode.MultipleChoices, _value, formatter, mediaType: new MediaTypeHeaderValue("bin/baz")); // Assert Assert.Equal("bin/baz", response.Content.Headers.ContentType.MediaType); } [Fact] public void RegisterForDispose_WhenRequestParameterIsNull_Throws() { HttpRequestMessage request = null; Assert.ThrowsArgumentNull(() => request.RegisterForDispose(resource: null), "request"); } [Fact] public void RegisterForDispose_WhenResourceParamterIsNull_DoesNothing() { _request.RegisterForDispose(resource: null); Assert.False(_request.Properties.ContainsKey(HttpPropertyKeys.DisposableRequestResourcesKey)); } [Fact] public void RegisterForDispose_WhenResourceListDoesNotExist_CreatesListAndAddsResource() { _request.Properties.Remove(HttpPropertyKeys.DisposableRequestResourcesKey); _request.RegisterForDispose(_disposable); var list = Assert.IsType>(_request.Properties[HttpPropertyKeys.DisposableRequestResourcesKey]); Assert.Equal(1, list.Count); Assert.Same(_disposable, list[0]); } [Fact] public void RegisterForDispose_WhenResourceListExists_AddsResource() { var list = new List(); _request.Properties[HttpPropertyKeys.DisposableRequestResourcesKey] = list; _request.RegisterForDispose(_disposable); Assert.Same(list, _request.Properties[HttpPropertyKeys.DisposableRequestResourcesKey]); Assert.Equal(1, list.Count); Assert.Same(_disposable, list[0]); } [Fact] public void DisposeRequestResources_WhenRequestParameterIsNull_Throws() { HttpRequestMessage request = null; Assert.ThrowsArgumentNull(() => request.DisposeRequestResources(), "request"); } [Fact] public void DisposeRequestResources_WhenResourceListDoesNotExists_DoesNothing() { _request.Properties.Remove(HttpPropertyKeys.DisposableRequestResourcesKey); _request.DisposeRequestResources(); Assert.False(_request.Properties.ContainsKey(HttpPropertyKeys.DisposableRequestResourcesKey)); } [Fact] public void DisposeRequestResources_WhenResourceListExists_DisposesResourceAndClearsReferences() { var list = new List { _disposable }; _request.Properties[HttpPropertyKeys.DisposableRequestResourcesKey] = list; _request.DisposeRequestResources(); _disposableMock.Verify(d => d.Dispose()); Assert.Empty(list); } [Fact] public void DisposeRequestResources_WhenResourcesDisposeMethodThrowsException_IgnoresExceptionsAndContinuesDisposingOtherResources() { Mock throwingDisposableMock = new Mock(); throwingDisposableMock.Setup(d => d.Dispose()).Throws(new Exception()); var list = new List { throwingDisposableMock.Object, _disposable }; _request.Properties[HttpPropertyKeys.DisposableRequestResourcesKey] = list; _request.DisposeRequestResources(); throwingDisposableMock.Verify(d => d.Dispose()); _disposableMock.Verify(d => d.Dispose()); Assert.Empty(list); } } }