using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using MxGateway.Server; using MxGateway.Server.Metrics; namespace MxGateway.Tests.Gateway; public sealed class GatewayApplicationTests { [Fact] public void Build_MapsLiveHealthEndpoint() { WebApplication app = GatewayApplication.Build([]); RouteEndpoint endpoint = Assert.Single( ((IEndpointRouteBuilder)app).DataSources .SelectMany(dataSource => dataSource.Endpoints) .OfType(), candidate => candidate.RoutePattern.RawText == "/health/live"); Assert.Equal("LiveHealth", endpoint.Metadata.GetMetadata()?.EndpointName); } [Fact] public void Build_RegistersGatewayMetrics() { WebApplication app = GatewayApplication.Build([]); GatewayMetrics metrics = app.Services.GetRequiredService(); Assert.NotNull(metrics); } [Fact] public void Build_WhenDashboardEnabled_MapsBlazorDashboardAndAuthEndpoints() { WebApplication app = GatewayApplication.Build([]); IReadOnlyList endpoints = GetRouteEndpoints(app); Assert.Contains(endpoints, endpoint => endpoint.RoutePattern.RawText == "/dashboard/"); Assert.Contains(endpoints, endpoint => endpoint.RoutePattern.RawText == "/dashboard/sessions"); Assert.Contains(endpoints, endpoint => endpoint.RoutePattern.RawText == "/dashboard/workers"); Assert.Contains(endpoints, endpoint => endpoint.RoutePattern.RawText == "/dashboard/events"); Assert.Contains(endpoints, endpoint => endpoint.RoutePattern.RawText == "/dashboard/settings"); Assert.Contains(endpoints, endpoint => endpoint.Metadata.GetMetadata()?.EndpointName == "DashboardLogin"); Assert.Contains(endpoints, endpoint => endpoint.Metadata.GetMetadata()?.EndpointName == "DashboardLogout"); } [Fact] public void Build_WhenDashboardDisabled_DoesNotMapDashboardRoutes() { WebApplication app = GatewayApplication.Build(["--MxGateway:Dashboard:Enabled=false"]); IReadOnlyList endpoints = GetRouteEndpoints(app); Assert.DoesNotContain(endpoints, endpoint => endpoint.RoutePattern.RawText?.StartsWith("/dashboard", StringComparison.Ordinal) == true); Assert.DoesNotContain(endpoints, endpoint => endpoint.Metadata.GetMetadata()?.EndpointName?.StartsWith( "Dashboard", StringComparison.Ordinal) == true); } [Theory] [InlineData( "MxGateway:Worker:ExecutablePath", "worker.dll", "MxGateway:Worker:ExecutablePath must point to a .exe file.")] [InlineData( "MxGateway:Events:QueueCapacity", "0", "MxGateway:Events:QueueCapacity must be greater than zero.")] [InlineData( "MxGateway:Authentication:PepperSecretName", "", "MxGateway:Authentication:PepperSecretName is required")] [InlineData( "MxGateway:Dashboard:PathBase", "dashboard", "MxGateway:Dashboard:PathBase must start with '/'.")] public async Task StartAsync_InvalidGatewayConfiguration_FailsStartup( string key, string value, string expectedFailure) { await using WebApplication app = GatewayApplication.Build( [$"--{key}={value}", "--urls=http://127.0.0.1:0"]); OptionsValidationException exception = await Assert.ThrowsAsync( () => app.StartAsync()); Assert.Contains( exception.Failures, failure => failure.Contains(expectedFailure, StringComparison.Ordinal)); } private static IReadOnlyList GetRouteEndpoints(WebApplication app) { return ((IEndpointRouteBuilder)app).DataSources .SelectMany(dataSource => dataSource.Endpoints) .OfType() .ToArray(); } }