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 }