get.porter.sh/porter@v1.3.0/pkg/porter/plugins_test.go (about) 1 package porter 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "path/filepath" 8 "strings" 9 "testing" 10 11 "get.porter.sh/porter/pkg/pkgmgmt" 12 "get.porter.sh/porter/pkg/pkgmgmt/client" 13 "get.porter.sh/porter/pkg/plugins" 14 "get.porter.sh/porter/pkg/printer" 15 "get.porter.sh/porter/pkg/test" 16 "get.porter.sh/porter/pkg/yaml" 17 "get.porter.sh/porter/tests" 18 "github.com/stretchr/testify/assert" 19 "github.com/stretchr/testify/require" 20 "github.com/xeipuuv/gojsonschema" 21 ) 22 23 func TestPorter_PrintPlugins(t *testing.T) { 24 t.Run("plaintext", func(t *testing.T) { 25 ctx := context.Background() 26 p := NewTestPorter(t) 27 defer p.Close() 28 29 opts := PrintPluginsOptions{ 30 PrintOptions: printer.PrintOptions{ 31 Format: printer.FormatPlaintext, 32 }, 33 } 34 err := p.PrintPlugins(ctx, opts) 35 36 require.Nil(t, err) 37 38 got := p.TestConfig.TestContext.GetOutput() 39 test.CompareGoldenFile(t, "testdata/plugins/list-output.txt", got) 40 }) 41 42 t.Run("yaml", func(t *testing.T) { 43 ctx := context.Background() 44 p := NewTestPorter(t) 45 defer p.Close() 46 47 opts := PrintPluginsOptions{ 48 PrintOptions: printer.PrintOptions{ 49 Format: printer.FormatYaml, 50 }, 51 } 52 err := p.PrintPlugins(ctx, opts) 53 54 require.Nil(t, err) 55 expected := `- name: plugin1 56 versioninfo: 57 version: v1.0 58 commit: abc123 59 author: Porter Authors 60 implementations: 61 - type: storage 62 name: blob 63 - type: storage 64 name: mongo 65 - name: plugin2 66 versioninfo: 67 version: v1.0 68 commit: abc123 69 author: Porter Authors 70 implementations: 71 - type: storage 72 name: blob 73 - type: storage 74 name: mongo 75 - name: unknown 76 versioninfo: 77 version: v1.0 78 commit: abc123 79 author: Porter Authors 80 implementations: [] 81 ` 82 actual := p.TestConfig.TestContext.GetOutput() 83 assert.Equal(t, expected, actual) 84 }) 85 86 t.Run("json", func(t *testing.T) { 87 ctx := context.Background() 88 p := NewTestPorter(t) 89 defer p.Close() 90 91 opts := PrintPluginsOptions{ 92 PrintOptions: printer.PrintOptions{ 93 Format: printer.FormatJson, 94 }, 95 } 96 err := p.PrintPlugins(ctx, opts) 97 98 require.Nil(t, err) 99 expected := `[ 100 { 101 "name": "plugin1", 102 "version": "v1.0", 103 "commit": "abc123", 104 "author": "Porter Authors", 105 "implementations": [ 106 { 107 "type": "storage", 108 "implementation": "blob" 109 }, 110 { 111 "type": "storage", 112 "implementation": "mongo" 113 } 114 ] 115 }, 116 { 117 "name": "plugin2", 118 "version": "v1.0", 119 "commit": "abc123", 120 "author": "Porter Authors", 121 "implementations": [ 122 { 123 "type": "storage", 124 "implementation": "blob" 125 }, 126 { 127 "type": "storage", 128 "implementation": "mongo" 129 } 130 ] 131 }, 132 { 133 "name": "unknown", 134 "version": "v1.0", 135 "commit": "abc123", 136 "author": "Porter Authors", 137 "implementations": null 138 } 139 ] 140 ` 141 actual := p.TestConfig.TestContext.GetOutput() 142 assert.Equal(t, expected, actual) 143 }) 144 } 145 146 func TestPorter_ShowPlugin(t *testing.T) { 147 t.Run("plaintext", func(t *testing.T) { 148 ctx := context.Background() 149 150 p := NewTestPorter(t) 151 defer p.Close() 152 153 opts := ShowPluginOptions{Name: "plugin1"} 154 opts.Format = printer.FormatPlaintext 155 err := p.ShowPlugin(ctx, opts) 156 require.NoError(t, err, "ShowPlugin failed") 157 158 expected := `Name: plugin1 159 Version: v1.0 160 Commit: abc123 161 Author: Porter Authors 162 163 --------------------------- 164 Type Implementation 165 --------------------------- 166 storage blob 167 storage mongo 168 ` 169 actual := p.TestConfig.TestContext.GetOutput() 170 assert.Equal(t, expected, actual) 171 }) 172 173 t.Run("yaml", func(t *testing.T) { 174 ctx := context.Background() 175 p := NewTestPorter(t) 176 defer p.Close() 177 178 opts := ShowPluginOptions{Name: "plugin1"} 179 opts.Format = printer.FormatYaml 180 err := p.ShowPlugin(ctx, opts) 181 require.NoError(t, err, "ShowPlugin failed") 182 183 expected := `name: plugin1 184 versioninfo: 185 version: v1.0 186 commit: abc123 187 author: Porter Authors 188 implementations: 189 - type: storage 190 name: blob 191 - type: storage 192 name: mongo 193 ` 194 actual := p.TestConfig.TestContext.GetOutput() 195 assert.Equal(t, expected, actual) 196 }) 197 198 t.Run("json", func(t *testing.T) { 199 ctx := context.Background() 200 201 p := NewTestPorter(t) 202 defer p.Close() 203 204 opts := ShowPluginOptions{Name: "plugin1"} 205 opts.Format = printer.FormatJson 206 err := p.ShowPlugin(ctx, opts) 207 require.NoError(t, err, "ShowPlugin failed") 208 209 expected := `{ 210 "name": "plugin1", 211 "version": "v1.0", 212 "commit": "abc123", 213 "author": "Porter Authors", 214 "implementations": [ 215 { 216 "type": "storage", 217 "implementation": "blob" 218 }, 219 { 220 "type": "storage", 221 "implementation": "mongo" 222 } 223 ] 224 } 225 ` 226 actual := p.TestConfig.TestContext.GetOutput() 227 assert.Equal(t, expected, actual) 228 }) 229 } 230 231 func TestPorter_InstallPlugin(t *testing.T) { 232 defaultFeedURL := pkgmgmt.DefaultPackageMirror + "/plugins/atom.xml" 233 type expectedResults struct { 234 output string 235 feedURL string 236 mirror string 237 } 238 testcases := []struct { 239 name string 240 args []string 241 config plugins.InstallOptions 242 expected expectedResults 243 }{ 244 { 245 name: "json file", 246 config: plugins.InstallOptions{File: "plugins.json"}, 247 expected: expectedResults{ 248 output: "installed plugin1 plugin v1.0 (abc123)\ninstalled plugin2 plugin v1.0 (abc123)\n", 249 feedURL: defaultFeedURL, 250 mirror: pkgmgmt.DefaultPackageMirror, 251 }, 252 }, 253 { 254 name: "yaml file", 255 config: plugins.InstallOptions{File: "plugins.yaml"}, 256 expected: expectedResults{ 257 output: "installed plugin1 plugin v1.0 (abc123)\ninstalled plugin2 plugin v1.0 (abc123)\n", 258 feedURL: defaultFeedURL, 259 mirror: pkgmgmt.DefaultPackageMirror, 260 }, 261 }, 262 { 263 name: "with feed url default", 264 config: plugins.InstallOptions{File: "plugins.yaml", InstallOptions: pkgmgmt.InstallOptions{FeedURL: "https://example-feed-url.com/"}}, 265 expected: expectedResults{ 266 output: "installed plugin1 plugin v1.0 (abc123)\ninstalled plugin2 plugin v1.0 (abc123)\n", 267 feedURL: "https://example-feed-url.com/", 268 mirror: pkgmgmt.DefaultPackageMirror, 269 }, 270 }, 271 { 272 name: "with mirror default", 273 config: plugins.InstallOptions{File: "plugins.json", InstallOptions: pkgmgmt.InstallOptions{PackageDownloadOptions: pkgmgmt.PackageDownloadOptions{Mirror: "https://example-mirror.com/"}}}, 274 expected: expectedResults{ 275 output: "installed plugin1 plugin v1.0 (abc123)\ninstalled plugin2 plugin v1.0 (abc123)\n", 276 feedURL: "https://example-mirror.com/plugins/atom.xml", 277 mirror: "https://example-mirror.com/", 278 }, 279 }, 280 { 281 name: "through arg", args: []string{"plugin1"}, 282 config: plugins.InstallOptions{InstallOptions: pkgmgmt.InstallOptions{URL: "https://example.com/"}}, 283 expected: expectedResults{ 284 output: "installed plugin1 plugin v1.0 (abc123)\n", 285 mirror: pkgmgmt.DefaultPackageMirror, 286 }, 287 }, 288 } 289 290 for _, tc := range testcases { 291 t.Run(tc.name, func(t *testing.T) { 292 p := NewTestPorter(t) 293 defer p.Close() 294 295 if tc.config.File != "" { 296 p.TestConfig.TestContext.AddTestFile(fmt.Sprintf("testdata/%s", tc.config.File), fmt.Sprintf("/%s", tc.config.File)) 297 } 298 err := tc.config.Validate(tc.args, p.Context) 299 require.NoError(t, err, "Validate failed") 300 301 pp := p.Plugins.(*client.TestPackageManager) 302 pp.InstallAssertions = append(pp.InstallAssertions, func(installOpt pkgmgmt.InstallOptions) error { 303 assert.Equal(t, tc.expected.feedURL, installOpt.FeedURL) 304 assert.Equal(t, tc.expected.mirror, installOpt.Mirror) 305 306 fmt.Fprint(p.Err, installOpt) 307 return nil 308 }) 309 err = p.InstallPlugin(context.Background(), tc.config) 310 require.NoError(t, err, "InstallPlugin failed") 311 312 gotOutput := p.TestConfig.TestContext.GetOutput() 313 assert.NotEmpty(t, gotOutput) 314 assert.Contains(t, tc.expected.output, gotOutput) 315 }) 316 } 317 } 318 319 func TestPorter_InstallPluginsSchema(t *testing.T) { 320 p := NewTestPorter(t) 321 schema, err := os.ReadFile(filepath.Join(p.RepoRoot, "pkg/schema/plugins.schema.json")) 322 require.NoError(t, err, "failed to read plugins.schema.json file") 323 testcases := []struct { 324 name string 325 path string 326 wantErr string 327 }{ 328 { 329 name: "valid", 330 path: "testdata/plugins.json", 331 wantErr: "", 332 }, 333 { 334 name: "invalid", 335 path: "testdata/invalid-plugins.json", 336 wantErr: "(root): Additional property invalid-field is not allowed\nplugins.plugin1: Additional property random-field is not allowed\n", 337 }, 338 } 339 340 for _, tc := range testcases { 341 t.Run(tc.name, func(t *testing.T) { 342 // Load manifest as a go dump 343 testManifest, err := os.ReadFile(tc.path) 344 require.NoError(t, err, "failed to read %s", tc.path) 345 346 m := make(map[string]interface{}) 347 err = yaml.Unmarshal(testManifest, &m) 348 require.NoError(t, err, "failed to unmarshal %s", tc.path) 349 350 // Load the manifest schema returned from `porter schema` 351 manifestLoader := gojsonschema.NewGoLoader(m) 352 schemaLoader := gojsonschema.NewBytesLoader(schema) 353 354 // Validate the manifest against the schema 355 fails, err := gojsonschema.Validate(schemaLoader, manifestLoader) 356 require.NoError(t, err) 357 358 if tc.wantErr == "" { 359 assert.Empty(t, fails.Errors(), "expected %s to validate against the plugins schema", tc.path) 360 // Print any validation errors returned 361 for _, err := range fails.Errors() { 362 t.Logf("%s", err) 363 } 364 } else { 365 var allFails strings.Builder 366 for _, err := range fails.Errors() { 367 allFails.WriteString(err.String()) 368 allFails.WriteString("\n") 369 } 370 tests.RequireOutputContains(t, tc.wantErr, allFails.String()) 371 } 372 }) 373 } 374 } 375 376 func TestPorter_UninstallPlugin(t *testing.T) { 377 ctx := context.Background() 378 p := NewTestPorter(t) 379 defer p.Close() 380 381 opts := pkgmgmt.UninstallOptions{} 382 err := opts.Validate([]string{"plugin1"}) 383 require.NoError(t, err, "Validate failed") 384 385 err = p.UninstallPlugin(ctx, opts) 386 require.NoError(t, err, "UninstallPlugin failed") 387 388 wantOutput := "Uninstalled plugin1 plugin" 389 gotoutput := p.TestConfig.TestContext.GetOutput() 390 assert.Contains(t, wantOutput, gotoutput) 391 }