Files

83 lines
3.4 KiB
C#

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;
/// <summary>验证公共默认 GET 约定的真实路由行为及接口文档。</summary>
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<IActionDescriptorCollectionProvider>()
.ActionDescriptors.Items.OfType<ControllerActionDescriptor>(), a =>
a.ControllerTypeInfo.AsType() == typeof(DefaultGetTestController) && a.ActionName == "Read");
Assert.Equal("GET", Assert.Single(Assert.Single(action.ActionConstraints!.OfType<HttpMethodActionConstraint>()).HttpMethods));
Assert.Equal("GET", Assert.Single(Assert.Single(action.EndpointMetadata.OfType<IHttpMethodMetadata>()).HttpMethods));
var description = Assert.Single(_app.Services.GetRequiredService<IApiDescriptionGroupCollectionProvider>()
.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";
}