From 1ba68b045378598ca5a5843659bfd8ddeb15cb03 Mon Sep 17 00:00:00 2001 From: Thomas Riccardi Date: Tue, 8 Nov 2022 16:51:45 +0100 Subject: [PATCH] feat(cli): dynamic shell completion for main resources names (rollouts, experiments, analysisrun) (#2379) * PoC dynamic shell completion for 'kubectl-argo-rollouts get rollout [TAB]' works toward #2015 use ValidArgsFunction from cobra to list candidates for completion: https://github.com/spf13/cobra/blob/main/shell_completions.md#dynamic-completion-of-nouns use v2 of GenBashCompletion from cobra: https://github.com/spf13/cobra/blob/main/shell_completions.md#bash-completion-v2 - I chose to disable descriptions for completion (as a bash user I'm not used to that), but it's enabled for fish it seems, we can enable it if desired, I have no strong opinion on it Signed-off-by: Thomas Riccardi * Factorize resource names completion functions in new util/completion Signed-off-by: Thomas Riccardi * Add dynamic auto-completion to all ROLLOUT_NAME locations in CLI ...accoding to the Cobra auto-generated CLI documentation. Signed-off-by: Thomas Riccardi * Add dynamic auto-completion to all EXPERIMENT_NAME locations in CLI ...accoding to the Cobra auto-generated CLI documentation. Signed-off-by: Thomas Riccardi * Add dynamic auto-completion to all ANALYSISRUN_NAME locations in CLI ...accoding to the Cobra auto-generated CLI documentation. Signed-off-by: Thomas Riccardi * WIP unit-test dynamic completion Signed-off-by: Thomas Riccardi * Add unit-tests for new completion package - inspired from kubectl own completion package tests - fake cmd to avoid circular import - reuse info/testdata but we could build our own data for more isolated tests if needed - test all 3 types (Rollout, Experiment, AnalysisRun) - test both first argument completion and second argument Signed-off-by: Thomas Riccardi * Revert "WIP unit-test dynamic completion" Like in kubectl own completion tests, we now have an independent 'completion' package with its own tests. Stop testing in cmd/ callers packages. This reverts commit 75eb5ae74488713ad223babe2c201fde1908ec58. Signed-off-by: Thomas Riccardi Signed-off-by: Thomas Riccardi --- pkg/kubectl-argo-rollouts/cmd/abort/abort.go | 2 + .../cmd/completion/completion.go | 2 +- .../cmd/get/get_experiment.go | 2 + .../cmd/get/get_rollout.go | 2 + pkg/kubectl-argo-rollouts/cmd/pause/pause.go | 2 + .../cmd/promote/promote.go | 2 + .../cmd/restart/restart.go | 2 + pkg/kubectl-argo-rollouts/cmd/retry/retry.go | 3 + .../cmd/set/set_image.go | 2 + .../cmd/status/status.go | 2 + .../cmd/terminate/terminate.go | 3 + pkg/kubectl-argo-rollouts/cmd/undo/undo.go | 2 + .../util/completion/completion.go | 91 +++++++++++++ .../util/completion/completion_test.go | 125 ++++++++++++++++++ 14 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 pkg/kubectl-argo-rollouts/util/completion/completion.go create mode 100644 pkg/kubectl-argo-rollouts/util/completion/completion_test.go diff --git a/pkg/kubectl-argo-rollouts/cmd/abort/abort.go b/pkg/kubectl-argo-rollouts/cmd/abort/abort.go index f90062abc8..f946faf74f 100644 --- a/pkg/kubectl-argo-rollouts/cmd/abort/abort.go +++ b/pkg/kubectl-argo-rollouts/cmd/abort/abort.go @@ -12,6 +12,7 @@ import ( "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" clientset "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned/typed/rollouts/v1alpha1" "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options" + completionutil "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/util/completion" ) const ( @@ -52,6 +53,7 @@ func NewCmdAbort(o *options.ArgoRolloutsOptions) *cobra.Command { } return nil }, + ValidArgsFunction: completionutil.RolloutNameCompletionFunc(o), } return cmd } diff --git a/pkg/kubectl-argo-rollouts/cmd/completion/completion.go b/pkg/kubectl-argo-rollouts/cmd/completion/completion.go index 87546984b3..bc045f07b8 100644 --- a/pkg/kubectl-argo-rollouts/cmd/completion/completion.go +++ b/pkg/kubectl-argo-rollouts/cmd/completion/completion.go @@ -54,7 +54,7 @@ func NewCmdCompletion(o *options.ArgoRolloutsOptions) *cobra.Command { Run: func(cmd *cobra.Command, args []string) { switch args[0] { case "bash": - cmd.Root().GenBashCompletion(o.Out) + cmd.Root().GenBashCompletionV2(o.Out, false) case "zsh": cmd.Root().GenZshCompletion(o.Out) case "fish": diff --git a/pkg/kubectl-argo-rollouts/cmd/get/get_experiment.go b/pkg/kubectl-argo-rollouts/cmd/get/get_experiment.go index 3420f4705a..d40cf48cdb 100644 --- a/pkg/kubectl-argo-rollouts/cmd/get/get_experiment.go +++ b/pkg/kubectl-argo-rollouts/cmd/get/get_experiment.go @@ -13,6 +13,7 @@ import ( "github.com/argoproj/argo-rollouts/pkg/apiclient/rollout" "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/info" "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options" + completionutil "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/util/completion" "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/viewcontroller" ) @@ -64,6 +65,7 @@ func NewCmdGetExperiment(o *options.ArgoRolloutsOptions) *cobra.Command { } return nil }, + ValidArgsFunction: completionutil.ExperimentNameCompletionFunc(o), } cmd.Flags().BoolVarP(&getOptions.Watch, "watch", "w", false, "Watch live updates to the rollout") cmd.Flags().BoolVar(&getOptions.NoColor, "no-color", false, "Do not colorize output") diff --git a/pkg/kubectl-argo-rollouts/cmd/get/get_rollout.go b/pkg/kubectl-argo-rollouts/cmd/get/get_rollout.go index 03a7465751..48c40bad3b 100644 --- a/pkg/kubectl-argo-rollouts/cmd/get/get_rollout.go +++ b/pkg/kubectl-argo-rollouts/cmd/get/get_rollout.go @@ -14,6 +14,7 @@ import ( "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/cmd/signals" "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/info" "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options" + completionutil "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/util/completion" "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/viewcontroller" ) @@ -75,6 +76,7 @@ func NewCmdGetRollout(o *options.ArgoRolloutsOptions) *cobra.Command { } return nil }, + ValidArgsFunction: completionutil.RolloutNameCompletionFunc(o), } cmd.Flags().BoolVarP(&getOptions.Watch, "watch", "w", false, "Watch live updates to the rollout") cmd.Flags().BoolVar(&getOptions.NoColor, "no-color", false, "Do not colorize output") diff --git a/pkg/kubectl-argo-rollouts/cmd/pause/pause.go b/pkg/kubectl-argo-rollouts/cmd/pause/pause.go index 2b83759d1f..9a5e6b3903 100644 --- a/pkg/kubectl-argo-rollouts/cmd/pause/pause.go +++ b/pkg/kubectl-argo-rollouts/cmd/pause/pause.go @@ -9,6 +9,7 @@ import ( types "k8s.io/apimachinery/pkg/types" "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options" + completionutil "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/util/completion" ) const ( @@ -40,6 +41,7 @@ func NewCmdPause(o *options.ArgoRolloutsOptions) *cobra.Command { } return nil }, + ValidArgsFunction: completionutil.RolloutNameCompletionFunc(o), } return cmd } diff --git a/pkg/kubectl-argo-rollouts/cmd/promote/promote.go b/pkg/kubectl-argo-rollouts/cmd/promote/promote.go index 0ad90d4654..d71d68573f 100644 --- a/pkg/kubectl-argo-rollouts/cmd/promote/promote.go +++ b/pkg/kubectl-argo-rollouts/cmd/promote/promote.go @@ -12,6 +12,7 @@ import ( "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" clientset "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned/typed/rollouts/v1alpha1" "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options" + completionutil "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/util/completion" replicasetutil "github.com/argoproj/argo-rollouts/utils/replicaset" ) @@ -77,6 +78,7 @@ func NewCmdPromote(o *options.ArgoRolloutsOptions) *cobra.Command { return nil }, + ValidArgsFunction: completionutil.RolloutNameCompletionFunc(o), } cmd.Flags().BoolVarP(&skipCurrentStep, "skip-current-step", "c", false, "Skip currently running canary step") cmd.Flags().BoolVarP(&skipAllSteps, "skip-all-steps", "a", false, "Skip remaining steps") diff --git a/pkg/kubectl-argo-rollouts/cmd/restart/restart.go b/pkg/kubectl-argo-rollouts/cmd/restart/restart.go index c99ac12009..5864879d36 100644 --- a/pkg/kubectl-argo-rollouts/cmd/restart/restart.go +++ b/pkg/kubectl-argo-rollouts/cmd/restart/restart.go @@ -12,6 +12,7 @@ import ( "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" clientset "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned/typed/rollouts/v1alpha1" "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options" + completionutil "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/util/completion" timeutil "github.com/argoproj/argo-rollouts/utils/time" ) @@ -62,6 +63,7 @@ func NewCmdRestart(o *options.ArgoRolloutsOptions) *cobra.Command { fmt.Fprintf(o.Out, "rollout '%s' restarts in %s\n", ro.Name, in) return nil }, + ValidArgsFunction: completionutil.RolloutNameCompletionFunc(o), } cmd.Flags().StringVarP(&in, "in", "i", "", "Amount of time before a restart. (e.g. 30s, 5m, 1h)") return cmd diff --git a/pkg/kubectl-argo-rollouts/cmd/retry/retry.go b/pkg/kubectl-argo-rollouts/cmd/retry/retry.go index e5d74a3733..aa110da3fe 100644 --- a/pkg/kubectl-argo-rollouts/cmd/retry/retry.go +++ b/pkg/kubectl-argo-rollouts/cmd/retry/retry.go @@ -12,6 +12,7 @@ import ( "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" clientset "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned/typed/rollouts/v1alpha1" "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options" + completionutil "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/util/completion" ) const ( @@ -76,6 +77,7 @@ func NewCmdRetryRollout(o *options.ArgoRolloutsOptions) *cobra.Command { } return nil }, + ValidArgsFunction: completionutil.RolloutNameCompletionFunc(o), } return cmd } @@ -115,6 +117,7 @@ func NewCmdRetryExperiment(o *options.ArgoRolloutsOptions) *cobra.Command { } return nil }, + ValidArgsFunction: completionutil.ExperimentNameCompletionFunc(o), } return cmd } diff --git a/pkg/kubectl-argo-rollouts/cmd/set/set_image.go b/pkg/kubectl-argo-rollouts/cmd/set/set_image.go index c1a66c935c..45ed6faac9 100644 --- a/pkg/kubectl-argo-rollouts/cmd/set/set_image.go +++ b/pkg/kubectl-argo-rollouts/cmd/set/set_image.go @@ -14,6 +14,7 @@ import ( "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options" + completionutil "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/util/completion" ) const ( @@ -60,6 +61,7 @@ func NewCmdSetImage(o *options.ArgoRolloutsOptions) *cobra.Command { fmt.Fprintf(o.Out, "%s \"%s\" image updated\n", strings.ToLower(un.GetKind()), un.GetName()) return nil }, + ValidArgsFunction: completionutil.RolloutNameCompletionFunc(o), } return cmd } diff --git a/pkg/kubectl-argo-rollouts/cmd/status/status.go b/pkg/kubectl-argo-rollouts/cmd/status/status.go index 826debb207..79d7272d88 100644 --- a/pkg/kubectl-argo-rollouts/cmd/status/status.go +++ b/pkg/kubectl-argo-rollouts/cmd/status/status.go @@ -8,6 +8,7 @@ import ( "github.com/argoproj/argo-rollouts/pkg/apiclient/rollout" "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/cmd/signals" "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options" + completionutil "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/util/completion" "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/viewcontroller" "github.com/spf13/cobra" ) @@ -89,6 +90,7 @@ func NewCmdStatus(o *options.ArgoRolloutsOptions) *cobra.Command { return nil }, + ValidArgsFunction: completionutil.RolloutNameCompletionFunc(o), } cmd.Flags().BoolVarP(&statusOptions.Watch, "watch", "w", true, "Watch the status of the rollout until it's done") cmd.Flags().DurationVarP(&statusOptions.Timeout, "timeout", "t", time.Duration(0), "The length of time to watch before giving up. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). Zero means wait forever") diff --git a/pkg/kubectl-argo-rollouts/cmd/terminate/terminate.go b/pkg/kubectl-argo-rollouts/cmd/terminate/terminate.go index f3f6574311..f0dd7e91b1 100644 --- a/pkg/kubectl-argo-rollouts/cmd/terminate/terminate.go +++ b/pkg/kubectl-argo-rollouts/cmd/terminate/terminate.go @@ -9,6 +9,7 @@ import ( types "k8s.io/apimachinery/pkg/types" "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options" + completionutil "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/util/completion" ) const ( @@ -73,6 +74,7 @@ func NewCmdTerminateAnalysisRun(o *options.ArgoRolloutsOptions) *cobra.Command { } return nil }, + ValidArgsFunction: completionutil.AnalysisRunNameCompletionFunc(o), } return cmd } @@ -101,6 +103,7 @@ func NewCmdTerminateExperiment(o *options.ArgoRolloutsOptions) *cobra.Command { } return nil }, + ValidArgsFunction: completionutil.ExperimentNameCompletionFunc(o), } return cmd } diff --git a/pkg/kubectl-argo-rollouts/cmd/undo/undo.go b/pkg/kubectl-argo-rollouts/cmd/undo/undo.go index 58bfe11b7c..a282757b16 100644 --- a/pkg/kubectl-argo-rollouts/cmd/undo/undo.go +++ b/pkg/kubectl-argo-rollouts/cmd/undo/undo.go @@ -17,6 +17,7 @@ import ( "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options" + completionutil "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/util/completion" routils "github.com/argoproj/argo-rollouts/utils/unstructured" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -60,6 +61,7 @@ func NewCmdUndo(o *options.ArgoRolloutsOptions) *cobra.Command { fmt.Fprintf(o.Out, result) return nil }, + ValidArgsFunction: completionutil.RolloutNameCompletionFunc(o), } cmd.Flags().Int64Var(&toRevision, "to-revision", toRevision, "The revision to rollback to. Default to 0 (last revision).") return cmd diff --git a/pkg/kubectl-argo-rollouts/util/completion/completion.go b/pkg/kubectl-argo-rollouts/util/completion/completion.go new file mode 100644 index 0000000000..654a4bc8d1 --- /dev/null +++ b/pkg/kubectl-argo-rollouts/util/completion/completion.go @@ -0,0 +1,91 @@ +package completion + +import ( + "strings" + + "github.com/spf13/cobra" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options" +) + +// RolloutNameCompletionFunc Returns a completion function that completes as a first argument +// the Rollouts names that match the toComplete prefix. +func RolloutNameCompletionFunc(o *options.ArgoRolloutsOptions) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { + return func(c *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + // list Rollouts names + ctx := c.Context() + opts := metav1.ListOptions{} + rolloutIf := o.RolloutsClientset().ArgoprojV1alpha1().Rollouts(o.Namespace()) + rolloutList, err := rolloutIf.List(ctx, opts) + if err != nil { + return []string{}, cobra.ShellCompDirectiveError + } + + var rolloutNames []string + for _, ro := range rolloutList.Items { + if strings.HasPrefix(ro.Name, toComplete) { + rolloutNames = append(rolloutNames, ro.Name) + } + } + + return rolloutNames, cobra.ShellCompDirectiveNoFileComp + } +} + +// ExperimentNameCompletionFunc Returns a completion function that completes as a first argument +// the Experiments names that match the toComplete prefix. +func ExperimentNameCompletionFunc(o *options.ArgoRolloutsOptions) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { + return func(c *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + // list Experiments names + ctx := c.Context() + opts := metav1.ListOptions{} + expIf := o.RolloutsClientset().ArgoprojV1alpha1().Experiments(o.Namespace()) + expList, err := expIf.List(ctx, opts) + if err != nil { + return []string{}, cobra.ShellCompDirectiveError + } + + var expNames []string + for _, exp := range expList.Items { + if strings.HasPrefix(exp.Name, toComplete) { + expNames = append(expNames, exp.Name) + } + } + + return expNames, cobra.ShellCompDirectiveNoFileComp + } +} + +// AnalysisRunNameCompletionFunc Returns a completion function that completes as a first argument +// the AnalysisRuns names that match the toComplete prefix. +func AnalysisRunNameCompletionFunc(o *options.ArgoRolloutsOptions) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { + return func(c *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + // list AnalysisRuns names + ctx := c.Context() + opts := metav1.ListOptions{} + arIf := o.RolloutsClientset().ArgoprojV1alpha1().AnalysisRuns(o.Namespace()) + arList, err := arIf.List(ctx, opts) + if err != nil { + return []string{}, cobra.ShellCompDirectiveError + } + + var arNames []string + for _, ar := range arList.Items { + if strings.HasPrefix(ar.Name, toComplete) { + arNames = append(arNames, ar.Name) + } + } + + return arNames, cobra.ShellCompDirectiveNoFileComp + } +} diff --git a/pkg/kubectl-argo-rollouts/util/completion/completion_test.go b/pkg/kubectl-argo-rollouts/util/completion/completion_test.go new file mode 100644 index 0000000000..4d8a5f7a90 --- /dev/null +++ b/pkg/kubectl-argo-rollouts/util/completion/completion_test.go @@ -0,0 +1,125 @@ +package completion + +import ( + "sort" + "testing" + + "github.com/spf13/cobra" + cmdtesting "k8s.io/kubectl/pkg/cmd/testing" + + "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/info/testdata" + "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options" + fakeoptions "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options/fake" +) + +func TestRolloutNameCompletionFuncNoArgs(t *testing.T) { + rolloutObjs := testdata.NewCanaryRollout() + _, cmd, o := prepareCompletionTest(rolloutObjs) + compFunc := RolloutNameCompletionFunc(o) + + comps, directive := compFunc(cmd, []string{}, "") + checkCompletion(t, comps, []string{"canary-demo", "canary-demo-pingpong", "canary-demo-weights", "canary-demo-weights-na", "canary-demo-workloadRef", "canary-demo-workloadRef-deploy"}, directive, cobra.ShellCompDirectiveNoFileComp) + + comps, directive = compFunc(cmd, []string{}, "cana") + checkCompletion(t, comps, []string{"canary-demo", "canary-demo-pingpong", "canary-demo-weights", "canary-demo-weights-na", "canary-demo-workloadRef", "canary-demo-workloadRef-deploy"}, directive, cobra.ShellCompDirectiveNoFileComp) + + comps, directive = compFunc(cmd, []string{}, "canary-demo") + checkCompletion(t, comps, []string{"canary-demo", "canary-demo-pingpong", "canary-demo-weights", "canary-demo-weights-na", "canary-demo-workloadRef", "canary-demo-workloadRef-deploy"}, directive, cobra.ShellCompDirectiveNoFileComp) + + comps, directive = compFunc(cmd, []string{}, "canary-demo-p") + checkCompletion(t, comps, []string{"canary-demo-pingpong"}, directive, cobra.ShellCompDirectiveNoFileComp) + + comps, directive = compFunc(cmd, []string{}, "seagull") + checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestRolloutNameCompletionFuncOneArg(t *testing.T) { + rolloutObjs := testdata.NewCanaryRollout() + _, cmd, o := prepareCompletionTest(rolloutObjs) + compFunc := RolloutNameCompletionFunc(o) + + comps, directive := compFunc(cmd, []string{"canary"}, "") + checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestExperimentNameCompletionFuncNoArgs(t *testing.T) { + rolloutObjs := testdata.NewExperimentAnalysisRollout() + _, cmd, o := prepareCompletionTest(rolloutObjs) + compFunc := ExperimentNameCompletionFunc(o) + + comps, directive := compFunc(cmd, []string{}, "") + checkCompletion(t, comps, []string{"rollout-experiment-analysis-6f646bf7b7-1-vcv27"}, directive, cobra.ShellCompDirectiveNoFileComp) + + comps, directive = compFunc(cmd, []string{}, "roll") + checkCompletion(t, comps, []string{"rollout-experiment-analysis-6f646bf7b7-1-vcv27"}, directive, cobra.ShellCompDirectiveNoFileComp) + + comps, directive = compFunc(cmd, []string{}, "seagull") + checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestExperimentNameCompletionFuncOneArg(t *testing.T) { + rolloutObjs := testdata.NewExperimentAnalysisRollout() + _, cmd, o := prepareCompletionTest(rolloutObjs) + compFunc := ExperimentNameCompletionFunc(o) + + comps, directive := compFunc(cmd, []string{"canary"}, "") + checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestAnalysisRunNameCompletionFuncNoArgs(t *testing.T) { + rolloutObjs := testdata.NewExperimentAnalysisJobRollout() + _, cmd, o := prepareCompletionTest(rolloutObjs) + compFunc := AnalysisRunNameCompletionFunc(o) + + comps, directive := compFunc(cmd, []string{}, "") + checkCompletion(t, comps, []string{"canary-demo-645d5dbc4c-2-0-stress-test"}, directive, cobra.ShellCompDirectiveNoFileComp) + + comps, directive = compFunc(cmd, []string{}, "canary") + checkCompletion(t, comps, []string{"canary-demo-645d5dbc4c-2-0-stress-test"}, directive, cobra.ShellCompDirectiveNoFileComp) + + comps, directive = compFunc(cmd, []string{}, "seagull") + checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) +} + +func TestAnalysisRunNameCompletionFuncOneArg(t *testing.T) { + rolloutObjs := testdata.NewExperimentAnalysisJobRollout() + _, cmd, o := prepareCompletionTest(rolloutObjs) + compFunc := AnalysisRunNameCompletionFunc(o) + + comps, directive := compFunc(cmd, []string{"canary"}, "") + checkCompletion(t, comps, []string{}, directive, cobra.ShellCompDirectiveNoFileComp) + +} + +func prepareCompletionTest(r *testdata.RolloutObjects) (*cmdtesting.TestFactory, *cobra.Command, *options.ArgoRolloutsOptions) { + tf, o := fakeoptions.NewFakeArgoRolloutsOptions(r.AllObjects()...) + o.RESTClientGetter = tf.WithNamespace(r.Rollouts[0].Namespace) + defer tf.Cleanup() + + cmd := &cobra.Command{ + Short: "Fake cmd for unit-test", + PersistentPreRunE: o.PersistentPreRunE, + } + + return tf, cmd, o +} + +func checkCompletion(t *testing.T, comps, expectedComps []string, directive, expectedDirective cobra.ShellCompDirective) { + if e, d := expectedDirective, directive; e != d { + t.Errorf("expected directive\n%v\nbut got\n%v", e, d) + } + + sort.Strings(comps) + sort.Strings(expectedComps) + + if len(expectedComps) != len(comps) { + t.Fatalf("expected completions\n%v\nbut got\n%v", expectedComps, comps) + } + + for i := range comps { + if expectedComps[i] != comps[i] { + t.Errorf("expected completions\n%v\nbut got\n%v", expectedComps, comps) + break + } + } +}