// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Net.Http.Formatting; using System.Net.Http.Headers; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.WebApiCompatShim; using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Options; using Moq; using Xunit; namespace System.Net.Http { public class HttpRequestMessageExtensionsTest { [Fact] public void CreateResponse_MatchingMediaType_WhenMediaTypeStringIsInvalidFormat_Throws() { HttpRequestMessage request = CreateRequest(new DefaultHttpContext()); var ex = Assert.Throws( () => request.CreateResponse(HttpStatusCode.OK, CreateValue(), "foo/bar; param=value")); Assert.Contains("foo/bar; param=value", ex.Message); } [Fact] public void CreateResponse_MatchingMediaType_WhenRequestDoesNotHaveHttpContextThrows() { HttpRequestMessage request = CreateRequest(null); // Arrange // Act var ex = Assert.Throws( () => request.CreateResponse(HttpStatusCode.OK, CreateValue(), mediaType: "foo/bar")); Assert.Equal( "The HttpRequestMessage instance is not properly initialized. " + "Use HttpRequestMessageHttpContextExtensions.GetHttpRequestMessage to create an HttpRequestMessage " + "for the current request.", ex.Message); } [Fact] public void CreateResponse_DoingConneg_OnlyContent_RetrievesContentNegotiatorFromServices() { // Arrange var context = new DefaultHttpContext(); var services = new Mock(); services .Setup(s => s.GetService(typeof(IContentNegotiator))) .Returns(Mock.Of()) .Verifiable(); var options = new WebApiCompatShimOptions(); options.Formatters.AddRange(new MediaTypeFormatterCollection()); var optionsAccessor = new Mock>(); optionsAccessor.SetupGet(o => o.Value).Returns(options); services .Setup(s => s.GetService(typeof(IOptions))) .Returns(optionsAccessor.Object); context.RequestServices = services.Object; var request = CreateRequest(context); // Act request.CreateResponse(CreateValue()); // Assert services.Verify(); } [Fact] public void CreateResponse_DoingConneg_RetrievesContentNegotiatorFromServices() { // Arrange var context = new DefaultHttpContext(); var services = new Mock(); services .Setup(s => s.GetService(typeof(IContentNegotiator))) .Returns(Mock.Of()) .Verifiable(); var options = new WebApiCompatShimOptions(); options.Formatters.AddRange(new MediaTypeFormatterCollection()); var optionsAccessor = new Mock>(); optionsAccessor.SetupGet(o => o.Value).Returns(options); services .Setup(s => s.GetService(typeof(IOptions))) .Returns(optionsAccessor.Object); context.RequestServices = services.Object; var request = CreateRequest(context); // Act request.CreateResponse(HttpStatusCode.OK, CreateValue()); // Assert services.Verify(); } [Fact] public void CreateResponse_DoingConneg_PerformsContentNegotiationAndCreatesContentUsingResults() { // Arrange var context = new DefaultHttpContext(); var formatter = new XmlMediaTypeFormatter(); var contentNegotiator = new Mock(); contentNegotiator .Setup(c => c.Negotiate(It.IsAny(), It.IsAny(), It.IsAny>())) .Returns(new ContentNegotiationResult(formatter, mediaType: null)); context.RequestServices = CreateServices(contentNegotiator.Object, formatter); var request = CreateRequest(context); // Act var response = request.CreateResponse(HttpStatusCode.NoContent, "42"); // 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_WhenMediaTypeDoesNotMatch_Throws() { // Arrange var context = new DefaultHttpContext(); context.RequestServices = CreateServices(new DefaultContentNegotiator()); var request = CreateRequest(context); // Act var ex = Assert.Throws( () => request.CreateResponse(HttpStatusCode.OK, CreateValue(), mediaType: "foo/bar")); Assert.Equal( "Could not find a formatter matching the media type 'foo/bar' that can write an instance of 'System.Object'.", ex.Message); } [Fact] public void CreateResponse_MatchingMediaType_FindsMatchingFormatterAndCreatesResponse() { // Arrange var context = new DefaultHttpContext(); var formatter = new Mock { CallBase = true }; formatter.Setup(f => f.CanWriteType(typeof(object))).Returns(true).Verifiable(); formatter.Object.SupportedMediaTypes.Add(new MediaTypeHeaderValue("foo/bar")); context.RequestServices = CreateServices(new DefaultContentNegotiator(), formatter.Object); var expectedValue = CreateValue(); var request = CreateRequest(context); // Act var response = request.CreateResponse(HttpStatusCode.Gone, expectedValue, mediaType: "foo/bar"); // Assert Assert.Equal(HttpStatusCode.Gone, response.StatusCode); var content = Assert.IsType>(response.Content); Assert.Same(expectedValue, content.Value); Assert.Same(formatter.Object, content.Formatter); Assert.Equal("foo/bar", content.Headers.ContentType.MediaType); formatter.Verify(); } [Fact] public void CreateResponse_AcceptingFormatter_CreatesResponseWithDefaultMediaType() { // Arrange var context = new DefaultHttpContext(); var formatter = new Mock() { CallBase = true }; formatter .Setup(f => f.CanWriteType(typeof(object))) .Returns(true) .Verifiable(); formatter .Setup(f => f.SetDefaultContentHeaders(typeof(object), It.IsAny(), It.IsAny())) .Callback(SetMediaType) .Verifiable(); formatter.Object.SupportedMediaTypes.Add(new MediaTypeHeaderValue("foo/bar")); var expectedValue = CreateValue(); var request = CreateRequest(context); // Act var response = request.CreateResponse( HttpStatusCode.MultipleChoices, expectedValue, formatter.Object, mediaType: (string)null); // Assert Assert.Equal(HttpStatusCode.MultipleChoices, response.StatusCode); var content = Assert.IsType>(response.Content); Assert.Same(expectedValue, content.Value); Assert.Same(formatter.Object, content.Formatter); Assert.Equal("foo/bar", content.Headers.ContentType.MediaType); formatter.Verify(); } private static void SetMediaType(Type type, HttpContentHeaders headers, MediaTypeHeaderValue value) { headers.ContentType = new MediaTypeHeaderValue("foo/bar"); } [Fact] public void CreateResponse_AcceptingFormatter_WithOverridenMediaTypeString_CreatesResponse() { // Arrange var context = new DefaultHttpContext(); var formatter = new Mock { CallBase = true }; formatter.Setup(f => f.CanWriteType(typeof(object))).Returns(true).Verifiable(); formatter.Object.SupportedMediaTypes.Add(new MediaTypeHeaderValue("foo/bar")); var expectedValue = CreateValue(); var request = CreateRequest(context); // Act var response = request.CreateResponse( HttpStatusCode.MultipleChoices, CreateValue(), formatter.Object, mediaType: "bin/baz"); // Assert Assert.Equal("bin/baz", response.Content.Headers.ContentType.MediaType); } [Fact] public void CreateResponse_AcceptingFormatter_WithOverridenMediaTypeHeader_CreatesResponse() { // Arrange var context = new DefaultHttpContext(); var formatter = new Mock { CallBase = true }; formatter.Setup(f => f.CanWriteType(typeof(object))).Returns(true).Verifiable(); formatter.Object.SupportedMediaTypes.Add(new MediaTypeHeaderValue("foo/bar")); var expectedValue = CreateValue(); var request = CreateRequest(context); // Act var response = request.CreateResponse( HttpStatusCode.MultipleChoices, CreateValue(), formatter.Object, mediaType: new MediaTypeHeaderValue("bin/baz")); // Assert Assert.Equal("bin/baz", response.Content.Headers.ContentType.MediaType); } private static IServiceProvider CreateServices( IContentNegotiator contentNegotiator = null, MediaTypeFormatter formatter = null) { var options = new WebApiCompatShimOptions(); if (formatter == null) { options.Formatters.AddRange(new MediaTypeFormatterCollection()); } else { options.Formatters.Add(formatter); } var optionsAccessor = new Mock>(); optionsAccessor.SetupGet(o => o.Value).Returns(options); var services = new Mock(MockBehavior.Strict); services .Setup(s => s.GetService(typeof(IOptions))) .Returns(optionsAccessor.Object); if (contentNegotiator != null) { services .Setup(s => s.GetService(typeof(IContentNegotiator))) .Returns(contentNegotiator); } return services.Object; } private static object CreateValue() { return new object(); } private static HttpRequestMessage CreateRequest(HttpContext context) { var request = new HttpRequestMessage(); request.Properties.Add(nameof(HttpContext), context); return request; } } }