github.com/windmeup/goreleaser@v1.21.95/internal/pipe/sbom/sbom_test.go (about) 1 package sbom 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "sort" 8 "strings" 9 "testing" 10 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 "github.com/windmeup/goreleaser/internal/artifact" 14 "github.com/windmeup/goreleaser/internal/skips" 15 "github.com/windmeup/goreleaser/internal/testctx" 16 "github.com/windmeup/goreleaser/internal/testlib" 17 "github.com/windmeup/goreleaser/internal/tmpl" 18 "github.com/windmeup/goreleaser/pkg/config" 19 "github.com/windmeup/goreleaser/pkg/context" 20 ) 21 22 func TestDescription(t *testing.T) { 23 require.NotEmpty(t, Pipe{}.String()) 24 } 25 26 func TestSBOMCatalogDefault(t *testing.T) { 27 defaultArgs := []string{"$artifact", "--file", "$document", "--output", "spdx-json"} 28 defaultSboms := []string{ 29 "{{ .ArtifactName }}.sbom", 30 } 31 defaultCmd := "syft" 32 tests := []struct { 33 configs []config.SBOM 34 artifact string 35 cmd string 36 sboms []string 37 args []string 38 env []string 39 err bool 40 }{ 41 { 42 configs: []config.SBOM{ 43 { 44 // empty 45 }, 46 }, 47 artifact: "archive", 48 cmd: defaultCmd, 49 sboms: defaultSboms, 50 args: defaultArgs, 51 env: []string{ 52 "SYFT_FILE_METADATA_CATALOGER_ENABLED=true", 53 }, 54 }, 55 { 56 configs: []config.SBOM{ 57 { 58 Artifacts: "package", 59 }, 60 }, 61 artifact: "package", 62 cmd: defaultCmd, 63 sboms: defaultSboms, 64 args: defaultArgs, 65 }, 66 { 67 configs: []config.SBOM{ 68 { 69 Artifacts: "archive", 70 }, 71 }, 72 artifact: "archive", 73 cmd: defaultCmd, 74 sboms: defaultSboms, 75 args: defaultArgs, 76 env: []string{ 77 "SYFT_FILE_METADATA_CATALOGER_ENABLED=true", 78 }, 79 }, 80 { 81 configs: []config.SBOM{ 82 { 83 Artifacts: "archive", 84 Env: []string{ 85 "something=something-else", 86 }, 87 }, 88 }, 89 artifact: "archive", 90 cmd: defaultCmd, 91 sboms: defaultSboms, 92 args: defaultArgs, 93 env: []string{ 94 "something=something-else", 95 }, 96 }, 97 { 98 configs: []config.SBOM{ 99 { 100 Artifacts: "any", 101 }, 102 }, 103 artifact: "any", 104 cmd: defaultCmd, 105 sboms: []string{}, 106 args: defaultArgs, 107 }, 108 { 109 configs: []config.SBOM{ 110 { 111 Artifacts: "binary", 112 }, 113 }, 114 artifact: "binary", 115 cmd: defaultCmd, 116 sboms: []string{"{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}.sbom"}, 117 args: defaultArgs, 118 }, 119 { 120 configs: []config.SBOM{ 121 { 122 Artifacts: "source", 123 }, 124 }, 125 artifact: "source", 126 cmd: defaultCmd, 127 sboms: defaultSboms, 128 args: defaultArgs, 129 env: []string{ 130 "SYFT_FILE_METADATA_CATALOGER_ENABLED=true", 131 }, 132 }, 133 { 134 // multiple documents are not allowed when artifacts != "any" 135 configs: []config.SBOM{ 136 { 137 Artifacts: "binary", 138 Documents: []string{ 139 "doc1", 140 "doc2", 141 }, 142 }, 143 }, 144 err: true, 145 }, 146 } 147 148 for _, test := range tests { 149 t.Run(fmt.Sprintf("artifact=%q", test.configs[0].Artifacts), func(t *testing.T) { 150 testlib.CheckPath(t, "syft") 151 ctx := testctx.NewWithCfg(config.Project{ 152 SBOMs: test.configs, 153 }) 154 err := Pipe{}.Default(ctx) 155 if test.err { 156 require.Error(t, err) 157 return 158 } 159 require.NoError(t, err) 160 require.Equal(t, ctx.Config.SBOMs[0].Cmd, test.cmd) 161 require.Equal(t, ctx.Config.SBOMs[0].Documents, test.sboms) 162 require.Equal(t, ctx.Config.SBOMs[0].Args, test.args) 163 require.Equal(t, ctx.Config.SBOMs[0].Env, test.env) 164 require.Equal(t, ctx.Config.SBOMs[0].Artifacts, test.artifact) 165 }) 166 } 167 } 168 169 func TestSBOMCatalogInvalidArtifacts(t *testing.T) { 170 ctx := testctx.NewWithCfg(config.Project{ 171 SBOMs: []config.SBOM{{Artifacts: "foo"}}, 172 }) 173 err := Pipe{}.Run(ctx) 174 require.EqualError(t, err, "invalid list of artifacts to catalog: foo") 175 } 176 177 func TestSeveralSBOMsWithTheSameID(t *testing.T) { 178 ctx := testctx.NewWithCfg(config.Project{ 179 SBOMs: []config.SBOM{ 180 { 181 ID: "a", 182 }, 183 { 184 ID: "a", 185 }, 186 }, 187 }) 188 require.EqualError(t, Pipe{}.Default(ctx), "found 2 sboms with the ID 'a', please fix your config") 189 } 190 191 func TestSkipCataloging(t *testing.T) { 192 t.Run("skip", func(t *testing.T) { 193 require.True(t, Pipe{}.Skip(testctx.New())) 194 }) 195 196 t.Run("skip SBOM cataloging", func(t *testing.T) { 197 ctx := testctx.NewWithCfg(config.Project{ 198 SBOMs: []config.SBOM{ 199 { 200 Artifacts: "all", 201 }, 202 }, 203 }, testctx.Skip(skips.SBOM)) 204 require.True(t, Pipe{}.Skip(ctx)) 205 }) 206 207 t.Run("dont skip", func(t *testing.T) { 208 ctx := testctx.NewWithCfg(config.Project{ 209 SBOMs: []config.SBOM{ 210 { 211 Artifacts: "all", 212 }, 213 }, 214 }) 215 require.False(t, Pipe{}.Skip(ctx)) 216 }) 217 } 218 219 func TestSBOMCatalogArtifacts(t *testing.T) { 220 tests := []struct { 221 desc string 222 ctx *context.Context 223 sbomPaths []string 224 sbomNames []string 225 expectedErrAs any 226 expectedErrMsg string 227 }{ 228 { 229 desc: "catalog errors", 230 expectedErrMsg: "cataloging artifacts: exit failed", 231 ctx: testctx.NewWithCfg(config.Project{ 232 SBOMs: []config.SBOM{ 233 { 234 Artifacts: "binary", 235 Cmd: "exit", 236 Args: []string{"1"}, 237 }, 238 }, 239 }), 240 }, 241 { 242 desc: "invalid args template", 243 expectedErrAs: &tmpl.Error{}, 244 ctx: testctx.NewWithCfg(config.Project{ 245 SBOMs: []config.SBOM{ 246 { 247 Artifacts: "binary", 248 Cmd: "exit", 249 Args: []string{"${FOO}-{{ .foo }{{}}{"}, 250 }, 251 }, 252 Env: []string{ 253 "FOO=BAR", 254 }, 255 }), 256 }, 257 { 258 desc: "catalog source archives", 259 ctx: testctx.NewWithCfg(config.Project{ 260 SBOMs: []config.SBOM{ 261 {Artifacts: "source"}, 262 }, 263 }), 264 sbomPaths: []string{"artifact5.tar.gz.sbom"}, 265 sbomNames: []string{"artifact5.tar.gz.sbom"}, 266 }, 267 { 268 desc: "catalog archives", 269 ctx: testctx.NewWithCfg(config.Project{ 270 SBOMs: []config.SBOM{ 271 {Artifacts: "archive"}, 272 }, 273 }), 274 sbomPaths: []string{"artifact1.sbom", "artifact2.sbom"}, 275 sbomNames: []string{"artifact1.sbom", "artifact2.sbom"}, 276 }, 277 { 278 desc: "catalog linux packages", 279 ctx: testctx.NewWithCfg(config.Project{ 280 SBOMs: []config.SBOM{ 281 {Artifacts: "package"}, 282 }, 283 }), 284 sbomPaths: []string{"package1.deb.sbom"}, 285 sbomNames: []string{"package1.deb.sbom"}, 286 }, 287 { 288 desc: "catalog binaries", 289 ctx: testctx.NewWithCfg(config.Project{ 290 SBOMs: []config.SBOM{ 291 {Artifacts: "binary"}, 292 }, 293 }), 294 sbomPaths: []string{ 295 "artifact3-name_1.2.2_linux_amd64.sbom", 296 "artifact4-name_1.2.2_linux_amd64.sbom", 297 }, 298 sbomNames: []string{ 299 "artifact3-name_1.2.2_linux_amd64.sbom", 300 "artifact4-name_1.2.2_linux_amd64.sbom", 301 }, 302 }, 303 { 304 desc: "manual cataloging", 305 ctx: testctx.NewWithCfg(config.Project{ 306 SBOMs: []config.SBOM{ 307 { 308 Artifacts: "any", 309 Args: []string{ 310 "--file", 311 "$document0", 312 "--output", 313 "spdx-json", 314 "artifact5.tar.gz", 315 }, 316 Documents: []string{ 317 "final.sbom", 318 }, 319 }, 320 }, 321 }), 322 sbomPaths: []string{"final.sbom"}, 323 sbomNames: []string{"final.sbom"}, 324 }, 325 { 326 desc: "multiple SBOM configs", 327 ctx: testctx.NewWithCfg(config.Project{ 328 Env: []string{ 329 "SBOM_SUFFIX=s2-ish", 330 }, 331 SBOMs: []config.SBOM{ 332 { 333 ID: "s1", 334 Artifacts: "binary", 335 }, 336 { 337 ID: "s2", 338 Artifacts: "archive", 339 Documents: []string{"{{ .ArtifactName }}.{{ .Env.SBOM_SUFFIX }}.sbom"}, 340 }, 341 }, 342 }), 343 sbomPaths: []string{ 344 "artifact1.s2-ish.sbom", 345 "artifact2.s2-ish.sbom", 346 "artifact3-name_1.2.2_linux_amd64.sbom", 347 "artifact4-name_1.2.2_linux_amd64.sbom", 348 }, 349 sbomNames: []string{ 350 "artifact1.s2-ish.sbom", 351 "artifact2.s2-ish.sbom", 352 "artifact3-name_1.2.2_linux_amd64.sbom", 353 "artifact4-name_1.2.2_linux_amd64.sbom", 354 }, 355 }, 356 { 357 desc: "catalog artifacts with filtered by ID", 358 ctx: testctx.NewWithCfg(config.Project{ 359 SBOMs: []config.SBOM{ 360 { 361 Artifacts: "binary", 362 IDs: []string{"foo"}, 363 }, 364 }, 365 }), 366 sbomPaths: []string{ 367 "artifact3-name_1.2.2_linux_amd64.sbom", 368 }, 369 sbomNames: []string{ 370 "artifact3-name_1.2.2_linux_amd64.sbom", 371 }, 372 }, 373 { 374 desc: "catalog binary artifacts with env in arguments", 375 ctx: testctx.NewWithCfg(config.Project{ 376 SBOMs: []config.SBOM{ 377 { 378 Artifacts: "binary", 379 Args: []string{ 380 "--file", 381 "$document", 382 "--output", 383 "spdx-json", 384 "$artifact", 385 }, 386 Documents: []string{ 387 "{{ .ArtifactName }}.{{ .Env.TEST_USER }}.sbom", 388 }, 389 }, 390 }, 391 Env: []string{ 392 "TEST_USER=test-user-name", 393 }, 394 }), 395 sbomPaths: []string{ 396 "artifact3-name.test-user-name.sbom", 397 "artifact4.test-user-name.sbom", 398 }, 399 sbomNames: []string{ 400 "artifact3-name.test-user-name.sbom", 401 "artifact4.test-user-name.sbom", 402 }, 403 }, 404 { 405 desc: "cataloging 'any' artifacts fails", 406 ctx: testctx.NewWithCfg(config.Project{ 407 SBOMs: []config.SBOM{ 408 { 409 Artifacts: "any", 410 Cmd: "false", 411 }, 412 }, 413 }), 414 expectedErrMsg: "cataloging artifacts: false failed: exit status 1: ", 415 }, 416 } 417 418 for _, test := range tests { 419 t.Run(test.desc, func(t *testing.T) { 420 testSBOMCataloging( 421 t, 422 test.ctx, 423 test.sbomPaths, 424 test.sbomNames, 425 test.expectedErrAs, 426 test.expectedErrMsg, 427 ) 428 }) 429 } 430 } 431 432 func testSBOMCataloging( 433 tb testing.TB, 434 ctx *context.Context, 435 sbomPaths, sbomNames []string, 436 expectedErrAs any, 437 expectedErrMsg string, 438 ) { 439 tb.Helper() 440 testlib.CheckPath(tb, "syft") 441 tmpdir := tb.TempDir() 442 443 ctx.Config.Dist = tmpdir 444 ctx.Version = "1.2.2" 445 446 // create some fake artifacts 447 artifacts := []string{"artifact1", "artifact2", "artifact3", "package1.deb"} 448 require.NoError(tb, os.Mkdir(filepath.Join(tmpdir, "linux_amd64"), os.ModePerm)) 449 for _, f := range artifacts { 450 file := filepath.Join(tmpdir, f) 451 require.NoError(tb, os.WriteFile(file, []byte("foo"), 0o644)) 452 } 453 require.NoError(tb, os.WriteFile(filepath.Join(tmpdir, "linux_amd64", "artifact4"), []byte("foo"), 0o644)) 454 artifacts = append(artifacts, "linux_amd64/artifact4") 455 require.NoError(tb, os.WriteFile(filepath.Join(tmpdir, "artifact5.tar.gz"), []byte("foo"), 0o644)) 456 artifacts = append(artifacts, "artifact5.tar.gz") 457 ctx.Artifacts.Add(&artifact.Artifact{ 458 Name: "artifact1", 459 Path: filepath.Join(tmpdir, "artifact1"), 460 Type: artifact.UploadableArchive, 461 Extra: map[string]interface{}{ 462 artifact.ExtraID: "foo", 463 }, 464 }) 465 ctx.Artifacts.Add(&artifact.Artifact{ 466 Name: "artifact2", 467 Path: filepath.Join(tmpdir, "artifact2"), 468 Type: artifact.UploadableArchive, 469 Extra: map[string]interface{}{ 470 artifact.ExtraID: "foo3", 471 }, 472 }) 473 ctx.Artifacts.Add(&artifact.Artifact{ 474 Name: "artifact3-name", 475 Path: filepath.Join(tmpdir, "artifact3"), 476 Goos: "linux", 477 Goarch: "amd64", 478 Type: artifact.UploadableBinary, 479 Extra: map[string]interface{}{ 480 artifact.ExtraID: "foo", 481 artifact.ExtraBinary: "artifact3-name", 482 }, 483 }) 484 ctx.Artifacts.Add(&artifact.Artifact{ 485 Name: "artifact4", 486 Path: filepath.Join(tmpdir, "linux_amd64", "artifact4"), 487 Goos: "linux", 488 Goarch: "amd64", 489 Type: artifact.Binary, 490 Extra: map[string]interface{}{ 491 artifact.ExtraID: "foo3", 492 artifact.ExtraBinary: "artifact4-name", 493 }, 494 }) 495 ctx.Artifacts.Add(&artifact.Artifact{ 496 Name: "artifact5.tar.gz", 497 Path: filepath.Join(tmpdir, "artifact5.tar.gz"), 498 Type: artifact.UploadableSourceArchive, 499 }) 500 ctx.Artifacts.Add(&artifact.Artifact{ 501 Name: "package1.deb", 502 Path: filepath.Join(tmpdir, "package1.deb"), 503 Type: artifact.LinuxPackage, 504 Extra: map[string]interface{}{ 505 artifact.ExtraID: "foo", 506 }, 507 }) 508 509 // configure the pipeline 510 require.NoError(tb, Pipe{}.Default(ctx)) 511 512 // run the pipeline 513 if expectedErrMsg != "" { 514 err := Pipe{}.Run(ctx) 515 require.Error(tb, err) 516 require.Contains(tb, err.Error(), expectedErrMsg) 517 return 518 } 519 if expectedErrAs != nil { 520 require.ErrorAs(tb, Pipe{}.Run(ctx), expectedErrAs) 521 return 522 } 523 524 require.NoError(tb, Pipe{}.Run(ctx)) 525 526 // ensure all artifacts have an ID 527 for _, arti := range ctx.Artifacts.Filter(artifact.ByType(artifact.SBOM)).List() { 528 require.NotEmptyf(tb, arti.ID(), ".Extra.ID on %s", arti.Path) 529 } 530 531 // verify that only the artifacts and the sboms are in the dist dir 532 gotFiles := []string{} 533 534 require.NoError(tb, filepath.Walk(tmpdir, 535 func(path string, info os.FileInfo, err error) error { 536 if err != nil { 537 return err 538 } 539 if info.IsDir() { 540 return nil 541 } 542 relPath, err := filepath.Rel(tmpdir, path) 543 if err != nil { 544 return err 545 } 546 gotFiles = append(gotFiles, relPath) 547 return nil 548 }), 549 ) 550 551 wantFiles := append(artifacts, sbomPaths...) 552 sort.Strings(wantFiles) 553 require.ElementsMatch(tb, wantFiles, gotFiles, "SBOM paths differ") 554 555 var sbomArtifacts []string 556 for _, sig := range ctx.Artifacts.Filter(artifact.ByType(artifact.SBOM)).List() { 557 sbomArtifacts = append(sbomArtifacts, sig.Name) 558 } 559 560 require.ElementsMatch(tb, sbomArtifacts, sbomNames, "SBOM names differ") 561 } 562 563 func Test_subprocessDistPath(t *testing.T) { 564 cwd, err := os.Getwd() 565 require.NoError(t, err) 566 567 tests := []struct { 568 name string 569 distDir string 570 pathRelativeToCwd string 571 expects string 572 }{ 573 { 574 name: "relative dist with anchor", 575 distDir: "./dist", 576 pathRelativeToCwd: "dist/my.sbom", 577 expects: "my.sbom", 578 }, 579 { 580 name: "relative dist without anchor", 581 distDir: "dist", 582 pathRelativeToCwd: "dist/my.sbom", 583 expects: "my.sbom", 584 }, 585 { 586 name: "relative dist with nested resource", 587 distDir: "dist", 588 pathRelativeToCwd: "dist/something/my.sbom", 589 expects: "something/my.sbom", 590 }, 591 { 592 name: "absolute dist with nested resource", 593 distDir: filepath.Join(cwd, "dist/"), 594 pathRelativeToCwd: "dist/something/my.sbom", 595 expects: "something/my.sbom", 596 }, 597 } 598 for _, test := range tests { 599 t.Run(test.name, func(t *testing.T) { 600 actual, err := subprocessDistPath(test.distDir, test.pathRelativeToCwd) 601 require.NoError(t, err) 602 assert.Equal(t, test.expects, actual) 603 }) 604 } 605 } 606 607 func Test_templateNames(t *testing.T) { 608 art := artifact.Artifact{ 609 Name: "name-it", 610 Path: "to/a/place", 611 Goos: "darwin", 612 Goarch: "amd64", 613 Type: artifact.Binary, 614 Extra: map[string]interface{}{ 615 artifact.ExtraID: "id-it", 616 "Binary": "binary-name", 617 }, 618 } 619 620 wd, err := os.Getwd() 621 require.NoError(t, err) 622 623 tests := []struct { 624 name string 625 dist string 626 version string 627 cfg config.SBOM 628 artifact artifact.Artifact 629 expectedValues map[string]string 630 expectedPaths []string 631 }{ 632 { 633 name: "default configuration", 634 artifact: art, 635 cfg: config.SBOM{}, 636 dist: "/somewhere/to/dist", 637 expectedPaths: []string{ 638 "/somewhere/to/dist/name-it.sbom", 639 }, 640 expectedValues: map[string]string{ 641 "artifact": "to/a/place", 642 "artifactID": "id-it", 643 "document": "/somewhere/to/dist/name-it.sbom", 644 "document0": "/somewhere/to/dist/name-it.sbom", 645 }, 646 }, 647 { 648 name: "default configuration + relative dist", 649 artifact: art, 650 cfg: config.SBOM{}, 651 dist: "somewhere/to/dist", 652 expectedPaths: []string{ 653 filepath.Join(wd, "somewhere/to/dist/name-it.sbom"), 654 }, 655 expectedValues: map[string]string{ 656 "artifact": "to/a/place", // note: this is always relative to ${dist} 657 "artifactID": "id-it", 658 "document": filepath.Join(wd, "somewhere/to/dist/name-it.sbom"), 659 "document0": filepath.Join(wd, "somewhere/to/dist/name-it.sbom"), 660 }, 661 }, 662 { 663 name: "custom document using $artifact", 664 // note: this configuration is probably a misconfiguration since it is placing SBOMs within each bin 665 // directory, however, it will behave as correctly as possible. 666 artifact: art, 667 cfg: config.SBOM{ 668 Documents: []string{ 669 // note: the artifact name is probably an incorrect value here since it can't express all attributes 670 // of the binary (os, arch, etc), so builds with multiple architectures will create SBOMs with the 671 // same name. 672 "${artifact}.cdx.sbom", 673 }, 674 }, 675 dist: "somewhere/to/dist", 676 expectedPaths: []string{ 677 filepath.Join(wd, "somewhere/to/dist/to/a/place.cdx.sbom"), 678 }, 679 expectedValues: map[string]string{ 680 "artifact": "to/a/place", 681 "artifactID": "id-it", 682 "document": filepath.Join(wd, "somewhere/to/dist/to/a/place.cdx.sbom"), 683 "document0": filepath.Join(wd, "somewhere/to/dist/to/a/place.cdx.sbom"), 684 }, 685 }, 686 { 687 name: "custom document using build vars", 688 artifact: art, 689 cfg: config.SBOM{ 690 Documents: []string{ 691 "{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}.cdx.sbom", 692 }, 693 }, 694 version: "1.0.0", 695 dist: "somewhere/to/dist", 696 expectedPaths: []string{ 697 filepath.Join(wd, "somewhere/to/dist/binary-name_1.0.0_darwin_amd64.cdx.sbom"), 698 }, 699 expectedValues: map[string]string{ 700 "artifact": "to/a/place", 701 "artifactID": "id-it", 702 "document": filepath.Join(wd, "somewhere/to/dist/binary-name_1.0.0_darwin_amd64.cdx.sbom"), 703 "document0": filepath.Join(wd, "somewhere/to/dist/binary-name_1.0.0_darwin_amd64.cdx.sbom"), 704 }, 705 }, 706 { 707 name: "env vars with go templated options", 708 artifact: art, 709 cfg: config.SBOM{ 710 Documents: []string{ 711 "{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}.cdx.sbom", 712 }, 713 Env: []string{ 714 "with-env-var=value", 715 "custom-os={{ .Os }}-unique", 716 "custom-arch={{ .Arch }}-unique", 717 }, 718 }, 719 version: "1.0.0", 720 dist: "somewhere/to/dist", 721 expectedPaths: []string{ 722 filepath.Join(wd, "somewhere/to/dist/binary-name_1.0.0_darwin_amd64.cdx.sbom"), 723 }, 724 expectedValues: map[string]string{ 725 "artifact": "to/a/place", 726 "artifactID": "id-it", 727 "with-env-var": "value", 728 "custom-os": "darwin-unique", 729 "custom-arch": "amd64-unique", 730 "document": filepath.Join(wd, "somewhere/to/dist/binary-name_1.0.0_darwin_amd64.cdx.sbom"), 731 "document0": filepath.Join(wd, "somewhere/to/dist/binary-name_1.0.0_darwin_amd64.cdx.sbom"), 732 }, 733 }, 734 } 735 for _, tt := range tests { 736 t.Run(tt.name, func(t *testing.T) { 737 ctx := testctx.NewWithCfg(config.Project{ 738 Dist: tt.dist, 739 }, testctx.WithVersion(tt.version)) 740 741 cfg := tt.cfg 742 require.NoError(t, setConfigDefaults(&cfg)) 743 744 var inputArgs []string 745 var expectedArgs []string 746 for key, value := range tt.expectedValues { 747 inputArgs = append(inputArgs, fmt.Sprintf("${%s}", key)) 748 expectedArgs = append(expectedArgs, value) 749 } 750 cfg.Args = inputArgs 751 752 actualArgs, actualEnvs, actualPaths, err := applyTemplate(ctx, cfg, &tt.artifact) 753 require.NoError(t, err) 754 755 assert.Equal(t, tt.expectedPaths, actualPaths, "paths differ") 756 757 assert.Equal(t, expectedArgs, actualArgs, "arguments differ") 758 759 actualEnv := make(map[string]string) 760 for _, str := range actualEnvs { 761 k, v, ok := strings.Cut(str, "=") 762 require.True(t, ok) 763 actualEnv[k] = v 764 } 765 766 for k, v := range tt.expectedValues { 767 assert.Equal(t, v, actualEnv[k]) 768 } 769 }) 770 } 771 } 772 773 func TestDependencies(t *testing.T) { 774 ctx := testctx.NewWithCfg(config.Project{ 775 SBOMs: []config.SBOM{ 776 {Cmd: "syft"}, 777 {Cmd: "foobar"}, 778 }, 779 }) 780 require.Equal(t, []string{"syft", "foobar"}, Pipe{}.Dependencies(ctx)) 781 }