/
githubmirror
/
aspnetcore
Обзор
Документация
Войти
/
githubmirror
/
aspnetcore
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Mvc/test/Mvc.FunctionalTests/ApiBehaviorTest.cs
456 строк
16 KB
Brennan
Add logging to MVC tests (#55592)
02 июл 2024, 06:34
Не верифицирован
02 июл 2024, 06:34
c790ed5
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Net; using System.Net.Http; using System.Reflection; using System.Text; using BasicWebSite.Models; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.InternalTesting; using Microsoft.AspNetCore.Mvc.NewtonsoftJson; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Xunit.Abstractions; namespace Microsoft.AspNetCore.Mvc.FunctionalTests; public abstract class ApiBehaviorTestBase<TStartup> : LoggedTest where TStartup : class { protected override void Initialize(TestContext context, MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper) { base.Initialize(context, methodInfo, testMethodArguments, testOutputHelper); Factory = new MvcTestFixture<TStartup>(LoggerFactory).WithWebHostBuilder(ConfigureWebHostBuilder); Client = Factory.CreateDefaultClient(); } public override void Dispose() { Factory.Dispose(); base.Dispose(); } private static void ConfigureWebHostBuilder(IWebHostBuilder builder) => builder.UseStartup<TStartup>(); public WebApplicationFactory<TStartup> Factory { get; private set; } public HttpClient Client { get; private set; } [Fact] public virtual async Task ActionsReturnBadRequest_WhenModelStateIsInvalid() { // Arrange using (new ActivityReplacer()) { var contactModel = new Contact { Name = "Abc", City = "Redmond", State = "WA", Zip = "Invalid", }; var contactString = JsonConvert.SerializeObject(contactModel); // Act var response = await Client.PostAsJsonAsync("/contact", contactModel); // Assert await response.AssertStatusCodeAsync(HttpStatusCode.BadRequest); Assert.Equal("application/problem+json", response.Content.Headers.ContentType.MediaType); var problemDetails = JsonConvert.DeserializeObject<ValidationProblemDetails>( await response.Content.ReadAsStringAsync(), new JsonSerializerSettings { Converters = { new ValidationProblemDetailsConverter() } }); Assert.Equal("One or more validation errors occurred.", problemDetails.Title); Assert.Equal("https://tools.ietf.org/html/rfc9110#section-15.5.1", problemDetails.Type); Assert.Collection( problemDetails.Errors.OrderBy(kvp => kvp.Key), kvp => { Assert.Equal("Name", kvp.Key); var error = Assert.Single(kvp.Value); Assert.Equal("The field Name must be a string with a minimum length of 5 and a maximum length of 30.", error); }, kvp => { Assert.Equal("Zip", kvp.Key); var error = Assert.Single(kvp.Value); Assert.Equal("The field Zip must match the regular expression '\\d{5}'.", error); } ); Assert.Collection( problemDetails.Extensions, kvp => { Assert.Equal("traceId", kvp.Key); Assert.NotNull(kvp.Value); }); } } [Fact] public async Task ActionsReturnUnsupportedMediaType_WhenMediaTypeIsNotSupported() { // Arrange var requestMessage = new HttpRequestMessage(HttpMethod.Post, "/contact") { Content = new StringContent("some content", Encoding.UTF8, "text/css"), }; // Act var response = await Client.SendAsync(requestMessage); // Assert await response.AssertStatusCodeAsync(HttpStatusCode.UnsupportedMediaType); } [Fact] public async Task ActionsReturnUnsupportedMediaType_WhenEncodingIsUnsupported() { // Arrange var requestMessage = new HttpRequestMessage(HttpMethod.Post, "/contact") { Content = new StringContent("some content", Encoding.Latin1, "application/json"), }; // Act var response = await Client.SendAsync(requestMessage); // Assert await response.AssertStatusCodeAsync(HttpStatusCode.UnsupportedMediaType); var content = await response.Content.ReadAsStringAsync(); var problemDetails = JsonConvert.DeserializeObject<ProblemDetails>(content); Assert.Equal((int)HttpStatusCode.UnsupportedMediaType, problemDetails.Status); Assert.Equal("Unsupported Media Type", problemDetails.Title); } [Fact] public Task ActionsWithApiBehavior_InferFromBodyParameters() => ActionsWithApiBehaviorInferFromBodyParameters("ActionWithInferredFromBodyParameter"); [Fact] public Task ActionsWithApiBehavior_InferFromBodyParameters_DoNotConsiderCancellationTokenSourceParameter() => ActionsWithApiBehaviorInferFromBodyParameters("ActionWithInferredFromBodyParameterAndCancellationToken"); private async Task ActionsWithApiBehaviorInferFromBodyParameters(string action) { // Arrange var input = new Contact { ContactId = 13, Name = "Test123", }; // Act var response = await Client.PostAsJsonAsync($"/contact/{action}", input); // Assert await response.AssertStatusCodeAsync(HttpStatusCode.OK); var result = JsonConvert.DeserializeObject<Contact>(await response.Content.ReadAsStringAsync()); Assert.Equal(input.ContactId, result.ContactId); Assert.Equal(input.Name, result.Name); } [Fact] public async Task ActionsWithApiBehavior_DoesNotInferFromBodyForCompositeComplexTypesParameters() { // Arrange var input = new Contact { ContactId = 13, Name = "Test123", }; var requestId = 1; // Act var response = await Client.PostAsJsonAsync($"/contact/ActionWithCompositeComplexTypeParameter/{requestId}", input); // Assert await response.AssertStatusCodeAsync(HttpStatusCode.OK); var result = JsonConvert.DeserializeObject<ContactRequest>(await response.Content.ReadAsStringAsync()); Assert.Equal(input.ContactId, result.ContactInfo.ContactId); Assert.Equal(input.Name, result.ContactInfo.Name); Assert.Equal(requestId, result.Id); } [Fact] public async Task ActionsWithApiBehavior_InferFromServicesParameters() { // Arrange var id = 1; var url = $"/contact/ActionWithInferredFromServicesParameter/{id}"; var response = await Client.GetAsync(url); // Assert await response.AssertStatusCodeAsync(HttpStatusCode.OK); var result = JsonConvert.DeserializeObject<Contact>(await response.Content.ReadAsStringAsync()); Assert.NotNull(result); Assert.Equal(id, result.ContactId); } [Fact] public async Task ActionsWithApiBehavior_InferQueryAndRouteParameters() { // Arrange var id = 31; var name = "test"; var email = "email@test.com"; var url = $"/contact/ActionWithInferredRouteAndQueryParameters/{name}/{id}?email={email}"; var response = await Client.PostAsync(url, new StringContent(string.Empty)); // Assert await response.AssertStatusCodeAsync(HttpStatusCode.OK); var result = JsonConvert.DeserializeObject<Contact>(await response.Content.ReadAsStringAsync()); Assert.Equal(id, result.ContactId); Assert.Equal(name, result.Name); Assert.Equal(email, result.Email); } [Fact] public async Task ActionsWithApiBehavior_InferEmptyPrefixForComplexValueProviderModel_Success() { // Arrange var id = 31; var name = "test_user"; var email = "email@test.com"; var url = $"/contact/ActionWithInferredEmptyPrefix?name={name}&contactid={id}&email={email}"; // Act var response = await Client.GetAsync(url); // Assert await response.AssertStatusCodeAsync(HttpStatusCode.OK); var result = await response.Content.ReadAsAsync<Contact>(); Assert.Equal(id, result.ContactId); Assert.Equal(name, result.Name); Assert.Equal(email, result.Email); } [Fact] public async Task ActionsWithApiBehavior_InferEmptyPrefixForComplexValueProviderModel_Ignored() { // Arrange var id = 31; var name = "test_user"; var email = "email@test.com"; var url = $"/contact/ActionWithInferredEmptyPrefix?contact.name={name}&contact.contactid={id}&contact.email={email}"; // Act var response = await Client.GetAsync(url); // Assert await response.AssertStatusCodeAsync(HttpStatusCode.OK); var result = await response.Content.ReadAsAsync<Contact>(); Assert.Equal(id, result.ContactId); Assert.Equal(name, result.Name); Assert.Equal(email, result.Email); } [Fact] public async Task ActionsWithApiBehavior_InferModelBinderType() { // Arrange var expected = "From TestModelBinder: Hello!"; // Act var response = await Client.GetAsync("/contact/ActionWithInferredModelBinderType?foo=Hello!"); // Assert await response.AssertStatusCodeAsync(HttpStatusCode.OK); var result = await response.Content.ReadAsStringAsync(); Assert.Equal(expected, result); } [Fact] public async Task ActionsWithApiBehavior_InferModelBinderTypeWithExplicitModelName() { // Arrange var expected = "From TestModelBinder: Hello!"; // Act var response = await Client.GetAsync("/contact/ActionWithInferredModelBinderTypeWithExplicitModelName?bar=Hello!"); // Assert await response.AssertStatusCodeAsync(HttpStatusCode.OK); var result = await response.Content.ReadAsStringAsync(); Assert.Equal(expected, result); } [Fact] public virtual async Task ClientErrorResultFilterExecutesForStatusCodeResults() { using (new ActivityReplacer()) { // Act var response = await Client.GetAsync("/contact/ActionReturningStatusCodeResult"); // Assert await response.AssertStatusCodeAsync(HttpStatusCode.NotFound); var content = await response.Content.ReadAsStringAsync(); var problemDetails = JsonConvert.DeserializeObject<ProblemDetails>( content, new JsonSerializerSettings { Converters = { new ProblemDetailsConverter() } }); Assert.Equal(404, problemDetails.Status); Assert.Collection( problemDetails.Extensions, kvp => { Assert.Equal("traceId", kvp.Key); Assert.NotNull(kvp.Value); }); } } [Fact] public virtual async Task SerializingProblemDetails_IgnoresNullValuedProperties() { // Arrange var expected = new[] { "status", "title", "traceId", "type" }; // Act var response = await Client.GetAsync("/contact/ActionReturningStatusCodeResult"); // Assert await response.AssertStatusCodeAsync(HttpStatusCode.NotFound); var content = await response.Content.ReadAsStringAsync(); // Verify that null-valued properties on ProblemDetails are not serialized. var json = JObject.Parse(content); Assert.Equal(expected, json.Properties().OrderBy(p => p.Name).Select(p => p.Name)); } [Fact] public virtual async Task SerializingProblemDetails_WithAllValuesSpecified() { // Arrange var expected = new[] { "detail", "instance", "status", "title", "tracking-id", "type" }; // Act var response = await Client.GetAsync("/contact/ActionReturningProblemDetails"); // Assert await response.AssertStatusCodeAsync(HttpStatusCode.NotFound); var content = await response.Content.ReadAsStringAsync(); var json = JObject.Parse(content); Assert.Equal(expected, json.Properties().OrderBy(p => p.Name).Select(p => p.Name)); } [Fact] public virtual async Task SerializingValidationProblemDetails_WithExtensionData() { // Act var response = await Client.GetAsync("/contact/ActionReturningValidationProblemDetails"); // Assert await response.AssertStatusCodeAsync(HttpStatusCode.BadRequest); var content = await response.Content.ReadAsStringAsync(); var validationProblemDetails = JsonConvert.DeserializeObject<ValidationProblemDetails>( content, new JsonSerializerSettings { Converters = { new ValidationProblemDetailsConverter() } }); Assert.Equal("Error", validationProblemDetails.Title); Assert.Equal(400, validationProblemDetails.Status); Assert.Collection( validationProblemDetails.Extensions, kvp => { Assert.Equal("tracking-id", kvp.Key); Assert.Equal("27", kvp.Value); }); Assert.Collection( validationProblemDetails.Errors, kvp => { Assert.Equal("Error1", kvp.Key); Assert.Equal(new[] { "Error Message" }, kvp.Value); }); } } public class ApiBehaviorTest : ApiBehaviorTestBase<BasicWebSite.StartupWithSystemTextJson> { [Fact] public override Task ActionsReturnBadRequest_WhenModelStateIsInvalid() { return base.ActionsReturnBadRequest_WhenModelStateIsInvalid(); } [Fact] public override Task ClientErrorResultFilterExecutesForStatusCodeResults() { return base.ClientErrorResultFilterExecutesForStatusCodeResults(); } [Fact] public override Task SerializingProblemDetails_IgnoresNullValuedProperties() { return base.SerializingProblemDetails_IgnoresNullValuedProperties(); } [Fact] public override Task SerializingProblemDetails_WithAllValuesSpecified() { return base.SerializingProblemDetails_WithAllValuesSpecified(); } [Fact] public override Task SerializingValidationProblemDetails_WithExtensionData() { return base.SerializingValidationProblemDetails_WithExtensionData(); } } public class ApiBehaviorTestNewtonsoftJson : ApiBehaviorTestBase<BasicWebSite.StartupWithoutEndpointRouting> { private static void ConfigureWebHostBuilder(IWebHostBuilder builder) => builder.UseStartup<BasicWebSite.StartupWithCustomInvalidModelStateFactory>(); [Fact] public async Task ActionsReturnBadRequest_UsesProblemDescriptionProviderAndApiConventionsToConfigureErrorResponse() { await using var factory = new MvcTestFixture<BasicWebSite.StartupWithCustomInvalidModelStateFactory>(LoggerFactory) .WithWebHostBuilder(ConfigureWebHostBuilder); var customInvalidModelStateClient = factory.CreateDefaultClient(); // Arrange var contactModel = new Contact { Name = "Abc", City = "Redmond", State = "WA", Zip = "Invalid", }; var expected = new Dictionary<string, string[]> { {"Name", new[] {"The field Name must be a string with a minimum length of 5 and a maximum length of 30."}}, {"Zip", new[] { @"The field Zip must match the regular expression '\d{5}'."}} }; // Act var response = await customInvalidModelStateClient.PostAsJsonAsync("/contact/PostWithVnd", contactModel); // Assert await response.AssertStatusCodeAsync(HttpStatusCode.BadRequest); Assert.Equal("application/vnd.error+json", response.Content.Headers.ContentType.MediaType); var content = await response.Content.ReadAsStringAsync(); var actual = JsonConvert.DeserializeObject<Dictionary<string, string[]>>(content); Assert.Equal(expected, actual); } }