github.com/Racer159/helm-experiment@v0.0.0-20230822001441-1eb31183f614/src/plugin_test.go (about) 1 /* 2 Copyright The Helm Authors. 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9 Unless required by applicable law or agreed to in writing, software 10 distributed under the License is distributed on an "AS IS" BASIS, 11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 See the License for the specific language governing permissions and 13 limitations under the License. 14 */ 15 16 package cmd 17 18 import ( 19 "bytes" 20 "os" 21 "runtime" 22 "sort" 23 "strings" 24 "testing" 25 26 "github.com/spf13/cobra" 27 "github.com/spf13/pflag" 28 29 "helm.sh/helm/v3/pkg/release" 30 ) 31 32 func TestManuallyProcessArgs(t *testing.T) { 33 input := []string{ 34 "--debug", 35 "--foo", "bar", 36 "--kubeconfig=/home/foo", 37 "--kubeconfig", "/home/foo", 38 "--kube-context=test1", 39 "--kube-context", "test1", 40 "--kube-as-user", "pikachu", 41 "--kube-as-group", "teatime", 42 "--kube-as-group", "admins", 43 "-n=test2", 44 "-n", "test2", 45 "--namespace=test2", 46 "--namespace", "test2", 47 "--home=/tmp", 48 "command", 49 } 50 51 expectKnown := []string{ 52 "--debug", 53 "--kubeconfig=/home/foo", 54 "--kubeconfig", "/home/foo", 55 "--kube-context=test1", 56 "--kube-context", "test1", 57 "--kube-as-user", "pikachu", 58 "--kube-as-group", "teatime", 59 "--kube-as-group", "admins", 60 "-n=test2", 61 "-n", "test2", 62 "--namespace=test2", 63 "--namespace", "test2", 64 } 65 66 expectUnknown := []string{ 67 "--foo", "bar", "--home=/tmp", "command", 68 } 69 70 known, unknown := manuallyProcessArgs(input) 71 72 for i, k := range known { 73 if k != expectKnown[i] { 74 t.Errorf("expected known flag %d to be %q, got %q", i, expectKnown[i], k) 75 } 76 } 77 for i, k := range unknown { 78 if k != expectUnknown[i] { 79 t.Errorf("expected unknown flag %d to be %q, got %q", i, expectUnknown[i], k) 80 } 81 } 82 83 } 84 85 func TestLoadPlugins(t *testing.T) { 86 settings.PluginsDirectory = "testdata/helmhome/helm/plugins" 87 settings.RepositoryConfig = "testdata/helmhome/helm/repositories.yaml" 88 settings.RepositoryCache = "testdata/helmhome/helm/repository" 89 90 var ( 91 out bytes.Buffer 92 cmd cobra.Command 93 ) 94 loadPlugins(&cmd, &out) 95 96 envs := strings.Join([]string{ 97 "fullenv", 98 "testdata/helmhome/helm/plugins/fullenv", 99 "testdata/helmhome/helm/plugins", 100 "testdata/helmhome/helm/repositories.yaml", 101 "testdata/helmhome/helm/repository", 102 os.Args[0], 103 }, "\n") 104 105 // Test that the YAML file was correctly converted to a command. 106 tests := []struct { 107 use string 108 short string 109 long string 110 expect string 111 args []string 112 code int 113 }{ 114 {"args", "echo args", "This echos args", "-a -b -c\n", []string{"-a", "-b", "-c"}, 0}, 115 {"echo", "echo stuff", "This echos stuff", "hello\n", []string{}, 0}, 116 {"env", "env stuff", "show the env", "env\n", []string{}, 0}, 117 {"exitwith", "exitwith code", "This exits with the specified exit code", "", []string{"2"}, 2}, 118 {"fullenv", "show env vars", "show all env vars", envs + "\n", []string{}, 0}, 119 } 120 121 plugins := cmd.Commands() 122 123 if len(plugins) != len(tests) { 124 t.Fatalf("Expected %d plugins, got %d", len(tests), len(plugins)) 125 } 126 127 for i := 0; i < len(plugins); i++ { 128 out.Reset() 129 tt := tests[i] 130 pp := plugins[i] 131 if pp.Use != tt.use { 132 t.Errorf("%d: Expected Use=%q, got %q", i, tt.use, pp.Use) 133 } 134 if pp.Short != tt.short { 135 t.Errorf("%d: Expected Use=%q, got %q", i, tt.short, pp.Short) 136 } 137 if pp.Long != tt.long { 138 t.Errorf("%d: Expected Use=%q, got %q", i, tt.long, pp.Long) 139 } 140 141 // Currently, plugins assume a Linux subsystem. Skip the execution 142 // tests until this is fixed 143 if runtime.GOOS != "windows" { 144 if err := pp.RunE(pp, tt.args); err != nil { 145 if tt.code > 0 { 146 perr, ok := err.(pluginError) 147 if !ok { 148 t.Errorf("Expected %s to return pluginError: got %v(%T)", tt.use, err, err) 149 } 150 if perr.code != tt.code { 151 t.Errorf("Expected %s to return %d: got %d", tt.use, tt.code, perr.code) 152 } 153 } else { 154 t.Errorf("Error running %s: %+v", tt.use, err) 155 } 156 } 157 if out.String() != tt.expect { 158 t.Errorf("Expected %s to output:\n%s\ngot\n%s", tt.use, tt.expect, out.String()) 159 } 160 } 161 } 162 } 163 164 type staticCompletionDetails struct { 165 use string 166 validArgs []string 167 flags []string 168 next []staticCompletionDetails 169 } 170 171 func TestLoadPluginsForCompletion(t *testing.T) { 172 settings.PluginsDirectory = "testdata/helmhome/helm/plugins" 173 174 var out bytes.Buffer 175 176 cmd := &cobra.Command{ 177 Use: "completion", 178 } 179 180 loadPlugins(cmd, &out) 181 182 tests := []staticCompletionDetails{ 183 {"args", []string{}, []string{}, []staticCompletionDetails{}}, 184 {"echo", []string{}, []string{}, []staticCompletionDetails{}}, 185 {"env", []string{}, []string{"global"}, []staticCompletionDetails{ 186 {"list", []string{}, []string{"a", "all", "log"}, []staticCompletionDetails{}}, 187 {"remove", []string{"all", "one"}, []string{}, []staticCompletionDetails{}}, 188 }}, 189 {"exitwith", []string{}, []string{}, []staticCompletionDetails{ 190 {"code", []string{}, []string{"a", "b"}, []staticCompletionDetails{}}, 191 }}, 192 {"fullenv", []string{}, []string{"q", "z"}, []staticCompletionDetails{ 193 {"empty", []string{}, []string{}, []staticCompletionDetails{}}, 194 {"full", []string{}, []string{}, []staticCompletionDetails{ 195 {"less", []string{}, []string{"a", "all"}, []staticCompletionDetails{}}, 196 {"more", []string{"one", "two"}, []string{"b", "ball"}, []staticCompletionDetails{}}, 197 }}, 198 }}, 199 } 200 checkCommand(t, cmd.Commands(), tests) 201 } 202 203 func checkCommand(t *testing.T, plugins []*cobra.Command, tests []staticCompletionDetails) { 204 if len(plugins) != len(tests) { 205 t.Fatalf("Expected commands %v, got %v", tests, plugins) 206 } 207 208 for i := 0; i < len(plugins); i++ { 209 pp := plugins[i] 210 tt := tests[i] 211 if pp.Use != tt.use { 212 t.Errorf("%s: Expected Use=%q, got %q", pp.Name(), tt.use, pp.Use) 213 } 214 215 targs := tt.validArgs 216 pargs := pp.ValidArgs 217 if len(targs) != len(pargs) { 218 t.Fatalf("%s: expected args %v, got %v", pp.Name(), targs, pargs) 219 } 220 221 sort.Strings(targs) 222 sort.Strings(pargs) 223 for j := range targs { 224 if targs[j] != pargs[j] { 225 t.Errorf("%s: expected validArg=%q, got %q", pp.Name(), targs[j], pargs[j]) 226 } 227 } 228 229 tflags := tt.flags 230 var pflags []string 231 pp.LocalFlags().VisitAll(func(flag *pflag.Flag) { 232 pflags = append(pflags, flag.Name) 233 if len(flag.Shorthand) > 0 && flag.Shorthand != flag.Name { 234 pflags = append(pflags, flag.Shorthand) 235 } 236 }) 237 if len(tflags) != len(pflags) { 238 t.Fatalf("%s: expected flags %v, got %v", pp.Name(), tflags, pflags) 239 } 240 241 sort.Strings(tflags) 242 sort.Strings(pflags) 243 for j := range tflags { 244 if tflags[j] != pflags[j] { 245 t.Errorf("%s: expected flag=%q, got %q", pp.Name(), tflags[j], pflags[j]) 246 } 247 } 248 // Check the next level 249 checkCommand(t, pp.Commands(), tt.next) 250 } 251 } 252 253 func TestPluginDynamicCompletion(t *testing.T) { 254 255 tests := []cmdTestCase{{ 256 name: "completion for plugin", 257 cmd: "__complete args ''", 258 golden: "output/plugin_args_comp.txt", 259 rels: []*release.Release{}, 260 }, { 261 name: "completion for plugin with flag", 262 cmd: "__complete args --myflag ''", 263 golden: "output/plugin_args_flag_comp.txt", 264 rels: []*release.Release{}, 265 }, { 266 name: "completion for plugin with global flag", 267 cmd: "__complete args --namespace mynamespace ''", 268 golden: "output/plugin_args_ns_comp.txt", 269 rels: []*release.Release{}, 270 }, { 271 name: "completion for plugin with multiple args", 272 cmd: "__complete args --myflag --namespace mynamespace start", 273 golden: "output/plugin_args_many_args_comp.txt", 274 rels: []*release.Release{}, 275 }, { 276 name: "completion for plugin no directive", 277 cmd: "__complete echo -n mynamespace ''", 278 golden: "output/plugin_echo_no_directive.txt", 279 rels: []*release.Release{}, 280 }} 281 for _, test := range tests { 282 settings.PluginsDirectory = "testdata/helmhome/helm/plugins" 283 runTestCmd(t, []cmdTestCase{test}) 284 } 285 } 286 287 func TestLoadPlugins_HelmNoPlugins(t *testing.T) { 288 settings.PluginsDirectory = "testdata/helmhome/helm/plugins" 289 settings.RepositoryConfig = "testdata/helmhome/helm/repository" 290 291 os.Setenv("HELM_NO_PLUGINS", "1") 292 293 out := bytes.NewBuffer(nil) 294 cmd := &cobra.Command{} 295 loadPlugins(cmd, out) 296 plugins := cmd.Commands() 297 298 if len(plugins) != 0 { 299 t.Fatalf("Expected 0 plugins, got %d", len(plugins)) 300 } 301 } 302 303 func TestPluginCmdsCompletion(t *testing.T) { 304 305 tests := []cmdTestCase{{ 306 name: "completion for plugin update", 307 cmd: "__complete plugin update ''", 308 golden: "output/plugin_list_comp.txt", 309 rels: []*release.Release{}, 310 }, { 311 name: "completion for plugin update, no filter", 312 cmd: "__complete plugin update full", 313 golden: "output/plugin_list_comp.txt", 314 rels: []*release.Release{}, 315 }, { 316 name: "completion for plugin update repetition", 317 cmd: "__complete plugin update args ''", 318 golden: "output/plugin_repeat_comp.txt", 319 rels: []*release.Release{}, 320 }, { 321 name: "completion for plugin uninstall", 322 cmd: "__complete plugin uninstall ''", 323 golden: "output/plugin_list_comp.txt", 324 rels: []*release.Release{}, 325 }, { 326 name: "completion for plugin uninstall, no filter", 327 cmd: "__complete plugin uninstall full", 328 golden: "output/plugin_list_comp.txt", 329 rels: []*release.Release{}, 330 }, { 331 name: "completion for plugin uninstall repetition", 332 cmd: "__complete plugin uninstall args ''", 333 golden: "output/plugin_repeat_comp.txt", 334 rels: []*release.Release{}, 335 }, { 336 name: "completion for plugin list", 337 cmd: "__complete plugin list ''", 338 golden: "output/empty_nofile_comp.txt", 339 rels: []*release.Release{}, 340 }, { 341 name: "completion for plugin install no args", 342 cmd: "__complete plugin install ''", 343 golden: "output/empty_default_comp.txt", 344 rels: []*release.Release{}, 345 }, { 346 name: "completion for plugin install one arg", 347 cmd: "__complete plugin list /tmp ''", 348 golden: "output/empty_nofile_comp.txt", 349 rels: []*release.Release{}, 350 }, {}} 351 for _, test := range tests { 352 settings.PluginsDirectory = "testdata/helmhome/helm/plugins" 353 runTestCmd(t, []cmdTestCase{test}) 354 } 355 } 356 357 func TestPluginFileCompletion(t *testing.T) { 358 checkFileCompletion(t, "plugin", false) 359 } 360 361 func TestPluginInstallFileCompletion(t *testing.T) { 362 checkFileCompletion(t, "plugin install", true) 363 checkFileCompletion(t, "plugin install mypath", false) 364 } 365 366 func TestPluginListFileCompletion(t *testing.T) { 367 checkFileCompletion(t, "plugin list", false) 368 } 369 370 func TestPluginUninstallFileCompletion(t *testing.T) { 371 checkFileCompletion(t, "plugin uninstall", false) 372 checkFileCompletion(t, "plugin uninstall myplugin", false) 373 } 374 375 func TestPluginUpdateFileCompletion(t *testing.T) { 376 checkFileCompletion(t, "plugin update", false) 377 checkFileCompletion(t, "plugin update myplugin", false) 378 }