mirror of
https://github.com/helm/helm.git
synced 2026-08-07 16:41:27 +00:00
feat(test): add some completion tests
Signed-off-by: Marc Khouzam <marc.khouzam@montreal.ca>
This commit is contained in:
88
cmd/helm/flags_test.go
Normal file
88
cmd/helm/flags_test.go
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
helmtime "helm.sh/helm/v3/pkg/time"
|
||||
)
|
||||
|
||||
func outputFlagCompletionTest(t *testing.T, cmdName string) {
|
||||
releasesMockWithStatus := func(info *release.Info, hooks ...*release.Hook) []*release.Release {
|
||||
info.LastDeployed = helmtime.Unix(1452902400, 0).UTC()
|
||||
return []*release.Release{{
|
||||
Name: "athos",
|
||||
Namespace: "default",
|
||||
Info: info,
|
||||
Chart: &chart.Chart{},
|
||||
Hooks: hooks,
|
||||
}, {
|
||||
Name: "porthos",
|
||||
Namespace: "default",
|
||||
Info: info,
|
||||
Chart: &chart.Chart{},
|
||||
Hooks: hooks,
|
||||
}, {
|
||||
Name: "aramis",
|
||||
Namespace: "default",
|
||||
Info: info,
|
||||
Chart: &chart.Chart{},
|
||||
Hooks: hooks,
|
||||
}, {
|
||||
Name: "dartagnan",
|
||||
Namespace: "gascony",
|
||||
Info: info,
|
||||
Chart: &chart.Chart{},
|
||||
Hooks: hooks,
|
||||
}}
|
||||
}
|
||||
|
||||
tests := []cmdTestCase{{
|
||||
name: "completion for output flag long and before arg",
|
||||
cmd: fmt.Sprintf("__complete %s --output ''", cmdName),
|
||||
golden: "output/output-comp.txt",
|
||||
rels: releasesMockWithStatus(&release.Info{
|
||||
Status: release.StatusDeployed,
|
||||
}),
|
||||
}, {
|
||||
name: "completion for output flag long and after arg",
|
||||
cmd: fmt.Sprintf("__complete %s aramis --output ''", cmdName),
|
||||
golden: "output/output-comp.txt",
|
||||
rels: releasesMockWithStatus(&release.Info{
|
||||
Status: release.StatusDeployed,
|
||||
}),
|
||||
}, {
|
||||
name: "completion for output flag short and before arg",
|
||||
cmd: fmt.Sprintf("__complete %s -o ''", cmdName),
|
||||
golden: "output/output-comp.txt",
|
||||
rels: releasesMockWithStatus(&release.Info{
|
||||
Status: release.StatusDeployed,
|
||||
}),
|
||||
}, {
|
||||
name: "completion for output flag short and after arg",
|
||||
cmd: fmt.Sprintf("__complete %s aramis -o ''", cmdName),
|
||||
golden: "output/output-comp.txt",
|
||||
rels: releasesMockWithStatus(&release.Info{
|
||||
Status: release.StatusDeployed,
|
||||
}),
|
||||
}}
|
||||
runTestCmd(t, tests)
|
||||
}
|
||||
@@ -41,3 +41,7 @@ func TestGetCmd(t *testing.T) {
|
||||
}}
|
||||
runTestCmd(t, tests)
|
||||
}
|
||||
|
||||
func TestGetAllRevisionCompletion(t *testing.T) {
|
||||
revisionFlagCompletionTest(t, "get all")
|
||||
}
|
||||
|
||||
@@ -36,3 +36,7 @@ func TestGetHooks(t *testing.T) {
|
||||
}}
|
||||
runTestCmd(t, tests)
|
||||
}
|
||||
|
||||
func TestGetHooksRevisionCompletion(t *testing.T) {
|
||||
revisionFlagCompletionTest(t, "get hooks")
|
||||
}
|
||||
|
||||
@@ -36,3 +36,7 @@ func TestGetManifest(t *testing.T) {
|
||||
}}
|
||||
runTestCmd(t, tests)
|
||||
}
|
||||
|
||||
func TestGetManifestRevisionCompletion(t *testing.T) {
|
||||
revisionFlagCompletionTest(t, "get manifest")
|
||||
}
|
||||
|
||||
@@ -36,3 +36,7 @@ func TestGetNotesCmd(t *testing.T) {
|
||||
}}
|
||||
runTestCmd(t, tests)
|
||||
}
|
||||
|
||||
func TestGetNotesRevisionCompletion(t *testing.T) {
|
||||
revisionFlagCompletionTest(t, "get notes")
|
||||
}
|
||||
|
||||
@@ -52,3 +52,11 @@ func TestGetValuesCmd(t *testing.T) {
|
||||
}}
|
||||
runTestCmd(t, tests)
|
||||
}
|
||||
|
||||
func TestGetValuesRevisionCompletion(t *testing.T) {
|
||||
revisionFlagCompletionTest(t, "get values")
|
||||
}
|
||||
|
||||
func TestGetValuesOutputCompletion(t *testing.T) {
|
||||
outputFlagCompletionTest(t, "get values")
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -96,11 +97,39 @@ func storageFixture() *storage.Storage {
|
||||
return storage.Init(driver.NewMemory())
|
||||
}
|
||||
|
||||
// go-shellwords does not handle empty arguments properly
|
||||
// https://github.com/mattn/go-shellwords/issues/5#issuecomment-573431458
|
||||
//
|
||||
// This method checks if the last argument was an empty one,
|
||||
// and if go-shellwords missed it, we add it ourselves.
|
||||
//
|
||||
// This is important for completion tests as completion often
|
||||
// uses an empty last parameter.
|
||||
func checkLastEmpty(in string, out []string) []string {
|
||||
lastIndex := len(in) - 1
|
||||
|
||||
if lastIndex >= 1 && (in[lastIndex] == '"' && in[lastIndex-1] == '"' ||
|
||||
in[lastIndex] == '\'' && in[lastIndex-1] == '\'') {
|
||||
// The last parameter of 'in' was empty ("" or ''), let's make sure it was detected.
|
||||
if len(out) > 0 && out[len(out)-1] != "" {
|
||||
// Bug from go-shellwords:
|
||||
// 'out' does not have the empty parameter. We add it ourselves as a workaround.
|
||||
out = append(out, "")
|
||||
} else {
|
||||
fmt.Println("WARNING: go-shellwords seems to have been fixed. This workaround can be removed.")
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func executeActionCommandC(store *storage.Storage, cmd string) (*cobra.Command, string, error) {
|
||||
args, err := shellwords.Parse(cmd)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
// Workaround the bug in shellwords
|
||||
args = checkLastEmpty(cmd, args)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
actionConfig := &action.Configuration{
|
||||
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
@@ -68,3 +69,42 @@ func TestHistoryCmd(t *testing.T) {
|
||||
}}
|
||||
runTestCmd(t, tests)
|
||||
}
|
||||
|
||||
func TestHistoryOutputCompletion(t *testing.T) {
|
||||
outputFlagCompletionTest(t, "history")
|
||||
}
|
||||
|
||||
func revisionFlagCompletionTest(t *testing.T, cmdName string) {
|
||||
mk := func(name string, vers int, status release.Status) *release.Release {
|
||||
return release.Mock(&release.MockReleaseOptions{
|
||||
Name: name,
|
||||
Version: vers,
|
||||
Status: status,
|
||||
})
|
||||
}
|
||||
|
||||
releases := []*release.Release{
|
||||
mk("musketeers", 11, release.StatusDeployed),
|
||||
mk("musketeers", 10, release.StatusSuperseded),
|
||||
mk("musketeers", 9, release.StatusSuperseded),
|
||||
mk("musketeers", 8, release.StatusSuperseded),
|
||||
}
|
||||
|
||||
tests := []cmdTestCase{{
|
||||
name: "completion for revision flag",
|
||||
cmd: fmt.Sprintf("__complete %s musketeers --revision ''", cmdName),
|
||||
rels: releases,
|
||||
golden: "output/revision-comp.txt",
|
||||
}, {
|
||||
name: "completion for revision flag with too few args",
|
||||
cmd: fmt.Sprintf("__complete %s --revision ''", cmdName),
|
||||
rels: releases,
|
||||
golden: "output/revision-wrong-args-comp.txt",
|
||||
}, {
|
||||
name: "completion for revision flag with too many args",
|
||||
cmd: fmt.Sprintf("__complete %s three musketeers --revision ''", cmdName),
|
||||
rels: releases,
|
||||
golden: "output/revision-wrong-args-comp.txt",
|
||||
}}
|
||||
runTestCmd(t, tests)
|
||||
}
|
||||
|
||||
@@ -193,3 +193,7 @@ func TestInstall(t *testing.T) {
|
||||
|
||||
runTestActionCmd(t, tests)
|
||||
}
|
||||
|
||||
func TestInstallOutputCompletion(t *testing.T) {
|
||||
outputFlagCompletionTest(t, "install")
|
||||
}
|
||||
|
||||
@@ -206,3 +206,7 @@ func TestListCmd(t *testing.T) {
|
||||
}}
|
||||
runTestCmd(t, tests)
|
||||
}
|
||||
|
||||
func TestListOutputCompletion(t *testing.T) {
|
||||
outputFlagCompletionTest(t, "list")
|
||||
}
|
||||
|
||||
25
cmd/helm/repo_list_test.go
Normal file
25
cmd/helm/repo_list_test.go
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRepoListOutputCompletion(t *testing.T) {
|
||||
outputFlagCompletionTest(t, "repo list")
|
||||
}
|
||||
@@ -232,7 +232,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
|
||||
newDocsCmd(out),
|
||||
|
||||
// Setup the special hidden __complete command to allow for dynamic auto-completion
|
||||
completion.NewCompleteCmd(settings),
|
||||
completion.NewCompleteCmd(settings, out),
|
||||
)
|
||||
|
||||
// Add annotation to flags for which we can generate completion choices
|
||||
|
||||
@@ -49,5 +49,8 @@ func TestSearchHubCmd(t *testing.T) {
|
||||
t.Log(out)
|
||||
t.Log(expected)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSearchHubOutputCompletion(t *testing.T) {
|
||||
outputFlagCompletionTest(t, "search hub")
|
||||
}
|
||||
|
||||
@@ -83,3 +83,7 @@ func TestSearchRepositoriesCmd(t *testing.T) {
|
||||
}
|
||||
runTestCmd(t, tests)
|
||||
}
|
||||
|
||||
func TestSearchRepoOutputCompletion(t *testing.T) {
|
||||
outputFlagCompletionTest(t, "search repo")
|
||||
}
|
||||
|
||||
@@ -108,3 +108,66 @@ func mustParseTime(t string) helmtime.Time {
|
||||
res, _ := helmtime.Parse(time.RFC3339, t)
|
||||
return res
|
||||
}
|
||||
|
||||
func TestStatusCompletion(t *testing.T) {
|
||||
releasesMockWithStatus := func(info *release.Info, hooks ...*release.Hook) []*release.Release {
|
||||
info.LastDeployed = helmtime.Unix(1452902400, 0).UTC()
|
||||
return []*release.Release{{
|
||||
Name: "athos",
|
||||
Namespace: "default",
|
||||
Info: info,
|
||||
Chart: &chart.Chart{},
|
||||
Hooks: hooks,
|
||||
}, {
|
||||
Name: "porthos",
|
||||
Namespace: "default",
|
||||
Info: info,
|
||||
Chart: &chart.Chart{},
|
||||
Hooks: hooks,
|
||||
}, {
|
||||
Name: "aramis",
|
||||
Namespace: "default",
|
||||
Info: info,
|
||||
Chart: &chart.Chart{},
|
||||
Hooks: hooks,
|
||||
}, {
|
||||
Name: "dartagnan",
|
||||
Namespace: "gascony",
|
||||
Info: info,
|
||||
Chart: &chart.Chart{},
|
||||
Hooks: hooks,
|
||||
}}
|
||||
}
|
||||
|
||||
tests := []cmdTestCase{{
|
||||
name: "completion for status",
|
||||
cmd: "__complete status a",
|
||||
golden: "output/status-comp.txt",
|
||||
rels: releasesMockWithStatus(&release.Info{
|
||||
Status: release.StatusDeployed,
|
||||
}),
|
||||
}, {
|
||||
name: "completion for status with too many arguments",
|
||||
cmd: "__complete status dartagnan ''",
|
||||
golden: "output/status-wrong-args-comp.txt",
|
||||
rels: releasesMockWithStatus(&release.Info{
|
||||
Status: release.StatusDeployed,
|
||||
}),
|
||||
}, {
|
||||
name: "completion for status with too many arguments",
|
||||
cmd: "__complete status --debug a",
|
||||
golden: "output/status-comp.txt",
|
||||
rels: releasesMockWithStatus(&release.Info{
|
||||
Status: release.StatusDeployed,
|
||||
}),
|
||||
}}
|
||||
runTestCmd(t, tests)
|
||||
}
|
||||
|
||||
func TestStatusRevisionCompletion(t *testing.T) {
|
||||
revisionFlagCompletionTest(t, "status")
|
||||
}
|
||||
|
||||
func TestStatusOutputCompletion(t *testing.T) {
|
||||
outputFlagCompletionTest(t, "status")
|
||||
}
|
||||
|
||||
4
cmd/helm/testdata/output/output-comp.txt
vendored
Normal file
4
cmd/helm/testdata/output/output-comp.txt
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
table
|
||||
json
|
||||
yaml
|
||||
:0
|
||||
5
cmd/helm/testdata/output/revision-comp.txt
vendored
Normal file
5
cmd/helm/testdata/output/revision-comp.txt
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
8
|
||||
9
|
||||
10
|
||||
11
|
||||
:0
|
||||
1
cmd/helm/testdata/output/revision-wrong-args-comp.txt
vendored
Normal file
1
cmd/helm/testdata/output/revision-wrong-args-comp.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
:4
|
||||
3
cmd/helm/testdata/output/status-comp.txt
vendored
Normal file
3
cmd/helm/testdata/output/status-comp.txt
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
aramis
|
||||
athos
|
||||
:4
|
||||
1
cmd/helm/testdata/output/status-wrong-args-comp.txt
vendored
Normal file
1
cmd/helm/testdata/output/status-wrong-args-comp.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
:4
|
||||
@@ -258,3 +258,7 @@ func prepareMockRelease(releaseName string, t *testing.T) (func(n string, v int,
|
||||
|
||||
return relMock, ch, chartPath
|
||||
}
|
||||
|
||||
func TestUpgradeOutputCompletion(t *testing.T) {
|
||||
outputFlagCompletionTest(t, "upgrade")
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package completion
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
@@ -111,7 +112,7 @@ func (d BashCompDirective) string() string {
|
||||
}
|
||||
|
||||
// NewCompleteCmd add a special hidden command that an be used to request completions
|
||||
func NewCompleteCmd(settings *cli.EnvSettings) *cobra.Command {
|
||||
func NewCompleteCmd(settings *cli.EnvSettings, out io.Writer) *cobra.Command {
|
||||
debug = settings.Debug
|
||||
return &cobra.Command{
|
||||
Use: fmt.Sprintf("%s [command-line]", CompRequestCmd),
|
||||
@@ -169,14 +170,14 @@ func NewCompleteCmd(settings *cli.EnvSettings) *cobra.Command {
|
||||
completions, directive := completionFn(finalCmd, argsWoFlags, toComplete)
|
||||
for _, comp := range completions {
|
||||
// Print each possible completion to stdout for the completion script to consume.
|
||||
fmt.Println(comp)
|
||||
fmt.Fprintln(out, comp)
|
||||
}
|
||||
|
||||
// As the last printout, print the completion directive for the
|
||||
// completion script to parse.
|
||||
// The directive integer must be that last character following a single :
|
||||
// The completion script expects :directive
|
||||
fmt.Printf("\n:%d\n", directive)
|
||||
fmt.Fprintln(out, fmt.Sprintf(":%d", directive))
|
||||
|
||||
// Print some helpful info to stderr for the user to understand.
|
||||
// Output from stderr should be ignored from the completion script.
|
||||
|
||||
Reference in New Issue
Block a user