using FinalFactory.Rendezvous.Server.Abuse; using FinalFactory.Rendezvous.Server.Deployment; namespace FinalFactory.Rendezvous.Tests.Deployment; public sealed class DeploymentOptionsTests { [Fact] public void ProductionConfigurationAcceptsExplicitPublicEndpointsAndTrustBoundary() { DeploymentOptions options = ValidOptions(); AbuseProtectionOptions abuse = new() { TrustedProxyAddresses = ["192.0.2.10"], }; IReadOnlyList errors = options.ValidateProduction( abuse, "rendezvous.finalfactory.at"); Assert.Empty(errors); } [Fact] public void ProductionConfigurationRejectsUnsafeAndAmbiguousDefaults() { DeploymentOptions options = new() { SingleActiveInstance = false, }; IReadOnlyList errors = options.ValidateProduction( new AbuseProtectionOptions(), "*"); Assert.Contains(errors, error => error.Contains("SingleActiveInstance", StringComparison.Ordinal)); Assert.Contains(errors, error => error.Contains("PublicHttpBaseUrl", StringComparison.Ordinal)); Assert.Contains(errors, error => error.Contains("PublicUdpHost", StringComparison.Ordinal)); Assert.Contains(errors, error => error.Contains("TrustedProxyAddresses", StringComparison.Ordinal)); Assert.Contains(errors, error => error.Contains("AllowedHosts", StringComparison.Ordinal)); } [Theory] [InlineData("http://rendezvous.example.com/")] [InlineData("https://user@example.com/")] [InlineData("https://rendezvous.example.com/path")] [InlineData("https://localhost/")] [InlineData("https://10.0.0.1/")] [InlineData("https://192.0.2.10/")] [InlineData("https://[::ffff:10.0.0.1]/")] [InlineData("https://[ff02::1]/")] [InlineData("https://rendezvous.invalid/")] public void ProductionConfigurationRejectsUnsafeHttpEndpoint(string endpoint) { DeploymentOptions options = ValidOptions() with { PublicHttpBaseUrl = endpoint }; IReadOnlyList errors = options.ValidateProduction( new AbuseProtectionOptions { TrustedProxyAddresses = ["192.0.2.10"] }, "rendezvous.finalfactory.at"); Assert.Contains(errors, error => error.Contains("PublicHttpBaseUrl", StringComparison.Ordinal)); } [Theory] [InlineData("0.1.2.3")] [InlineData("100.64.0.1")] [InlineData("192.0.2.1")] [InlineData("198.18.0.1")] [InlineData("198.51.100.1")] [InlineData("203.0.113.1")] [InlineData("224.0.0.1")] [InlineData("255.255.255.255")] [InlineData("::ffff:192.168.1.1")] [InlineData("2001:db8::1")] [InlineData("ff02::1")] [InlineData("rendezvous.example.com")] [InlineData("rendezvous.home.arpa")] [InlineData("rendezvous.alt")] [InlineData("service.test")] public void ProductionConfigurationRejectsNonPublicUdpEndpoint(string endpoint) { DeploymentOptions options = ValidOptions() with { PublicUdpHost = endpoint }; IReadOnlyList errors = options.ValidateProduction( new AbuseProtectionOptions { TrustedProxyAddresses = ["192.0.2.10"] }, "rendezvous.finalfactory.at"); Assert.Contains(errors, error => error.Contains("PublicUdpHost", StringComparison.Ordinal)); } [Fact] public void IsolatedSmokeTestRequiresExplicitPrivateEndpointOverride() { DeploymentOptions options = ValidOptions() with { PublicHttpBaseUrl = "https://localhost/", PublicUdpHost = "127.0.0.1", AllowPrivatePublicEndpoints = true, }; IReadOnlyList errors = options.ValidateProduction( new AbuseProtectionOptions { TrustedProxyAddresses = ["127.0.0.1"] }, "localhost"); Assert.Empty(errors); } [Fact] public void ProductionConfigurationRejectsInvalidPortsDeadlinesAndMismatchedHostFilter() { DeploymentOptions options = ValidOptions() with { PublicUdpPort = 0, DrainDeadlineSeconds = 31, MinimumDrainSeconds = 6, }; IReadOnlyList errors = options.ValidateProduction( new AbuseProtectionOptions { TrustedProxyAddresses = ["192.0.2.10"] }, "different.finalfactory.at"); Assert.Contains(errors, error => error.Contains("PublicUdpPort", StringComparison.Ordinal)); Assert.Contains(errors, error => error.Contains("DrainDeadlineSeconds", StringComparison.Ordinal)); Assert.Contains(errors, error => error.Contains("MinimumDrainSeconds", StringComparison.Ordinal)); Assert.Contains(errors, error => error.Contains("exact host", StringComparison.Ordinal)); } private static DeploymentOptions ValidOptions() => new() { PublicHttpBaseUrl = "https://rendezvous.finalfactory.at/", PublicUdpHost = "rendezvous-udp.finalfactory.at", PublicUdpPort = 9050, DrainDeadlineSeconds = 10, MinimumDrainSeconds = 1, SingleActiveInstance = true, }; }