-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c9b3453
commit 18e326b
Showing
4 changed files
with
348 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package ssh | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
) | ||
|
||
type CommandLineOption interface{} | ||
|
||
type ( | ||
OptionPrivateKey struct{ Value string } | ||
OptionRemotePort struct{ Value string } | ||
OptionLoginName struct{ Value string } | ||
OptionAddress struct{ Value string } | ||
) | ||
|
||
func constructKeyValueOption(optionFlag, optionValue string) string { | ||
optionValue = strings.TrimSpace(optionValue) | ||
if optionValue != "" { | ||
return fmt.Sprintf(" %s %s", optionFlag, optionValue) | ||
} | ||
return "" | ||
} | ||
|
||
func addOption(sb *strings.Builder, rawParameter CommandLineOption) { | ||
var option string | ||
switch p := rawParameter.(type) { | ||
case OptionPrivateKey: | ||
option = constructKeyValueOption("-i", p.Value) | ||
case OptionRemotePort: | ||
option = constructKeyValueOption("-p", p.Value) | ||
case OptionLoginName: | ||
option = constructKeyValueOption("-l", p.Value) | ||
case OptionAddress: | ||
if p.Value != "" { | ||
option = fmt.Sprintf(" %s", p.Value) | ||
} | ||
default: | ||
return | ||
} | ||
|
||
sb.WriteString(option) | ||
} | ||
|
||
// ConstructCMD - build connect command from main app and its arguments | ||
// cmd - main executable | ||
// options - set of command line options. See Option... public variables. | ||
func ConstructCMD(cmd string, options ...CommandLineOption) string { | ||
sb := strings.Builder{} | ||
sb.WriteString(cmd) | ||
|
||
for _, argument := range options { | ||
addOption(&sb, argument) | ||
} | ||
|
||
return sb.String() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
package ssh | ||
|
||
import ( | ||
"strings" | ||
"testing" | ||
) | ||
|
||
func Test_ConstructKeyValueOption(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
optionFlag string | ||
optionValue string | ||
expectedResult string | ||
}{ | ||
{ | ||
name: "Option with value", | ||
optionFlag: "-i", | ||
optionValue: "private_key", | ||
expectedResult: " -i private_key", | ||
}, | ||
{ | ||
name: "Option with empty value", | ||
optionFlag: "-p", | ||
optionValue: "", | ||
expectedResult: "", | ||
}, | ||
{ | ||
name: "Option with space-padded value", | ||
optionFlag: "-l", | ||
optionValue: " login_name ", | ||
expectedResult: " -l login_name", | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
result := constructKeyValueOption(tt.optionFlag, tt.optionValue) | ||
|
||
if result != tt.expectedResult { | ||
t.Errorf("Expected result %s, but got %s", tt.expectedResult, result) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func Test_AddOption(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
rawParameter CommandLineOption | ||
expectedResult string | ||
}{ | ||
{ | ||
name: "OptionPrivateKey with value", | ||
rawParameter: OptionPrivateKey{Value: "private_key"}, | ||
expectedResult: " -i private_key", | ||
}, | ||
{ | ||
name: "OptionRemotePort with empty value", | ||
rawParameter: OptionRemotePort{Value: ""}, | ||
expectedResult: "", | ||
}, | ||
{ | ||
name: "OptionLoginName with value", | ||
rawParameter: OptionLoginName{Value: "login_name"}, | ||
expectedResult: " -l login_name", | ||
}, | ||
{ | ||
name: "OptionAddress with empty value", | ||
rawParameter: OptionAddress{Value: ""}, | ||
expectedResult: "", | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
var sb strings.Builder | ||
addOption(&sb, tt.rawParameter) | ||
|
||
result := sb.String() | ||
if result != tt.expectedResult { | ||
t.Errorf("Expected result %s, but got %s", tt.expectedResult, result) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func Test_ConstructCMD(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
cmd string | ||
options []CommandLineOption | ||
expectedResult string | ||
}{ | ||
{ | ||
name: "Command with Options", | ||
cmd: "ssh", | ||
options: []CommandLineOption{OptionPrivateKey{Value: "private_key"}, OptionRemotePort{Value: "22"}}, | ||
expectedResult: "ssh -i private_key -p 22", | ||
}, | ||
{ | ||
name: "Command without Options", | ||
cmd: "ls", | ||
options: []CommandLineOption{}, | ||
expectedResult: "ls", | ||
}, | ||
{ | ||
name: "Command with Address Option", | ||
cmd: "ping", | ||
options: []CommandLineOption{OptionAddress{Value: "example.com"}}, | ||
expectedResult: "ping example.com", | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
result := ConstructCMD(tt.cmd, tt.options...) | ||
|
||
if result != tt.expectedResult { | ||
t.Errorf("Expected result %s, but got %s", tt.expectedResult, result) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
package utils | ||
|
||
import ( | ||
"os/exec" | ||
"testing" | ||
|
||
"github.com/grafviktor/goto/internal/model" | ||
"github.com/grafviktor/goto/internal/utils/ssh" | ||
) | ||
|
||
func TestCheckAppInstalled(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
appName string | ||
expectedError bool | ||
}{ | ||
{ | ||
name: "Installed App", | ||
appName: "cd", // Assuming 'ls' is always installed | ||
expectedError: false, | ||
}, | ||
{ | ||
name: "Uninstalled App", | ||
appName: "nonexistentapp", | ||
expectedError: true, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
err := CheckAppInstalled(tt.appName) | ||
|
||
if tt.expectedError && err == nil { | ||
t.Errorf("Expected an error, but got nil") | ||
} | ||
|
||
if !tt.expectedError && err != nil { | ||
t.Errorf("Expected no error, but got: %v", err) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestHostModelToOptionsAdaptor(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
host model.Host | ||
expectedOptions []ssh.CommandLineOption | ||
}{ | ||
{ | ||
name: "Valid Host", | ||
host: model.Host{ | ||
Address: "example.com", | ||
LoginName: "user", | ||
RemotePort: "22", | ||
PrivateKeyPath: "/path/to/private_key", | ||
}, | ||
expectedOptions: []ssh.CommandLineOption{ | ||
ssh.OptionAddress{Value: "example.com"}, | ||
ssh.OptionLoginName{Value: "user"}, | ||
ssh.OptionRemotePort{Value: "22"}, | ||
ssh.OptionPrivateKey{Value: "/path/to/private_key"}, | ||
}, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
result := HostModelToOptionsAdaptor(tt.host) | ||
|
||
if len(result) != len(tt.expectedOptions) { | ||
t.Errorf("Expected %d options, but got %d", len(tt.expectedOptions), len(result)) | ||
} | ||
|
||
for i := range result { | ||
if result[i] != tt.expectedOptions[i] { | ||
t.Errorf("Expected option %v, but got %v", tt.expectedOptions[i], result[i]) | ||
} | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestBuildProcess(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
cmd string | ||
expectedCmd *exec.Cmd | ||
}{ | ||
{ | ||
name: "Simple Command", | ||
cmd: "cd", | ||
expectedCmd: exec.Command("cd"), | ||
}, | ||
{ | ||
name: "Command with Arguments", | ||
cmd: "echo hello", | ||
expectedCmd: exec.Command("echo", "hello"), | ||
}, | ||
{ | ||
name: "Empty Command", | ||
cmd: "", | ||
expectedCmd: nil, // Expecting nil as there is no valid command | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
result := BuildProcess(tt.cmd) | ||
|
||
if tt.expectedCmd == nil && result != nil { | ||
t.Errorf("Expected nil, but got %+v", result) | ||
} else if tt.expectedCmd != nil && result == nil { | ||
t.Errorf("Expected %+v, but got nil", tt.expectedCmd) | ||
} else if tt.expectedCmd != nil && result != nil { | ||
// Compare relevant fields of the Cmd struct | ||
if tt.expectedCmd.Path != result.Path { | ||
t.Errorf("Expected Path %s, but got %s", tt.expectedCmd.Path, result.Path) | ||
} | ||
if len(tt.expectedCmd.Args) != len(result.Args) { | ||
t.Errorf("Expected %d arguments, but got %d", len(tt.expectedCmd.Args), len(result.Args)) | ||
} | ||
for i := range tt.expectedCmd.Args { | ||
if tt.expectedCmd.Args[i] != result.Args[i] { | ||
t.Errorf("Expected argument %s, but got %s", tt.expectedCmd.Args[i], result.Args[i]) | ||
} | ||
} | ||
} | ||
}) | ||
} | ||
} |