github.com/replicatedhq/ship@v0.55.0/pkg/lifecycle/render/github/render_test.go (about) 1 package github 2 3 import ( 4 "path/filepath" 5 "reflect" 6 "testing" 7 8 "github.com/replicatedhq/libyaml" 9 "github.com/replicatedhq/ship/pkg/api" 10 "github.com/replicatedhq/ship/pkg/constants" 11 "github.com/replicatedhq/ship/pkg/templates" 12 "github.com/replicatedhq/ship/pkg/test-mocks/state" 13 "github.com/replicatedhq/ship/pkg/testing/logger" 14 "github.com/spf13/viper" 15 "github.com/stretchr/testify/require" 16 ) 17 18 func Test_getDestPath(t *testing.T) { 19 type args struct { 20 githubPath string 21 asset api.GitHubAsset 22 } 23 tests := []struct { 24 name string 25 args args 26 want string 27 wantErr bool 28 }{ 29 { 30 name: "basic file", 31 args: args{ 32 githubPath: "README.md", 33 asset: api.GitHubAsset{ 34 Path: "README.md", 35 StripPath: "", 36 AssetShared: api.AssetShared{ 37 Dest: "./", 38 }, 39 }, 40 }, 41 want: "README.md", 42 wantErr: false, 43 }, 44 { 45 name: "file in subdir", 46 args: args{ 47 githubPath: "subdir/README.md", 48 asset: api.GitHubAsset{ 49 Path: "subdir/", 50 StripPath: "", 51 AssetShared: api.AssetShared{ 52 Dest: "./", 53 }, 54 }, 55 }, 56 want: "subdir/README.md", 57 wantErr: false, 58 }, 59 { 60 name: "file in subdir with dest dir", 61 args: args{ 62 githubPath: "subdir/README.md", 63 asset: api.GitHubAsset{ 64 Path: "subdir/", 65 StripPath: "", 66 AssetShared: api.AssetShared{ 67 Dest: "./dest", 68 }, 69 }, 70 }, 71 want: "dest/subdir/README.md", 72 wantErr: false, 73 }, 74 { 75 name: "file in stripped subdir with dest dir", 76 args: args{ 77 githubPath: "subdir/README.md", 78 asset: api.GitHubAsset{ 79 Path: "subdir/", 80 StripPath: "true", 81 AssetShared: api.AssetShared{ 82 Dest: "./dest", 83 }, 84 }, 85 }, 86 want: "dest/README.md", 87 wantErr: false, 88 }, 89 { 90 name: "literal file in stripped subdir with dest dir", 91 args: args{ 92 githubPath: "dir/subdir/README.md", 93 asset: api.GitHubAsset{ 94 Path: "dir/subdir/README.md", 95 StripPath: "true", 96 AssetShared: api.AssetShared{ 97 Dest: "dest", 98 }, 99 }, 100 }, 101 want: "dest/README.md", 102 wantErr: false, 103 }, 104 { 105 name: "file in stripped subdir that lacks a trailing slash with dest dir", 106 args: args{ 107 githubPath: "dir/subdir/README.md", 108 asset: api.GitHubAsset{ 109 Path: "dir/subdir", 110 StripPath: "true", 111 AssetShared: api.AssetShared{ 112 Dest: "dest", 113 }, 114 }, 115 }, 116 want: "dest/README.md", 117 wantErr: false, 118 }, 119 { 120 name: "templated dest dir", 121 args: args{ 122 githubPath: "dir/subdir/README.md", 123 asset: api.GitHubAsset{ 124 Path: "dir/subdir", 125 StripPath: "false", 126 AssetShared: api.AssetShared{ 127 Dest: "dest{{repl Add 1 1}}", 128 }, 129 }, 130 }, 131 want: "dest2/dir/subdir/README.md", 132 wantErr: false, 133 }, 134 { 135 name: "templated stripPath (eval to true)", 136 args: args{ 137 githubPath: "dir/subdir/README.md", 138 asset: api.GitHubAsset{ 139 Path: "dir/subdir", 140 StripPath: `{{repl ParseBool "true"}}`, 141 AssetShared: api.AssetShared{ 142 Dest: "dest", 143 }, 144 }, 145 }, 146 want: "dest/README.md", 147 wantErr: false, 148 }, 149 { 150 name: "templated stripPath (eval to false)", 151 args: args{ 152 githubPath: "dir/subdir/README.md", 153 asset: api.GitHubAsset{ 154 Path: "dir/subdir", 155 StripPath: `{{repl ParseBool "false"}}`, 156 AssetShared: api.AssetShared{ 157 Dest: "dest", 158 }, 159 }, 160 }, 161 want: "dest/dir/subdir/README.md", 162 wantErr: false, 163 }, 164 { 165 name: "strip path of root dir file", 166 args: args{ 167 githubPath: "README.md", 168 asset: api.GitHubAsset{ 169 Path: "", 170 StripPath: "true", 171 AssetShared: api.AssetShared{ 172 Dest: "dest", 173 }, 174 }, 175 }, 176 want: "dest/README.md", 177 wantErr: false, 178 }, 179 { 180 name: "not a valid template function (dest)", 181 args: args{ 182 githubPath: "README.md", 183 asset: api.GitHubAsset{ 184 Path: "", 185 StripPath: "true", 186 AssetShared: api.AssetShared{ 187 Dest: "{{repl NotATemplateFunction }}", 188 }, 189 }, 190 }, 191 want: "", 192 wantErr: true, 193 }, 194 { 195 name: "not a valid template function (stripPath)", 196 args: args{ 197 githubPath: "README.md", 198 asset: api.GitHubAsset{ 199 Path: "", 200 StripPath: "{{repl NotATemplateFunction }}", 201 AssetShared: api.AssetShared{ 202 Dest: "dest", 203 }, 204 }, 205 }, 206 want: "", 207 wantErr: true, 208 }, 209 { 210 name: "file in root", 211 args: args{ 212 githubPath: "subdir/README.md", 213 asset: api.GitHubAsset{ 214 Path: "", 215 StripPath: "", 216 AssetShared: api.AssetShared{ 217 Dest: "/bin/runc", 218 }, 219 }, 220 }, 221 want: "", 222 wantErr: true, 223 }, 224 { 225 name: "file in parent dir", 226 args: args{ 227 githubPath: "subdir/README.md", 228 asset: api.GitHubAsset{ 229 Path: "abc/", 230 StripPath: "", 231 AssetShared: api.AssetShared{ 232 Dest: "../../../bin/runc", 233 }, 234 }, 235 }, 236 want: "", 237 wantErr: true, 238 }, 239 } 240 for _, tt := range tests { 241 t.Run(tt.name, func(t *testing.T) { 242 req := require.New(t) 243 244 testLogger := &logger.TestLogger{T: t} 245 v := viper.New() 246 bb := templates.NewBuilderBuilder(testLogger, v, &state.MockManager{}) 247 builder, err := bb.FullBuilder(api.ReleaseMetadata{}, []libyaml.ConfigGroup{}, map[string]interface{}{}) 248 req.NoError(err) 249 250 got, err := getDestPath(tt.args.githubPath, tt.args.asset, builder) 251 if !tt.wantErr { 252 req.NoErrorf(err, "getDestPath(%s, %+v, builder) error = %v", tt.args.githubPath, tt.args.asset, err) 253 } else { 254 req.Error(err) 255 } 256 257 // convert the returned file to forwardslash format before testing - otherwise this test fails when the separator isn't '/' 258 req.Equal(tt.want, filepath.ToSlash(got)) 259 }) 260 } 261 } 262 263 func Test_getDestPathNoProxy(t *testing.T) { 264 tests := []struct { 265 name string 266 asset api.GitHubAsset 267 want string 268 wantErr bool 269 renderRoot string 270 }{ 271 { 272 name: "basic file", 273 asset: api.GitHubAsset{ 274 Path: "README.md", 275 StripPath: "", 276 AssetShared: api.AssetShared{ 277 Dest: "./", 278 }, 279 }, 280 want: "installer/README.md", 281 wantErr: false, 282 }, 283 { 284 name: "basic file, no installer prefix", 285 renderRoot: "./", 286 asset: api.GitHubAsset{ 287 Path: "README.md", 288 StripPath: "", 289 AssetShared: api.AssetShared{ 290 Dest: "./", 291 }, 292 }, 293 want: "README.md", 294 wantErr: false, 295 }, 296 { 297 name: "basic file, customer installer prefix", 298 renderRoot: "my-installer", 299 asset: api.GitHubAsset{ 300 Path: "README.md", 301 StripPath: "", 302 AssetShared: api.AssetShared{ 303 Dest: "./", 304 }, 305 }, 306 want: "my-installer/README.md", 307 wantErr: false, 308 }, 309 { 310 name: "file in subdir", 311 asset: api.GitHubAsset{ 312 Path: "subdir/README.md", 313 StripPath: "", 314 AssetShared: api.AssetShared{ 315 Dest: "./", 316 }, 317 }, 318 want: "installer/subdir/README.md", 319 wantErr: false, 320 }, 321 { 322 name: "file in subdir with dest dir", 323 asset: api.GitHubAsset{ 324 Path: "subdir/README.md", 325 StripPath: "", 326 AssetShared: api.AssetShared{ 327 Dest: "./dest", 328 }, 329 }, 330 want: "installer/dest/subdir/README.md", 331 wantErr: false, 332 }, 333 { 334 name: "file in stripped subdir with dest dir", 335 asset: api.GitHubAsset{ 336 Path: "subdir/README.md", 337 StripPath: "true", 338 AssetShared: api.AssetShared{ 339 Dest: "./dest", 340 }, 341 }, 342 want: "installer/dest/README.md", 343 wantErr: false, 344 }, 345 { 346 name: "literal file in stripped subdir with dest dir", 347 asset: api.GitHubAsset{ 348 Path: "dir/subdir/README.md", 349 StripPath: "true", 350 AssetShared: api.AssetShared{ 351 Dest: "dest", 352 }, 353 }, 354 want: "installer/dest/README.md", 355 wantErr: false, 356 }, 357 { 358 name: "templated dest dir", 359 asset: api.GitHubAsset{ 360 Path: "dir/subdir/README.md", 361 StripPath: "false", 362 AssetShared: api.AssetShared{ 363 Dest: "dest{{repl Add 1 1}}", 364 }, 365 }, 366 want: "installer/dest2/dir/subdir/README.md", 367 wantErr: false, 368 }, 369 { 370 name: "templated stripPath (eval to true)", 371 asset: api.GitHubAsset{ 372 Path: "dir/subdir/README.md", 373 StripPath: `{{repl ParseBool "true"}}`, 374 AssetShared: api.AssetShared{ 375 Dest: "dest", 376 }, 377 }, 378 want: "installer/dest/README.md", 379 wantErr: false, 380 }, 381 { 382 name: "templated stripPath (eval to false)", 383 asset: api.GitHubAsset{ 384 Path: "dir/subdir/README.md", 385 StripPath: `{{repl ParseBool "false"}}`, 386 AssetShared: api.AssetShared{ 387 Dest: "dest", 388 }, 389 }, 390 want: "installer/dest/dir/subdir/README.md", 391 wantErr: false, 392 }, 393 { 394 name: "strip path of root dir file", 395 asset: api.GitHubAsset{ 396 Path: "README.md", 397 StripPath: "true", 398 AssetShared: api.AssetShared{ 399 Dest: "dest", 400 }, 401 }, 402 want: "installer/dest/README.md", 403 wantErr: false, 404 }, 405 { 406 name: "not a valid template function (dest)", 407 asset: api.GitHubAsset{ 408 Path: "README.md", 409 StripPath: "true", 410 AssetShared: api.AssetShared{ 411 Dest: "{{repl NotATemplateFunction }}", 412 }, 413 }, 414 want: "", 415 wantErr: true, 416 }, 417 { 418 name: "not a valid template function (stripPath)", 419 asset: api.GitHubAsset{ 420 Path: "README.md", 421 StripPath: "{{repl NotATemplateFunction }}", 422 AssetShared: api.AssetShared{ 423 Dest: "dest", 424 }, 425 }, 426 want: "", 427 wantErr: true, 428 }, 429 } 430 for _, tt := range tests { 431 t.Run(tt.name, func(t *testing.T) { 432 req := require.New(t) 433 434 testLogger := &logger.TestLogger{T: t} 435 v := viper.New() 436 bb := templates.NewBuilderBuilder(testLogger, v, &state.MockManager{}) 437 builder, err := bb.FullBuilder(api.ReleaseMetadata{}, []libyaml.ConfigGroup{}, map[string]interface{}{}) 438 req.NoError(err) 439 440 if tt.renderRoot == "" { 441 tt.renderRoot = constants.InstallerPrefixPath 442 } 443 got, err := (&LocalRenderer{Logger: testLogger}).getDestPathNoProxy(tt.asset, builder, tt.renderRoot) 444 if !tt.wantErr { 445 req.NoError(err) 446 } else { 447 req.Error(err) 448 } 449 450 // convert the returned file to forwardslash format before testing - otherwise this test fails when the separator isn't '/' 451 req.Equal(tt.want, filepath.ToSlash(got)) 452 }) 453 } 454 } 455 456 func Test_filterGithubContents(t *testing.T) { 457 type args struct { 458 githubContents []api.GithubContent 459 asset api.GitHubAsset 460 } 461 tests := []struct { 462 name string 463 args args 464 want []api.GithubFile 465 }{ 466 { 467 name: "has slash prefix and suffix", 468 args: args{ 469 githubContents: []api.GithubContent{{ 470 Path: "subdir", 471 Files: []api.GithubFile{{Name: "1"}}, 472 }}, 473 asset: api.GitHubAsset{ 474 Path: "/subdir/", 475 }, 476 }, 477 want: []api.GithubFile{{Name: "1"}}, 478 }, 479 { 480 name: "is root", 481 args: args{ 482 githubContents: []api.GithubContent{{ 483 Path: "/", 484 Files: []api.GithubFile{{Name: "1"}}, 485 }}, 486 asset: api.GitHubAsset{ 487 Path: "/", 488 }, 489 }, 490 want: []api.GithubFile{{Name: "1"}}, 491 }, 492 } 493 for _, tt := range tests { 494 t.Run(tt.name, func(t *testing.T) { 495 if got := filterGithubContents(tt.args.githubContents, tt.args.asset); !reflect.DeepEqual(got, tt.want) { 496 t.Errorf("filterGithubContent() = %v, want %v", got, tt.want) 497 } 498 }) 499 } 500 } 501 502 func Test_createUpstreamURL(t *testing.T) { 503 tests := []struct { 504 name string 505 asset api.GitHubAsset 506 configGroups []libyaml.ConfigGroup 507 want string 508 }{ 509 { 510 name: "no templated values", 511 asset: api.GitHubAsset{ 512 Repo: "org/repo", 513 Ref: "some-ref", 514 Path: "a-path", 515 }, 516 configGroups: []libyaml.ConfigGroup{}, 517 want: "github.com/org/repo/tree/some-ref/a-path", 518 }, 519 { 520 name: "templated repo, ref, and path", 521 asset: api.GitHubAsset{ 522 Repo: "{{repl ConfigOption \"drink\"}}", 523 Ref: "{{repl ConfigOption \"fruity\"}}", 524 Path: "{{repl ConfigOption \"cinammon\"}}", 525 }, 526 configGroups: []libyaml.ConfigGroup{ 527 { 528 Name: "cereal", 529 Items: []*libyaml.ConfigItem{ 530 { 531 Name: "drink", 532 Value: "org/repo", 533 }, 534 { 535 Name: "fruity", 536 Value: "pomegranate", 537 }, 538 { 539 Name: "cinammon", 540 Value: "horchata", 541 }, 542 }, 543 }, 544 }, 545 want: "github.com/org/repo/tree/pomegranate/horchata", 546 }, 547 { 548 name: "templated path to file", 549 asset: api.GitHubAsset{ 550 Repo: "org/repo", 551 Ref: "{{repl ConfigOption \"fruity\"}}", 552 Path: "{{repl ConfigOption \"hotdog\"}}", 553 }, 554 configGroups: []libyaml.ConfigGroup{ 555 { 556 Name: "cereal", 557 Items: []*libyaml.ConfigItem{ 558 { 559 Name: "drink", 560 Value: "org/repo", 561 }, 562 { 563 Name: "fruity", 564 Value: "pomegranate", 565 }, 566 { 567 Name: "hotdog", 568 Value: "/some/another/file.yml", 569 }, 570 }, 571 }, 572 }, 573 want: "github.com/org/repo/blob/pomegranate/some/another/file.yml", 574 }, 575 { 576 name: "templated values with empty ref defaults to master", 577 asset: api.GitHubAsset{ 578 Repo: "{{repl ConfigOption \"drink\"}}", 579 Ref: "{{repl ConfigOption \"fruity\"}}", 580 Path: "{{repl ConfigOption \"hotdog\"}}", 581 }, 582 configGroups: []libyaml.ConfigGroup{ 583 { 584 Name: "cereal", 585 Items: []*libyaml.ConfigItem{ 586 { 587 Name: "drink", 588 Value: "another/some-other", 589 }, 590 { 591 Name: "fruity", 592 Value: "", 593 }, 594 { 595 Name: "hotdog", 596 Value: "/going/places/ship.yml", 597 }, 598 }, 599 }, 600 }, 601 want: "github.com/another/some-other/blob/master/going/places/ship.yml", 602 }, 603 } 604 for _, tt := range tests { 605 req := require.New(t) 606 t.Run(tt.name, func(t *testing.T) { 607 v := viper.New() 608 testLogger := &logger.TestLogger{T: t} 609 bb := templates.NewBuilderBuilder(testLogger, v, &state.MockManager{}) 610 builder, err := bb.FullBuilder(api.ReleaseMetadata{}, tt.configGroups, map[string]interface{}{}) 611 req.NoError(err) 612 613 got, err := createUpstreamURL(tt.asset, builder) 614 req.NoError(err) 615 req.Equal(tt.want, got) 616 }) 617 } 618 }