using AvParser.Core.Proxies; namespace AvParser.Core.Tests.Proxies; public class ProxyEndpointTests { [Theory] [InlineData("1.2.3.4:8080", ProxyProtocol.Http, "1.2.3.4", 8080)] [InlineData("http://1.2.3.4:8080", ProxyProtocol.Http, "1.2.3.4", 8080)] [InlineData("https://proxy.example.com:3128", ProxyProtocol.Https, "proxy.example.com", 3128)] [InlineData("socks4://1.2.3.4:1080", ProxyProtocol.Socks4, "1.2.3.4", 1080)] [InlineData("socks5://1.2.3.4:1080", ProxyProtocol.Socks5, "1.2.3.4", 1080)] [InlineData(" socks5h://1.2.3.4:1080 ", ProxyProtocol.Socks5, "1.2.3.4", 1080)] public void Parses_the_forms_public_lists_actually_use(string text, ProxyProtocol protocol, string host, int port) { ProxyEndpoint.TryParse(text, out var endpoint).ShouldBeTrue(); endpoint!.Protocol.ShouldBe(protocol); endpoint.Host.ShouldBe(host); endpoint.Port.ShouldBe(port); } [Fact] public void Parses_credentials() { ProxyEndpoint.TryParse("http://alice:s3cret@1.2.3.4:8080", out var endpoint).ShouldBeTrue(); endpoint!.Username.ShouldBe("alice"); endpoint.Password.ShouldBe("s3cret"); endpoint.HasCredentials.ShouldBeTrue(); } [Fact] public void Takes_the_last_at_sign_so_a_password_may_contain_one() { ProxyEndpoint.TryParse("alice:p@ss@1.2.3.4:8080", out var endpoint).ShouldBeTrue(); endpoint!.Username.ShouldBe("alice"); endpoint.Password.ShouldBe("p@ss"); endpoint.Host.ShouldBe("1.2.3.4"); } [Theory] [InlineData(null)] [InlineData("")] [InlineData(" ")] [InlineData("1.2.3.4")] [InlineData("1.2.3.4:")] [InlineData("1.2.3.4:0")] [InlineData("1.2.3.4:70000")] [InlineData("1.2.3.4:notaport")] [InlineData(":8080")] [InlineData("gopher://1.2.3.4:8080")] [InlineData("@1.2.3.4:8080")] public void Rejects_malformed_input(string? text) => ProxyEndpoint.TryParse(text, out _).ShouldBeFalse(); [Fact] public void Key_is_case_insensitive_and_identifies_the_address() { ProxyEndpoint.TryParse("SOCKS5://Proxy.Example.COM:1080", out var upper).ShouldBeTrue(); ProxyEndpoint.TryParse("socks5://proxy.example.com:1080", out var lower).ShouldBeTrue(); upper!.Key.ShouldBe(lower!.Key); } [Fact] public void Key_separates_the_same_host_on_different_protocols() { ProxyEndpoint.TryParse("http://1.2.3.4:1080", out var http).ShouldBeTrue(); ProxyEndpoint.TryParse("socks5://1.2.3.4:1080", out var socks).ShouldBeTrue(); http!.Key.ShouldNotBe(socks!.Key); } [Theory] [InlineData(ProxyProtocol.Http, "http")] [InlineData(ProxyProtocol.Https, "http")] [InlineData(ProxyProtocol.Socks4, "socks4")] [InlineData(ProxyProtocol.Socks5, "socks5")] public void Https_proxies_are_still_reached_over_the_http_scheme(ProxyProtocol protocol, string scheme) { // .NET has no "https" proxy scheme — an HTTPS-capable proxy tunnels TLS with CONNECT // over a plain http:// proxy URI. Getting this wrong makes every such proxy unusable. new ProxyEndpoint(protocol, "1.2.3.4", 8080).Scheme.ShouldBe(scheme); } [Fact] public void Round_trips_through_its_own_string_form() { var original = new ProxyEndpoint(ProxyProtocol.Socks5, "1.2.3.4", 1080); ProxyEndpoint.TryParse(original.ToString(), out var parsed).ShouldBeTrue(); parsed!.Key.ShouldBe(original.Key); } [Theory] [InlineData("transparent", ProxyAnonymity.Transparent)] [InlineData("anonymous", ProxyAnonymity.Anonymous)] [InlineData("elite", ProxyAnonymity.Elite)] [InlineData("high", ProxyAnonymity.Elite)] [InlineData("nonsense", ProxyAnonymity.Unknown)] [InlineData(null, ProxyAnonymity.Unknown)] public void Parses_anonymity(string? text, ProxyAnonymity expected) => ProxyEndpoint.ParseAnonymity(text).ShouldBe(expected); }