using System.Net; using System.Text.Json; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.Configuration; namespace Flyshot.Server.IntegrationTests; /// /// 锁定标准 MVC 宿主需要提供的 Swagger 与 CORS 行为,避免后续回退成只够跑通的最小配置。 /// public sealed class HostMvcConfigurationTests(FlyshotServerFactory factory) : IClassFixture { /// /// 验证宿主会公开标准 Swagger JSON,并且文档标题和旧 HTTP 兼容路径都能从配置和控制器路由中导出。 /// [Fact] public async Task SwaggerDocument_ExposesConfiguredMetadataAndLegacyRoutes() { using var configuredFactory = CreateConfiguredFactory(factory); using var client = configuredFactory.CreateClient(); using var response = await client.GetAsync("/swagger/v1/swagger.json"); Assert.Equal(HttpStatusCode.OK, response.StatusCode); await using var responseStream = await response.Content.ReadAsStreamAsync(); using var document = await JsonDocument.ParseAsync(responseStream); var root = document.RootElement; var paths = root.GetProperty("paths"); Assert.Equal("3.0.1", root.GetProperty("openapi").GetString()); Assert.Equal("Flyshot Replacement HTTP API", root.GetProperty("info").GetProperty("title").GetString()); // OpenAPI 文档会把部分带尾斜杠的路由规范化为无尾斜杠形式,这里同时接受两种键。 Assert.True(paths.TryGetProperty("/robot_info/", out _) || paths.TryGetProperty("/robot_info", out _)); Assert.True(paths.TryGetProperty("/healthz", out _) || paths.TryGetProperty("/healthz/", out _)); } /// /// 验证宿主会按配置对旧 HTTP API 路由返回标准 CORS 预检响应。 /// [Fact] public async Task CorsPreflight_ReturnsConfiguredAllowOriginHeaders() { using var configuredFactory = CreateConfiguredFactory(factory); using var client = configuredFactory.CreateClient(); using var request = new HttpRequestMessage(HttpMethod.Options, "/robot_info/"); request.Headers.Add("Origin", "http://localhost:3000"); request.Headers.Add("Access-Control-Request-Method", "GET"); request.Headers.Add("Access-Control-Request-Headers", "content-type"); using var response = await client.SendAsync(request); Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); Assert.True(response.Headers.TryGetValues("Access-Control-Allow-Origin", out var allowedOrigins)); Assert.Contains("http://localhost:3000", allowedOrigins); Assert.True(response.Headers.TryGetValues("Access-Control-Allow-Methods", out var allowedMethods)); Assert.Contains("GET", string.Join(",", allowedMethods)); } /// /// 为测试宿主注入标准 Swagger 与 CORS 配置,避免依赖开发机本地环境。 /// private static WebApplicationFactory CreateConfiguredFactory(FlyshotServerFactory factory) { return factory.WithWebHostBuilder(builder => { builder.ConfigureAppConfiguration((_, configurationBuilder) => { configurationBuilder.AddInMemoryCollection(new Dictionary { ["Swagger:Enabled"] = "true", ["Swagger:DocumentName"] = "v1", ["Swagger:Title"] = "Flyshot Replacement HTTP API", ["Swagger:Version"] = "v1", ["Cors:PolicyName"] = "LegacyHttpApi", ["Cors:AllowedOrigins:0"] = "http://localhost:3000", ["Cors:AllowedMethods:0"] = "GET", ["Cors:AllowedMethods:1"] = "POST", ["Cors:AllowedMethods:2"] = "OPTIONS", ["Cors:AllowedHeaders:0"] = "content-type" }); }); }); } }