github.com/BarDweller/libpak@v0.0.0-20230630201634-8dd5cfc15ec9/layer_test.go (about)

     1  /*
     2   * Copyright 2018-2023 the original author or authors.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *      https://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package libpak_test
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"net/http"
    23  	"os"
    24  	"path/filepath"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/buildpacks/libcnb"
    29  	. "github.com/onsi/gomega"
    30  	"github.com/onsi/gomega/ghttp"
    31  	"github.com/sclevine/spec"
    32  
    33  	"github.com/BarDweller/libpak"
    34  	"github.com/BarDweller/libpak/bard"
    35  )
    36  
    37  func testLayer(t *testing.T, context spec.G, it spec.S) {
    38  	var (
    39  		Expect = NewWithT(t).Expect
    40  
    41  		layersDir string
    42  		layer     libcnb.Layer
    43  	)
    44  
    45  	it.Before(func() {
    46  		layersDir = t.TempDir()
    47  		layer.Path = filepath.Join(layersDir, "test-layer")
    48  
    49  		layer.Exec.Path = layer.Path
    50  		layer.Metadata = map[string]interface{}{}
    51  		layer.Profile = libcnb.Profile{}
    52  	})
    53  
    54  	it.After(func() {
    55  		Expect(os.RemoveAll(layersDir)).To(Succeed())
    56  	})
    57  
    58  	context("LayerContributor", func() {
    59  		var (
    60  			lc libpak.LayerContributor
    61  		)
    62  
    63  		it.Before(func() {
    64  			lc.Logger = bard.NewLogger(bytes.NewBuffer(nil))
    65  			lc.ExpectedMetadata = map[string]interface{}{
    66  				"alpha": "test-alpha",
    67  				"bravo": map[string]interface{}{
    68  					"bravo-1": "test-bravo-1",
    69  					"bravo-2": "test-bravo-2",
    70  				},
    71  			}
    72  		})
    73  
    74  		it("calls function with no existing metadata", func() {
    75  			var called bool
    76  
    77  			_, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
    78  				called = true
    79  				return layer, nil
    80  			})
    81  			Expect(err).NotTo(HaveOccurred())
    82  
    83  			Expect(called).To(BeTrue())
    84  		})
    85  
    86  		it("calls function with non-matching metadata", func() {
    87  
    88  			layer.Metadata["alpha"] = "test-alpha"
    89  
    90  			var called bool
    91  
    92  			_, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
    93  				called = true
    94  				return layer, nil
    95  			})
    96  			Expect(err).NotTo(HaveOccurred())
    97  
    98  			Expect(called).To(BeTrue())
    99  		})
   100  
   101  		context("reloads layers not restored", func() {
   102  			var called bool
   103  
   104  			it.Before(func() {
   105  				layer.Metadata = map[string]interface{}{
   106  					"alpha": "test-alpha",
   107  					"bravo": map[string]interface{}{
   108  						"bravo-1": "test-bravo-1",
   109  						"bravo-2": "test-bravo-2",
   110  					},
   111  				}
   112  				called = false
   113  			})
   114  
   115  			it("calls function with matching metadata but no layer directory on cache layer", func() {
   116  				Expect(os.WriteFile(fmt.Sprintf("%s.toml", layer.Path), []byte{}, 0644)).To(Succeed())
   117  				Expect(os.RemoveAll(layer.Path)).To(Succeed())
   118  				lc.ExpectedTypes.Cache = true
   119  
   120  				_, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
   121  					called = true
   122  					return layer, nil
   123  				})
   124  				Expect(err).NotTo(HaveOccurred())
   125  
   126  				Expect(called).To(BeTrue())
   127  			})
   128  
   129  			it("calls function with matching metadata but no layer directory on build layer", func() {
   130  				Expect(os.WriteFile(fmt.Sprintf("%s.toml", layer.Path), []byte{}, 0644)).To(Succeed())
   131  				Expect(os.RemoveAll(layer.Path)).To(Succeed())
   132  				lc.ExpectedTypes.Build = true
   133  
   134  				_, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
   135  					called = true
   136  					return layer, nil
   137  				})
   138  				Expect(err).NotTo(HaveOccurred())
   139  
   140  				Expect(called).To(BeTrue())
   141  			})
   142  
   143  			it("calls function with matching metadata but an empty layer directory on build layer", func() {
   144  				Expect(os.WriteFile(fmt.Sprintf("%s.toml", layer.Path), []byte{}, 0644)).To(Succeed())
   145  				Expect(os.MkdirAll(layer.Path, 0755)).To(Succeed())
   146  				lc.ExpectedTypes.Build = true
   147  
   148  				_, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
   149  					called = true
   150  					return layer, nil
   151  				})
   152  				Expect(err).NotTo(HaveOccurred())
   153  
   154  				Expect(called).To(BeTrue())
   155  			})
   156  
   157  			it("does not call function with matching metadata when layer directory exists and has a file in it", func() {
   158  				Expect(os.WriteFile(fmt.Sprintf("%s.toml", layer.Path), []byte{}, 0644)).To(Succeed())
   159  				Expect(os.MkdirAll(layer.Path, 0755)).To(Succeed())
   160  				Expect(os.WriteFile(filepath.Join(layer.Path, "foo"), []byte{}, 0644)).To(Succeed())
   161  				lc.ExpectedTypes.Build = true
   162  
   163  				_, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
   164  					called = true
   165  					return layer, nil
   166  				})
   167  				Expect(err).NotTo(HaveOccurred())
   168  
   169  				Expect(called).To(BeFalse())
   170  			})
   171  
   172  			it("does not call function with matching metadata when layer TOML missing", func() {
   173  				Expect(os.MkdirAll(layer.Path, 0755)).To(Succeed())
   174  				layer.Build = true
   175  
   176  				_, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
   177  					called = true
   178  					return layer, nil
   179  				})
   180  				Expect(err).NotTo(HaveOccurred())
   181  
   182  				Expect(called).To(BeFalse())
   183  			})
   184  		})
   185  
   186  		it("does not call function with matching metadata", func() {
   187  			layer.Metadata = map[string]interface{}{
   188  				"alpha": "test-alpha",
   189  				"bravo": map[string]interface{}{
   190  					"bravo-1": "test-bravo-1",
   191  					"bravo-2": "test-bravo-2",
   192  				},
   193  			}
   194  
   195  			var called bool
   196  
   197  			_, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
   198  				called = true
   199  				return layer, nil
   200  			})
   201  			Expect(err).NotTo(HaveOccurred())
   202  
   203  			Expect(called).To(BeFalse())
   204  		})
   205  
   206  		it("returns function error", func() {
   207  			_, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
   208  				return libcnb.Layer{}, fmt.Errorf("test-error")
   209  			})
   210  			Expect(err).To(MatchError("test-error"))
   211  		})
   212  
   213  		it("adds expected metadata to layer", func() {
   214  			layer, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
   215  				return layer, nil
   216  			})
   217  			Expect(err).NotTo(HaveOccurred())
   218  
   219  			Expect(layer.Metadata).To(Equal(map[string]interface{}{
   220  				"alpha": "test-alpha",
   221  				"bravo": map[string]interface{}{
   222  					"bravo-1": "test-bravo-1",
   223  					"bravo-2": "test-bravo-2",
   224  				},
   225  			}))
   226  		})
   227  
   228  		it("sets build layer flag", func() {
   229  			lc.ExpectedTypes.Build = true
   230  			layer, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
   231  				return layer, nil
   232  			})
   233  			Expect(err).NotTo(HaveOccurred())
   234  
   235  			Expect(layer.LayerTypes.Build).To(BeTrue())
   236  		})
   237  
   238  		it("sets cache layer flag", func() {
   239  			lc.ExpectedTypes.Cache = true
   240  			layer, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
   241  				return layer, nil
   242  			})
   243  			Expect(err).NotTo(HaveOccurred())
   244  
   245  			Expect(layer.LayerTypes.Cache).To(BeTrue())
   246  		})
   247  
   248  		it("sets launch layer flag", func() {
   249  			lc.ExpectedTypes.Launch = true
   250  			layer, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
   251  				return layer, nil
   252  			})
   253  			Expect(err).NotTo(HaveOccurred())
   254  
   255  			Expect(layer.LayerTypes.Launch).To(BeTrue())
   256  		})
   257  
   258  		it("sets layer flags regardless of caching behavior (required for 0.6 API)", func() {
   259  			lc.ExpectedTypes.Launch = true
   260  			lc.ExpectedTypes.Cache = true
   261  			lc.ExpectedTypes.Build = true
   262  
   263  			layer.Metadata = map[string]interface{}{
   264  				"alpha": "test-alpha",
   265  				"bravo": map[string]interface{}{
   266  					"bravo-1": "test-bravo-1",
   267  					"bravo-2": "test-bravo-2",
   268  				},
   269  			}
   270  
   271  			var called bool
   272  
   273  			layer, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
   274  				called = true
   275  				return layer, nil
   276  			})
   277  			Expect(err).NotTo(HaveOccurred())
   278  			Expect(called).To(BeFalse())
   279  
   280  			Expect(layer.LayerTypes.Launch).To(BeTrue())
   281  			Expect(layer.LayerTypes.Cache).To(BeTrue())
   282  			Expect(layer.LayerTypes.Build).To(BeTrue())
   283  		})
   284  	})
   285  
   286  	context("DependencyLayerContributor", func() {
   287  		var (
   288  			dependency libpak.BuildModuleDependency
   289  			dlc        libpak.DependencyLayerContributor
   290  			server     *ghttp.Server
   291  		)
   292  
   293  		it.Before(func() {
   294  			RegisterTestingT(t)
   295  			server = ghttp.NewServer()
   296  
   297  			deprecationDate, err := time.Parse(time.RFC3339, "2021-04-01T00:00:00Z")
   298  			Expect(err).ToNot(HaveOccurred())
   299  
   300  			dependency = libpak.BuildModuleDependency{
   301  				ID:      "test-id",
   302  				Name:    "test-name",
   303  				Version: "1.1.1",
   304  				URI:     fmt.Sprintf("%s/test-path", server.URL()),
   305  				SHA256:  "576dd8416de5619ea001d9662291d62444d1292a38e96956bc4651c01f14bca1",
   306  				Stacks:  []string{"test-stack"},
   307  				Licenses: []libpak.BuildModuleDependencyLicense{
   308  					{
   309  						Type: "test-type",
   310  						URI:  "test-uri",
   311  					},
   312  				},
   313  				CPEs:            []string{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"},
   314  				PURL:            "pkg:generic/some-java11@11.0.2?arch=amd64",
   315  				DeprecationDate: deprecationDate,
   316  			}
   317  
   318  			layer.Metadata = map[string]interface{}{}
   319  
   320  			dlc.Logger = bard.NewLogger(bytes.NewBuffer(nil))
   321  			dlc.ExpectedMetadata = dependency
   322  			dlc.Dependency = dependency
   323  			dlc.DependencyCache.CachePath = layer.Path
   324  			dlc.DependencyCache.DownloadPath = layer.Path
   325  		})
   326  
   327  		it.After(func() {
   328  			server.Close()
   329  		})
   330  
   331  		it("calls function with no existing metadata", func() {
   332  			server.AppendHandlers(ghttp.RespondWith(http.StatusOK, "test-fixture"))
   333  
   334  			var called bool
   335  
   336  			_, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) {
   337  				defer artifact.Close()
   338  
   339  				called = true
   340  				return layer, nil
   341  			})
   342  			Expect(err).NotTo(HaveOccurred())
   343  
   344  			Expect(called).To(BeTrue())
   345  		})
   346  
   347  		it("modifies request", func() {
   348  			server.AppendHandlers(ghttp.CombineHandlers(
   349  				ghttp.VerifyHeaderKV("Test-Key", "test-value"),
   350  				ghttp.RespondWith(http.StatusOK, "test-fixture"),
   351  			))
   352  
   353  			dlc.RequestModifierFuncs = append(dlc.RequestModifierFuncs, func(request *http.Request) (*http.Request, error) {
   354  				request.Header.Add("Test-Key", "test-value")
   355  				return request, nil
   356  			})
   357  
   358  			_, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) {
   359  				defer artifact.Close()
   360  				return layer, nil
   361  			})
   362  			Expect(err).NotTo(HaveOccurred())
   363  		})
   364  
   365  		it("calls function with non-matching metadata", func() {
   366  			layer.Metadata["alpha"] = "test-alpha"
   367  
   368  			server.AppendHandlers(ghttp.RespondWith(http.StatusOK, "test-fixture"))
   369  
   370  			var called bool
   371  
   372  			_, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) {
   373  				defer artifact.Close()
   374  
   375  				called = true
   376  				return layer, nil
   377  			})
   378  			Expect(err).NotTo(HaveOccurred())
   379  
   380  			Expect(called).To(BeTrue())
   381  		})
   382  
   383  		it("does not call function with matching metadata", func() {
   384  			layer.Metadata = map[string]interface{}{
   385  				"id":      dependency.ID,
   386  				"name":    dependency.Name,
   387  				"version": dependency.Version,
   388  				"uri":     dependency.URI,
   389  				"sha256":  dependency.SHA256,
   390  				"stacks":  []interface{}{dependency.Stacks[0]},
   391  				"licenses": []map[string]interface{}{
   392  					{
   393  						"type": dependency.Licenses[0].Type,
   394  						"uri":  dependency.Licenses[0].URI,
   395  					},
   396  				},
   397  				"cpes":             []interface{}{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"},
   398  				"purl":             "pkg:generic/some-java11@11.0.2?arch=amd64",
   399  				"deprecation_date": dependency.DeprecationDate,
   400  			}
   401  
   402  			var called bool
   403  
   404  			_, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) {
   405  				defer artifact.Close()
   406  
   407  				called = true
   408  				return layer, nil
   409  			})
   410  			Expect(err).NotTo(HaveOccurred())
   411  
   412  			Expect(called).To(BeFalse())
   413  		})
   414  
   415  		it("returns function error", func() {
   416  			server.AppendHandlers(ghttp.RespondWith(http.StatusOK, "test-fixture"))
   417  
   418  			_, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) {
   419  				defer artifact.Close()
   420  
   421  				return libcnb.Layer{}, fmt.Errorf("test-error")
   422  			})
   423  			Expect(err).To(MatchError("test-error"))
   424  		})
   425  
   426  		it("adds expected metadata to layer", func() {
   427  			server.AppendHandlers(ghttp.RespondWith(http.StatusOK, "test-fixture"))
   428  
   429  			layer, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) {
   430  				defer artifact.Close()
   431  				return layer, nil
   432  			})
   433  			Expect(err).NotTo(HaveOccurred())
   434  
   435  			Expect(layer.Metadata).To(Equal(map[string]interface{}{
   436  				"id":      dependency.ID,
   437  				"name":    dependency.Name,
   438  				"version": dependency.Version,
   439  				"uri":     dependency.URI,
   440  				"sha256":  dependency.SHA256,
   441  				"stacks":  []interface{}{dependency.Stacks[0]},
   442  				"licenses": []map[string]interface{}{
   443  					{
   444  						"type": dependency.Licenses[0].Type,
   445  						"uri":  dependency.Licenses[0].URI,
   446  					},
   447  				},
   448  				"cpes":             []interface{}{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"},
   449  				"purl":             "pkg:generic/some-java11@11.0.2?arch=amd64",
   450  				"deprecation_date": dependency.DeprecationDate,
   451  			}))
   452  		})
   453  
   454  		it("sets layer flags regardless of caching behavior (required for 0.6 API)", func() {
   455  			layer.Metadata = map[string]interface{}{
   456  				"id":      dependency.ID,
   457  				"name":    dependency.Name,
   458  				"version": dependency.Version,
   459  				"uri":     dependency.URI,
   460  				"sha256":  dependency.SHA256,
   461  				"stacks":  []interface{}{dependency.Stacks[0]},
   462  				"licenses": []map[string]interface{}{
   463  					{
   464  						"type": dependency.Licenses[0].Type,
   465  						"uri":  dependency.Licenses[0].URI,
   466  					},
   467  				},
   468  				"cpes":             []interface{}{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"},
   469  				"purl":             "pkg:generic/some-java11@11.0.2?arch=amd64",
   470  				"deprecation_date": dependency.DeprecationDate,
   471  			}
   472  			dlc.ExpectedTypes.Launch = true
   473  			dlc.ExpectedTypes.Cache = true
   474  			dlc.ExpectedTypes.Build = true
   475  
   476  			var called bool
   477  
   478  			layer, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) {
   479  				defer artifact.Close()
   480  
   481  				called = true
   482  				return layer, nil
   483  			})
   484  			Expect(err).NotTo(HaveOccurred())
   485  
   486  			Expect(called).To(BeFalse())
   487  
   488  			Expect(layer.LayerTypes.Launch).To(BeTrue())
   489  			Expect(layer.LayerTypes.Cache).To(BeTrue())
   490  			Expect(layer.LayerTypes.Build).To(BeTrue())
   491  		})
   492  
   493  		it("adds expected Syft SBOM file", func() {
   494  			server.AppendHandlers(ghttp.RespondWith(http.StatusOK, "test-fixture"))
   495  
   496  			layer, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) {
   497  				defer artifact.Close()
   498  				return layer, nil
   499  			})
   500  			Expect(err).NotTo(HaveOccurred())
   501  
   502  			outputFile := layer.SBOMPath(libcnb.SyftJSON)
   503  			Expect(outputFile).To(BeARegularFile())
   504  
   505  			data, err := os.ReadFile(outputFile)
   506  			Expect(err).ToNot(HaveOccurred())
   507  			Expect(string(data)).To(ContainSubstring(`"Artifacts":[`))
   508  			Expect(string(data)).To(ContainSubstring(`"FoundBy":"libpak",`))
   509  			Expect(string(data)).To(ContainSubstring(`"PURL":"pkg:generic/some-java11@11.0.2?arch=amd64"`))
   510  			Expect(string(data)).To(ContainSubstring(`"Schema":{`))
   511  			Expect(string(data)).To(ContainSubstring(`"Descriptor":{`))
   512  			Expect(string(data)).To(ContainSubstring(`"Source":{`))
   513  		})
   514  	})
   515  
   516  	context("HelperLayerContributor", func() {
   517  		var (
   518  			buildpack libcnb.Buildpack
   519  			hlc       libpak.HelperLayerContributor
   520  		)
   521  
   522  		it.Before(func() {
   523  			buildpack.Info = libcnb.BuildpackInfo{
   524  				ID:       "test-id",
   525  				Name:     "test-name",
   526  				Version:  "test-version",
   527  				Homepage: "test-homepage",
   528  			}
   529  
   530  			buildpack.Path = t.TempDir()
   531  			file := filepath.Join(buildpack.Path, "bin")
   532  			Expect(os.MkdirAll(file, 0755)).To(Succeed())
   533  
   534  			file = filepath.Join(file, "helper")
   535  			Expect(os.WriteFile(file, []byte{}, 0755)).To(Succeed())
   536  
   537  			hlc = libpak.HelperLayerContributor{
   538  				Path:          file,
   539  				BuildpackInfo: buildpack.Info,
   540  				Logger:        bard.NewLogger(bytes.NewBuffer(nil)),
   541  				Names:         []string{"test-name-1", "test-name-2"},
   542  			}
   543  		})
   544  
   545  		it.After(func() {
   546  			Expect(os.RemoveAll(buildpack.Path)).To(Succeed())
   547  		})
   548  
   549  		it("calls function with no existing metadata", func() {
   550  			_, err := hlc.Contribute(layer)
   551  			Expect(err).NotTo(HaveOccurred())
   552  
   553  			Expect(filepath.Join(layer.Exec.FilePath("test-name-1"))).To(BeAnExistingFile())
   554  		})
   555  
   556  		it("calls function with non-matching metadata", func() {
   557  			layer.Metadata["alpha"] = "other-alpha"
   558  
   559  			_, err := hlc.Contribute(layer)
   560  			Expect(err).NotTo(HaveOccurred())
   561  
   562  			file := filepath.Join(layer.Exec.FilePath("test-name-1"))
   563  			Expect(file).To(BeAnExistingFile())
   564  			Expect(os.Readlink(file)).To(Equal(filepath.Join(layer.Path, "helper")))
   565  
   566  			file = filepath.Join(layer.Exec.FilePath("test-name-2"))
   567  			Expect(file).To(BeAnExistingFile())
   568  			Expect(os.Readlink(file)).To(Equal(filepath.Join(layer.Path, "helper")))
   569  		})
   570  
   571  		it("does not call function with matching metadata", func() {
   572  			buildpackInfo := map[string]interface{}{
   573  				"id":          buildpack.Info.ID,
   574  				"name":        buildpack.Info.Name,
   575  				"version":     buildpack.Info.Version,
   576  				"homepage":    buildpack.Info.Homepage,
   577  				"clear-env":   buildpack.Info.ClearEnvironment,
   578  				"description": "",
   579  			}
   580  			layer.Metadata["buildpackInfo"] = buildpackInfo
   581  			layer.Metadata["helperNames"] = []interface{}{hlc.Names[0], hlc.Names[1]}
   582  
   583  			_, err := hlc.Contribute(layer)
   584  
   585  			Expect(err).NotTo(HaveOccurred())
   586  
   587  			Expect(filepath.Join(layer.Exec.FilePath("test-name-1"))).NotTo(BeAnExistingFile())
   588  			Expect(filepath.Join(layer.Exec.FilePath("test-name-2"))).NotTo(BeAnExistingFile())
   589  		})
   590  
   591  		it("adds expected metadata to layer", func() {
   592  			layer, err := hlc.Contribute(layer)
   593  			Expect(err).NotTo(HaveOccurred())
   594  
   595  			buildpackInfo := map[string]interface{}{
   596  				"id":          buildpack.Info.ID,
   597  				"name":        buildpack.Info.Name,
   598  				"version":     buildpack.Info.Version,
   599  				"homepage":    buildpack.Info.Homepage,
   600  				"clear-env":   buildpack.Info.ClearEnvironment,
   601  				"description": "",
   602  			}
   603  			Expect(layer.Metadata).To(Equal(map[string]interface{}{"buildpackInfo": buildpackInfo, "helperNames": []interface{}{hlc.Names[0], hlc.Names[1]}}))
   604  		})
   605  
   606  		it("sets layer flags regardless of caching behavior (required for 0.6 API)", func() {
   607  			buildpackInfo := map[string]interface{}{
   608  				"id":          buildpack.Info.ID,
   609  				"name":        buildpack.Info.Name,
   610  				"version":     buildpack.Info.Version,
   611  				"homepage":    buildpack.Info.Homepage,
   612  				"clear-env":   buildpack.Info.ClearEnvironment,
   613  				"description": "",
   614  			}
   615  			layer.Metadata["buildpackInfo"] = buildpackInfo
   616  			layer.Metadata["helperNames"] = []interface{}{hlc.Names[0], hlc.Names[1]}
   617  
   618  			// Launch is the only one set & always true
   619  
   620  			layer, err := hlc.Contribute(layer)
   621  			Expect(err).NotTo(HaveOccurred())
   622  
   623  			Expect(filepath.Join(layer.Exec.FilePath("test-name-1"))).NotTo(BeAnExistingFile())
   624  			Expect(filepath.Join(layer.Exec.FilePath("test-name-2"))).NotTo(BeAnExistingFile())
   625  
   626  			Expect(layer.LayerTypes.Launch).To(BeTrue())
   627  			Expect(layer.LayerTypes.Cache).To(BeFalse())
   628  			Expect(layer.LayerTypes.Build).To(BeFalse())
   629  		})
   630  
   631  		it("adds expected Syft SBOM file", func() {
   632  			layer.Metadata = map[string]interface{}{}
   633  
   634  			_, err := hlc.Contribute(layer)
   635  			Expect(err).NotTo(HaveOccurred())
   636  
   637  			Expect(filepath.Join(layer.Exec.FilePath("test-name-1"))).To(BeAnExistingFile())
   638  			Expect(filepath.Join(layer.Exec.FilePath("test-name-2"))).To(BeAnExistingFile())
   639  
   640  			outputFile := layer.SBOMPath(libcnb.SyftJSON)
   641  			Expect(outputFile).To(BeARegularFile())
   642  
   643  			data, err := os.ReadFile(outputFile)
   644  			Expect(err).ToNot(HaveOccurred())
   645  			Expect(string(data)).To(ContainSubstring(`"Artifacts":[`))
   646  			Expect(string(data)).To(ContainSubstring(`"FoundBy":"libpak",`))
   647  			Expect(string(data)).To(ContainSubstring(`"PURL":"pkg:generic/test-id@test-version"`))
   648  			Expect(string(data)).To(ContainSubstring(`"CPEs":["cpe:2.3:a:test-id:test-name-1:test-version:*:*:*:*:*:*:*","cpe:2.3:a:test-id:test-name-2:test-version:*:*:*:*:*:*:*"]`))
   649  			Expect(string(data)).To(ContainSubstring(`"Schema":{`))
   650  			Expect(string(data)).To(ContainSubstring(`"Descriptor":{`))
   651  			Expect(string(data)).To(ContainSubstring(`"Source":{`))
   652  		})
   653  	})
   654  }