diff --git a/src/mnemonicode/mnemonicode_test.go b/src/mnemonicode/mnemonicode_test.go new file mode 100644 index 00000000..31301f1e --- /dev/null +++ b/src/mnemonicode/mnemonicode_test.go @@ -0,0 +1,171 @@ +package mnemonicode + +import ( + "testing" +) + +func TestWordsRequired(t *testing.T) { + tests := []struct { + name string + length int + want int + }{ + {"empty", 0, 0}, + {"1 byte", 1, 1}, + {"2 bytes", 2, 2}, + {"3 bytes", 3, 3}, + {"4 bytes", 4, 3}, + {"5 bytes", 5, 4}, + {"8 bytes", 8, 6}, + {"12 bytes", 12, 9}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := WordsRequired(tt.length); got != tt.want { + t.Errorf("WordsRequired(%d) = %d, want %d", tt.length, got, tt.want) + } + }) + } +} + +func TestEncodeWordList(t *testing.T) { + tests := []struct { + name string + dst []string + src []byte + want int + }{ + { + name: "empty input", + dst: []string{}, + src: []byte{}, + want: 0, + }, + { + name: "single byte", + dst: []string{}, + src: []byte{0x01}, + want: 1, + }, + { + name: "two bytes", + dst: []string{}, + src: []byte{0x01, 0x02}, + want: 2, + }, + { + name: "three bytes", + dst: []string{}, + src: []byte{0x01, 0x02, 0x03}, + want: 3, + }, + { + name: "four bytes", + dst: []string{}, + src: []byte{0x01, 0x02, 0x03, 0x04}, + want: 3, + }, + { + name: "eight bytes", + dst: []string{}, + src: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, + want: 6, + }, + { + name: "with existing dst", + dst: []string{"existing"}, + src: []byte{0x01}, + want: 2, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := EncodeWordList(tt.dst, tt.src) + if len(result) != tt.want { + t.Errorf("EncodeWordList() returned %d words, want %d", len(result), tt.want) + } + + // Check that all words are valid + for i, word := range result { + if word == "" { + t.Errorf("EncodeWordList() returned empty word at index %d", i) + } + } + }) + } +} + +func TestEncodeWordListConsistency(t *testing.T) { + input := []byte{0x12, 0x34, 0x56, 0x78} + + // Encode twice with empty dst + result1 := EncodeWordList([]string{}, input) + result2 := EncodeWordList([]string{}, input) + + if len(result1) != len(result2) { + t.Errorf("Inconsistent result lengths: %d vs %d", len(result1), len(result2)) + } + + for i := range result1 { + if result1[i] != result2[i] { + t.Errorf("Inconsistent result at index %d: %s vs %s", i, result1[i], result2[i]) + } + } +} + +func TestEncodeWordListCapacityHandling(t *testing.T) { + // Test with dst that has sufficient capacity + dst := make([]string, 1, 10) + dst[0] = "existing" + input := []byte{0x01, 0x02} + + result := EncodeWordList(dst, input) + + if len(result) != 3 { // 1 existing + 2 new + t.Errorf("Expected 3 words, got %d", len(result)) + } + + if result[0] != "existing" { + t.Errorf("Expected first word to be 'existing', got %s", result[0]) + } +} + +func TestEncodeWordListBoundaryValues(t *testing.T) { + tests := []struct { + name string + src []byte + }{ + {"max single byte", []byte{0xFF}}, + {"max two bytes", []byte{0xFF, 0xFF}}, + {"max three bytes", []byte{0xFF, 0xFF, 0xFF}}, + {"max four bytes", []byte{0xFF, 0xFF, 0xFF, 0xFF}}, + {"all zeros", []byte{0x00, 0x00, 0x00, 0x00}}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := EncodeWordList([]string{}, tt.src) + expectedLen := WordsRequired(len(tt.src)) + + if len(result) != expectedLen { + t.Errorf("Expected %d words, got %d", expectedLen, len(result)) + } + + // Ensure all words are from the WordList + for _, word := range result { + found := false + for _, validWord := range WordList { + if word == validWord { + found = true + break + } + } + if !found { + t.Errorf("Invalid word generated: %s", word) + } + } + }) + } +} \ No newline at end of file diff --git a/src/models/models_test.go b/src/models/models_test.go new file mode 100644 index 00000000..d4aaca91 --- /dev/null +++ b/src/models/models_test.go @@ -0,0 +1,179 @@ +package models + +import ( + "net" + "strings" + "testing" + "time" +) + +func TestConstants(t *testing.T) { + if TCP_BUFFER_SIZE != 1024*64 { + t.Errorf("TCP_BUFFER_SIZE = %d, want %d", TCP_BUFFER_SIZE, 1024*64) + } + + if DEFAULT_PORT != "9009" { + t.Errorf("DEFAULT_PORT = %s, want %s", DEFAULT_PORT, "9009") + } + + if DEFAULT_PASSPHRASE != "pass123" { + t.Errorf("DEFAULT_PASSPHRASE = %s, want %s", DEFAULT_PASSPHRASE, "pass123") + } +} + +func TestPublicDNSServers(t *testing.T) { + if len(publicDNS) == 0 { + t.Error("publicDNS list should not be empty") + } + + // Check that we have both IPv4 and IPv6 servers + hasIPv4 := false + hasIPv6 := false + + for _, dns := range publicDNS { + if strings.Contains(dns, "[") { + hasIPv6 = true + } else { + hasIPv4 = true + } + } + + if !hasIPv4 { + t.Error("publicDNS should contain IPv4 servers") + } + + if !hasIPv6 { + t.Error("publicDNS should contain IPv6 servers") + } + + // Verify known DNS servers are present + expectedServers := []string{ + "1.1.1.1", // Cloudflare + "8.8.8.8", // Google + "9.9.9.9", // Quad9 + "208.67.220.220", // OpenDNS + } + + for _, expected := range expectedServers { + found := false + for _, dns := range publicDNS { + if dns == expected { + found = true + break + } + } + if !found { + t.Errorf("Expected DNS server %s not found in publicDNS", expected) + } + } +} + +func TestLocalLookupIP(t *testing.T) { + tests := []struct { + name string + address string + wantErr bool + }{ + { + name: "localhost", + address: "localhost", + wantErr: false, + }, + { + name: "invalid hostname", + address: "this-hostname-should-not-exist-12345", + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ip, err := localLookupIP(tt.address) + + if (err != nil) != tt.wantErr { + t.Errorf("localLookupIP() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if !tt.wantErr && ip == "" { + t.Error("localLookupIP() returned empty IP for valid hostname") + } + + if !tt.wantErr { + // Verify it's a valid IP address + if net.ParseIP(ip) == nil { + t.Errorf("localLookupIP() returned invalid IP: %s", ip) + } + } + }) + } +} + +func TestRemoteLookupIPTimeout(t *testing.T) { + // Test with an invalid DNS server that should timeout + start := time.Now() + _, err := remoteLookupIP("example.com", "192.0.2.1") + duration := time.Since(start) + + // Should timeout within reasonable time (we set 500ms timeout) + if duration > time.Second { + t.Errorf("remoteLookupIP took too long: %v", duration) + } + + if err == nil { + t.Error("remoteLookupIP should have failed with invalid DNS server") + } +} + +func TestLookupFunction(t *testing.T) { + // Test the main lookup function + tests := []struct { + name string + address string + wantErr bool + }{ + { + name: "localhost", + address: "localhost", + wantErr: false, + }, + { + name: "invalid hostname", + address: "this-hostname-should-definitely-not-exist-98765", + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ip, err := lookup(tt.address) + + if (err != nil) != tt.wantErr { + t.Errorf("lookup() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if !tt.wantErr && ip == "" { + t.Error("lookup() returned empty IP for valid hostname") + } + + if !tt.wantErr { + // Verify it's a valid IP address + if net.ParseIP(ip) == nil { + t.Errorf("lookup() returned invalid IP: %s", ip) + } + } + }) + } +} + +func TestGetConfigFile(t *testing.T) { + fname, err := getConfigFile(false) + if err != nil { + t.Skip("Could not get config directory") + } + + if !strings.HasSuffix(fname, "internal-dns") { + t.Errorf("Expected config file to end with 'internal-dns', got %s", fname) + } +}