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  }