Skip to content

Commit

Permalink
Merge pull request #11 from UiPath/feature/main-test
Browse files Browse the repository at this point in the history
Add test to validate main.go
  • Loading branch information
thschmitt authored Jan 12, 2023
2 parents fcb6872 + 108b042 commit f9bd67e
Show file tree
Hide file tree
Showing 8 changed files with 268 additions and 62 deletions.
9 changes: 5 additions & 4 deletions commandline/command_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,13 +201,14 @@ func (b CommandBuilder) createOperationCommand(definition parser.Definition, ope
reader, writer := io.Pipe()
go func(reader *io.PipeReader) {
defer wg.Done()
defer reader.Close()
io.Copy(b.StdOut, reader)
}(reader)

go func(context executor.ExecutionContext, output io.WriteCloser) {
go func(context executor.ExecutionContext, writer *io.PipeWriter) {
defer wg.Done()
defer output.Close()
err = b.executeCommand(context, output)
defer writer.Close()
err = b.executeCommand(context, writer)
}(*executionContext, writer)

wg.Wait()
Expand Down Expand Up @@ -335,7 +336,7 @@ func (b CommandBuilder) createConfigCommand() *cli.Command {
return &cli.Command{
Name: "config",
Description: "Interactive command to configure the CLI",
Hidden: true,
Hidden: false,
Flags: flags,
Action: func(context *cli.Context) error {
auth := context.String(authFlagName)
Expand Down
56 changes: 41 additions & 15 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,23 @@ func readDefinition(path string) (*commandline.DefinitionData, error) {
return commandline.NewDefinitionData(name, data), nil
}

func readDefinitions() ([]commandline.DefinitionData, error) {
func definitionsPath() (string, error) {
path := os.Getenv("UIPATHCLI_DEFINITIONS_PATH")
if path != "" {
return path, nil
}
currentDirectory, err := os.Executable()
definitionsDirectory := filepath.Join(filepath.Dir(currentDirectory), DefinitionsDirectory)
if err != nil {
return nil, fmt.Errorf("Error reading definition files from folder '%s': %v", definitionsDirectory, err)
return "", fmt.Errorf("Error reading definition files from folder '%s': %v", definitionsDirectory, err)
}
return definitionsDirectory, nil
}

func readDefinitions() ([]commandline.DefinitionData, error) {
definitionsDirectory, err := definitionsPath()
if err != nil {
return nil, err
}
files, err := os.ReadDir(definitionsDirectory)
if err != nil {
Expand All @@ -53,16 +65,24 @@ func readDefinitions() ([]commandline.DefinitionData, error) {
return result, nil
}

func readConfiguration() (string, []byte, error) {
func configurationFilePath() (string, error) {
filename := os.Getenv("UIPATHCLI_CONFIGURATION_PATH")
if filename != "" {
return filename, nil
}
homeDir, err := os.UserHomeDir()
if err != nil {
return "", nil, fmt.Errorf("Error reading configuration file: %v", err)
}
filename := os.Getenv("UIPATHCLI_CONFIGURATION_PATH")
if filename == "" {
filename = filepath.Join(homeDir, ".uipathcli", "config")
return "", fmt.Errorf("Error reading configuration file: %v", err)
}
filename = filepath.Join(homeDir, ".uipathcli", "config")
return filename, nil
}

func readConfiguration() (string, []byte, error) {
filename, err := configurationFilePath()
if err != nil {
return "", nil, err
}
data, err := os.ReadFile(filename)
if err != nil && errors.Is(err, os.ErrNotExist) {
return filename, []byte{}, nil
Expand All @@ -73,17 +93,24 @@ func readConfiguration() (string, []byte, error) {
return filename, data, nil
}

func readPlugins() (*config.PluginConfig, error) {
func pluginsFilePath() (string, error) {
filename := os.Getenv("UIPATHCLI_PLUGINS_PATH")
if filename != "" {
return filename, nil
}
homeDir, err := os.UserHomeDir()
if err != nil {
return nil, fmt.Errorf("Error reading plugins file: %v", err)
return "", fmt.Errorf("Error reading plugins file: %v", err)
}
filename = filepath.Join(homeDir, ".uipathcli", "plugins")
return filename, nil
}

filename := os.Getenv("UIPATHCLI_PLUGINS_PATH")
if filename == "" {
filename = filepath.Join(homeDir, ".uipathcli", "plugins")
func readPlugins() (*config.PluginConfig, error) {
filename, err := pluginsFilePath()
if err != nil {
return nil, err
}

data, err := os.ReadFile(filename)
if err != nil && !errors.Is(err, os.ErrNotExist) {
return nil, fmt.Errorf("Error reading plugins file '%s': %v", filename, err)
Expand Down Expand Up @@ -173,5 +200,4 @@ func main() {
if err != nil {
os.Exit(1)
}
os.Exit(0)
}
163 changes: 163 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package main

import (
"encoding/hex"
"io"
"math/rand"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
"time"
)

func init() {
rand.Seed(time.Now().UnixNano())
}

func TestMainReadsDefinitions(t *testing.T) {
config := createFile(t, ".uipathcli", "config")
definition := createFile(t, "definitions", "service-a.yaml")

t.Setenv("UIPATHCLI_CONFIGURATION_PATH", config)
t.Setenv("UIPATHCLI_DEFINITIONS_PATH", filepath.Dir(definition))

os.Args = []string{"uipathcli", "--help"}
output := captureOutput(t, func() {
main()
})

expected := `service-a`
if !strings.Contains(output, expected) {
t.Errorf("Expected %s in output, but got: %v", expected, output)
}
}

func TestMainParsesDefinition(t *testing.T) {
config := createFile(t, ".uipathcli", "config")
definition := createFile(t, "definitions", "service-a.yaml")
os.WriteFile(definition, []byte(`
paths:
/ping:
get:
summary: This is a simple get operation
operationId: ping
`), 0600)

t.Setenv("UIPATHCLI_CONFIGURATION_PATH", config)
t.Setenv("UIPATHCLI_DEFINITIONS_PATH", filepath.Dir(definition))

os.Args = []string{"uipathcli", "service-a", "--help"}
output := captureOutput(t, func() {
main()
})

expected := `ping`
if !strings.Contains(output, expected) {
t.Errorf("Expected operation name %s in output, but got: %v", expected, output)
}
expected = `This is a simple get operation`
if !strings.Contains(output, expected) {
t.Errorf("Expected description %s in output, but got: %v", expected, output)
}
}

func TestMainCallsService(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.String() == "/identity_/connect/token" {
w.WriteHeader(200)
w.Write([]byte(`{"access_token": "my-jwt-access-token", "expires_in": 3600, "token_type": "Bearer", "scope": "OR.Ping"}`))
return
}
w.WriteHeader(200)
w.Write([]byte(`{"id":1234}`))
}))
defer srv.Close()

config := createFile(t, ".uipathcli", "config")
os.WriteFile(config, []byte(`
profiles:
- name: default
uri: `+srv.URL+`
path:
organization: my-org
tenant: defaulttenant
auth:
clientId: 71b784bc-3f7b-4e5a-a731-db25bb829025
clientSecret: NGI&4b(chsHcsX^C
`), 0600)

definition := createFile(t, "definitions", "service-a.yaml")
os.WriteFile(definition, []byte(`
paths:
/ping:
get:
summary: Simple ping
operationId: ping
`), 0600)

t.Setenv("UIPATHCLI_CONFIGURATION_PATH", config)
t.Setenv("UIPATHCLI_DEFINITIONS_PATH", filepath.Dir(definition))

os.Args = []string{"uipathcli", "service-a", "ping"}
output := captureOutput(t, func() {
main()
})

expected := `{
"id": 1234
}`
if !strings.Contains(output, expected) {
t.Errorf("Expected %s in output, but got: %v", expected, output)
}
}

func captureOutput(t *testing.T, runnable func()) string {
realStdout := os.Stdout
reader, fakeStdout, err := os.Pipe()
if err != nil {
t.Fatal(err)
}
defer func() { os.Stdout = realStdout }()
os.Stdout = fakeStdout

output := make(chan []byte)
go func(reader *os.File) {
defer reader.Close()
data, err := io.ReadAll(reader)
if err != nil {
panic(err)
}
output <- data
}(reader)

runnable()

err = fakeStdout.Close()
if err != nil {
t.Fatal(err)
}
return string(<-output)
}

func createFile(t *testing.T, directory string, name string) string {
path := filepath.Join(os.TempDir(), randomDirectoryName(), directory, name)
err := os.MkdirAll(filepath.Dir(path), 0700)
if err != nil {
t.Fatal(err)
}
tempFile, err := os.CreateTemp(filepath.Dir(path), name)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { os.Remove(tempFile.Name()) })
return tempFile.Name()
}

func randomDirectoryName() string {
randBytes := make([]byte, 16)
rand.Read(randBytes)
return hex.EncodeToString(randBytes)
}
26 changes: 10 additions & 16 deletions test/autocomplete_enable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func TestEnableAutocompleteInvalidShellShowsError(t *testing.T) {
}

func TestEnableAutocompletePowershellShowsSuccessOutput(t *testing.T) {
profilePath := autoCompleteProfilePath(t)
profilePath := createFile(t)

context := NewContextBuilder().
WithDefinition("myservice", "").
Expand All @@ -38,7 +38,7 @@ Successfully enabled command completion! Restart your shell for the changes to t
}

func TestEnableAutocompletePowershellCreatesProfileFile(t *testing.T) {
profilePath := autoCompleteProfilePath(t)
profilePath := createFile(t)

context := NewContextBuilder().
WithDefinition("myservice", "").
Expand All @@ -64,7 +64,7 @@ Register-ArgumentCompleter -Native -CommandName uipathcli -ScriptBlock $uipathcl
}

func TestEnableAutocompletePowershellUpdatesExistingProfileFile(t *testing.T) {
profilePath := autoCompleteProfilePath(t)
profilePath := createFile(t)
os.WriteFile(profilePath, []byte("existing content\nshould not change"), 0644)

context := NewContextBuilder().
Expand Down Expand Up @@ -92,7 +92,7 @@ Register-ArgumentCompleter -Native -CommandName uipathcli -ScriptBlock $uipathcl
}

func TestEnableAutocompletePowershellNoChangesIfEnabledAlready(t *testing.T) {
profilePath := autoCompleteProfilePath(t)
profilePath := createFile(t)
initialContent := `
$uipathcli_auto_complete = {
param($wordToComplete, $commandAst, $cursorPosition)
Expand All @@ -118,7 +118,7 @@ Register-ArgumentCompleter -Native -CommandName uipathcli -ScriptBlock $uipathcl
}

func TestEnableAutocompletePowershellShowsAlreadyEnabledOutput(t *testing.T) {
profilePath := autoCompleteProfilePath(t)
profilePath := createFile(t)
initialContent := `
$uipathcli_auto_complete = {
param($wordToComplete, $commandAst, $cursorPosition)
Expand Down Expand Up @@ -148,7 +148,7 @@ Command completion is already enabled.
}

func TestEnableAutocompleteBashShowsSuccessOutput(t *testing.T) {
profilePath := autoCompleteProfilePath(t)
profilePath := createFile(t)

context := NewContextBuilder().
WithDefinition("myservice", "").
Expand All @@ -167,7 +167,7 @@ Successfully enabled command completion! Restart your shell for the changes to t
}

func TestEnableAutocompleteBashCreatesProfileFile(t *testing.T) {
profilePath := autoCompleteProfilePath(t)
profilePath := createFile(t)

context := NewContextBuilder().
WithDefinition("myservice", "").
Expand All @@ -194,7 +194,7 @@ complete -f -F _uipathcli_auto_complete uipathcli
}

func TestEnableAutocompleteBashUpdatesExistingProfileFile(t *testing.T) {
profilePath := autoCompleteProfilePath(t)
profilePath := createFile(t)
os.WriteFile(profilePath, []byte("\nexisting content\nshould not change\n"), 0644)

context := NewContextBuilder().
Expand Down Expand Up @@ -225,7 +225,7 @@ complete -f -F _uipathcli_auto_complete uipathcli
}

func TestEnableAutocompleteBashNoChangesIfEnabledAlready(t *testing.T) {
profilePath := autoCompleteProfilePath(t)
profilePath := createFile(t)
initialContent := `
function _uipathcli_auto_complete()
{
Expand All @@ -252,7 +252,7 @@ complete -f -F _uipathcli_auto_complete uipathcli
}

func TestEnableAutocompleteBashShowsAlreadyEnabledOutput(t *testing.T) {
profilePath := autoCompleteProfilePath(t)
profilePath := createFile(t)
initialContent := `
function _uipathcli_auto_complete()
{
Expand Down Expand Up @@ -281,9 +281,3 @@ Command completion is already enabled.
t.Errorf("Should show output that auto-complete is already enabled, got: %v", result.StdOut)
}
}

func autoCompleteProfilePath(t *testing.T) string {
tempFile, _ := os.CreateTemp("", "uipathcli-test-profile")
t.Cleanup(func() { os.Remove(tempFile.Name()) })
return tempFile.Name()
}
Loading

0 comments on commit f9bd67e

Please sign in to comment.