github.com/stefanmcshane/helm@v0.0.0-20221213002717-88a4a2c6e77d/pkg/plugin/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 plugin // import "github.com/stefanmcshane/helm/pkg/plugin" 17 18 import ( 19 "fmt" 20 "os" 21 "path/filepath" 22 "reflect" 23 "runtime" 24 "testing" 25 26 "github.com/stefanmcshane/helm/pkg/cli" 27 ) 28 29 func checkCommand(p *Plugin, extraArgs []string, osStrCmp string, t *testing.T) { 30 cmd, args, err := p.PrepareCommand(extraArgs) 31 if err != nil { 32 t.Fatal(err) 33 } 34 if cmd != "echo" { 35 t.Fatalf("Expected echo, got %q", cmd) 36 } 37 38 if l := len(args); l != 5 { 39 t.Fatalf("expected 5 args, got %d", l) 40 } 41 42 expect := []string{"-n", osStrCmp, "--debug", "--foo", "bar"} 43 for i := 0; i < len(args); i++ { 44 if expect[i] != args[i] { 45 t.Errorf("Expected arg=%q, got %q", expect[i], args[i]) 46 } 47 } 48 49 // Test with IgnoreFlags. This should omit --debug, --foo, bar 50 p.Metadata.IgnoreFlags = true 51 cmd, args, err = p.PrepareCommand(extraArgs) 52 if err != nil { 53 t.Fatal(err) 54 } 55 if cmd != "echo" { 56 t.Fatalf("Expected echo, got %q", cmd) 57 } 58 if l := len(args); l != 2 { 59 t.Fatalf("expected 2 args, got %d", l) 60 } 61 expect = []string{"-n", osStrCmp} 62 for i := 0; i < len(args); i++ { 63 if expect[i] != args[i] { 64 t.Errorf("Expected arg=%q, got %q", expect[i], args[i]) 65 } 66 } 67 } 68 69 func TestPrepareCommand(t *testing.T) { 70 p := &Plugin{ 71 Dir: "/tmp", // Unused 72 Metadata: &Metadata{ 73 Name: "test", 74 Command: "echo -n foo", 75 }, 76 } 77 argv := []string{"--debug", "--foo", "bar"} 78 79 checkCommand(p, argv, "foo", t) 80 } 81 82 func TestPlatformPrepareCommand(t *testing.T) { 83 p := &Plugin{ 84 Dir: "/tmp", // Unused 85 Metadata: &Metadata{ 86 Name: "test", 87 Command: "echo -n os-arch", 88 PlatformCommand: []PlatformCommand{ 89 {OperatingSystem: "linux", Architecture: "i386", Command: "echo -n linux-i386"}, 90 {OperatingSystem: "linux", Architecture: "amd64", Command: "echo -n linux-amd64"}, 91 {OperatingSystem: "linux", Architecture: "arm64", Command: "echo -n linux-arm64"}, 92 {OperatingSystem: "linux", Architecture: "ppc64le", Command: "echo -n linux-ppc64le"}, 93 {OperatingSystem: "linux", Architecture: "s390x", Command: "echo -n linux-s390x"}, 94 {OperatingSystem: "windows", Architecture: "amd64", Command: "echo -n win-64"}, 95 }, 96 }, 97 } 98 var osStrCmp string 99 os := runtime.GOOS 100 arch := runtime.GOARCH 101 if os == "linux" && arch == "i386" { 102 osStrCmp = "linux-i386" 103 } else if os == "linux" && arch == "amd64" { 104 osStrCmp = "linux-amd64" 105 } else if os == "linux" && arch == "arm64" { 106 osStrCmp = "linux-arm64" 107 } else if os == "linux" && arch == "ppc64le" { 108 osStrCmp = "linux-ppc64le" 109 } else if os == "linux" && arch == "s390x" { 110 osStrCmp = "linux-s390x" 111 } else if os == "windows" && arch == "amd64" { 112 osStrCmp = "win-64" 113 } else { 114 osStrCmp = "os-arch" 115 } 116 117 argv := []string{"--debug", "--foo", "bar"} 118 checkCommand(p, argv, osStrCmp, t) 119 } 120 121 func TestPartialPlatformPrepareCommand(t *testing.T) { 122 p := &Plugin{ 123 Dir: "/tmp", // Unused 124 Metadata: &Metadata{ 125 Name: "test", 126 Command: "echo -n os-arch", 127 PlatformCommand: []PlatformCommand{ 128 {OperatingSystem: "linux", Architecture: "i386", Command: "echo -n linux-i386"}, 129 {OperatingSystem: "windows", Architecture: "amd64", Command: "echo -n win-64"}, 130 }, 131 }, 132 } 133 var osStrCmp string 134 os := runtime.GOOS 135 arch := runtime.GOARCH 136 if os == "linux" { 137 osStrCmp = "linux-i386" 138 } else if os == "windows" && arch == "amd64" { 139 osStrCmp = "win-64" 140 } else { 141 osStrCmp = "os-arch" 142 } 143 144 argv := []string{"--debug", "--foo", "bar"} 145 checkCommand(p, argv, osStrCmp, t) 146 } 147 148 func TestNoPrepareCommand(t *testing.T) { 149 p := &Plugin{ 150 Dir: "/tmp", // Unused 151 Metadata: &Metadata{ 152 Name: "test", 153 }, 154 } 155 argv := []string{"--debug", "--foo", "bar"} 156 157 _, _, err := p.PrepareCommand(argv) 158 if err == nil { 159 t.Fatalf("Expected error to be returned") 160 } 161 } 162 163 func TestNoMatchPrepareCommand(t *testing.T) { 164 p := &Plugin{ 165 Dir: "/tmp", // Unused 166 Metadata: &Metadata{ 167 Name: "test", 168 PlatformCommand: []PlatformCommand{ 169 {OperatingSystem: "no-os", Architecture: "amd64", Command: "echo -n linux-i386"}, 170 }, 171 }, 172 } 173 argv := []string{"--debug", "--foo", "bar"} 174 175 if _, _, err := p.PrepareCommand(argv); err == nil { 176 t.Fatalf("Expected error to be returned") 177 } 178 } 179 180 func TestLoadDir(t *testing.T) { 181 dirname := "testdata/plugdir/good/hello" 182 plug, err := LoadDir(dirname) 183 if err != nil { 184 t.Fatalf("error loading Hello plugin: %s", err) 185 } 186 187 if plug.Dir != dirname { 188 t.Fatalf("Expected dir %q, got %q", dirname, plug.Dir) 189 } 190 191 expect := &Metadata{ 192 Name: "hello", 193 Version: "0.1.0", 194 Usage: "usage", 195 Description: "description", 196 Command: "$HELM_PLUGIN_DIR/hello.sh", 197 IgnoreFlags: true, 198 Hooks: map[string]string{ 199 Install: "echo installing...", 200 }, 201 } 202 203 if !reflect.DeepEqual(expect, plug.Metadata) { 204 t.Fatalf("Expected plugin metadata %v, got %v", expect, plug.Metadata) 205 } 206 } 207 208 func TestLoadDirDuplicateEntries(t *testing.T) { 209 dirname := "testdata/plugdir/bad/duplicate-entries" 210 if _, err := LoadDir(dirname); err == nil { 211 t.Errorf("successfully loaded plugin with duplicate entries when it should've failed") 212 } 213 } 214 215 func TestDownloader(t *testing.T) { 216 dirname := "testdata/plugdir/good/downloader" 217 plug, err := LoadDir(dirname) 218 if err != nil { 219 t.Fatalf("error loading Hello plugin: %s", err) 220 } 221 222 if plug.Dir != dirname { 223 t.Fatalf("Expected dir %q, got %q", dirname, plug.Dir) 224 } 225 226 expect := &Metadata{ 227 Name: "downloader", 228 Version: "1.2.3", 229 Usage: "usage", 230 Description: "download something", 231 Command: "echo Hello", 232 Downloaders: []Downloaders{ 233 { 234 Protocols: []string{"myprotocol", "myprotocols"}, 235 Command: "echo Download", 236 }, 237 }, 238 } 239 240 if !reflect.DeepEqual(expect, plug.Metadata) { 241 t.Fatalf("Expected metadata %v, got %v", expect, plug.Metadata) 242 } 243 } 244 245 func TestLoadAll(t *testing.T) { 246 247 // Verify that empty dir loads: 248 if plugs, err := LoadAll("testdata"); err != nil { 249 t.Fatalf("error loading dir with no plugins: %s", err) 250 } else if len(plugs) > 0 { 251 t.Fatalf("expected empty dir to have 0 plugins") 252 } 253 254 basedir := "testdata/plugdir/good" 255 plugs, err := LoadAll(basedir) 256 if err != nil { 257 t.Fatalf("Could not load %q: %s", basedir, err) 258 } 259 260 if l := len(plugs); l != 3 { 261 t.Fatalf("expected 3 plugins, found %d", l) 262 } 263 264 if plugs[0].Metadata.Name != "downloader" { 265 t.Errorf("Expected first plugin to be echo, got %q", plugs[0].Metadata.Name) 266 } 267 if plugs[1].Metadata.Name != "echo" { 268 t.Errorf("Expected first plugin to be echo, got %q", plugs[0].Metadata.Name) 269 } 270 if plugs[2].Metadata.Name != "hello" { 271 t.Errorf("Expected second plugin to be hello, got %q", plugs[1].Metadata.Name) 272 } 273 } 274 275 func TestFindPlugins(t *testing.T) { 276 cases := []struct { 277 name string 278 plugdirs string 279 expected int 280 }{ 281 { 282 name: "plugdirs is empty", 283 plugdirs: "", 284 expected: 0, 285 }, 286 { 287 name: "plugdirs isn't dir", 288 plugdirs: "./plugin_test.go", 289 expected: 0, 290 }, 291 { 292 name: "plugdirs doesn't have plugin", 293 plugdirs: ".", 294 expected: 0, 295 }, 296 { 297 name: "normal", 298 plugdirs: "./testdata/plugdir/good", 299 expected: 3, 300 }, 301 } 302 for _, c := range cases { 303 t.Run(t.Name(), func(t *testing.T) { 304 plugin, _ := FindPlugins(c.plugdirs) 305 if len(plugin) != c.expected { 306 t.Errorf("expected: %v, got: %v", c.expected, len(plugin)) 307 } 308 }) 309 } 310 } 311 312 func TestSetupEnv(t *testing.T) { 313 name := "pequod" 314 base := filepath.Join("testdata/helmhome/helm/plugins", name) 315 316 s := cli.New() 317 s.PluginsDirectory = "testdata/helmhome/helm/plugins" 318 319 SetupPluginEnv(s, name, base) 320 for _, tt := range []struct { 321 name, expect string 322 }{ 323 {"HELM_PLUGIN_NAME", name}, 324 {"HELM_PLUGIN_DIR", base}, 325 } { 326 if got := os.Getenv(tt.name); got != tt.expect { 327 t.Errorf("Expected $%s=%q, got %q", tt.name, tt.expect, got) 328 } 329 } 330 } 331 332 func TestValidatePluginData(t *testing.T) { 333 for i, item := range []struct { 334 pass bool 335 plug *Plugin 336 }{ 337 {true, mockPlugin("abcdefghijklmnopqrstuvwxyz0123456789_-ABC")}, 338 {true, mockPlugin("foo-bar-FOO-BAR_1234")}, 339 {false, mockPlugin("foo -bar")}, 340 {false, mockPlugin("$foo -bar")}, // Test leading chars 341 {false, mockPlugin("foo -bar ")}, // Test trailing chars 342 {false, mockPlugin("foo\nbar")}, // Test newline 343 } { 344 err := validatePluginData(item.plug, fmt.Sprintf("test-%d", i)) 345 if item.pass && err != nil { 346 t.Errorf("failed to validate case %d: %s", i, err) 347 } else if !item.pass && err == nil { 348 t.Errorf("expected case %d to fail", i) 349 } 350 } 351 } 352 353 func TestDetectDuplicates(t *testing.T) { 354 plugs := []*Plugin{ 355 mockPlugin("foo"), 356 mockPlugin("bar"), 357 } 358 if err := detectDuplicates(plugs); err != nil { 359 t.Error("no duplicates in the first set") 360 } 361 plugs = append(plugs, mockPlugin("foo")) 362 if err := detectDuplicates(plugs); err == nil { 363 t.Error("duplicates in the second set") 364 } 365 } 366 367 func mockPlugin(name string) *Plugin { 368 return &Plugin{ 369 Metadata: &Metadata{ 370 Name: name, 371 Version: "v0.1.2", 372 Usage: "Mock plugin", 373 Description: "Mock plugin for testing", 374 Command: "echo mock plugin", 375 }, 376 Dir: "no-such-dir", 377 } 378 }