// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package command import ( "fmt" "strings" "testing" "github.com/hashicorp/nomad/api" "github.com/hashicorp/nomad/ci" "github.com/mitchellh/cli" "github.com/posener/complete" "github.com/stretchr/testify/require" ) func TestVarGetCommand_Implements(t *testing.T) { ci.Parallel(t) var _ cli.Command = &VarGetCommand{} } func TestVarGetCommand_Fails(t *testing.T) { ci.Parallel(t) t.Run("bad_args", func(t *testing.T) { ci.Parallel(t) ui := cli.NewMockUi() cmd := &VarGetCommand{Meta: Meta{Ui: ui}} code := cmd.Run([]string{"some", "bad", "args"}) out := ui.ErrorWriter.String() require.Equal(t, 1, code, "expected exit code 1, got: %d") require.Contains(t, out, commandErrorText(cmd), "expected help output, got: %s", out) }) t.Run("bad_address", func(t *testing.T) { ci.Parallel(t) ui := cli.NewMockUi() cmd := &VarGetCommand{Meta: Meta{Ui: ui}} code := cmd.Run([]string{"-address=nope", "foo"}) out := ui.ErrorWriter.String() require.Equal(t, 1, code, "expected exit code 1, got: %d") require.Contains(t, ui.ErrorWriter.String(), "retrieving variable", "connection error, got: %s", out) require.Zero(t, ui.OutputWriter.String()) }) t.Run("missing_template", func(t *testing.T) { ci.Parallel(t) ui := cli.NewMockUi() cmd := &VarGetCommand{Meta: Meta{Ui: ui}} code := cmd.Run([]string{`-out=go-template`, "foo"}) out := strings.TrimSpace(ui.ErrorWriter.String()) require.Equal(t, 1, code, "expected exit code 1, got: %d", code) require.Equal(t, errMissingTemplate+"\n"+commandErrorText(cmd), out) require.Zero(t, ui.OutputWriter.String()) }) t.Run("unexpected_template", func(t *testing.T) { ci.Parallel(t) ui := cli.NewMockUi() cmd := &VarGetCommand{Meta: Meta{Ui: ui}} code := cmd.Run([]string{`-out=json`, `-template="bad"`, "foo"}) out := strings.TrimSpace(ui.ErrorWriter.String()) require.Equal(t, 1, code, "expected exit code 1, got: %d", code) require.Equal(t, errUnexpectedTemplate+"\n"+commandErrorText(cmd), out) require.Zero(t, ui.OutputWriter.String()) }) } func TestVarGetCommand(t *testing.T) { ci.Parallel(t) // Create a server srv, client, url := testServer(t, true, nil) defer srv.Shutdown() testCases := []struct { name string format string template string expected string testPath string // defaulted to "test/var" in code; used for not-found exitCode int isError bool }{ { name: "json", format: "json", }, { name: "table", format: "table", }, { name: "go-template", format: "go-template", template: `{{.Namespace}}.{{.Path}}`, expected: "TestVarGetCommand-2-go-template.test/var", }, { name: "not-found", format: "json", expected: errVariableNotFound, testPath: "not-found", isError: true, exitCode: 1, }, } for i, tc := range testCases { t.Run(fmt.Sprintf("%v_%s", i, tc.name), func(t *testing.T) { tc := tc ci.Parallel(t) var err error // Create a namespace for the test case testNS := strings.Map(validNS, t.Name()) _, err = client.Namespaces().Register(&api.Namespace{Name: testNS}, nil) require.NoError(t, err) t.Cleanup(func() { _, _ = client.Namespaces().Delete(testNS, nil) }) // Create a var to get sv := testVariable() sv.Namespace = testNS sv, _, err = client.Variables().Create(sv, nil) require.NoError(t, err) t.Cleanup(func() { _, _ = client.Variables().Delete(sv.Path, nil) }) // Build and run the command ui := cli.NewMockUi() cmd := &VarGetCommand{Meta: Meta{Ui: ui}} args := []string{ "-address=" + url, "-namespace=" + testNS, "-out=" + tc.format, } if tc.template != "" { args = append(args, "-template="+tc.template) } args = append(args, sv.Path) if tc.testPath != "" { // replace path with test case override args[len(args)-1] = tc.testPath } code := cmd.Run(args) // Check the output require.Equal(t, tc.exitCode, code, "expected exit %v, got: %d; %v", tc.exitCode, code, ui.ErrorWriter.String()) if tc.isError { require.Equal(t, tc.expected, strings.TrimSpace(ui.ErrorWriter.String())) return } switch tc.format { case "json": require.Equal(t, sv.AsPrettyJSON(), strings.TrimSpace(ui.OutputWriter.String())) case "table": out := ui.OutputWriter.String() outs := strings.Split(out, "\n") require.Len(t, outs, 9) require.Equal(t, "Namespace = "+testNS, outs[0]) require.Equal(t, "Path = test/var", outs[1]) case "go-template": require.Equal(t, tc.expected, strings.TrimSpace(ui.OutputWriter.String())) default: t.Fatalf("invalid format: %q", tc.format) } }) } t.Run("Autocomplete", func(t *testing.T) { ci.Parallel(t) ui := cli.NewMockUi() cmd := &VarGetCommand{Meta: Meta{Ui: ui, flagAddress: url}} // Create a var testNS := strings.Map(validNS, t.Name()) _, err := client.Namespaces().Register(&api.Namespace{Name: testNS}, nil) require.NoError(t, err) t.Cleanup(func() { _, _ = client.Namespaces().Delete(testNS, nil) }) sv := testVariable() sv.Path = "special/variable" sv.Namespace = testNS sv, _, err = client.Variables().Create(sv, nil) require.NoError(t, err) t.Cleanup(func() { _, _ = client.Variables().Delete(sv.Path, nil) }) args := complete.Args{Last: "s"} predictor := cmd.AutocompleteArgs() res := predictor.Predict(args) require.Equal(t, 1, len(res)) require.Equal(t, sv.Path, res[0]) }) } func validNS(r rune) rune { if r == '/' || r == '_' { return '-' } return r }