github.com/rrashidov/libpak@v0.0.0-20230911084305-75119185bb4d/layer_test.go (about)

     1  /*
     2   * Copyright 2018-2020 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  	"fmt"
    21  	"net/http"
    22  	"os"
    23  	"path/filepath"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/buildpacks/libcnb"
    28  	. "github.com/onsi/gomega"
    29  	"github.com/onsi/gomega/ghttp"
    30  	"github.com/sclevine/spec"
    31  
    32  	"github.com/paketo-buildpacks/libpak"
    33  	"github.com/paketo-buildpacks/libpak/bard"
    34  )
    35  
    36  func testLayer(t *testing.T, context spec.G, it spec.S) {
    37  	var (
    38  		Expect = NewWithT(t).Expect
    39  
    40  		layersDir string
    41  		layer     libcnb.Layer
    42  	)
    43  
    44  	it.Before(func() {
    45  		layersDir = t.TempDir()
    46  		layer.Path = filepath.Join(layersDir, "test-layer")
    47  
    48  		layer.Exec.Path = layer.Path
    49  		layer.Metadata = map[string]interface{}{}
    50  		layer.Profile = libcnb.Profile{}
    51  	})
    52  
    53  	it.After(func() {
    54  		Expect(os.RemoveAll(layersDir)).To(Succeed())
    55  	})
    56  
    57  	context("LayerContributor", func() {
    58  		var (
    59  			lc libpak.LayerContributor
    60  		)
    61  
    62  		it.Before(func() {
    63  			lc.ExpectedMetadata = map[string]interface{}{
    64  				"alpha": "test-alpha",
    65  				"bravo": map[string]interface{}{
    66  					"bravo-1": "test-bravo-1",
    67  					"bravo-2": "test-bravo-2",
    68  				},
    69  			}
    70  		})
    71  
    72  		it("calls function with no existing metadata", func() {
    73  			var called bool
    74  
    75  			_, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
    76  				called = true
    77  				return layer, nil
    78  			})
    79  			Expect(err).NotTo(HaveOccurred())
    80  
    81  			Expect(called).To(BeTrue())
    82  		})
    83  
    84  		it("calls function with non-matching metadata", func() {
    85  			layer.Metadata["alpha"] = "test-alpha"
    86  
    87  			var called bool
    88  
    89  			_, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
    90  				called = true
    91  				return layer, nil
    92  			})
    93  			Expect(err).NotTo(HaveOccurred())
    94  
    95  			Expect(called).To(BeTrue())
    96  		})
    97  
    98  		context("reloads layers not restored", func() {
    99  			var called bool
   100  
   101  			it.Before(func() {
   102  				layer.Metadata = map[string]interface{}{
   103  					"alpha": "test-alpha",
   104  					"bravo": map[string]interface{}{
   105  						"bravo-1": "test-bravo-1",
   106  						"bravo-2": "test-bravo-2",
   107  					},
   108  				}
   109  			})
   110  
   111  			it("calls function with matching metadata but no layer directory on cache layer", func() {
   112  				Expect(os.WriteFile(fmt.Sprintf("%s.toml", layer.Path), []byte{}, 0644)).To(Succeed())
   113  				Expect(os.RemoveAll(layer.Path)).To(Succeed())
   114  				lc.ExpectedTypes.Cache = true
   115  
   116  				_, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
   117  					called = true
   118  					return layer, nil
   119  				})
   120  				Expect(err).NotTo(HaveOccurred())
   121  
   122  				Expect(called).To(BeTrue())
   123  			})
   124  
   125  			it("calls function with matching metadata but no layer directory on build layer", func() {
   126  				Expect(os.WriteFile(fmt.Sprintf("%s.toml", layer.Path), []byte{}, 0644)).To(Succeed())
   127  				Expect(os.RemoveAll(layer.Path)).To(Succeed())
   128  				lc.ExpectedTypes.Build = true
   129  
   130  				_, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
   131  					called = true
   132  					return layer, nil
   133  				})
   134  				Expect(err).NotTo(HaveOccurred())
   135  
   136  				Expect(called).To(BeTrue())
   137  			})
   138  
   139  			it("calls function with matching metadata but an empty layer directory on build layer", func() {
   140  				Expect(os.WriteFile(fmt.Sprintf("%s.toml", layer.Path), []byte{}, 0644)).To(Succeed())
   141  				Expect(os.MkdirAll(layer.Path, 0755)).To(Succeed())
   142  				lc.ExpectedTypes.Build = true
   143  
   144  				_, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
   145  					called = true
   146  					return layer, nil
   147  				})
   148  				Expect(err).NotTo(HaveOccurred())
   149  
   150  				Expect(called).To(BeTrue())
   151  			})
   152  
   153  			it("does not call function with matching metadata when layer directory exists and has a file in it", func() {
   154  				Expect(os.WriteFile(fmt.Sprintf("%s.toml", layer.Path), []byte{}, 0644)).To(Succeed())
   155  				Expect(os.MkdirAll(layer.Path, 0755)).To(Succeed())
   156  				Expect(os.WriteFile(filepath.Join(layer.Path, "foo"), []byte{}, 0644)).To(Succeed())
   157  				lc.ExpectedTypes.Build = true
   158  
   159  				_, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
   160  					called = true
   161  					return layer, nil
   162  				})
   163  				Expect(err).NotTo(HaveOccurred())
   164  
   165  				Expect(called).To(BeFalse())
   166  			})
   167  
   168  			it("does not call function with matching metadata when layer TOML missing", func() {
   169  				Expect(os.MkdirAll(layer.Path, 0755)).To(Succeed())
   170  				layer.Build = true
   171  
   172  				_, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
   173  					called = true
   174  					return layer, nil
   175  				})
   176  				Expect(err).NotTo(HaveOccurred())
   177  
   178  				Expect(called).To(BeFalse())
   179  			})
   180  		})
   181  
   182  		it("does not call function with matching metadata", func() {
   183  			layer.Metadata = map[string]interface{}{
   184  				"alpha": "test-alpha",
   185  				"bravo": map[string]interface{}{
   186  					"bravo-1": "test-bravo-1",
   187  					"bravo-2": "test-bravo-2",
   188  				},
   189  			}
   190  
   191  			var called bool
   192  
   193  			_, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
   194  				called = true
   195  				return layer, nil
   196  			})
   197  			Expect(err).NotTo(HaveOccurred())
   198  
   199  			Expect(called).To(BeFalse())
   200  		})
   201  
   202  		it("returns function error", func() {
   203  			_, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
   204  				return libcnb.Layer{}, fmt.Errorf("test-error")
   205  			})
   206  			Expect(err).To(MatchError("test-error"))
   207  		})
   208  
   209  		it("adds expected metadata to layer", func() {
   210  			layer, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
   211  				return layer, nil
   212  			})
   213  			Expect(err).NotTo(HaveOccurred())
   214  
   215  			Expect(layer.Metadata).To(Equal(map[string]interface{}{
   216  				"alpha": "test-alpha",
   217  				"bravo": map[string]interface{}{
   218  					"bravo-1": "test-bravo-1",
   219  					"bravo-2": "test-bravo-2",
   220  				},
   221  			}))
   222  		})
   223  
   224  		it("sets build layer flag", func() {
   225  			lc.ExpectedTypes.Build = true
   226  			layer, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
   227  				return layer, nil
   228  			})
   229  			Expect(err).NotTo(HaveOccurred())
   230  
   231  			Expect(layer.LayerTypes.Build).To(BeTrue())
   232  		})
   233  
   234  		it("sets cache layer flag", func() {
   235  			lc.ExpectedTypes.Cache = true
   236  			layer, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
   237  				return layer, nil
   238  			})
   239  			Expect(err).NotTo(HaveOccurred())
   240  
   241  			Expect(layer.LayerTypes.Cache).To(BeTrue())
   242  		})
   243  
   244  		it("sets launch layer flag", func() {
   245  			lc.ExpectedTypes.Launch = true
   246  			layer, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
   247  				return layer, nil
   248  			})
   249  			Expect(err).NotTo(HaveOccurred())
   250  
   251  			Expect(layer.LayerTypes.Launch).To(BeTrue())
   252  		})
   253  
   254  		it("sets layer flags regardless of caching behavior (required for 0.6 API)", func() {
   255  			lc.ExpectedTypes.Launch = true
   256  			lc.ExpectedTypes.Cache = true
   257  			lc.ExpectedTypes.Build = true
   258  
   259  			layer.Metadata = map[string]interface{}{
   260  				"alpha": "test-alpha",
   261  				"bravo": map[string]interface{}{
   262  					"bravo-1": "test-bravo-1",
   263  					"bravo-2": "test-bravo-2",
   264  				},
   265  			}
   266  
   267  			var called bool
   268  
   269  			layer, err := lc.Contribute(layer, func() (libcnb.Layer, error) {
   270  				called = true
   271  				return layer, nil
   272  			})
   273  			Expect(err).NotTo(HaveOccurred())
   274  			Expect(called).To(BeFalse())
   275  
   276  			Expect(layer.LayerTypes.Launch).To(BeTrue())
   277  			Expect(layer.LayerTypes.Cache).To(BeTrue())
   278  			Expect(layer.LayerTypes.Build).To(BeTrue())
   279  		})
   280  	})
   281  
   282  	context("NewDependencyLayer", func() {
   283  		var dep libpak.BuildpackDependency
   284  
   285  		it.Before(func() {
   286  			dep = libpak.BuildpackDependency{
   287  				ID:      "test-id",
   288  				Name:    "test-name",
   289  				Version: "1.1.1",
   290  				URI:     "test-uri",
   291  				SHA256:  "576dd8416de5619ea001d9662291d62444d1292a38e96956bc4651c01f14bca1",
   292  				Stacks:  []string{"test-stack"},
   293  				Licenses: []libpak.BuildpackDependencyLicense{
   294  					{
   295  						Type: "test-type",
   296  						URI:  "test-uri",
   297  					},
   298  				},
   299  			}
   300  		})
   301  
   302  		it("returns a BOM entry for the layer", func() {
   303  			_, entry := libpak.NewDependencyLayer(dep, libpak.DependencyCache{}, libcnb.LayerTypes{})
   304  			Expect(entry.Name).To(Equal("test-id"))
   305  			Expect(entry.Metadata["name"]).To(Equal("test-name"))
   306  			Expect(entry.Metadata["version"]).To(Equal("1.1.1"))
   307  			Expect(entry.Metadata["uri"]).To(Equal("test-uri"))
   308  			Expect(entry.Metadata["sha256"]).To(Equal("576dd8416de5619ea001d9662291d62444d1292a38e96956bc4651c01f14bca1"))
   309  			Expect(entry.Metadata["licenses"]).To(Equal([]libpak.BuildpackDependencyLicense{
   310  				{
   311  					Type: "test-type",
   312  					URI:  "test-uri",
   313  				},
   314  			}))
   315  		})
   316  
   317  		context("launch layer type", func() {
   318  			it("only sets launch on the entry", func() {
   319  				_, entry := libpak.NewDependencyLayer(dep, libpak.DependencyCache{}, libcnb.LayerTypes{
   320  					Launch: true,
   321  				})
   322  				Expect(entry.Launch).To(BeTrue())
   323  				Expect(entry.Build).To(BeFalse())
   324  			})
   325  		})
   326  
   327  		context("launch and build layer type", func() {
   328  			it("sets launch and build on the entry", func() {
   329  				_, entry := libpak.NewDependencyLayer(dep, libpak.DependencyCache{}, libcnb.LayerTypes{
   330  					Launch: true,
   331  					Build:  true,
   332  				})
   333  				Expect(entry.Launch).To(BeTrue())
   334  				Expect(entry.Build).To(BeTrue())
   335  			})
   336  		})
   337  
   338  		context("launch and cache layer type", func() {
   339  			it("sets launch and build on the entry", func() {
   340  				_, entry := libpak.NewDependencyLayer(dep, libpak.DependencyCache{}, libcnb.LayerTypes{
   341  					Launch: true,
   342  					Cache:  true,
   343  				})
   344  				Expect(entry.Launch).To(BeTrue())
   345  				Expect(entry.Build).To(BeTrue())
   346  			})
   347  		})
   348  
   349  		context("build layer type", func() {
   350  			it("sets build on the entry", func() {
   351  				_, entry := libpak.NewDependencyLayer(dep, libpak.DependencyCache{}, libcnb.LayerTypes{
   352  					Build: true,
   353  				})
   354  				Expect(entry.Launch).To(BeFalse())
   355  				Expect(entry.Build).To(BeTrue())
   356  			})
   357  		})
   358  
   359  		context("cache layer type", func() {
   360  			it("sets build on the entry", func() {
   361  				_, entry := libpak.NewDependencyLayer(dep, libpak.DependencyCache{}, libcnb.LayerTypes{
   362  					Cache: true,
   363  				})
   364  				Expect(entry.Launch).To(BeFalse())
   365  				Expect(entry.Build).To(BeTrue())
   366  			})
   367  		})
   368  
   369  		context("no layer types", func() {
   370  			it("sets build on the entry", func() {
   371  				_, entry := libpak.NewDependencyLayer(dep, libpak.DependencyCache{}, libcnb.LayerTypes{})
   372  				Expect(entry.Launch).To(BeFalse())
   373  				Expect(entry.Build).To(BeTrue())
   374  			})
   375  		})
   376  	})
   377  
   378  	context("DependencyLayerContributor", func() {
   379  		var (
   380  			dependency libpak.BuildpackDependency
   381  			dlc        libpak.DependencyLayerContributor
   382  			server     *ghttp.Server
   383  		)
   384  
   385  		it.Before(func() {
   386  			RegisterTestingT(t)
   387  			server = ghttp.NewServer()
   388  
   389  			deprecationDate, err := time.Parse(time.RFC3339, "2021-04-01T00:00:00Z")
   390  			Expect(err).ToNot(HaveOccurred())
   391  
   392  			dependency = libpak.BuildpackDependency{
   393  				ID:      "test-id",
   394  				Name:    "test-name",
   395  				Version: "1.1.1",
   396  				URI:     fmt.Sprintf("%s/test-path", server.URL()),
   397  				SHA256:  "576dd8416de5619ea001d9662291d62444d1292a38e96956bc4651c01f14bca1",
   398  				Stacks:  []string{"test-stack"},
   399  				Licenses: []libpak.BuildpackDependencyLicense{
   400  					{
   401  						Type: "test-type",
   402  						URI:  "test-uri",
   403  					},
   404  				},
   405  				CPEs:            []string{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"},
   406  				PURL:            "pkg:generic/some-java11@11.0.2?arch=amd64",
   407  				DeprecationDate: deprecationDate,
   408  			}
   409  
   410  			layer.Metadata = map[string]interface{}{}
   411  
   412  			dlc.ExpectedMetadata = dependency
   413  			dlc.Dependency = dependency
   414  			dlc.DependencyCache.CachePath = layer.Path
   415  			dlc.DependencyCache.DownloadPath = layer.Path
   416  		})
   417  
   418  		it.After(func() {
   419  			server.Close()
   420  		})
   421  
   422  		it("calls function with no existing metadata", func() {
   423  			server.AppendHandlers(ghttp.RespondWith(http.StatusOK, "test-fixture"))
   424  
   425  			var called bool
   426  
   427  			_, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) {
   428  				defer artifact.Close()
   429  
   430  				called = true
   431  				return layer, nil
   432  			})
   433  			Expect(err).NotTo(HaveOccurred())
   434  
   435  			Expect(called).To(BeTrue())
   436  		})
   437  
   438  		it("modifies request", func() {
   439  			server.AppendHandlers(ghttp.CombineHandlers(
   440  				ghttp.VerifyHeaderKV("Test-Key", "test-value"),
   441  				ghttp.RespondWith(http.StatusOK, "test-fixture"),
   442  			))
   443  
   444  			dlc.RequestModifierFuncs = append(dlc.RequestModifierFuncs, func(request *http.Request) (*http.Request, error) {
   445  				request.Header.Add("Test-Key", "test-value")
   446  				return request, nil
   447  			})
   448  
   449  			_, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) {
   450  				defer artifact.Close()
   451  				return layer, nil
   452  			})
   453  			Expect(err).NotTo(HaveOccurred())
   454  		})
   455  
   456  		it("calls function with non-matching metadata", func() {
   457  			layer.Metadata["alpha"] = "test-alpha"
   458  
   459  			server.AppendHandlers(ghttp.RespondWith(http.StatusOK, "test-fixture"))
   460  
   461  			var called bool
   462  
   463  			_, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) {
   464  				defer artifact.Close()
   465  
   466  				called = true
   467  				return layer, nil
   468  			})
   469  			Expect(err).NotTo(HaveOccurred())
   470  
   471  			Expect(called).To(BeTrue())
   472  		})
   473  
   474  		it("does not call function with matching metadata", func() {
   475  			layer.Metadata = map[string]interface{}{
   476  				"id":      dependency.ID,
   477  				"name":    dependency.Name,
   478  				"version": dependency.Version,
   479  				"uri":     dependency.URI,
   480  				"sha256":  dependency.SHA256,
   481  				"stacks":  []interface{}{dependency.Stacks[0]},
   482  				"licenses": []map[string]interface{}{
   483  					{
   484  						"type": dependency.Licenses[0].Type,
   485  						"uri":  dependency.Licenses[0].URI,
   486  					},
   487  				},
   488  				"cpes":             []interface{}{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"},
   489  				"purl":             "pkg:generic/some-java11@11.0.2?arch=amd64",
   490  				"deprecation_date": dependency.DeprecationDate,
   491  			}
   492  
   493  			var called bool
   494  
   495  			_, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) {
   496  				defer artifact.Close()
   497  
   498  				called = true
   499  				return layer, nil
   500  			})
   501  			Expect(err).NotTo(HaveOccurred())
   502  
   503  			Expect(called).To(BeFalse())
   504  		})
   505  
   506  		it("does not call function with non-matching deprecation_date format", func() {
   507  			dependency = libpak.BuildpackDependency{
   508  				ID:      "test-id",
   509  				Name:    "test-name",
   510  				Version: "1.1.1",
   511  				URI:     fmt.Sprintf("%s/test-path", server.URL()),
   512  				SHA256:  "576dd8416de5619ea001d9662291d62444d1292a38e96956bc4651c01f14bca1",
   513  				Stacks:  []string{"test-stack"},
   514  				Licenses: []libpak.BuildpackDependencyLicense{
   515  					{
   516  						Type: "test-type",
   517  						URI:  "test-uri",
   518  					},
   519  				},
   520  				CPEs:            []string{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"},
   521  				PURL:            "pkg:generic/some-java11@11.0.2?arch=amd64",
   522  				DeprecationDate: dependency.DeprecationDate, // parsed as '2021-04-01 00:00:00 +0000 UTC'
   523  			}
   524  			dlc.ExpectedMetadata = map[string]interface{}{ "dependency":dependency}
   525  
   526  			layer.Metadata = map[string]interface{}{ "dependency": map[string]interface{}{
   527  				"id":      dependency.ID,
   528  				"name":    dependency.Name,
   529  				"version": dependency.Version,
   530  				"uri":     dependency.URI,
   531  				"sha256":  dependency.SHA256,
   532  				"stacks":  []interface{}{dependency.Stacks[0]},
   533  				"licenses": []map[string]interface{}{
   534  					{
   535  						"type": dependency.Licenses[0].Type,
   536  						"uri":  dependency.Licenses[0].URI,
   537  					},
   538  				},
   539  				"cpes":             []interface{}{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"},
   540  				"purl":             "pkg:generic/some-java11@11.0.2?arch=amd64",
   541  				"deprecation_date": "2021-04-01T00:00:00Z", // does not match without truncation
   542  			}}
   543  
   544  			var called bool
   545  
   546  			_, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) {
   547  				defer artifact.Close()
   548  
   549  				called = true
   550  				return layer, nil
   551  			})
   552  			Expect(err).NotTo(HaveOccurred())
   553  
   554  			Expect(called).To(BeFalse())
   555  		})
   556  
   557  		it("does not call function with missing deprecation_date", func() {
   558  			dependency = libpak.BuildpackDependency{
   559  				ID:      "test-id",
   560  				Name:    "test-name",
   561  				Version: "1.1.1",
   562  				URI:     fmt.Sprintf("%s/test-path", server.URL()),
   563  				SHA256:  "576dd8416de5619ea001d9662291d62444d1292a38e96956bc4651c01f14bca1",
   564  				Stacks:  []string{"test-stack"},
   565  				Licenses: []libpak.BuildpackDependencyLicense{
   566  					{
   567  						Type: "test-type",
   568  						URI:  "test-uri",
   569  					},
   570  				},
   571  				CPEs:            []string{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"},
   572  				PURL:            "pkg:generic/some-java11@11.0.2?arch=amd64",
   573  			}
   574  			dlc.ExpectedMetadata = map[string]interface{}{ "dependency":dependency}
   575  
   576  			layer.Metadata = map[string]interface{}{ "dependency": map[string]interface{}{
   577  				"id":      dependency.ID,
   578  				"name":    dependency.Name,
   579  				"version": dependency.Version,
   580  				"uri":     dependency.URI,
   581  				"sha256":  dependency.SHA256,
   582  				"stacks":  []interface{}{dependency.Stacks[0]},
   583  				"licenses": []map[string]interface{}{
   584  					{
   585  						"type": dependency.Licenses[0].Type,
   586  						"uri":  dependency.Licenses[0].URI,
   587  					},
   588  				},
   589  				"cpes":             []interface{}{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"},
   590  				"purl":             "pkg:generic/some-java11@11.0.2?arch=amd64",
   591  				"deprecation_date": "0001-01-01T00:00:00Z",
   592  			}}
   593  
   594  			var called bool
   595  
   596  			_, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) {
   597  				defer artifact.Close()
   598  
   599  				called = true
   600  				return layer, nil
   601  			})
   602  			Expect(err).NotTo(HaveOccurred())
   603  
   604  			Expect(called).To(BeFalse())
   605  		})
   606  
   607  		it("returns function error", func() {
   608  			server.AppendHandlers(ghttp.RespondWith(http.StatusOK, "test-fixture"))
   609  
   610  			_, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) {
   611  				defer artifact.Close()
   612  
   613  				return libcnb.Layer{}, fmt.Errorf("test-error")
   614  			})
   615  			Expect(err).To(MatchError("test-error"))
   616  		})
   617  
   618  		it("adds expected metadata to layer", func() {
   619  			server.AppendHandlers(ghttp.RespondWith(http.StatusOK, "test-fixture"))
   620  
   621  			layer, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) {
   622  				defer artifact.Close()
   623  				return layer, nil
   624  			})
   625  			Expect(err).NotTo(HaveOccurred())
   626  
   627  			Expect(layer.Metadata).To(Equal(map[string]interface{}{
   628  				"id":      dependency.ID,
   629  				"name":    dependency.Name,
   630  				"version": dependency.Version,
   631  				"uri":     dependency.URI,
   632  				"sha256":  dependency.SHA256,
   633  				"stacks":  []interface{}{dependency.Stacks[0]},
   634  				"licenses": []map[string]interface{}{
   635  					{
   636  						"type": dependency.Licenses[0].Type,
   637  						"uri":  dependency.Licenses[0].URI,
   638  					},
   639  				},
   640  				"cpes":             []interface{}{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"},
   641  				"purl":             "pkg:generic/some-java11@11.0.2?arch=amd64",
   642  				"deprecation_date": dependency.DeprecationDate,
   643  			}))
   644  		})
   645  
   646  		it("sets layer flags regardless of caching behavior (required for 0.6 API)", func() {
   647  			layer.Metadata = map[string]interface{}{
   648  				"id":      dependency.ID,
   649  				"name":    dependency.Name,
   650  				"version": dependency.Version,
   651  				"uri":     dependency.URI,
   652  				"sha256":  dependency.SHA256,
   653  				"stacks":  []interface{}{dependency.Stacks[0]},
   654  				"licenses": []map[string]interface{}{
   655  					{
   656  						"type": dependency.Licenses[0].Type,
   657  						"uri":  dependency.Licenses[0].URI,
   658  					},
   659  				},
   660  				"cpes":             []interface{}{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"},
   661  				"purl":             "pkg:generic/some-java11@11.0.2?arch=amd64",
   662  				"deprecation_date": dependency.DeprecationDate,
   663  			}
   664  			dlc.ExpectedTypes.Launch = true
   665  			dlc.ExpectedTypes.Cache = true
   666  			dlc.ExpectedTypes.Build = true
   667  
   668  			var called bool
   669  
   670  			layer, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) {
   671  				defer artifact.Close()
   672  
   673  				called = true
   674  				return layer, nil
   675  			})
   676  			Expect(err).NotTo(HaveOccurred())
   677  
   678  			Expect(called).To(BeFalse())
   679  
   680  			Expect(layer.LayerTypes.Launch).To(BeTrue())
   681  			Expect(layer.LayerTypes.Cache).To(BeTrue())
   682  			Expect(layer.LayerTypes.Build).To(BeTrue())
   683  		})
   684  
   685  		it("adds expected Syft SBOM file", func() {
   686  			server.AppendHandlers(ghttp.RespondWith(http.StatusOK, "test-fixture"))
   687  
   688  			layer, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) {
   689  				defer artifact.Close()
   690  				return layer, nil
   691  			})
   692  			Expect(err).NotTo(HaveOccurred())
   693  
   694  			outputFile := layer.SBOMPath(libcnb.SyftJSON)
   695  			Expect(outputFile).To(BeARegularFile())
   696  
   697  			data, err := os.ReadFile(outputFile)
   698  			Expect(err).ToNot(HaveOccurred())
   699  			Expect(string(data)).To(ContainSubstring(`"Artifacts":[`))
   700  			Expect(string(data)).To(ContainSubstring(`"FoundBy":"libpak",`))
   701  			Expect(string(data)).To(ContainSubstring(`"PURL":"pkg:generic/some-java11@11.0.2?arch=amd64"`))
   702  			Expect(string(data)).To(ContainSubstring(`"Schema":{`))
   703  			Expect(string(data)).To(ContainSubstring(`"Descriptor":{`))
   704  			Expect(string(data)).To(ContainSubstring(`"Source":{`))
   705  		})
   706  	})
   707  
   708  	context("NewHelperLayer", func() {
   709  		it("returns a BOM entry with version equal to buildpack version", func() {
   710  			_, entry := libpak.NewHelperLayer(libcnb.Buildpack{
   711  				API: "0.6",
   712  				Info: libcnb.BuildpackInfo{
   713  					Version: "test-version",
   714  				},
   715  			}, "test-name-1", "test-name-2")
   716  			Expect(entry).To(Equal(
   717  				libcnb.BOMEntry{
   718  					Name: filepath.Base("helper"),
   719  					Metadata: map[string]interface{}{
   720  						"layer":   "helper",
   721  						"names":   []string{"test-name-1", "test-name-2"},
   722  						"version": "test-version",
   723  					},
   724  					Launch: true,
   725  					Build:  false,
   726  				},
   727  			))
   728  		})
   729  
   730  		it("returns a BOM entry on API 0.7 too", func() {
   731  			_, entry := libpak.NewHelperLayer(libcnb.Buildpack{
   732  				API: "0.7",
   733  				Info: libcnb.BuildpackInfo{
   734  					Version: "test-version",
   735  				},
   736  			}, "test-name-1", "test-name-2")
   737  			Expect(entry).To(Equal(libcnb.BOMEntry{
   738  				Name: filepath.Base("helper"),
   739  				Metadata: map[string]interface{}{
   740  					"layer":   "helper",
   741  					"names":   []string{"test-name-1", "test-name-2"},
   742  					"version": "test-version",
   743  				},
   744  				Launch: true,
   745  				Build:  false,
   746  			}))
   747  		})
   748  	})
   749  
   750  	context("HelperLayerContributor", func() {
   751  		var (
   752  			buildpack libcnb.Buildpack
   753  			hlc       libpak.HelperLayerContributor
   754  		)
   755  
   756  		it.Before(func() {
   757  			buildpack.Info = libcnb.BuildpackInfo{
   758  				ID:       "test-id",
   759  				Name:     "test-name",
   760  				Version:  "test-version",
   761  				Homepage: "test-homepage",
   762  			}
   763  
   764  			buildpack.Path = t.TempDir()
   765  			file := filepath.Join(buildpack.Path, "bin")
   766  			Expect(os.MkdirAll(file, 0755)).To(Succeed())
   767  
   768  			file = filepath.Join(file, "helper")
   769  			Expect(os.WriteFile(file, []byte{}, 0755)).To(Succeed())
   770  
   771  			hlc = libpak.HelperLayerContributor{
   772  				Path:          file,
   773  				BuildpackInfo: buildpack.Info,
   774  				Logger:        bard.Logger{},
   775  				Names:         []string{"test-name-1", "test-name-2"},
   776  			}
   777  		})
   778  
   779  		it.After(func() {
   780  			Expect(os.RemoveAll(buildpack.Path)).To(Succeed())
   781  		})
   782  
   783  		it("calls function with no existing metadata", func() {
   784  			_, err := hlc.Contribute(layer)
   785  			Expect(err).NotTo(HaveOccurred())
   786  
   787  			Expect(filepath.Join(layer.Exec.FilePath("test-name-1"))).To(BeAnExistingFile())
   788  		})
   789  
   790  		it("calls function with non-matching metadata", func() {
   791  			layer.Metadata["alpha"] = "other-alpha"
   792  
   793  			_, err := hlc.Contribute(layer)
   794  			Expect(err).NotTo(HaveOccurred())
   795  
   796  			file := filepath.Join(layer.Exec.FilePath("test-name-1"))
   797  			Expect(file).To(BeAnExistingFile())
   798  			Expect(os.Readlink(file)).To(Equal(filepath.Join(layer.Path, "helper")))
   799  
   800  			file = filepath.Join(layer.Exec.FilePath("test-name-2"))
   801  			Expect(file).To(BeAnExistingFile())
   802  			Expect(os.Readlink(file)).To(Equal(filepath.Join(layer.Path, "helper")))
   803  		})
   804  
   805  		it("does not call function with matching metadata", func() {
   806  			buildpackInfo := map[string]interface{}{
   807  				"id":          buildpack.Info.ID,
   808  				"name":        buildpack.Info.Name,
   809  				"version":     buildpack.Info.Version,
   810  				"homepage":    buildpack.Info.Homepage,
   811  				"clear-env":   buildpack.Info.ClearEnvironment,
   812  				"description": "",
   813  			}
   814  			layer.Metadata["buildpackInfo"] = buildpackInfo
   815  			layer.Metadata["helperNames"] = []interface{}{hlc.Names[0], hlc.Names[1]}
   816  
   817  			_, err := hlc.Contribute(layer)
   818  
   819  			Expect(err).NotTo(HaveOccurred())
   820  
   821  			Expect(filepath.Join(layer.Exec.FilePath("test-name-1"))).NotTo(BeAnExistingFile())
   822  			Expect(filepath.Join(layer.Exec.FilePath("test-name-2"))).NotTo(BeAnExistingFile())
   823  		})
   824  
   825  		it("adds expected metadata to layer", func() {
   826  			layer, err := hlc.Contribute(layer)
   827  			Expect(err).NotTo(HaveOccurred())
   828  
   829  			buildpackInfo := map[string]interface{}{
   830  				"id":          buildpack.Info.ID,
   831  				"name":        buildpack.Info.Name,
   832  				"version":     buildpack.Info.Version,
   833  				"homepage":    buildpack.Info.Homepage,
   834  				"clear-env":   buildpack.Info.ClearEnvironment,
   835  				"description": "",
   836  			}
   837  			Expect(layer.Metadata).To(Equal(map[string]interface{}{"buildpackInfo": buildpackInfo, "helperNames": []interface{}{hlc.Names[0], hlc.Names[1]}}))
   838  		})
   839  
   840  		it("sets layer flags regardless of caching behavior (required for 0.6 API)", func() {
   841  			buildpackInfo := map[string]interface{}{
   842  				"id":          buildpack.Info.ID,
   843  				"name":        buildpack.Info.Name,
   844  				"version":     buildpack.Info.Version,
   845  				"homepage":    buildpack.Info.Homepage,
   846  				"clear-env":   buildpack.Info.ClearEnvironment,
   847  				"description": "",
   848  			}
   849  			layer.Metadata["buildpackInfo"] = buildpackInfo
   850  			layer.Metadata["helperNames"] = []interface{}{hlc.Names[0], hlc.Names[1]}
   851  
   852  			// Launch is the only one set & always true
   853  
   854  			layer, err := hlc.Contribute(layer)
   855  			Expect(err).NotTo(HaveOccurred())
   856  
   857  			Expect(filepath.Join(layer.Exec.FilePath("test-name-1"))).NotTo(BeAnExistingFile())
   858  			Expect(filepath.Join(layer.Exec.FilePath("test-name-2"))).NotTo(BeAnExistingFile())
   859  
   860  			Expect(layer.LayerTypes.Launch).To(BeTrue())
   861  			Expect(layer.LayerTypes.Cache).To(BeFalse())
   862  			Expect(layer.LayerTypes.Build).To(BeFalse())
   863  		})
   864  
   865  		it("adds expected Syft SBOM file", func() {
   866  			layer.Metadata = map[string]interface{}{}
   867  
   868  			_, err := hlc.Contribute(layer)
   869  			Expect(err).NotTo(HaveOccurred())
   870  
   871  			Expect(filepath.Join(layer.Exec.FilePath("test-name-1"))).To(BeAnExistingFile())
   872  			Expect(filepath.Join(layer.Exec.FilePath("test-name-2"))).To(BeAnExistingFile())
   873  
   874  			outputFile := layer.SBOMPath(libcnb.SyftJSON)
   875  			Expect(outputFile).To(BeARegularFile())
   876  
   877  			data, err := os.ReadFile(outputFile)
   878  			Expect(err).ToNot(HaveOccurred())
   879  			Expect(string(data)).To(ContainSubstring(`"Artifacts":[`))
   880  			Expect(string(data)).To(ContainSubstring(`"FoundBy":"libpak",`))
   881  			Expect(string(data)).To(ContainSubstring(`"PURL":"pkg:generic/test-id@test-version"`))
   882  			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:*:*:*:*:*:*:*"]`))
   883  			Expect(string(data)).To(ContainSubstring(`"Schema":{`))
   884  			Expect(string(data)).To(ContainSubstring(`"Descriptor":{`))
   885  			Expect(string(data)).To(ContainSubstring(`"Source":{`))
   886  		})
   887  	})
   888  }