using AvParser.Core.Proxies; using AvParser.Infrastructure.Proxies; using AvParser.Infrastructure.Storage; using Microsoft.Extensions.Logging.Abstractions; namespace AvParser.Infrastructure.Tests; public sealed class CustomProxySourceTests : IDisposable { private readonly string _directory = Path.Combine( Path.GetTempPath(), "AvParserTests", Guid.NewGuid().ToString("N") ); private CustomProxySource Create() => new(new AppPaths(_directory), NullLogger.Instance); public void Dispose() { if (Directory.Exists(_directory)) { Directory.Delete(_directory, recursive: true); } } [Fact] public async Task An_absent_file_reads_as_an_empty_list() { using var source = Create(); (await source.GetProxiesAsync(TestContext.Current.CancellationToken)).ShouldBeEmpty(); } [Fact] public async Task Added_proxies_survive_a_reload() { using (var source = Create()) { ProxyEndpoint.TryParse("socks5://1.2.3.4:1080", out var endpoint).ShouldBeTrue(); (await source.AddAsync([endpoint!], TestContext.Current.CancellationToken)).ShouldBe(1); } // A brand-new instance reads from disk rather than from the in-memory cache. using var reopened = Create(); var proxies = await reopened.GetProxiesAsync(TestContext.Current.CancellationToken); proxies.ShouldHaveSingleItem().Port.ShouldBe(1080); } [Fact] public async Task Adding_the_same_address_twice_is_a_no_op() { using var source = Create(); ProxyEndpoint.TryParse("1.2.3.4:8080", out var endpoint).ShouldBeTrue(); (await source.AddAsync([endpoint!], TestContext.Current.CancellationToken)).ShouldBe(1); (await source.AddAsync([endpoint!], TestContext.Current.CancellationToken)).ShouldBe(0); (await source.GetProxiesAsync(TestContext.Current.CancellationToken)).Count.ShouldBe(1); } [Fact] public async Task Credentials_are_persisted() { using (var source = Create()) { ProxyEndpoint.TryParse("http://alice:s3cret@1.2.3.4:8080", out var endpoint).ShouldBeTrue(); await source.AddAsync([endpoint!], TestContext.Current.CancellationToken); } using var reopened = Create(); var proxy = (await reopened.GetProxiesAsync(TestContext.Current.CancellationToken)).ShouldHaveSingleItem(); proxy.Username.ShouldBe("alice"); proxy.Password.ShouldBe("s3cret"); } [Fact] public async Task Removing_reports_whether_the_address_was_there() { using var source = Create(); ProxyEndpoint.TryParse("1.2.3.4:8080", out var endpoint).ShouldBeTrue(); await source.AddAsync([endpoint!], TestContext.Current.CancellationToken); (await source.RemoveAsync(endpoint!, TestContext.Current.CancellationToken)).ShouldBeTrue(); (await source.RemoveAsync(endpoint!, TestContext.Current.CancellationToken)).ShouldBeFalse(); (await source.GetProxiesAsync(TestContext.Current.CancellationToken)).ShouldBeEmpty(); } [Fact] public async Task Clearing_empties_the_list() { using var source = Create(); ProxyEndpoint.TryParse("1.2.3.4:8080", out var a).ShouldBeTrue(); ProxyEndpoint.TryParse("5.6.7.8:8080", out var b).ShouldBeTrue(); await source.AddAsync([a!, b!], TestContext.Current.CancellationToken); await source.ClearAsync(TestContext.Current.CancellationToken); (await source.GetProxiesAsync(TestContext.Current.CancellationToken)).ShouldBeEmpty(); } [Fact] public void Pasted_lists_are_split_on_anything_reasonable() { var (parsed, rejected) = CustomProxySource.ParseList( "socks5://1.2.3.4:1080\n2.3.4.5:8080, 3.4.5.6:3128;4.5.6.7:80" ); parsed.Count.ShouldBe(4); rejected.ShouldBeEmpty(); } [Fact] public void Comments_and_blank_lines_are_ignored() { var (parsed, rejected) = CustomProxySource.ParseList("# my proxies\n\n1.2.3.4:8080\n"); parsed.ShouldHaveSingleItem(); rejected.ShouldBeEmpty(); } [Fact] public void Bad_lines_are_reported_rather_than_dropped() { // Silently accepting 2 of 3 is impossible to act on when the paste is hundreds long. var (parsed, rejected) = CustomProxySource.ParseList("1.2.3.4:8080\nnot-a-proxy\n5.6.7.8:1080"); parsed.Count.ShouldBe(2); rejected.ShouldHaveSingleItem().ShouldBe("not-a-proxy"); } [Fact] public void An_empty_paste_yields_nothing() { var (parsed, rejected) = CustomProxySource.ParseList(" "); parsed.ShouldBeEmpty(); rejected.ShouldBeEmpty(); } }