diff options
Diffstat (limited to 'pkg/tor')
| -rw-r--r-- | pkg/tor/tor_test.go | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/pkg/tor/tor_test.go b/pkg/tor/tor_test.go new file mode 100644 index 0000000..b6411e0 --- /dev/null +++ b/pkg/tor/tor_test.go @@ -0,0 +1,282 @@ +package tor + +import ( + "strings" + "testing" +) + +func TestValidateOnionAddress(t *testing.T) { + tests := []struct { + name string + addr string + wantErr bool + }{ + // Valid v3 .onion addresses + { + name: "valid v3 onion with port", + addr: "abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrstuv.onion:8080", + wantErr: false, + }, + { + name: "valid v3 onion without port", + addr: "abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrstuv.onion", + wantErr: false, + }, + { + name: "valid v3 onion lowercase", + addr: "vwxyz234567abcdefghijklmnopqrstuvwxyz234567abcdefghijk.onion:443", + wantErr: false, + }, + + // Invalid addresses + { + name: "empty", + addr: "", + wantErr: true, + }, + { + name: "clearnet domain", + addr: "example.com:8080", + wantErr: true, + }, + { + name: "clearnet IP", + addr: "192.168.1.1:8080", + wantErr: true, + }, + { + name: "v2 onion (too short)", + addr: "abcdefghijklmnop.onion:8080", + wantErr: true, + }, + { + name: "uppercase not allowed", + addr: "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567ABCDEFGHIJKLMNOPQRSTUV.onion", + wantErr: true, + }, + { + name: "invalid characters", + addr: "abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrst01.onion", + wantErr: true, // 0 and 1 not in base32 + }, + { + name: "too long", + addr: "abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrstuvwxyz.onion", + wantErr: true, + }, + { + name: "missing .onion", + addr: "abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrstuv", + wantErr: true, + }, + { + name: "invalid port", + addr: "abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrstuv.onion:999999", + wantErr: true, + }, + { + name: "localhost", + addr: "localhost:8080", + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := ValidateOnionAddress(tt.addr) + if (err != nil) != tt.wantErr { + t.Errorf("ValidateOnionAddress(%q) error = %v, wantErr %v", tt.addr, err, tt.wantErr) + } + }) + } +} + +func TestValidateOnionAddress_ErrorMessages(t *testing.T) { + tests := []struct { + addr string + errContains string + }{ + {"", "empty"}, + {"example.com", ".onion"}, + {"short.onion", "invalid"}, + } + + for _, tt := range tests { + err := ValidateOnionAddress(tt.addr) + if err == nil { + t.Errorf("Expected error for %q", tt.addr) + continue + } + if !strings.Contains(strings.ToLower(err.Error()), strings.ToLower(tt.errContains)) { + t.Errorf("Error %q should contain %q", err.Error(), tt.errContains) + } + } +} + +func TestOnionRegex(t *testing.T) { + // Valid patterns + validAddrs := []string{ + "abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrstuv.onion", + "abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrstuv.onion:80", + "abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrstuv.onion:8080", + "abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrstuv.onion:65535", + } + + for _, addr := range validAddrs { + if !OnionRegex.MatchString(addr) { + t.Errorf("OnionRegex should match %q", addr) + } + } + + // Invalid patterns + invalidAddrs := []string{ + "tooshort.onion", + "example.com", + "127.0.0.1:8080", + "UPPERCASE.onion", + } + + for _, addr := range invalidAddrs { + if OnionRegex.MatchString(addr) { + t.Errorf("OnionRegex should NOT match %q", addr) + } + } +} + +func TestDefaultProxyAddresses(t *testing.T) { + if len(DefaultProxyAddresses) == 0 { + t.Error("DefaultProxyAddresses should not be empty") + } + + for _, addr := range DefaultProxyAddresses { + if !strings.HasPrefix(addr, "socks5://") { + t.Errorf("Proxy address should start with socks5://: %s", addr) + } + } +} + +func TestNewDialer(t *testing.T) { + dialer := NewDialer() + + if dialer == nil { + t.Fatal("NewDialer() returned nil") + } + + if len(dialer.ProxyAddresses) == 0 { + t.Error("Dialer should have default proxy addresses") + } + + if dialer.Timeout == 0 { + t.Error("Dialer should have default timeout") + } + + if dialer.KeepAlive == 0 { + t.Error("Dialer should have default keepalive") + } +} + +func TestDialer_CustomSettings(t *testing.T) { + dialer := NewDialer() + dialer.ProxyAddresses = []string{"socks5://custom:1234"} + + if len(dialer.ProxyAddresses) != 1 { + t.Error("Should allow custom proxy addresses") + } +} + +// Note: These tests don't actually connect to Tor (that would require Tor running) +// They just test the validation and configuration logic + +func TestConnect_InvalidAddress(t *testing.T) { + _, err := Connect("invalid-address", nil) + if err == nil { + t.Error("Connect should fail with invalid address") + } +} + +func TestConnect_EmptyAddress(t *testing.T) { + _, err := Connect("", nil) + if err == nil { + t.Error("Connect should fail with empty address") + } +} + +func TestDialer_Dial_InvalidAddress(t *testing.T) { + dialer := NewDialer() + _, err := dialer.Dial("not-an-onion") + if err == nil { + t.Error("Dial should fail with invalid address") + } +} + +func TestConnectWithCallback_InvalidAddress(t *testing.T) { + var progressMessages []string + callback := func(msg string) { + progressMessages = append(progressMessages, msg) + } + + _, err := ConnectWithCallback("invalid", nil, callback) + if err == nil { + t.Error("ConnectWithCallback should fail with invalid address") + } +} + +func TestTestProxyAvailable_InvalidURL(t *testing.T) { + err := TestProxyAvailable("not-a-valid-url") + if err == nil { + t.Error("TestProxyAvailable should fail with invalid URL") + } +} + +func TestTestProxyAvailable_NotRunning(t *testing.T) { + // This proxy shouldn't exist + err := TestProxyAvailable("socks5://127.0.0.1:59999") + if err == nil { + t.Error("TestProxyAvailable should fail when proxy not running") + } +} + +func TestDialer_IsAvailable_NoProxy(t *testing.T) { + dialer := &Dialer{ + ProxyAddresses: []string{"socks5://127.0.0.1:59999"}, // Non-existent + } + + if dialer.IsAvailable() { + t.Error("IsAvailable should return false when no proxy running") + } +} + +// Test constants +func TestConstants(t *testing.T) { + if DefaultConnectionTimeout == 0 { + t.Error("DefaultConnectionTimeout should not be zero") + } + if DefaultKeepAlive == 0 { + t.Error("DefaultKeepAlive should not be zero") + } + if ProxyTestTimeout == 0 { + t.Error("ProxyTestTimeout should not be zero") + } +} + +// Benchmark validation +func BenchmarkValidateOnionAddress_Valid(b *testing.B) { + addr := "abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrstuv.onion:8080" + for i := 0; i < b.N; i++ { + ValidateOnionAddress(addr) + } +} + +func BenchmarkValidateOnionAddress_Invalid(b *testing.B) { + addr := "example.com" + for i := 0; i < b.N; i++ { + ValidateOnionAddress(addr) + } +} + +func BenchmarkOnionRegex_Match(b *testing.B) { + addr := "abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrstuv.onion:8080" + for i := 0; i < b.N; i++ { + OnionRegex.MatchString(addr) + } +} |
