using AvParser.Core.Proxies;
using AvParser.Core.Settings;
using AvParser.Infrastructure.Proxies;
using AvParser.UI.Services;
using AvParser.UI.ViewModels;
using ReactiveUI.Primitives.Signals;
namespace AvParser.UI.HeadlessTests;
///
/// Test doubles for the headless tests.
///
///
/// Intentionally duplicated from AvParser.UI.Tests rather than shared through a fourth
/// project: these are a few lines of trivial stand-ins, and a shared test-kit assembly would have
/// to opt out of the tests/ conventions (self-executing xUnit exe) to build at all.
///
internal sealed class FakePage(string titleKey, string iconKey = "IconHome") : PageViewModel
{
public override string TitleKey { get; } = titleKey;
public override string IconKey { get; } = iconKey;
}
///
internal sealed class FakeThemeService(AppTheme initial = AppTheme.System) : IThemeService, IDisposable
{
private readonly BehaviorSignal _current = new(initial);
public AppTheme Current => _current.Value;
public IObservable Changes => _current;
public void Apply(AppTheme theme)
{
if (theme != _current.Value)
{
_current.OnNext(theme);
}
}
public void Dispose() => _current.Dispose();
}
/// In-memory settings, so tests never touch the developer's real profile.
internal sealed class FakeSettingsService(AppSettings? initial = null) : ISettingsService, IDisposable
{
private readonly BehaviorSignal _current = new(initial ?? new AppSettings());
public AppSettings Current => _current.Value;
public IObservable Changes => _current;
public void Update(Func mutate) => _current.OnNext(mutate(_current.Value));
public Task FlushAsync(CancellationToken cancellationToken = default) => Task.CompletedTask;
public void Dispose() => _current.Dispose();
}
/// An editable proxy list held in memory.
internal sealed class FakeMutableProxySource : IMutableProxySource
{
public string Id => "fake-custom";
public string DisplayName => "Fake custom list";
public ProxySourceKind Kind => ProxySourceKind.Custom;
public List Endpoints { get; } = [];
public Task> GetProxiesAsync(CancellationToken cancellationToken = default) =>
Task.FromResult>(Endpoints.ToArray());
public Task AddAsync(IEnumerable endpoints, CancellationToken cancellationToken = default)
{
Endpoints.AddRange(endpoints);
return Task.FromResult(Endpoints.Count);
}
public Task RemoveAsync(ProxyEndpoint endpoint, CancellationToken cancellationToken = default) =>
Task.FromResult(Endpoints.RemoveAll(e => e.Key == endpoint.Key) > 0);
public Task ClearAsync(CancellationToken cancellationToken = default)
{
Endpoints.Clear();
return Task.CompletedTask;
}
}
/// A probe that says everything is dead. The view never calls it in these tests.
internal sealed class FakeProxyProbe : IProxyProbe
{
public Task ProbeAsync(
ProxyEndpoint endpoint,
ProxyOptions options,
CancellationToken cancellationToken = default
) => Task.FromResult(ProxyProbeResult.Failure("not probed in tests"));
}
/// A remembered-state store that keeps everything in memory.
internal sealed class FakeProxyStateStore : IProxyStateStore
{
public Dictionary State { get; } = new(StringComparer.Ordinal);
public Task> LoadAsync(
CancellationToken cancellationToken = default
) => Task.FromResult>(State);
public Task SaveAsync(IEnumerable entries, CancellationToken cancellationToken = default) =>
Task.CompletedTask;
}
///
/// A loader that does nothing.
///
///
/// The real one probes on startup, which would race these view tests: the load is fired from the
/// page constructor and would mark every fake proxy dead partway through an assertion.
///
internal sealed class FakeProxyPoolLoader : IProxyPoolLoader
{
public bool IsLoaded => true;
public bool IsToppingUp => false;
public Task EnsureLoadedAsync() => Task.FromResult(new ProxyPoolLoadResult(0, 0, 0));
public Task SaveStateAsync(CancellationToken cancellationToken = default) => Task.CompletedTask;
}