opentofu/main_test.go
James Bardin 270ab97418 don't error when processing autocomplete commands
The shell autocomplete command will use the binary name as the first
argument which does not show up under the list of subcommands.
2021-03-31 13:28:08 -04:00

312 lines
6.3 KiB
Go

package main
import (
"fmt"
"os"
"reflect"
"testing"
"github.com/mitchellh/cli"
)
func TestMain_cliArgsFromEnv(t *testing.T) {
// Set up the state. This test really messes with the environment and
// global state so we set things up to be restored.
// Restore original CLI args
oldArgs := os.Args
defer func() { os.Args = oldArgs }()
// Set up test command and restore that
Commands = make(map[string]cli.CommandFactory)
defer func() {
Commands = nil
}()
testCommandName := "unit-test-cli-args"
testCommand := &testCommandCLI{}
Commands[testCommandName] = func() (cli.Command, error) {
return testCommand, nil
}
cases := []struct {
Name string
Args []string
Value string
Expected []string
Err bool
}{
{
"no env",
[]string{testCommandName, "foo", "bar"},
"",
[]string{"foo", "bar"},
false,
},
{
"both env var and CLI",
[]string{testCommandName, "foo", "bar"},
"-foo bar",
[]string{"-foo", "bar", "foo", "bar"},
false,
},
{
"only env var",
[]string{testCommandName},
"-foo bar",
[]string{"-foo", "bar"},
false,
},
{
"cli string has blank values",
[]string{testCommandName, "bar", "", "baz"},
"-foo bar",
[]string{"-foo", "bar", "bar", "", "baz"},
false,
},
{
"cli string has blank values before the command",
[]string{"", testCommandName, "bar"},
"-foo bar",
[]string{"-foo", "bar", "bar"},
false,
},
{
// this should fail gracefully, this is just testing
// that we don't panic with our slice arithmetic
"no command",
[]string{},
"-foo bar",
nil,
true,
},
{
"single quoted strings",
[]string{testCommandName, "foo"},
"-foo 'bar baz'",
[]string{"-foo", "bar baz", "foo"},
false,
},
{
"double quoted strings",
[]string{testCommandName, "foo"},
`-foo "bar baz"`,
[]string{"-foo", "bar baz", "foo"},
false,
},
{
"double quoted single quoted strings",
[]string{testCommandName, "foo"},
`-foo "'bar baz'"`,
[]string{"-foo", "'bar baz'", "foo"},
false,
},
}
for i, tc := range cases {
t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
os.Unsetenv(EnvCLI)
defer os.Unsetenv(EnvCLI)
// Set the env var value
if tc.Value != "" {
if err := os.Setenv(EnvCLI, tc.Value); err != nil {
t.Fatalf("err: %s", err)
}
}
// Set up the args
args := make([]string, len(tc.Args)+1)
args[0] = oldArgs[0] // process name
copy(args[1:], tc.Args)
// Run it!
os.Args = args
testCommand.Args = nil
exit := wrappedMain()
if (exit != 0) != tc.Err {
t.Fatalf("bad: %d", exit)
}
if tc.Err {
return
}
// Verify
if !reflect.DeepEqual(testCommand.Args, tc.Expected) {
t.Fatalf("bad: %#v", testCommand.Args)
}
})
}
}
// This test just has more options than the test above. Use this for
// more control over behavior at the expense of more complex test structures.
func TestMain_cliArgsFromEnvAdvanced(t *testing.T) {
// Restore original CLI args
oldArgs := os.Args
defer func() { os.Args = oldArgs }()
// Set up test command and restore that
Commands = make(map[string]cli.CommandFactory)
defer func() {
Commands = nil
}()
cases := []struct {
Name string
Command string
EnvVar string
Args []string
Value string
Expected []string
Err bool
}{
{
"targeted to another command",
"command",
EnvCLI + "_foo",
[]string{"command", "foo", "bar"},
"-flag",
[]string{"foo", "bar"},
false,
},
{
"targeted to this command",
"command",
EnvCLI + "_command",
[]string{"command", "foo", "bar"},
"-flag",
[]string{"-flag", "foo", "bar"},
false,
},
{
"targeted to a command with a hyphen",
"command-name",
EnvCLI + "_command_name",
[]string{"command-name", "foo", "bar"},
"-flag",
[]string{"-flag", "foo", "bar"},
false,
},
{
"targeted to a command with a space",
"command name",
EnvCLI + "_command_name",
[]string{"command", "name", "foo", "bar"},
"-flag",
[]string{"-flag", "foo", "bar"},
false,
},
}
for i, tc := range cases {
t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
// Set up test command and restore that
testCommandName := tc.Command
testCommand := &testCommandCLI{}
defer func() { delete(Commands, testCommandName) }()
Commands[testCommandName] = func() (cli.Command, error) {
return testCommand, nil
}
os.Unsetenv(tc.EnvVar)
defer os.Unsetenv(tc.EnvVar)
// Set the env var value
if tc.Value != "" {
if err := os.Setenv(tc.EnvVar, tc.Value); err != nil {
t.Fatalf("err: %s", err)
}
}
// Set up the args
args := make([]string, len(tc.Args)+1)
args[0] = oldArgs[0] // process name
copy(args[1:], tc.Args)
// Run it!
os.Args = args
testCommand.Args = nil
exit := wrappedMain()
if (exit != 0) != tc.Err {
t.Fatalf("unexpected exit status %d; want 0", exit)
}
if tc.Err {
return
}
// Verify
if !reflect.DeepEqual(testCommand.Args, tc.Expected) {
t.Fatalf("bad: %#v", testCommand.Args)
}
})
}
}
// verify that we output valid autocomplete results
func TestMain_autoComplete(t *testing.T) {
// Restore original CLI args
oldArgs := os.Args
defer func() { os.Args = oldArgs }()
// Set up test command and restore that
Commands = make(map[string]cli.CommandFactory)
defer func() {
Commands = nil
}()
// Set up test command and restore that
Commands["foo"] = func() (cli.Command, error) {
return &testCommandCLI{}, nil
}
os.Setenv("COMP_LINE", "terraform versio")
defer os.Unsetenv("COMP_LINE")
// Run it!
os.Args = []string{"terraform", "terraform", "versio"}
exit := wrappedMain()
if exit != 0 {
t.Fatalf("unexpected exit status %d; want 0", exit)
}
}
type testCommandCLI struct {
Args []string
}
func (c *testCommandCLI) Run(args []string) int {
c.Args = args
return 0
}
func (c *testCommandCLI) Synopsis() string { return "" }
func (c *testCommandCLI) Help() string { return "" }
func TestWarnOutput(t *testing.T) {
mock := cli.NewMockUi()
wrapped := &ui{mock}
wrapped.Warn("WARNING")
stderr := mock.ErrorWriter.String()
stdout := mock.OutputWriter.String()
if stderr != "" {
t.Fatalf("unexpected stderr: %q", stderr)
}
if stdout != "WARNING\n" {
t.Fatalf("unexpected stdout: %q\n", stdout)
}
}