summaryrefslogtreecommitdiffstats
path: root/pkg/tor
diff options
context:
space:
mode:
authorClaude <noreply@anthropic.com>2025-11-23 10:38:12 +0000
committerClaude <noreply@anthropic.com>2025-11-23 10:38:12 +0000
commit3d5286365747c7d2418be16544116f67964b8c48 (patch)
treedc0040ee69ced3d8578587461e0fe98abd61e2a9 /pkg/tor
parent69a75acea56bbff39fa1c81d4813ebcb4a617f06 (diff)
downloadnoshitalk-3d5286365747c7d2418be16544116f67964b8c48.tar.gz
noshitalk-3d5286365747c7d2418be16544116f67964b8c48.tar.xz
noshitalk-3d5286365747c7d2418be16544116f67964b8c48.zip
Add tests, Makefile, update README, remove duplicates
- Add comprehensive test suites for all shared packages: - pkg/crypto: padding, encryption, ECDH, auth tests - pkg/protocol: message serialization tests - pkg/identity: identity save/load tests - pkg/tor: onion address validation tests - Add Makefile with build automation targets - Update README with new modular architecture docs - Remove duplicate monolithic .go files from root (code now organized in cmd/ and pkg/ directories)
Diffstat (limited to 'pkg/tor')
-rw-r--r--pkg/tor/tor_test.go282
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)
+ }
+}