Commit 46d6b06
Changed files (12)
internal
stopwatch
timer
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)
+ }
+}