using System.Net; using MAX.Framework.Conventions; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ActionConstraints; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace Store.Users.Tests; /// 验证公共默认 GET 约定的真实路由行为及接口文档。 public sealed class DefaultHttpGetTests : IAsyncLifetime { private WebApplication _app = null!; private HttpClient _client = null!; public async Task InitializeAsync() { var builder = WebApplication.CreateBuilder(); builder.WebHost.UseTestServer(); builder.Logging.ClearProviders(); builder.Services.AddControllers().AddApplicationPart(typeof(DefaultGetTestController).Assembly); _app = builder.Build(); _app.MapControllers(); await _app.StartAsync(); _client = _app.GetTestClient(); } [Theory] [InlineData("Read", "GET", "POST")] [InlineData("Create", "POST", "GET")] [InlineData("Replace", "PUT", "GET")] public async Task UnmarkedAction_DefaultsToGet_AndExplicitMethodsTakePrecedence(string action, string allowed, string rejected) { using var validRequest = new HttpRequestMessage(new HttpMethod(allowed), $"/default-get-tests/{action}"); using var validResponse = await _client.SendAsync(validRequest); Assert.Equal(HttpStatusCode.OK, validResponse.StatusCode); using var invalidRequest = new HttpRequestMessage(new HttpMethod(rejected), $"/default-get-tests/{action}"); using var invalidResponse = await _client.SendAsync(invalidRequest); Assert.Equal(HttpStatusCode.MethodNotAllowed, invalidResponse.StatusCode); Assert.Contains(allowed, invalidResponse.Content.Headers.Allow); } [Fact] public void ActionConstraints_EndpointMetadata_AndApiExplorer_Agree() { var action = Assert.Single(_app.Services.GetRequiredService() .ActionDescriptors.Items.OfType(), a => a.ControllerTypeInfo.AsType() == typeof(DefaultGetTestController) && a.ActionName == "Read"); Assert.Equal("GET", Assert.Single(Assert.Single(action.ActionConstraints!.OfType()).HttpMethods)); Assert.Equal("GET", Assert.Single(Assert.Single(action.EndpointMetadata.OfType()).HttpMethods)); var description = Assert.Single(_app.Services.GetRequiredService() .ApiDescriptionGroups.Items.SelectMany(g => g.Items), d => d.ActionDescriptor.Id == action.Id); Assert.Equal("GET", description.HttpMethod); } public async Task DisposeAsync() { _client?.Dispose(); if (_app != null) await _app.DisposeAsync(); } } [ApiController] [DefaultHttpGet] [Route("default-get-tests/[action]")] public sealed class DefaultGetTestController : ControllerBase { public string Read() => "read"; [HttpPost] public string Create() => "created"; [HttpPut] public string Replace() => "replaced"; }