github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/plugin/plugin_test.go (about)

     1  package plugin_test
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"path/filepath"
     7  	"runtime"
     8  	"testing"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	"github.com/devseccon/trivy/pkg/log"
    14  	"github.com/devseccon/trivy/pkg/plugin"
    15  )
    16  
    17  func TestPlugin_Run(t *testing.T) {
    18  	if runtime.GOOS == "windows" {
    19  		// the test.sh script can't be run on windows so skipping
    20  		t.Skip("Test satisfied adequately by Linux tests")
    21  	}
    22  	type fields struct {
    23  		Name        string
    24  		Repository  string
    25  		Version     string
    26  		Usage       string
    27  		Description string
    28  		Platforms   []plugin.Platform
    29  		GOOS        string
    30  		GOARCH      string
    31  	}
    32  	type args struct {
    33  		args []string
    34  	}
    35  	tests := []struct {
    36  		name    string
    37  		fields  fields
    38  		args    args
    39  		wantErr string
    40  	}{
    41  		{
    42  			name: "happy path",
    43  			fields: fields{
    44  				Name:        "test_plugin",
    45  				Repository:  "github.com/devseccon/trivy-plugin-test",
    46  				Version:     "0.1.0",
    47  				Usage:       "test",
    48  				Description: "test",
    49  				Platforms: []plugin.Platform{
    50  					{
    51  						Selector: &plugin.Selector{
    52  							OS:   "linux",
    53  							Arch: "amd64",
    54  						},
    55  						URI: "github.com/devseccon/trivy-plugin-test",
    56  						Bin: "test.sh",
    57  					},
    58  				},
    59  				GOOS:   "linux",
    60  				GOARCH: "amd64",
    61  			},
    62  		},
    63  		{
    64  			name: "no selector",
    65  			fields: fields{
    66  				Name:        "test_plugin",
    67  				Repository:  "github.com/devseccon/trivy-plugin-test",
    68  				Version:     "0.1.0",
    69  				Usage:       "test",
    70  				Description: "test",
    71  				Platforms: []plugin.Platform{
    72  					{
    73  						URI: "github.com/devseccon/trivy-plugin-test",
    74  						Bin: "test.sh",
    75  					},
    76  				},
    77  			},
    78  		},
    79  		{
    80  			name: "no matched platform",
    81  			fields: fields{
    82  				Name:        "test_plugin",
    83  				Repository:  "github.com/devseccon/trivy-plugin-test",
    84  				Version:     "0.1.0",
    85  				Usage:       "test",
    86  				Description: "test",
    87  				Platforms: []plugin.Platform{
    88  					{
    89  						Selector: &plugin.Selector{
    90  							OS:   "darwin",
    91  							Arch: "amd64",
    92  						},
    93  						URI: "github.com/devseccon/trivy-plugin-test",
    94  						Bin: "test.sh",
    95  					},
    96  				},
    97  				GOOS:   "linux",
    98  				GOARCH: "amd64",
    99  			},
   100  			wantErr: "platform not found",
   101  		},
   102  		{
   103  			name: "no execution file",
   104  			fields: fields{
   105  				Name:        "test_plugin",
   106  				Repository:  "github.com/devseccon/trivy-plugin-test",
   107  				Version:     "0.1.0",
   108  				Usage:       "test",
   109  				Description: "test",
   110  				Platforms: []plugin.Platform{
   111  					{
   112  						Selector: &plugin.Selector{
   113  							OS:   "linux",
   114  							Arch: "amd64",
   115  						},
   116  						URI: "github.com/devseccon/trivy-plugin-test",
   117  						Bin: "nonexistence.sh",
   118  					},
   119  				},
   120  				GOOS:   "linux",
   121  				GOARCH: "amd64",
   122  			},
   123  			wantErr: "no such file or directory",
   124  		},
   125  		{
   126  			name: "plugin exec error",
   127  			fields: fields{
   128  				Name:        "error_plugin",
   129  				Repository:  "github.com/devseccon/trivy-plugin-error",
   130  				Version:     "0.1.0",
   131  				Usage:       "test",
   132  				Description: "test",
   133  				Platforms: []plugin.Platform{
   134  					{
   135  						Selector: &plugin.Selector{
   136  							OS:   "linux",
   137  							Arch: "amd64",
   138  						},
   139  						URI: "github.com/devseccon/trivy-plugin-test",
   140  						Bin: "test.sh",
   141  					},
   142  				},
   143  				GOOS:   "linux",
   144  				GOARCH: "amd64",
   145  			},
   146  			wantErr: "plugin exec: exit status 1",
   147  		},
   148  	}
   149  	for _, tt := range tests {
   150  		t.Run(tt.name, func(t *testing.T) {
   151  			os.Setenv("XDG_DATA_HOME", "testdata")
   152  			defer os.Unsetenv("XDG_DATA_HOME")
   153  
   154  			p := plugin.Plugin{
   155  				Name:        tt.fields.Name,
   156  				Repository:  tt.fields.Repository,
   157  				Version:     tt.fields.Version,
   158  				Usage:       tt.fields.Usage,
   159  				Description: tt.fields.Description,
   160  				Platforms:   tt.fields.Platforms,
   161  				GOOS:        tt.fields.GOOS,
   162  				GOARCH:      tt.fields.GOARCH,
   163  			}
   164  
   165  			err := p.Run(context.Background(), tt.args.args)
   166  			if tt.wantErr != "" {
   167  				require.NotNil(t, err)
   168  				assert.Contains(t, err.Error(), tt.wantErr)
   169  				return
   170  			}
   171  			assert.NoError(t, err)
   172  		})
   173  	}
   174  }
   175  
   176  func TestInstall(t *testing.T) {
   177  	if runtime.GOOS == "windows" {
   178  		// the test.sh script can't be run on windows so skipping
   179  		t.Skip("Test satisfied adequately by Linux tests")
   180  	}
   181  	tests := []struct {
   182  		name     string
   183  		url      string
   184  		want     plugin.Plugin
   185  		wantFile string
   186  		wantErr  string
   187  	}{
   188  		{
   189  			name: "happy path",
   190  			url:  "testdata/test_plugin",
   191  			want: plugin.Plugin{
   192  				Name:        "test_plugin",
   193  				Repository:  "github.com/devseccon/trivy-plugin-test",
   194  				Version:     "0.1.0",
   195  				Usage:       "test",
   196  				Description: "test",
   197  				Platforms: []plugin.Platform{
   198  					{
   199  						Selector: &plugin.Selector{
   200  							OS:   "linux",
   201  							Arch: "amd64",
   202  						},
   203  						URI: "./test.sh",
   204  						Bin: "./test.sh",
   205  					},
   206  				},
   207  				GOOS:   "linux",
   208  				GOARCH: "amd64",
   209  			},
   210  			wantFile: ".trivy/plugins/test_plugin/test.sh",
   211  		},
   212  		{
   213  			name: "plugin not found",
   214  			url:  "testdata/not_found",
   215  			want: plugin.Plugin{
   216  				Name:        "test_plugin",
   217  				Repository:  "github.com/devseccon/trivy-plugin-test",
   218  				Version:     "0.1.0",
   219  				Usage:       "test",
   220  				Description: "test",
   221  				Platforms: []plugin.Platform{
   222  					{
   223  						Selector: &plugin.Selector{
   224  							OS:   "linux",
   225  							Arch: "amd64",
   226  						},
   227  						URI: "./test.sh",
   228  						Bin: "./test.sh",
   229  					},
   230  				},
   231  				GOOS:   "linux",
   232  				GOARCH: "amd64",
   233  			},
   234  			wantErr: "no such file or directory",
   235  		},
   236  		{
   237  			name: "no plugin.yaml",
   238  			url:  "testdata/no_yaml",
   239  			want: plugin.Plugin{
   240  				Name:        "no_yaml",
   241  				Repository:  "github.com/devseccon/trivy-plugin-test",
   242  				Version:     "0.1.0",
   243  				Usage:       "test",
   244  				Description: "test",
   245  				Platforms: []plugin.Platform{
   246  					{
   247  						Selector: &plugin.Selector{
   248  							OS:   "linux",
   249  							Arch: "amd64",
   250  						},
   251  						URI: "./test.sh",
   252  						Bin: "./test.sh",
   253  					},
   254  				},
   255  				GOOS:   "linux",
   256  				GOARCH: "amd64",
   257  			},
   258  			wantErr: "file open error",
   259  		},
   260  	}
   261  
   262  	log.InitLogger(false, true)
   263  	for _, tt := range tests {
   264  		t.Run(tt.name, func(t *testing.T) {
   265  			// The test plugin will be installed here
   266  			dst := t.TempDir()
   267  			os.Setenv("XDG_DATA_HOME", dst)
   268  
   269  			got, err := plugin.Install(context.Background(), tt.url, false)
   270  			if tt.wantErr != "" {
   271  				require.NotNil(t, err)
   272  				assert.Contains(t, err.Error(), tt.wantErr)
   273  				return
   274  			}
   275  			assert.NoError(t, err)
   276  
   277  			assert.Equal(t, tt.want, got)
   278  			assert.FileExists(t, filepath.Join(dst, tt.wantFile))
   279  		})
   280  	}
   281  }
   282  
   283  func TestUninstall(t *testing.T) {
   284  	if runtime.GOOS == "windows" {
   285  		// the test.sh script can't be run on windows so skipping
   286  		t.Skip("Test satisfied adequately by Linux tests")
   287  	}
   288  	pluginName := "test_plugin"
   289  
   290  	tempDir := t.TempDir()
   291  	pluginDir := filepath.Join(tempDir, ".trivy", "plugins", pluginName)
   292  
   293  	// Create the test plugin directory
   294  	err := os.MkdirAll(pluginDir, os.ModePerm)
   295  	require.NoError(t, err)
   296  
   297  	// Create the test file
   298  	err = os.WriteFile(filepath.Join(pluginDir, "test.sh"), []byte(`foo`), os.ModePerm)
   299  	require.NoError(t, err)
   300  
   301  	// Uninstall the plugin
   302  	err = plugin.Uninstall(pluginName)
   303  	assert.NoError(t, err)
   304  	assert.NoFileExists(t, pluginDir)
   305  }
   306  
   307  func TestInformation(t *testing.T) {
   308  	if runtime.GOOS == "windows" {
   309  		// the test.sh script can't be run on windows so skipping
   310  		t.Skip("Test satisfied adequately by Linux tests")
   311  	}
   312  	pluginName := "test_plugin"
   313  
   314  	tempDir := t.TempDir()
   315  	pluginDir := filepath.Join(tempDir, ".trivy", "plugins", pluginName)
   316  
   317  	t.Setenv("XDG_DATA_HOME", tempDir)
   318  
   319  	// Create the test plugin directory
   320  	err := os.MkdirAll(pluginDir, os.ModePerm)
   321  	require.NoError(t, err)
   322  
   323  	// write the plugin name
   324  	pluginMetadata := `name: "test_plugin"
   325  repository: github.com/devseccon/trivy-plugin-test
   326  version: "0.1.0"
   327  usage: test
   328  description: A simple test plugin`
   329  
   330  	err = os.WriteFile(filepath.Join(pluginDir, "plugin.yaml"), []byte(pluginMetadata), os.ModePerm)
   331  	require.NoError(t, err)
   332  
   333  	// Get Information for the plugin
   334  	info, err := plugin.Information(pluginName)
   335  	require.NoError(t, err)
   336  	assert.Equal(t, "\nPlugin: test_plugin\n  Description: A simple test plugin\n  Version:     0.1.0\n  Usage:       test\n", info)
   337  
   338  	// Get Information for unknown plugin
   339  	info, err = plugin.Information("unknown")
   340  	require.Error(t, err)
   341  	assert.Equal(t, "could not find a plugin called 'unknown', did you install it?", err.Error())
   342  }
   343  
   344  func TestLoadAll1(t *testing.T) {
   345  	if runtime.GOOS == "windows" {
   346  		// the test.sh script can't be run on windows so skipping
   347  		t.Skip("Test satisfied adequately by Linux tests")
   348  	}
   349  	tests := []struct {
   350  		name    string
   351  		dir     string
   352  		want    []plugin.Plugin
   353  		wantErr string
   354  	}{
   355  		{
   356  			name: "happy path",
   357  			dir:  "testdata",
   358  			want: []plugin.Plugin{
   359  				{
   360  					Name:        "test_plugin",
   361  					Repository:  "github.com/devseccon/trivy-plugin-test",
   362  					Version:     "0.1.0",
   363  					Usage:       "test",
   364  					Description: "test",
   365  					Platforms: []plugin.Platform{
   366  						{
   367  							Selector: &plugin.Selector{
   368  								OS:   "linux",
   369  								Arch: "amd64",
   370  							},
   371  							URI: "./test.sh",
   372  							Bin: "./test.sh",
   373  						},
   374  					},
   375  					GOOS:   "linux",
   376  					GOARCH: "amd64",
   377  				},
   378  			},
   379  		},
   380  		{
   381  			name:    "sad path",
   382  			dir:     "sad",
   383  			wantErr: "no such file or directory",
   384  		},
   385  	}
   386  	for _, tt := range tests {
   387  		t.Run(tt.name, func(t *testing.T) {
   388  			os.Setenv("XDG_DATA_HOME", tt.dir)
   389  			defer os.Unsetenv("XDG_DATA_HOME")
   390  
   391  			got, err := plugin.LoadAll()
   392  			if tt.wantErr != "" {
   393  				require.NotNil(t, err)
   394  				assert.Contains(t, err.Error(), tt.wantErr)
   395  				return
   396  			}
   397  			assert.NoError(t, err)
   398  			assert.Equal(t, tt.want, got)
   399  		})
   400  	}
   401  }
   402  
   403  func TestUpdate(t *testing.T) {
   404  	if runtime.GOOS == "windows" {
   405  		// the test.sh script can't be run on windows so skipping
   406  		t.Skip("Test satisfied adequately by Linux tests")
   407  	}
   408  	pluginName := "test_plugin"
   409  
   410  	tempDir := t.TempDir()
   411  	pluginDir := filepath.Join(tempDir, ".trivy", "plugins", pluginName)
   412  
   413  	t.Setenv("XDG_DATA_HOME", tempDir)
   414  
   415  	// Create the test plugin directory
   416  	err := os.MkdirAll(pluginDir, os.ModePerm)
   417  	require.NoError(t, err)
   418  
   419  	// write the plugin name
   420  	pluginMetadata := `name: "test_plugin"
   421  repository: testdata/test_plugin
   422  version: "0.0.5"
   423  usage: test
   424  description: A simple test plugin`
   425  
   426  	err = os.WriteFile(filepath.Join(pluginDir, "plugin.yaml"), []byte(pluginMetadata), os.ModePerm)
   427  	require.NoError(t, err)
   428  
   429  	// verify initial version
   430  	verifyVersion(t, pluginName, "0.0.5")
   431  
   432  	// Update the existing plugin
   433  	err = plugin.Update(pluginName)
   434  	require.NoError(t, err)
   435  
   436  	// verify plugin updated
   437  	verifyVersion(t, pluginName, "0.1.0")
   438  }
   439  
   440  func verifyVersion(t *testing.T, pluginName, expectedVersion string) {
   441  	plugins, err := plugin.LoadAll()
   442  	require.NoError(t, err)
   443  	for _, plugin := range plugins {
   444  		if plugin.Name == pluginName {
   445  			assert.Equal(t, expectedVersion, plugin.Version)
   446  		}
   447  	}
   448  }