Skip to content

Commit

Permalink
Merge pull request #60 from guergabo/unit-tests
Browse files Browse the repository at this point in the history
test(unit): unit tests for switch command
  • Loading branch information
achille-roussel authored May 24, 2024
2 parents d8d484e + aa62726 commit ea4a980
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 32 deletions.
2 changes: 1 addition & 1 deletion cli/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ If the browser does not open, please visit the following URL:
}

if loginErr != nil {
failure("Authentication failed. Please contact support at support@dispatch.run")
failure(cmd, "Authentication failed. Please contact support at support@dispatch.run")
fmt.Printf("Error: %s\n", loginErr)
} else if loggedIn {
success("Authentication successful")
Expand Down
27 changes: 17 additions & 10 deletions cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,31 @@ import (
"github.com/spf13/cobra"
)

// Main is the entry point of the command line.
func Main() error {
cmd := &cobra.Command{
Version: version(),
Use: "dispatch",
Long: `Welcome to Dispatch!
var (
DispatchCmdLong = `Welcome to Dispatch!
To get started, use the login command to authenticate with Dispatch or create an account.
Documentation: https://docs.dispatch.run
Discord: https://dispatch.run/discord
Support: support@dispatch.run
`,
`
)

// Main is the entry point of the command line.
func Main() error {
cmd := &cobra.Command{
Version: version(),
Use: "dispatch",
Long: DispatchCmdLong,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
return loadEnvFromFile(DotEnvFilePath)
},
RunE: func(cmd *cobra.Command, args []string) error {
return cmd.Help()
},
}

cmd.PersistentFlags().StringVarP(&DispatchApiKeyCli, "api-key", "k", "", "Dispatch API key (env: DISPATCH_API_KEY)")
cmd.PersistentFlags().StringVarP(&DotEnvFilePath, "env-file", "", "", "Path to .env file")

Expand All @@ -38,10 +43,12 @@ Support: support@dispatch.run
Title: "Dispatch Commands:",
})

// Passing the global variables to the commands make testing in parallel possible.
cmd.AddCommand(loginCommand())
cmd.AddCommand(switchCommand())
cmd.AddCommand(runCommand())
cmd.AddCommand(switchCommand(DispatchConfigPath))
cmd.AddCommand(verificationCommand())
cmd.AddCommand(runCommand())
cmd.AddCommand(versionCommand())

return cmd.ExecuteContext(context.Background())
}
10 changes: 6 additions & 4 deletions cli/style.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package cli

import (
"fmt"
"strings"

"github.com/charmbracelet/bubbles/spinner"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/spf13/cobra"
)

