using System; using System.Net; using System.Net.Http; using System.Threading.Tasks; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Moq; using TeleBot.Http; using Xunit; namespace TeleBot.Tests.Security { /// /// Comprehensive tests to verify TOR proxy configuration and usage. /// These tests prove that TeleBot routes all traffic through TOR. /// public class TorProxyTests { private readonly Mock _mockLogger; public TorProxyTests() { _mockLogger = new Mock(); } [Fact] public void Socks5HttpHandler_WithTorEnabled_ConfiguresProxy() { // Arrange var config = CreateConfiguration(enableTor: true, torPort: 9050); // Act var handler = Socks5HttpHandler.Create(config, _mockLogger.Object); // Assert Assert.NotNull(handler); Assert.True(handler.UseProxy, "UseProxy should be true when TOR is enabled"); Assert.NotNull(handler.Proxy); var proxy = handler.Proxy as WebProxy; Assert.NotNull(proxy); Assert.Contains("9050", proxy.Address?.ToString() ?? ""); Assert.Contains("socks5", proxy.Address?.ToString() ?? ""); } [Fact] public void Socks5HttpHandler_WithTorDisabled_NoProxy() { // Arrange var config = CreateConfiguration(enableTor: false); // Act var handler = Socks5HttpHandler.Create(config, _mockLogger.Object); // Assert Assert.NotNull(handler); // When TOR is disabled, should still work but without proxy } [Fact] public void Socks5HttpHandler_WithTorEnabled_DisablesAutoRedirect() { // Arrange var config = CreateConfiguration(enableTor: true); // Act var handler = Socks5HttpHandler.Create(config, _mockLogger.Object); // Assert - Security check Assert.False(handler.AllowAutoRedirect, "Auto-redirect must be disabled to prevent deanonymization"); Assert.Equal(0, handler.MaxAutomaticRedirections); } [Fact] public void Socks5HttpHandler_WithTorEnabled_ConfiguresConnectionPooling() { // Arrange var config = CreateConfiguration(enableTor: true); // Act var handler = Socks5HttpHandler.Create(config, _mockLogger.Object); // Assert - Performance and security Assert.Equal(TimeSpan.FromMinutes(5), handler.PooledConnectionLifetime); Assert.Equal(TimeSpan.FromMinutes(2), handler.PooledConnectionIdleTimeout); } [Fact] public void Socks5HttpHandler_CreateWithTor_UsesSpecifiedPort() { // Arrange int customPort = 9999; // Act var handler = Socks5HttpHandler.CreateWithTor(customPort, _mockLogger.Object); // Assert Assert.NotNull(handler); Assert.True(handler.UseProxy); var proxy = handler.Proxy as WebProxy; Assert.Contains($"{customPort}", proxy?.Address?.ToString() ?? ""); } [Fact] public void Socks5HttpHandler_CreateDirect_NoProxy() { // Act var handler = Socks5HttpHandler.CreateDirect(_mockLogger.Object); // Assert Assert.NotNull(handler); // Direct handler should not have proxy configured } [Fact] public void Socks5HttpHandler_WithTorEnabled_LogsConfiguration() { // Arrange var config = CreateConfiguration(enableTor: true, torPort: 9050); // Act var handler = Socks5HttpHandler.Create(config, _mockLogger.Object); // Assert - Verify logging _mockLogger.Verify( x => x.Log( LogLevel.Information, It.IsAny(), It.Is((v, t) => v.ToString()!.Contains("SOCKS5") && v.ToString()!.Contains("9050")), It.IsAny(), It.IsAny>()), Times.Once, "Should log SOCKS5 proxy configuration"); } [Fact] public void Socks5HttpHandler_WithTorDisabled_LogsWarning() { // Arrange var config = CreateConfiguration(enableTor: false); // Act var handler = Socks5HttpHandler.Create(config, _mockLogger.Object); // Assert - Verify security warning _mockLogger.Verify( x => x.Log( LogLevel.Warning, It.IsAny(), It.Is((v, t) => v.ToString()!.Contains("DISABLED")), It.IsAny(), It.IsAny>()), Times.Once, "Should log warning when TOR is disabled"); } [Theory] [InlineData(true, 9050)] [InlineData(true, 9051)] [InlineData(true, 9052)] [InlineData(false, 9050)] public void Socks5HttpHandler_VariousConfigurations_CreatesHandler(bool enableTor, int port) { // Arrange var config = CreateConfiguration(enableTor, port); // Act var handler = Socks5HttpHandler.Create(config, _mockLogger.Object); // Assert Assert.NotNull(handler); Assert.Equal(enableTor, handler.UseProxy); } [Fact] public void Socks5HttpHandler_ProxyBypassLocal_IsFalse() { // Arrange var config = CreateConfiguration(enableTor: true); // Act var handler = Socks5HttpHandler.Create(config, _mockLogger.Object); // Assert - Security: All traffic must go through TOR var proxy = handler.Proxy as WebProxy; Assert.NotNull(proxy); Assert.False(proxy.BypassProxyOnLocal, "Local traffic must also go through TOR for complete anonymity"); } [Fact] public void Socks5HttpHandler_DefaultCredentials_IsFalse() { // Arrange var config = CreateConfiguration(enableTor: true); // Act var handler = Socks5HttpHandler.Create(config, _mockLogger.Object); // Assert - Security var proxy = handler.Proxy as WebProxy; Assert.NotNull(proxy); Assert.False(proxy.UseDefaultCredentials, "Should not use default credentials for security"); } /// /// Test that proves configuration is read correctly from appsettings /// [Fact] public void Configuration_AppsettingsFormat_IsCorrect() { // Arrange var configData = new Dictionary { ["Privacy:EnableTor"] = "true", ["Privacy:TorSocksPort"] = "9050", ["LittleShop:UseTor"] = "true" }; var configuration = new ConfigurationBuilder() .AddInMemoryCollection(configData!) .Build(); // Act var torEnabled = configuration.GetValue("Privacy:EnableTor"); var torPort = configuration.GetValue("Privacy:TorSocksPort"); var useTor = configuration.GetValue("LittleShop:UseTor"); // Assert - Proof of configuration format Assert.True(torEnabled, "Privacy:EnableTor must be true in production config"); Assert.Equal(9050, torPort); Assert.True(useTor, "LittleShop:UseTor must be true in production config"); } // Helper method to create test configuration private IConfiguration CreateConfiguration(bool enableTor, int torPort = 9050) { var configData = new Dictionary { ["Privacy:EnableTor"] = enableTor.ToString(), ["Privacy:TorSocksPort"] = torPort.ToString() }; return new ConfigurationBuilder() .AddInMemoryCollection(configData!) .Build(); } } }