Commit 46d6b06

Karn Wong <[email protected]>
2026-01-31 08:11:37
tests: init
1 parent 178c4fb
internal/generate/key_test.go
@@ -0,0 +1,49 @@
+package generate
+
+import (
+	"encoding/base64"
+	"testing"
+)
+
+func TestGenerateKey(t *testing.T) {
+	key, err := generateKey(48)
+	if err != nil {
+		t.Fatalf("generateKey() returned error: %v", err)
+	}
+
+	if len(key) == 0 {
+		t.Error("generateKey() returned empty key")
+	}
+
+	// Verify it's valid base64
+	_, err = base64.URLEncoding.DecodeString(key)
+	if err != nil {
+		t.Errorf("generateKey() returned invalid base64: %v", err)
+	}
+}
+
+func TestGenerateKeyDifferentSizes(t *testing.T) {
+	sizes := []int{16, 32, 48, 64}
+
+	for _, size := range sizes {
+		t.Run(string(rune(size)), func(t *testing.T) {
+			key, err := generateKey(size)
+			if err != nil {
+				t.Fatalf("generateKey(%d) returned error: %v", size, err)
+			}
+
+			if len(key) == 0 {
+				t.Errorf("generateKey(%d) returned empty key", size)
+			}
+		})
+	}
+}
+
+func TestKey(t *testing.T) {
+	// Test that Key() doesn't panic and returns error properly
+	err := Key()
+	// May fail due to clipboard, but should return error not panic
+	if err != nil {
+		t.Logf("Key() returned error (expected in test environment): %v", err)
+	}
+}
internal/generate/passphrase_test.go
@@ -0,0 +1,37 @@
+package generate
+
+import (
+	"strings"
+	"testing"
+)
+
+func TestGeneratePassphrase(t *testing.T) {
+	passphrase, err := generatePassphrase()
+	if err != nil {
+		t.Fatalf("generatePassphrase() returned error: %v", err)
+	}
+
+	if len(passphrase) == 0 {
+		t.Error("generatePassphrase() returned empty passphrase")
+	}
+
+	// Should contain dashes separating words
+	if !strings.Contains(passphrase, "-") {
+		t.Error("generatePassphrase() should contain dashes between words")
+	}
+
+	// Should have 6 words (5 dashes)
+	words := strings.Split(passphrase, "-")
+	if len(words) != 6 {
+		t.Errorf("generatePassphrase() returned %d words, expected 6", len(words))
+	}
+}
+
+func TestPassphrase(t *testing.T) {
+	// Test that Passphrase() doesn't panic and returns error properly
+	err := Passphrase()
+	// May fail due to clipboard, but should return error not panic
+	if err != nil {
+		t.Logf("Passphrase() returned error (expected in test environment): %v", err)
+	}
+}
internal/generate/password_test.go
@@ -0,0 +1,29 @@
+package generate
+
+import (
+	"testing"
+)
+
+func TestGeneratePassword(t *testing.T) {
+	password, err := generatePassword()
+	if err != nil {
+		t.Fatalf("generatePassword() returned error: %v", err)
+	}
+
+	if len(password) == 0 {
+		t.Error("generatePassword() returned empty password")
+	}
+
+	if len(password) != 32 {
+		t.Errorf("generatePassword() returned password of length %d, expected 32", len(password))
+	}
+}
+
+func TestPassword(t *testing.T) {
+	// Test that Password() doesn't panic and returns error properly
+	err := Password()
+	// May fail due to clipboard, but should return error not panic
+	if err != nil {
+		t.Logf("Password() returned error (expected in test environment): %v", err)
+	}
+}
internal/generate/qrcode_test.go
@@ -0,0 +1,43 @@
+package generate
+
+import (
+	"testing"
+)
+
+func TestGenerateQRCode(t *testing.T) {
+	tests := []struct {
+		name    string
+		url     string
+		wantErr bool
+	}{
+		{
+			name:    "valid URL",
+			url:     "https://example.com",
+			wantErr: false,
+		},
+		{
+			name:    "empty URL",
+			url:     "",
+			wantErr: true, // QR code library doesn't accept empty strings
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			png, stdout, err := generateQRCode(tt.url)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("generateQRCode() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+
+			if !tt.wantErr {
+				if len(png) == 0 {
+					t.Error("generateQRCode() returned empty PNG")
+				}
+				if len(stdout) == 0 {
+					t.Error("generateQRCode() returned empty stdout")
+				}
+			}
+		})
+	}
+}
internal/generate/ssh_key_test.go
@@ -0,0 +1,85 @@
+package generate
+
+import (
+	"os"
+	"strings"
+	"testing"
+)
+
+func TestGenerateSSHKeyEDSA(t *testing.T) {
+	publicKey, privateKey, err := generateSSHKeyEDSA()
+	if err != nil {
+		t.Fatalf("generateSSHKeyEDSA() returned error: %v", err)
+	}
+
+	if len(publicKey) == 0 {
+		t.Error("generateSSHKeyEDSA() returned empty public key")
+	}
+
+	if len(privateKey) == 0 {
+		t.Error("generateSSHKeyEDSA() returned empty private key")
+	}
+
+	// Check public key format
+	if !strings.HasPrefix(publicKey, "ssh-ed25519 ") {
+		t.Error("generateSSHKeyEDSA() public key should start with 'ssh-ed25519 '")
+	}
+
+	// Check private key format
+	if !strings.Contains(privateKey, "BEGIN OPENSSH PRIVATE KEY") {
+		t.Error("generateSSHKeyEDSA() private key should contain PEM header")
+	}
+}
+
+func TestReturnKeyPath(t *testing.T) {
+	path, err := returnKeyPath("test_key")
+	if err != nil {
+		t.Fatalf("returnKeyPath() returned error: %v", err)
+	}
+
+	if len(path) == 0 {
+		t.Error("returnKeyPath() returned empty path")
+	}
+
+	if !strings.Contains(path, "test_key") {
+		t.Error("returnKeyPath() should contain the filename")
+	}
+}
+
+func TestWriteStringToFile(t *testing.T) {
+	tmpFile := "/tmp/test_ssh_key_write"
+	defer func(name string) {
+		err := os.Remove(name)
+		if err != nil {
+			t.Error("error removing tmp file")
+		}
+	}(tmpFile)
+
+	err := writeStringToFile(tmpFile, "test content", 0600)
+	if err != nil {
+		t.Fatalf("writeStringToFile() returned error: %v", err)
+	}
+
+	// Verify file exists
+	if _, err := os.Stat(tmpFile); os.IsNotExist(err) {
+		t.Error("writeStringToFile() did not create file")
+	}
+
+	// Verify content
+	content, err := os.ReadFile(tmpFile)
+	if err != nil {
+		t.Fatalf("Failed to read test file: %v", err)
+	}
+
+	if string(content) != "test content" {
+		t.Errorf("writeStringToFile() wrote %q, want %q", string(content), "test content")
+	}
+}
+
+func TestSSHKey(t *testing.T) {
+	// Test with no args (should return error)
+	err := SSHKey([]string{})
+	if err == nil {
+		t.Error("SSHKey() with no args should return error")
+	}
+}
internal/get/iface_test.go
@@ -0,0 +1,18 @@
+package get
+
+import (
+	"testing"
+)
+
+func TestGetIface(t *testing.T) {
+	iface, err := getIface()
+	// May fail in some test environments
+	if err != nil {
+		t.Logf("getIface() returned error (acceptable in test environment): %v", err)
+		return
+	}
+
+	if iface == "" {
+		t.Error("getIface() returned empty interface name")
+	}
+}
internal/get/ip_test.go
@@ -0,0 +1,54 @@
+package get
+
+import (
+	"net"
+	"testing"
+)
+
+func TestGetInternalIP(t *testing.T) {
+	ip, err := getInternalIP()
+	// May fail in some test environments, but should not panic
+	if err != nil {
+		t.Logf("getInternalIP() returned error (acceptable in test environment): %v", err)
+		return
+	}
+
+	if ip != "" {
+		// Verify it's a valid IP
+		if net.ParseIP(ip) == nil {
+			t.Errorf("getInternalIP() returned invalid IP: %s", ip)
+		}
+	}
+}
+
+func TestGetLocalIP(t *testing.T) {
+	ip, err := getLocalIP()
+	// May fail in some test environments, but should not panic
+	if err != nil {
+		t.Logf("getLocalIP() returned error (acceptable in test environment): %v", err)
+		return
+	}
+
+	if ip != "" {
+		// Verify it's a valid IP
+		if net.ParseIP(ip) == nil {
+			t.Errorf("getLocalIP() returned invalid IP: %s", ip)
+		}
+	}
+}
+
+func TestGetPublicIP(t *testing.T) {
+	// This requires network access, so we just test it doesn't panic
+	_, err := getPublicIP()
+	if err != nil {
+		t.Logf("getPublicIP() returned error (expected without network): %v", err)
+	}
+}
+
+func TestGetIPLocation(t *testing.T) {
+	// This requires network access, so we just test it doesn't panic
+	_, err := getIPLocation("8.8.8.8")
+	if err != nil {
+		t.Logf("getIPLocation() returned error (expected without network): %v", err)
+	}
+}
internal/get/ipinfo_test.go
@@ -0,0 +1,13 @@
+package get
+
+import (
+	"testing"
+)
+
+func TestGetIPInfo(t *testing.T) {
+	// This requires network access, so we just test it doesn't panic
+	_, err := getIPInfo("8.8.8.8")
+	if err != nil {
+		t.Logf("getIPInfo() returned error (expected without network): %v", err)
+	}
+}
internal/stopwatch/stopwatch_test.go
@@ -0,0 +1,14 @@
+package stopwatch
+
+import (
+	"testing"
+)
+
+func TestStopwatch(t *testing.T) {
+	// Test that Stopwatch() doesn't panic and returns error properly
+	// This will fail in test environment without TTY but should return error
+	err := Stopwatch()
+	if err != nil {
+		t.Logf("Stopwatch() returned error (expected in test environment): %v", err)
+	}
+}
internal/timer/timer_test.go
@@ -0,0 +1,121 @@
+package timer
+
+import (
+	"testing"
+	"time"
+
+	tea "github.com/charmbracelet/bubbletea"
+)
+
+func TestNewModel(t *testing.T) {
+	m := NewModel()
+
+	if m.progress.FullColor != titleColor {
+		t.Errorf("NewModel() progress color = %v, want %v", m.progress.FullColor, titleColor)
+	}
+
+	if m.duration != 0 {
+		t.Errorf("NewModel() duration = %v, want 0", m.duration)
+	}
+
+	if !m.startTime.IsZero() {
+		t.Errorf("NewModel() startTime should be zero")
+	}
+}
+
+func TestTimerValidation(t *testing.T) {
+	tests := []struct {
+		name    string
+		args    []string
+		wantErr bool
+	}{
+		{
+			name:    "no args",
+			args:    []string{},
+			wantErr: true,
+		},
+		{
+			name:    "invalid duration",
+			args:    []string{"invalid"},
+			wantErr: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			err := Timer(tt.args)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("Timer() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
+
+func TestModelInit(t *testing.T) {
+	m := NewModel()
+	cmd := m.Init()
+
+	if cmd == nil {
+		t.Error("Init() should return a command")
+	}
+}
+
+func TestModelUpdate(t *testing.T) {
+	m := NewModel()
+	m.duration = 5 * time.Second
+	m.startTime = time.Now()
+
+	t.Run("tick message", func(t *testing.T) {
+		newModel, cmd := m.Update(tickMsg(time.Now()))
+		if cmd == nil {
+			t.Error("Update with tickMsg should return a command")
+		}
+		if newModel.(Model).quitting {
+			t.Error("Model should not be quitting after tick")
+		}
+	})
+
+	t.Run("quit key", func(t *testing.T) {
+		newModel, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'q'}})
+		if !newModel.(Model).quitting {
+			t.Error("Model should be quitting after 'q' key")
+		}
+	})
+
+	t.Run("ctrl+c", func(t *testing.T) {
+		newModel, _ := m.Update(tea.KeyMsg{Type: tea.KeyCtrlC})
+		if !newModel.(Model).quitting {
+			t.Error("Model should be quitting after ctrl+c")
+		}
+	})
+}
+
+func TestModelView(t *testing.T) {
+	m := NewModel()
+	m.duration = 5 * time.Second
+	m.startTime = time.Now()
+
+	t.Run("normal view", func(t *testing.T) {
+		view := m.View()
+		if view == "" {
+			t.Error("View() should return non-empty string when not quitting")
+		}
+	})
+
+	t.Run("quitting view", func(t *testing.T) {
+		m.quitting = true
+		view := m.View()
+		if view != "" {
+			t.Error("View() should return empty string when quitting")
+		}
+	})
+}
+
+func TestTickCmd(t *testing.T) {
+	now := time.Now()
+	msg := tickCmd(now)
+
+	if _, ok := msg.(tickMsg); !ok {
+		t.Error("tickCmd() should return tickMsg type")
+	}
+}
internal/utils/args_test.go
@@ -0,0 +1,63 @@
+package utils
+
+import (
+	"testing"
+)
+
+func TestSetURL(t *testing.T) {
+	tests := []struct {
+		name     string
+		args     []string
+		expected string
+	}{
+		{
+			name:     "valid URL from args",
+			args:     []string{"https://example.com"},
+			expected: "https://example.com",
+		},
+		{
+			name:     "empty args returns empty or exits",
+			args:     []string{},
+			expected: "", // Will exit in actual code, but we can't test that easily
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if len(tt.args) > 0 {
+				result := SetURL(tt.args)
+				if result != tt.expected {
+					t.Errorf("SetURL() = %v, want %v", result, tt.expected)
+				}
+			}
+		})
+	}
+}
+
+func TestSetIP(t *testing.T) {
+	tests := []struct {
+		name     string
+		args     []string
+		expected string
+	}{
+		{
+			name:     "valid IP from args",
+			args:     []string{"192.168.1.1"},
+			expected: "192.168.1.1",
+		},
+		{
+			name:     "empty args returns empty",
+			args:     []string{},
+			expected: "",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			result := SetIP(tt.args)
+			if result != tt.expected {
+				t.Errorf("SetIP() = %v, want %v", result, tt.expected)
+			}
+		})
+	}
+}
internal/utils/clipboard_test.go
@@ -0,0 +1,32 @@
+package utils
+
+import (
+	"testing"
+)
+
+func TestReadFromClipboard(t *testing.T) {
+	// Test that function returns without panicking
+	_, err := ReadFromClipboard()
+	// Error is acceptable if clipboard is not available
+	if err != nil {
+		t.Logf("ReadFromClipboard returned error (expected in test environment): %v", err)
+	}
+}
+
+func TestWriteToClipboard(t *testing.T) {
+	// Test that function returns without panicking
+	err := WriteToClipboard("test")
+	// Error is acceptable if clipboard is not available
+	if err != nil {
+		t.Logf("WriteToClipboard returned error (expected in test environment): %v", err)
+	}
+}
+
+func TestWriteToClipboardImage(t *testing.T) {
+	// Test with empty byte slice
+	err := WriteToClipboardImage([]byte{})
+	// Error is acceptable if clipboard is not available
+	if err != nil {
+		t.Logf("WriteToClipboardImage returned error (expected in test environment): %v", err)
+	}
+}