github.com/vnforks/kid@v5.11.1+incompatible/model/manifest_test.go (about) 1 // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package model 5 6 import ( 7 "encoding/json" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 "strings" 12 "testing" 13 14 "gopkg.in/yaml.v2" 15 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/require" 18 ) 19 20 func TestFindManifest(t *testing.T) { 21 for _, tc := range []struct { 22 Filename string 23 Contents string 24 ExpectError bool 25 ExpectNotExist bool 26 }{ 27 {"foo", "bar", true, true}, 28 {"plugin.json", "bar", true, false}, 29 {"plugin.json", `{"id": "foo"}`, false, false}, 30 {"plugin.json", `{"id": "FOO"}`, false, false}, 31 {"plugin.yaml", `id: foo`, false, false}, 32 {"plugin.yaml", "bar", true, false}, 33 {"plugin.yml", `id: foo`, false, false}, 34 {"plugin.yml", `id: FOO`, false, false}, 35 {"plugin.yml", "bar", true, false}, 36 } { 37 dir, err := ioutil.TempDir("", "mm-plugin-test") 38 require.NoError(t, err) 39 defer os.RemoveAll(dir) 40 41 path := filepath.Join(dir, tc.Filename) 42 f, err := os.Create(path) 43 require.NoError(t, err) 44 _, err = f.WriteString(tc.Contents) 45 f.Close() 46 require.NoError(t, err) 47 48 m, mpath, err := FindManifest(dir) 49 assert.True(t, (err != nil) == tc.ExpectError, tc.Filename) 50 assert.True(t, (err != nil && os.IsNotExist(err)) == tc.ExpectNotExist, tc.Filename) 51 if !tc.ExpectNotExist { 52 assert.Equal(t, path, mpath, tc.Filename) 53 } else { 54 assert.Empty(t, mpath, tc.Filename) 55 } 56 if !tc.ExpectError { 57 require.NotNil(t, m, tc.Filename) 58 assert.NotEmpty(t, m.Id, tc.Filename) 59 assert.Equal(t, strings.ToLower(m.Id), m.Id) 60 } 61 } 62 } 63 64 func TestManifestUnmarshal(t *testing.T) { 65 expected := Manifest{ 66 Id: "theid", 67 MinServerVersion: "5.6.0", 68 Server: &ManifestServer{ 69 Executable: "theexecutable", 70 Executables: &ManifestExecutables{ 71 LinuxAmd64: "theexecutable-linux-amd64", 72 DarwinAmd64: "theexecutable-darwin-amd64", 73 WindowsAmd64: "theexecutable-windows-amd64", 74 }, 75 }, 76 Webapp: &ManifestWebapp{ 77 BundlePath: "thebundlepath", 78 }, 79 SettingsSchema: &PluginSettingsSchema{ 80 Header: "theheadertext", 81 Footer: "thefootertext", 82 Settings: []*PluginSetting{ 83 &PluginSetting{ 84 Key: "thesetting", 85 DisplayName: "thedisplayname", 86 Type: "dropdown", 87 HelpText: "thehelptext", 88 RegenerateHelpText: "theregeneratehelptext", 89 Placeholder: "theplaceholder", 90 Options: []*PluginOption{ 91 &PluginOption{ 92 DisplayName: "theoptiondisplayname", 93 Value: "thevalue", 94 }, 95 }, 96 Default: "thedefault", 97 }, 98 }, 99 }, 100 } 101 102 var yamlResult Manifest 103 require.NoError(t, yaml.Unmarshal([]byte(` 104 id: theid 105 min_server_version: 5.6.0 106 server: 107 executable: theexecutable 108 executables: 109 linux-amd64: theexecutable-linux-amd64 110 darwin-amd64: theexecutable-darwin-amd64 111 windows-amd64: theexecutable-windows-amd64 112 webapp: 113 bundle_path: thebundlepath 114 settings_schema: 115 header: theheadertext 116 footer: thefootertext 117 settings: 118 - key: thesetting 119 display_name: thedisplayname 120 type: dropdown 121 help_text: thehelptext 122 regenerate_help_text: theregeneratehelptext 123 placeholder: theplaceholder 124 options: 125 - display_name: theoptiondisplayname 126 value: thevalue 127 default: thedefault 128 `), &yamlResult)) 129 assert.Equal(t, expected, yamlResult) 130 131 var jsonResult Manifest 132 require.NoError(t, json.Unmarshal([]byte(`{ 133 "id": "theid", 134 "min_server_version": "5.6.0", 135 "server": { 136 "executable": "theexecutable", 137 "executables": { 138 "linux-amd64": "theexecutable-linux-amd64", 139 "darwin-amd64": "theexecutable-darwin-amd64", 140 "windows-amd64": "theexecutable-windows-amd64" 141 } 142 }, 143 "webapp": { 144 "bundle_path": "thebundlepath" 145 }, 146 "settings_schema": { 147 "header": "theheadertext", 148 "footer": "thefootertext", 149 "settings": [ 150 { 151 "key": "thesetting", 152 "display_name": "thedisplayname", 153 "type": "dropdown", 154 "help_text": "thehelptext", 155 "regenerate_help_text": "theregeneratehelptext", 156 "placeholder": "theplaceholder", 157 "options": [ 158 { 159 "display_name": "theoptiondisplayname", 160 "value": "thevalue" 161 } 162 ], 163 "default": "thedefault" 164 } 165 ] 166 } 167 }`), &jsonResult)) 168 assert.Equal(t, expected, jsonResult) 169 } 170 171 func TestFindManifest_FileErrors(t *testing.T) { 172 for _, tc := range []string{"plugin.yaml", "plugin.json"} { 173 dir, err := ioutil.TempDir("", "mm-plugin-test") 174 require.NoError(t, err) 175 defer os.RemoveAll(dir) 176 177 path := filepath.Join(dir, tc) 178 require.NoError(t, os.Mkdir(path, 0700)) 179 180 m, mpath, err := FindManifest(dir) 181 assert.Nil(t, m) 182 assert.Equal(t, path, mpath) 183 assert.Error(t, err, tc) 184 assert.False(t, os.IsNotExist(err), tc) 185 } 186 } 187 188 func TestFindManifest_FolderPermission(t *testing.T) { 189 if os.Geteuid() == 0 { 190 t.Skip("skipping test while running as root: can't effectively remove permissions") 191 } 192 193 for _, tc := range []string{"plugin.yaml", "plugin.json"} { 194 dir, err := ioutil.TempDir("", "mm-plugin-test") 195 require.NoError(t, err) 196 defer os.RemoveAll(dir) 197 198 path := filepath.Join(dir, tc) 199 require.NoError(t, os.Mkdir(path, 0700)) 200 201 // User does not have permission in the plugin folder 202 err = os.Chmod(dir, 0066) 203 require.NoError(t, err) 204 205 m, mpath, err := FindManifest(dir) 206 assert.Nil(t, m) 207 assert.Equal(t, "", mpath) 208 assert.Error(t, err, tc) 209 assert.False(t, os.IsNotExist(err), tc) 210 } 211 } 212 213 func TestManifestJson(t *testing.T) { 214 manifest := &Manifest{ 215 Id: "theid", 216 Server: &ManifestServer{ 217 Executable: "theexecutable", 218 }, 219 Webapp: &ManifestWebapp{ 220 BundlePath: "thebundlepath", 221 }, 222 SettingsSchema: &PluginSettingsSchema{ 223 Header: "theheadertext", 224 Footer: "thefootertext", 225 Settings: []*PluginSetting{ 226 &PluginSetting{ 227 Key: "thesetting", 228 DisplayName: "thedisplayname", 229 Type: "dropdown", 230 HelpText: "thehelptext", 231 RegenerateHelpText: "theregeneratehelptext", 232 Placeholder: "theplaceholder", 233 Options: []*PluginOption{ 234 &PluginOption{ 235 DisplayName: "theoptiondisplayname", 236 Value: "thevalue", 237 }, 238 }, 239 Default: "thedefault", 240 }, 241 }, 242 }, 243 } 244 245 json := manifest.ToJson() 246 newManifest := ManifestFromJson(strings.NewReader(json)) 247 assert.Equal(t, newManifest, manifest) 248 assert.Equal(t, newManifest.ToJson(), json) 249 assert.Equal(t, ManifestFromJson(strings.NewReader("junk")), (*Manifest)(nil)) 250 251 manifestList := []*Manifest{manifest} 252 json = ManifestListToJson(manifestList) 253 newManifestList := ManifestListFromJson(strings.NewReader(json)) 254 assert.Equal(t, newManifestList, manifestList) 255 assert.Equal(t, ManifestListToJson(newManifestList), json) 256 } 257 258 func TestManifestHasClient(t *testing.T) { 259 manifest := &Manifest{ 260 Id: "theid", 261 Server: &ManifestServer{ 262 Executable: "theexecutable", 263 }, 264 Webapp: &ManifestWebapp{ 265 BundlePath: "thebundlepath", 266 }, 267 } 268 269 assert.True(t, manifest.HasClient()) 270 271 manifest.Webapp = nil 272 assert.False(t, manifest.HasClient()) 273 } 274 275 func TestManifestClientManifest(t *testing.T) { 276 manifest := &Manifest{ 277 Id: "theid", 278 Name: "thename", 279 Description: "thedescription", 280 Version: "0.0.1", 281 MinServerVersion: "5.6.0", 282 Server: &ManifestServer{ 283 Executable: "theexecutable", 284 }, 285 Webapp: &ManifestWebapp{ 286 BundlePath: "thebundlepath", 287 BundleHash: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, 288 }, 289 SettingsSchema: &PluginSettingsSchema{ 290 Header: "theheadertext", 291 Footer: "thefootertext", 292 Settings: []*PluginSetting{ 293 &PluginSetting{ 294 Key: "thesetting", 295 DisplayName: "thedisplayname", 296 Type: "dropdown", 297 HelpText: "thehelptext", 298 RegenerateHelpText: "theregeneratehelptext", 299 Placeholder: "theplaceholder", 300 Options: []*PluginOption{ 301 &PluginOption{ 302 DisplayName: "theoptiondisplayname", 303 Value: "thevalue", 304 }, 305 }, 306 Default: "thedefault", 307 }, 308 }, 309 }, 310 } 311 312 sanitized := manifest.ClientManifest() 313 314 assert.Equal(t, manifest.Id, sanitized.Id) 315 assert.Equal(t, manifest.Version, sanitized.Version) 316 assert.Equal(t, manifest.MinServerVersion, sanitized.MinServerVersion) 317 assert.Equal(t, "/static/theid/theid_000102030405060708090a0b0c0d0e0f_bundle.js", sanitized.Webapp.BundlePath) 318 assert.Equal(t, manifest.Webapp.BundleHash, sanitized.Webapp.BundleHash) 319 assert.Equal(t, manifest.SettingsSchema, sanitized.SettingsSchema) 320 assert.Empty(t, sanitized.Name) 321 assert.Empty(t, sanitized.Description) 322 assert.Empty(t, sanitized.Server) 323 324 assert.NotEmpty(t, manifest.Id) 325 assert.NotEmpty(t, manifest.Version) 326 assert.NotEmpty(t, manifest.MinServerVersion) 327 assert.NotEmpty(t, manifest.Webapp) 328 assert.NotEmpty(t, manifest.Name) 329 assert.NotEmpty(t, manifest.Description) 330 assert.NotEmpty(t, manifest.Server) 331 assert.NotEmpty(t, manifest.SettingsSchema) 332 } 333 334 func TestManifestGetExecutableForRuntime(t *testing.T) { 335 testCases := []struct { 336 Description string 337 Manifest *Manifest 338 GoOs string 339 GoArch string 340 ExpectedExecutable string 341 }{ 342 { 343 "no server", 344 &Manifest{}, 345 "linux", 346 "amd64", 347 "", 348 }, 349 { 350 "no executable", 351 &Manifest{ 352 Server: &ManifestServer{}, 353 }, 354 "linux", 355 "amd64", 356 "", 357 }, 358 { 359 "single executable", 360 &Manifest{ 361 Server: &ManifestServer{ 362 Executable: "path/to/executable", 363 }, 364 }, 365 "linux", 366 "amd64", 367 "path/to/executable", 368 }, 369 { 370 "single executable, different runtime", 371 &Manifest{ 372 Server: &ManifestServer{ 373 Executable: "path/to/executable", 374 }, 375 }, 376 "darwin", 377 "amd64", 378 "path/to/executable", 379 }, 380 { 381 "multiple executables, no match", 382 &Manifest{ 383 Server: &ManifestServer{ 384 Executables: &ManifestExecutables{ 385 LinuxAmd64: "linux-amd64/path/to/executable", 386 DarwinAmd64: "darwin-amd64/path/to/executable", 387 WindowsAmd64: "windows-amd64/path/to/executable", 388 }, 389 }, 390 }, 391 "other", 392 "amd64", 393 "", 394 }, 395 { 396 "multiple executables, linux-amd64 match", 397 &Manifest{ 398 Server: &ManifestServer{ 399 Executables: &ManifestExecutables{ 400 LinuxAmd64: "linux-amd64/path/to/executable", 401 DarwinAmd64: "darwin-amd64/path/to/executable", 402 WindowsAmd64: "windows-amd64/path/to/executable", 403 }, 404 }, 405 }, 406 "linux", 407 "amd64", 408 "linux-amd64/path/to/executable", 409 }, 410 { 411 "multiple executables, linux-amd64 match, single executable ignored", 412 &Manifest{ 413 Server: &ManifestServer{ 414 Executables: &ManifestExecutables{ 415 LinuxAmd64: "linux-amd64/path/to/executable", 416 DarwinAmd64: "darwin-amd64/path/to/executable", 417 WindowsAmd64: "windows-amd64/path/to/executable", 418 }, 419 Executable: "path/to/executable", 420 }, 421 }, 422 "linux", 423 "amd64", 424 "linux-amd64/path/to/executable", 425 }, 426 { 427 "multiple executables, darwin-amd64 match", 428 &Manifest{ 429 Server: &ManifestServer{ 430 Executables: &ManifestExecutables{ 431 LinuxAmd64: "linux-amd64/path/to/executable", 432 DarwinAmd64: "darwin-amd64/path/to/executable", 433 WindowsAmd64: "windows-amd64/path/to/executable", 434 }, 435 }, 436 }, 437 "darwin", 438 "amd64", 439 "darwin-amd64/path/to/executable", 440 }, 441 { 442 "multiple executables, windows-amd64 match", 443 &Manifest{ 444 Server: &ManifestServer{ 445 Executables: &ManifestExecutables{ 446 LinuxAmd64: "linux-amd64/path/to/executable", 447 DarwinAmd64: "darwin-amd64/path/to/executable", 448 WindowsAmd64: "windows-amd64/path/to/executable", 449 }, 450 }, 451 }, 452 "windows", 453 "amd64", 454 "windows-amd64/path/to/executable", 455 }, 456 { 457 "multiple executables, no match, single executable fallback", 458 &Manifest{ 459 Server: &ManifestServer{ 460 Executables: &ManifestExecutables{ 461 LinuxAmd64: "linux-amd64/path/to/executable", 462 DarwinAmd64: "darwin-amd64/path/to/executable", 463 WindowsAmd64: "windows-amd64/path/to/executable", 464 }, 465 Executable: "path/to/executable", 466 }, 467 }, 468 "other", 469 "amd64", 470 "path/to/executable", 471 }, 472 { 473 "deprecated backend field, ignored since server present", 474 &Manifest{ 475 Server: &ManifestServer{ 476 Executables: &ManifestExecutables{ 477 LinuxAmd64: "linux-amd64/path/to/executable", 478 DarwinAmd64: "darwin-amd64/path/to/executable", 479 WindowsAmd64: "windows-amd64/path/to/executable", 480 }, 481 }, 482 Backend: &ManifestServer{ 483 Executables: &ManifestExecutables{ 484 LinuxAmd64: "linux-amd64/path/to/executable/backend", 485 DarwinAmd64: "darwin-amd64/path/to/executable/backend", 486 WindowsAmd64: "windows-amd64/path/to/executable/backend", 487 }, 488 }, 489 }, 490 "linux", 491 "amd64", 492 "linux-amd64/path/to/executable", 493 }, 494 { 495 "deprecated backend field used, since no server present", 496 &Manifest{ 497 Backend: &ManifestServer{ 498 Executables: &ManifestExecutables{ 499 LinuxAmd64: "linux-amd64/path/to/executable/backend", 500 DarwinAmd64: "darwin-amd64/path/to/executable/backend", 501 WindowsAmd64: "windows-amd64/path/to/executable/backend", 502 }, 503 }, 504 }, 505 "linux", 506 "amd64", 507 "linux-amd64/path/to/executable/backend", 508 }, 509 } 510 511 for _, testCase := range testCases { 512 t.Run(testCase.Description, func(t *testing.T) { 513 assert.Equal( 514 t, 515 testCase.ExpectedExecutable, 516 testCase.Manifest.GetExecutableForRuntime(testCase.GoOs, testCase.GoArch), 517 ) 518 }) 519 } 520 } 521 522 func TestManifestHasServer(t *testing.T) { 523 testCases := []struct { 524 Description string 525 Manifest *Manifest 526 Expected bool 527 }{ 528 { 529 "no server", 530 &Manifest{}, 531 false, 532 }, 533 { 534 "no executable, but server still considered present", 535 &Manifest{ 536 Server: &ManifestServer{}, 537 }, 538 true, 539 }, 540 { 541 "single executable", 542 &Manifest{ 543 Server: &ManifestServer{ 544 Executable: "path/to/executable", 545 }, 546 }, 547 true, 548 }, 549 { 550 "multiple executables", 551 &Manifest{ 552 Server: &ManifestServer{ 553 Executables: &ManifestExecutables{ 554 LinuxAmd64: "linux-amd64/path/to/executable", 555 DarwinAmd64: "darwin-amd64/path/to/executable", 556 WindowsAmd64: "windows-amd64/path/to/executable", 557 }, 558 }, 559 }, 560 true, 561 }, 562 { 563 "single executable defined via deprecated backend", 564 &Manifest{ 565 Backend: &ManifestServer{ 566 Executable: "path/to/executable", 567 }, 568 }, 569 true, 570 }, 571 { 572 "multiple executables defined via deprecated backend", 573 &Manifest{ 574 Backend: &ManifestServer{ 575 Executables: &ManifestExecutables{ 576 LinuxAmd64: "linux-amd64/path/to/executable", 577 DarwinAmd64: "darwin-amd64/path/to/executable", 578 WindowsAmd64: "windows-amd64/path/to/executable", 579 }, 580 }, 581 }, 582 true, 583 }, 584 } 585 586 for _, testCase := range testCases { 587 t.Run(testCase.Description, func(t *testing.T) { 588 assert.Equal(t, testCase.Expected, testCase.Manifest.HasServer()) 589 }) 590 } 591 } 592 593 func TestManifestHasWebapp(t *testing.T) { 594 testCases := []struct { 595 Description string 596 Manifest *Manifest 597 Expected bool 598 }{ 599 { 600 "no webapp", 601 &Manifest{}, 602 false, 603 }, 604 { 605 "no bundle path, but webapp still considered present", 606 &Manifest{ 607 Webapp: &ManifestWebapp{}, 608 }, 609 true, 610 }, 611 { 612 "bundle path defined", 613 &Manifest{ 614 Webapp: &ManifestWebapp{ 615 BundlePath: "path/to/bundle", 616 }, 617 }, 618 true, 619 }, 620 } 621 622 for _, testCase := range testCases { 623 t.Run(testCase.Description, func(t *testing.T) { 624 assert.Equal(t, testCase.Expected, testCase.Manifest.HasWebapp()) 625 }) 626 } 627 } 628 629 func TestManifestMeetMinServerVersion(t *testing.T) { 630 for name, test := range map[string]struct { 631 MinServerVersion string 632 ServerVersion string 633 ShouldError bool 634 ShouldFulfill bool 635 }{ 636 "generously fulfilled": { 637 MinServerVersion: "5.5.0", 638 ServerVersion: "5.6.0", 639 ShouldError: false, 640 ShouldFulfill: true, 641 }, 642 "exactly fulfilled": { 643 MinServerVersion: "5.6.0", 644 ServerVersion: "5.6.0", 645 ShouldError: false, 646 ShouldFulfill: true, 647 }, 648 "not fulfilled": { 649 MinServerVersion: "5.6.0", 650 ServerVersion: "5.5.0", 651 ShouldError: false, 652 ShouldFulfill: false, 653 }, 654 "fail to parse MinServerVersion": { 655 MinServerVersion: "abc", 656 ServerVersion: "5.5.0", 657 ShouldError: true, 658 }, 659 } { 660 t.Run(name, func(t *testing.T) { 661 assert := assert.New(t) 662 663 manifest := Manifest{ 664 MinServerVersion: test.MinServerVersion, 665 } 666 fulfilled, err := manifest.MeetMinServerVersion(test.ServerVersion) 667 668 if test.ShouldError { 669 assert.NotNil(err) 670 assert.False(fulfilled) 671 return 672 } 673 assert.Nil(err) 674 assert.Equal(test.ShouldFulfill, fulfilled) 675 }) 676 } 677 }