mirror of
https://github.com/Gu1llaum-3/sshm.git
synced 2025-10-19 01:17:20 +02:00
Track SSH connections (configured + manual) with searchable history view. Press Ctrl+H to view history, Ctrl+L to return. Add manual connections to config with 'a'. - Parse and store manual SSH connections - History TUI with search and filtering - Connection count and timestamps
278 lines
6.2 KiB
Go
278 lines
6.2 KiB
Go
package history
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
func TestParseSSHArgs(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
args []string
|
|
wantConn *ManualConnection
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "user@host",
|
|
args: []string{"user@example.com"},
|
|
wantConn: &ManualConnection{
|
|
User: "user",
|
|
Hostname: "example.com",
|
|
Port: "22",
|
|
},
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "user@host with -p port",
|
|
args: []string{"-p", "2222", "user@example.com"},
|
|
wantConn: &ManualConnection{
|
|
User: "user",
|
|
Hostname: "example.com",
|
|
Port: "2222",
|
|
},
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "user@host with -p2222 (no space)",
|
|
args: []string{"-p2222", "user@example.com"},
|
|
wantConn: &ManualConnection{
|
|
User: "user",
|
|
Hostname: "example.com",
|
|
Port: "2222",
|
|
},
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "user@host with -i identity",
|
|
args: []string{"-i", "~/.ssh/id_rsa", "user@example.com"},
|
|
wantConn: &ManualConnection{
|
|
User: "user",
|
|
Hostname: "example.com",
|
|
Port: "22",
|
|
Identity: "~/.ssh/id_rsa",
|
|
},
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "complete connection",
|
|
args: []string{"-p", "2222", "-i", "~/.ssh/id_rsa", "guillaume@127.0.0.1"},
|
|
wantConn: &ManualConnection{
|
|
User: "guillaume",
|
|
Hostname: "127.0.0.1",
|
|
Port: "2222",
|
|
Identity: "~/.ssh/id_rsa",
|
|
},
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "just hostname (no user)",
|
|
args: []string{"example.com"},
|
|
wantConn: &ManualConnection{
|
|
Hostname: "example.com",
|
|
Port: "22",
|
|
// User will be current system user, so we don't check it
|
|
},
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "config file args should return false",
|
|
args: []string{"-F", "~/.ssh/config", "host"},
|
|
wantConn: nil,
|
|
wantOk: false,
|
|
},
|
|
{
|
|
name: "empty args",
|
|
args: []string{},
|
|
wantConn: nil,
|
|
wantOk: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
gotConn, gotOk := ParseSSHArgs(tt.args)
|
|
|
|
if gotOk != tt.wantOk {
|
|
t.Errorf("ParseSSHArgs() gotOk = %v, want %v", gotOk, tt.wantOk)
|
|
return
|
|
}
|
|
|
|
if !tt.wantOk {
|
|
if gotConn != nil {
|
|
t.Errorf("ParseSSHArgs() gotConn = %v, want nil", gotConn)
|
|
}
|
|
return
|
|
}
|
|
|
|
if gotConn == nil {
|
|
t.Errorf("ParseSSHArgs() gotConn = nil, want non-nil")
|
|
return
|
|
}
|
|
|
|
if gotConn.User != tt.wantConn.User {
|
|
// Skip user check if wantConn.User is empty (current user)
|
|
if tt.wantConn.User != "" {
|
|
t.Errorf("ParseSSHArgs() User = %v, want %v", gotConn.User, tt.wantConn.User)
|
|
}
|
|
}
|
|
if gotConn.Hostname != tt.wantConn.Hostname {
|
|
t.Errorf("ParseSSHArgs() Hostname = %v, want %v", gotConn.Hostname, tt.wantConn.Hostname)
|
|
}
|
|
if gotConn.Port != tt.wantConn.Port {
|
|
t.Errorf("ParseSSHArgs() Port = %v, want %v", gotConn.Port, tt.wantConn.Port)
|
|
}
|
|
if gotConn.Identity != tt.wantConn.Identity {
|
|
t.Errorf("ParseSSHArgs() Identity = %v, want %v", gotConn.Identity, tt.wantConn.Identity)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIsManualSSHCommand(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
args []string
|
|
want bool
|
|
}{
|
|
{
|
|
name: "user@host is manual",
|
|
args: []string{"user@example.com"},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "with -p flag is manual",
|
|
args: []string{"-p", "2222", "host"},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "with -p2222 is manual",
|
|
args: []string{"-p2222", "host"},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "just hostname is not manual",
|
|
args: []string{"myhost"},
|
|
want: false,
|
|
},
|
|
{
|
|
name: "empty is not manual",
|
|
args: []string{},
|
|
want: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := IsManualSSHCommand(tt.args); got != tt.want {
|
|
t.Errorf("IsManualSSHCommand() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestManualConnectionID(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
conn ManualConnection
|
|
wantHostID string
|
|
wantUser string
|
|
wantHostname string
|
|
wantPort string
|
|
}{
|
|
{
|
|
name: "complete connection",
|
|
conn: ManualConnection{
|
|
User: "guillaume",
|
|
Hostname: "127.0.0.1",
|
|
Port: "2222",
|
|
Identity: "~/.ssh/id_rsa",
|
|
},
|
|
wantHostID: "manual:guillaume@127.0.0.1:2222",
|
|
wantUser: "guillaume",
|
|
wantHostname: "127.0.0.1",
|
|
wantPort: "2222",
|
|
},
|
|
{
|
|
name: "default port",
|
|
conn: ManualConnection{
|
|
User: "user",
|
|
Hostname: "example.com",
|
|
Port: "",
|
|
},
|
|
wantHostID: "manual:user@example.com:22",
|
|
wantUser: "user",
|
|
wantHostname: "example.com",
|
|
wantPort: "22",
|
|
},
|
|
{
|
|
name: "no user specified",
|
|
conn: ManualConnection{
|
|
Hostname: "example.com",
|
|
Port: "2222",
|
|
},
|
|
wantHostID: "manual:default@example.com:2222",
|
|
wantUser: "default",
|
|
wantHostname: "example.com",
|
|
wantPort: "2222",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Test generation
|
|
gotHostID := generateManualHostID(tt.conn)
|
|
if gotHostID != tt.wantHostID {
|
|
t.Errorf("generateManualHostID() = %v, want %v", gotHostID, tt.wantHostID)
|
|
}
|
|
|
|
// Test IsManualConnection
|
|
if !IsManualConnection(gotHostID) {
|
|
t.Errorf("IsManualConnection(%v) = false, want true", gotHostID)
|
|
}
|
|
|
|
// Test parsing
|
|
user, hostname, port, ok := ParseManualConnectionID(gotHostID)
|
|
if !ok {
|
|
t.Errorf("ParseManualConnectionID() ok = false, want true")
|
|
}
|
|
if user != tt.wantUser {
|
|
t.Errorf("ParseManualConnectionID() user = %v, want %v", user, tt.wantUser)
|
|
}
|
|
if hostname != tt.wantHostname {
|
|
t.Errorf("ParseManualConnectionID() hostname = %v, want %v", hostname, tt.wantHostname)
|
|
}
|
|
if port != tt.wantPort {
|
|
t.Errorf("ParseManualConnectionID() port = %v, want %v", port, tt.wantPort)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseManualConnectionID_Invalid(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
hostID string
|
|
}{
|
|
{
|
|
name: "not a manual connection",
|
|
hostID: "myhost",
|
|
},
|
|
{
|
|
name: "missing components",
|
|
hostID: "manual:invalid",
|
|
},
|
|
{
|
|
name: "no @ sign",
|
|
hostID: "manual:hostname:22",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
_, _, _, ok := ParseManualConnectionID(tt.hostID)
|
|
if ok {
|
|
t.Errorf("ParseManualConnectionID() ok = true, want false for invalid input")
|
|
}
|
|
})
|
|
}
|
|
}
|