/* * Copyright (c) 2026 Proton AG * * This file is part of ProtonVPN. * * ProtonVPN is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ProtonVPN is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ProtonVPN. If not, see . */ using System.Reflection; using NSubstitute; using ProtonVPN.Client.Contracts.Profiles; using ProtonVPN.Client.Contracts.Services.Activation; using ProtonVPN.Client.Contracts.Services.Browsing; using ProtonVPN.Client.Contracts.Services.Navigation; using ProtonVPN.Client.Localization.Contracts; using ProtonVPN.Client.Logic.Connection.ConnectionErrors; using ProtonVPN.Client.Logic.Connection.Contracts; using ProtonVPN.Client.Logic.Connection.Contracts.Enums; using ProtonVPN.Client.Logic.Connection.Contracts.Extensions; using ProtonVPN.Client.Logic.Profiles.Contracts.Models; using ProtonVPN.Client.Settings.Contracts; namespace ProtonVPN.Client.Logic.Connection.Tests; [TestClass] public class ConnectionErrorFactoryTest { private readonly Dictionary _connectionErrorMap = new() { { VpnError.None, typeof(NoConnectionError) }, { VpnError.NoneKeepEnabledKillSwitch, typeof(NoConnectionError) }, { VpnError.AllServersExcluded, typeof(AllServersExcludedConnectionError) }, { VpnError.NoServers, typeof(NoServersConnectionError) }, { VpnError.WireGuardAdapterInUseError, typeof(WireGuardAdapterInUseConnectionError) }, { VpnError.MissingConnectionCertificate, typeof(MissingConnectionCertificateError) }, { VpnError.TlsCertificateError, typeof(TlsCertificateConnectionError) }, { VpnError.NoTapAdaptersError, typeof(NoTapAdaptersConnectionError) }, { VpnError.TapAdapterInUseError, typeof(TapAdapterInUseConnectionError) }, { VpnError.TapRequiresUpdateError, typeof(TapRequiresUpdateConnectionError) }, { VpnError.RpcServerUnavailable, typeof(RpcServerUnavailableConnectionError) }, { VpnError.SessionLimitReachedBasic, typeof(SessionLimitReachedConnectionError) }, { VpnError.SessionLimitReachedFree, typeof(SessionLimitReachedConnectionError) }, { VpnError.SessionLimitReachedPlus, typeof(SessionLimitReachedConnectionError) }, { VpnError.SessionLimitReachedPro, typeof(SessionLimitReachedConnectionError) }, { VpnError.SessionLimitReachedVisionary, typeof(SessionLimitReachedConnectionError) }, { VpnError.SessionLimitReachedUnknown, typeof(SessionLimitReachedConnectionError) }, { VpnError.InterfaceHasForwardingEnabled, typeof(MobileHotspotConnectionError) }, }; private ISettings? _settings; private IConnectionManager? _connectionManager; private ILocalizationProvider? _localizer; private IProfileEditor? _profileEditor; private IUrlsBrowser? _urlsBrowser; private IClientWindowsActivator? _clientWindowsActivator; private IClientViewsNavigator? _clientViewsNavigator; [TestInitialize] public void TestInitialize() { _settings = Substitute.For(); _connectionManager = Substitute.For(); _localizer = Substitute.For(); _profileEditor = Substitute.For(); _urlsBrowser = Substitute.For(); _clientWindowsActivator = Substitute.For(); _clientViewsNavigator = Substitute.For(); } [TestCleanup] public void TestCleanup() { _settings = null; _connectionManager = null; _localizer = null; _profileEditor = null; _urlsBrowser = null; _clientWindowsActivator = null; _clientViewsNavigator = null; } [TestMethod] public void GetConnectionError_ShouldReturn_ConnectionErrorForEachUserError() { FieldInfo? errorsForUserField = typeof(VpnErrorExtensions).GetField("_errorsForUser", BindingFlags.NonPublic | BindingFlags.Static); Assert.IsNotNull(errorsForUserField); List? errorsForUser = (List?)errorsForUserField?.GetValue(null); Assert.IsNotNull(errorsForUser); ConnectionErrorFactory connectionErrorFactory = new(_connectionManager!, GetConnectionErrors(), GetUnknownConnectionError()); foreach (VpnError error in errorsForUser) { IConnectionError connectionError = connectionErrorFactory.GetConnectionError(error); Assert.IsInstanceOfType(connectionError, _connectionErrorMap[error]); } } [TestMethod] public void GetConnectionError_ShouldReturn_NoServersForProfileConnectionError() { // Arrange _connectionManager!.CurrentConnectionIntent.Returns(ConnectionProfile.Default); ConnectionErrorFactory connectionErrorFactory = new(_connectionManager!, GetConnectionErrors(), GetUnknownConnectionError()); // Act IConnectionError connectionError = connectionErrorFactory.GetConnectionError(VpnError.NoServers); // Assert Assert.IsInstanceOfType(connectionError); } private Lazy> GetConnectionErrors() { return new Lazy>(() => [ new NoConnectionError(), new NoTapAdaptersConnectionError(_localizer!, _urlsBrowser!), new TapAdapterInUseConnectionError(_localizer!, _urlsBrowser!), new TapRequiresUpdateConnectionError(_localizer!, _urlsBrowser!), new RpcServerUnavailableConnectionError(_localizer!, _urlsBrowser!), new NoServersForProfileConnectionError(_localizer!, _profileEditor!, _connectionManager!), new NoServersConnectionError(_localizer!, _clientWindowsActivator!), new AllServersExcludedConnectionError(_localizer!, _clientViewsNavigator!), new WireGuardAdapterInUseConnectionError(_localizer!, _connectionManager!), new MissingConnectionCertificateError(_localizer!, _clientWindowsActivator!), new TlsCertificateConnectionError(_localizer!, _clientWindowsActivator!), new SessionLimitReachedConnectionError(_localizer!, _settings!, _clientWindowsActivator!), new MobileHotspotConnectionError(_localizer!, _clientWindowsActivator!), ]); } private Lazy GetUnknownConnectionError() { return new(() => new UnknownConnectionError(_localizer!, _clientWindowsActivator!)); } }