var (
Expand Down Expand Up @@ -110,12 +112,12 @@ func success(msg string) {
fmt.Println(successStyle.Render(msg))
}

func failure(msg string) {
fmt.Println(failureStyle.Render(msg) + "\n")
func failure(cmd *cobra.Command, msgs ...string) {
cmd.Println(failureStyle.Render(strings.Join(msgs, " ")) + "\n")
}

func simple(msg string) {
fmt.Println(msg)
func simple(cmd *cobra.Command, msgs ...string) {
cmd.Println(strings.Join(msgs, " "))
}

func dialog(msg string, args ...interface{}) {
Expand Down
38 changes: 22 additions & 16 deletions cli/switch.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,38 @@ import (
"github.com/spf13/cobra"
)

func switchCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "switch [organization]",
Short: "Switch between organizations",
Long: `Switch between Dispatch organizations.
var (
SwitchCmdLong = `Switch between Dispatch organizations.
The switch command is used to select which organization is used
when running a Dispatch application locally.
To manage your organizations, visit the Dispatch Console: https://console.dispatch.run/`
)

To manage your organizations, visit the Dispatch Console: https://console.dispatch.run/`,
func switchCommand(configPath string) *cobra.Command {
cmd := &cobra.Command{
Use: "switch [organization]",
Short: "Switch between organizations",
Long: SwitchCmdLong,
GroupID: "management",
RunE: func(cmd *cobra.Command, args []string) error {
cfg, err := LoadConfig(DispatchConfigPath)
cfg, err := LoadConfig(configPath)
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
failure(fmt.Sprintf("Failed to load Dispatch configuration: %v", err))
failure(cmd, fmt.Sprintf("Failed to load Dispatch configuration: %v", err))
}
simple("Please run `dispatch login` to login to Dispatch.")

// User must login to create a configuration file.
simple(cmd, "Please run `dispatch login` to login to Dispatch.")
return nil
}

// List organizations if no arguments were provided.
if len(args) == 0 {
fmt.Println("Available organizations:")
simple(cmd, "Available organizations:")
for org := range cfg.Organization {
fmt.Println("-", org)
simple(cmd, "-", org)
}
return nil
}
Expand All @@ -42,18 +48,18 @@ To manage your organizations, visit the Dispatch Console: https://console.dispat
name := args[0]
_, ok := cfg.Organization[name]
if !ok {
failure(fmt.Sprintf("Organization '%s' not found", name))
failure(cmd, fmt.Sprintf("Organization '%s' not found", name))

fmt.Println("Available organizations:")
simple(cmd, "Available organizations:")
for org := range cfg.Organization {
fmt.Println("-", org)
simple(cmd, "-", org)
}
return nil
}

simple(fmt.Sprintf("Switched to organization: %v", name))
simple(cmd, fmt.Sprintf("Switched to organization: %v", name))
cfg.Active = name
return CreateConfig(DispatchConfigPath, cfg)
return CreateConfig(configPath, cfg)
},
}
return cmd
Expand Down
128 changes: 128 additions & 0 deletions cli/switch_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package cli

import (
"bytes"
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
)

type testCase struct {
name string
args []string
configExists bool
configContent string
}

type expectedOutput struct {
stdout string
stderr string
}

func TestSwitchCommand(t *testing.T) {
tcs := []struct {
in testCase
out expectedOutput
}{
{
in: testCase{
name: "Config file doesn't exist",
args: []string{"org1"},
configExists: false,
},
out: expectedOutput{
stdout: "Please run `dispatch login` to login to Dispatch.\n",
},
},
{
in: testCase{
name: "No arguments provided",
args: []string{},
configExists: true,
configContent: `
# Warning = 'THIS FILE IS GENERATED. DO NOT EDIT!'
active = 'x-s-org'
[Organizations]
[Organizations.x-s-org]
api_key = 'x'
`,
},
out: expectedOutput{
stdout: "Available organizations:\n- x-s-org\n",
},
},
{
in: testCase{
name: "Switch to non-existing organization",
args: []string{"random"},
configExists: true,
configContent: `
# Warning = 'THIS FILE IS GENERATED. DO NOT EDIT!'
active = 'x-s-org'
[Organizations]
[Organizations.x-s-org]
api_key = 'x'
`,
},
out: expectedOutput{
stdout: "Organization 'random' not found\n\nAvailable organizations:\n- x-s-org\n",
},
},
{
in: testCase{
name: "Switch to existing organization",
args: []string{"x-s-org"},
configExists: true,
configContent: `
# Warning = 'THIS FILE IS GENERATED. DO NOT EDIT!'
active = 'x-s-org'
[Organizations]
[Organizations.x-s-org]
api_key = 'x'
`,
},
out: expectedOutput{
stdout: "Switched to organization: x-s-org\n",
},
},
}

for _, tc := range tcs {
tc := tc
t.Run(tc.in.name, func(t *testing.T) {
t.Parallel()

configPath := setupConfig(t, tc.in)
stdout := &bytes.Buffer{}
stderr := &bytes.Buffer{}
cmd := switchCommand(configPath)
cmd.SetOut(stdout)
cmd.SetErr(stderr)
cmd.SetArgs(tc.in.args)

if err := cmd.Execute(); err != nil {
t.Fatalf("Received unexpected error: %v", err)
}

assert.Equal(t, tc.out.stdout, stdout.String())
assert.Equal(t, tc.out.stderr, stderr.String())
})
}
}

func setupConfig(t *testing.T, tc testCase) string {
tempDir := t.TempDir() // unique temp dir for each test and cleaned up after test finishes
configPath := filepath.Join(tempDir, "config.yaml")
if tc.configExists {
err := os.WriteFile(configPath, []byte(tc.configContent), 0600)
assert.NoError(t, err)
} else {
os.Remove(configPath)
}
return configPath
}
6 changes: 5 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ require (
github.com/charmbracelet/bubbles v0.18.0
github.com/charmbracelet/bubbletea v0.25.0
github.com/charmbracelet/lipgloss v0.9.1
github.com/joho/godotenv v1.5.1
github.com/muesli/reflow v0.3.0
github.com/nlpodyssey/gopickle v0.3.0
github.com/pelletier/go-toml/v2 v2.2.0
github.com/spf13/cobra v1.8.0
github.com/stretchr/testify v1.9.0
golang.org/x/term v0.19.0
google.golang.org/protobuf v1.33.0
)
Expand All @@ -20,18 +22,20 @@ require (
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.6 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
Expand Down

0 comments on commit ea4a980

Please sign in to comment.