github.com/rrashidov/libpak@v0.0.0-20230911084305-75119185bb4d/dependency_cache_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  	"io"
    22  	"net/http"
    23  	"os"
    24  	"path/filepath"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/BurntSushi/toml"
    29  	"github.com/buildpacks/libcnb"
    30  	. "github.com/onsi/gomega"
    31  	"github.com/onsi/gomega/ghttp"
    32  	"github.com/sclevine/spec"
    33  
    34  	"github.com/paketo-buildpacks/libpak"
    35  )
    36  
    37  func testDependencyCache(t *testing.T, context spec.G, it spec.S) {
    38  	var (
    39  		Expect = NewWithT(t).Expect
    40  	)
    41  
    42  	context("NewDependencyCache", func() {
    43  		var ctx libcnb.BuildContext
    44  
    45  		it.Before(func() {
    46  			ctx = libcnb.BuildContext{
    47  				Buildpack: libcnb.Buildpack{
    48  					Info: libcnb.BuildpackInfo{
    49  						ID:      "some-buildpack-id",
    50  						Version: "some-buildpack-version",
    51  					},
    52  					Path: "some/path",
    53  				},
    54  			}
    55  		})
    56  
    57  		it("set default CachePath and UserAgent", func() {
    58  			dependencyCache, err := libpak.NewDependencyCache(ctx)
    59  			Expect(err).NotTo(HaveOccurred())
    60  			Expect(dependencyCache.CachePath).To(Equal(filepath.Join("some/path/dependencies")))
    61  			Expect(dependencyCache.UserAgent).To(Equal("some-buildpack-id/some-buildpack-version"))
    62  			Expect(dependencyCache.Mappings).To(Equal(map[string]string{}))
    63  		})
    64  
    65  		it("uses default timeout values", func() {
    66  			dependencyCache, err := libpak.NewDependencyCache(ctx)
    67  			Expect(err).NotTo(HaveOccurred())
    68  			Expect(dependencyCache.HttpClientTimeouts.DialerTimeout).To(Equal(6 * time.Second))
    69  			Expect(dependencyCache.HttpClientTimeouts.DialerKeepAlive).To(Equal(60 * time.Second))
    70  			Expect(dependencyCache.HttpClientTimeouts.TLSHandshakeTimeout).To(Equal(5 * time.Second))
    71  			Expect(dependencyCache.HttpClientTimeouts.ResponseHeaderTimeout).To(Equal(5 * time.Second))
    72  			Expect(dependencyCache.HttpClientTimeouts.ExpectContinueTimeout).To(Equal(1 * time.Second))
    73  		})
    74  
    75  		context("custom timeout setttings", func() {
    76  			it.Before(func() {
    77  				t.Setenv("BP_DIALER_TIMEOUT", "7")
    78  				t.Setenv("BP_DIALER_KEEP_ALIVE", "50")
    79  				t.Setenv("BP_TLS_HANDSHAKE_TIMEOUT", "2")
    80  				t.Setenv("BP_RESPONSE_HEADER_TIMEOUT", "3")
    81  				t.Setenv("BP_EXPECT_CONTINUE_TIMEOUT", "2")
    82  			})
    83  
    84  			it("uses custom timeout values", func() {
    85  				dependencyCache, err := libpak.NewDependencyCache(ctx)
    86  				Expect(err).NotTo(HaveOccurred())
    87  				Expect(dependencyCache.HttpClientTimeouts.DialerTimeout).To(Equal(7 * time.Second))
    88  				Expect(dependencyCache.HttpClientTimeouts.DialerKeepAlive).To(Equal(50 * time.Second))
    89  				Expect(dependencyCache.HttpClientTimeouts.TLSHandshakeTimeout).To(Equal(2 * time.Second))
    90  				Expect(dependencyCache.HttpClientTimeouts.ResponseHeaderTimeout).To(Equal(3 * time.Second))
    91  				Expect(dependencyCache.HttpClientTimeouts.ExpectContinueTimeout).To(Equal(2 * time.Second))
    92  			})
    93  		})
    94  
    95  		context("bindings with type dependencies exist", func() {
    96  			it.Before(func() {
    97  				ctx.Platform.Bindings = libcnb.Bindings{
    98  					{
    99  						Type: "dependency-mapping",
   100  						Secret: map[string]string{
   101  							"some-digest1": "some-uri1",
   102  							"some-digest2": "some-uri2",
   103  						},
   104  					},
   105  					{
   106  						Type: "not-dependency-mapping",
   107  						Secret: map[string]string{
   108  							"some-thing": "other-thing",
   109  						},
   110  					},
   111  					{
   112  						Type: "dependency-mapping",
   113  						Secret: map[string]string{
   114  							"some-digest3": "some-uri3",
   115  							"some-digest4": "some-uri4",
   116  						},
   117  					},
   118  				}
   119  			})
   120  
   121  			it("sets Mappings", func() {
   122  				dependencyCache, err := libpak.NewDependencyCache(ctx)
   123  				Expect(err).NotTo(HaveOccurred())
   124  				Expect(dependencyCache.Mappings).To(Equal(
   125  					map[string]string{
   126  						"some-digest1": "some-uri1",
   127  						"some-digest2": "some-uri2",
   128  						"some-digest3": "some-uri3",
   129  						"some-digest4": "some-uri4",
   130  					},
   131  				))
   132  			})
   133  
   134  			context("multiple bindings map the same digest", func() {
   135  				it.Before(func() {
   136  					ctx.Platform.Bindings = append(ctx.Platform.Bindings, libcnb.Binding{
   137  						Type: "dependency-mapping",
   138  						Secret: map[string]string{
   139  							"some-digest1": "other-uri",
   140  						},
   141  					})
   142  				})
   143  
   144  				it("errors", func() {
   145  					_, err := libpak.NewDependencyCache(ctx)
   146  					Expect(err).To(HaveOccurred())
   147  				})
   148  			})
   149  		})
   150  	})
   151  
   152  	context("artifacts", func() {
   153  		var (
   154  			cachePath       string
   155  			downloadPath    string
   156  			dependency      libpak.BuildpackDependency
   157  			dependencyCache libpak.DependencyCache
   158  			server          *ghttp.Server
   159  		)
   160  
   161  		it.Before(func() {
   162  			var err error
   163  
   164  			cachePath = t.TempDir()
   165  			Expect(err).NotTo(HaveOccurred())
   166  
   167  			downloadPath = t.TempDir()
   168  			Expect(err).NotTo(HaveOccurred())
   169  
   170  			RegisterTestingT(t)
   171  			server = ghttp.NewServer()
   172  
   173  			dependency = libpak.BuildpackDependency{
   174  				ID:              "test-id",
   175  				Name:            "test-name",
   176  				Version:         "1.1.1",
   177  				URI:             fmt.Sprintf("%s/test-path", server.URL()),
   178  				SHA256:          "576dd8416de5619ea001d9662291d62444d1292a38e96956bc4651c01f14bca1",
   179  				Stacks:          []string{"test-stack"},
   180  				DeprecationDate: time.Now(),
   181  				Licenses: []libpak.BuildpackDependencyLicense{
   182  					{
   183  						Type: "test-type",
   184  						URI:  "test-uri",
   185  					},
   186  				},
   187  				CPEs: []string{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"},
   188  				PURL: "pkg:generic/some-java11@11.0.2?arch=amd64",
   189  			}
   190  
   191  			dependencyCache = libpak.DependencyCache{
   192  				CachePath:    cachePath,
   193  				DownloadPath: downloadPath,
   194  				UserAgent:    "test-user-agent",
   195  			}
   196  		})
   197  
   198  		it.After(func() {
   199  			Expect(os.RemoveAll(cachePath)).To(Succeed())
   200  			Expect(os.RemoveAll(downloadPath)).To(Succeed())
   201  			server.Close()
   202  		})
   203  
   204  		copyFile := func(source string, destination string) {
   205  			in, err := os.Open(source)
   206  			Expect(err).NotTo(HaveOccurred())
   207  			defer in.Close()
   208  
   209  			Expect(os.MkdirAll(filepath.Dir(destination), 0755)).To(Succeed())
   210  			out, err := os.OpenFile(destination, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
   211  			Expect(err).NotTo(HaveOccurred())
   212  			defer out.Close()
   213  
   214  			_, err = io.Copy(out, in)
   215  			Expect(err).NotTo(HaveOccurred())
   216  		}
   217  
   218  		writeTOML := func(destination string, v interface{}) {
   219  			Expect(os.MkdirAll(filepath.Dir(destination), 0755)).To(Succeed())
   220  			out, err := os.OpenFile(destination, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
   221  			Expect(err).NotTo(HaveOccurred())
   222  			defer out.Close()
   223  
   224  			Expect(toml.NewEncoder(out).Encode(v)).To(Succeed())
   225  		}
   226  
   227  		it("returns from cache path", func() {
   228  			copyFile(filepath.Join("testdata", "test-file"), filepath.Join(cachePath, dependency.SHA256, "test-path"))
   229  			writeTOML(filepath.Join(cachePath, fmt.Sprintf("%s.toml", dependency.SHA256)), dependency)
   230  
   231  			a, err := dependencyCache.Artifact(dependency)
   232  			Expect(err).NotTo(HaveOccurred())
   233  
   234  			Expect(io.ReadAll(a)).To(Equal([]byte("test-fixture")))
   235  		})
   236  
   237  		it("returns from download path", func() {
   238  			copyFile(filepath.Join("testdata", "test-file"), filepath.Join(downloadPath, dependency.SHA256, "test-path"))
   239  			writeTOML(filepath.Join(downloadPath, fmt.Sprintf("%s.toml", dependency.SHA256)), dependency)
   240  
   241  			a, err := dependencyCache.Artifact(dependency)
   242  			Expect(err).NotTo(HaveOccurred())
   243  
   244  			Expect(io.ReadAll(a)).To(Equal([]byte("test-fixture")))
   245  		})
   246  
   247  		it("downloads", func() {
   248  			server.AppendHandlers(ghttp.CombineHandlers(
   249  				ghttp.VerifyRequest(http.MethodGet, "/test-path", ""),
   250  				ghttp.RespondWith(http.StatusOK, "test-fixture"),
   251  			))
   252  
   253  			a, err := dependencyCache.Artifact(dependency)
   254  			Expect(err).NotTo(HaveOccurred())
   255  
   256  			Expect(io.ReadAll(a)).To(Equal([]byte("test-fixture")))
   257  		})
   258  
   259  		context("uri is overridden HTTP", func() {
   260  			it.Before(func() {
   261  				dependencyCache.Mappings = map[string]string{
   262  					dependency.SHA256: fmt.Sprintf("%s/override-path", server.URL()),
   263  				}
   264  			})
   265  
   266  			it("downloads from override uri", func() {
   267  				server.AppendHandlers(ghttp.CombineHandlers(
   268  					ghttp.VerifyRequest(http.MethodGet, "/override-path", ""),
   269  					ghttp.RespondWith(http.StatusOK, "test-fixture"),
   270  				))
   271  
   272  				a, err := dependencyCache.Artifact(dependency)
   273  				Expect(err).NotTo(HaveOccurred())
   274  
   275  				Expect(io.ReadAll(a)).To(Equal([]byte("test-fixture")))
   276  			})
   277  		})
   278  
   279  		context("uri is overridden FILE", func() {
   280  			it.Before(func() {
   281  				sourcePath := t.TempDir()
   282  				sourceFile := filepath.Join(sourcePath, "source-file")
   283  				Expect(os.WriteFile(sourceFile, []byte("test-fixture"), 0644)).ToNot(HaveOccurred())
   284  
   285  				dependencyCache.Mappings = map[string]string{
   286  					dependency.SHA256: fmt.Sprintf("file://%s", sourceFile),
   287  				}
   288  			})
   289  
   290  			it("downloads from override filesystem", func() {
   291  				a, err := dependencyCache.Artifact(dependency)
   292  				Expect(err).NotTo(HaveOccurred())
   293  
   294  				Expect(io.ReadAll(a)).To(Equal([]byte("test-fixture")))
   295  			})
   296  		})
   297  
   298  		it("fails with invalid SHA256", func() {
   299  			server.AppendHandlers(ghttp.RespondWith(http.StatusOK, "invalid-fixture"))
   300  
   301  			_, err := dependencyCache.Artifact(dependency)
   302  			Expect(err).To(HaveOccurred())
   303  		})
   304  
   305  		it("skips cache with empty SHA256", func() {
   306  			copyFile(filepath.Join("testdata", "test-file"), filepath.Join(cachePath, dependency.SHA256, "test-path"))
   307  			writeTOML(filepath.Join(cachePath, fmt.Sprintf("%s.toml", dependency.SHA256)), dependency)
   308  			copyFile(filepath.Join("testdata", "test-file"), filepath.Join(downloadPath, dependency.SHA256, "test-path"))
   309  			writeTOML(filepath.Join(downloadPath, fmt.Sprintf("%s.toml", dependency.SHA256)), dependency)
   310  
   311  			dependency.SHA256 = ""
   312  			server.AppendHandlers(ghttp.RespondWith(http.StatusOK, "alternate-fixture"))
   313  
   314  			a, err := dependencyCache.Artifact(dependency)
   315  			Expect(err).NotTo(HaveOccurred())
   316  
   317  			Expect(io.ReadAll(a)).To(Equal([]byte("alternate-fixture")))
   318  		})
   319  
   320  		it("sets User-Agent", func() {
   321  			server.AppendHandlers(ghttp.CombineHandlers(
   322  				ghttp.VerifyHeaderKV("User-Agent", "test-user-agent"),
   323  				ghttp.RespondWith(http.StatusOK, "test-fixture"),
   324  			))
   325  
   326  			a, err := dependencyCache.Artifact(dependency)
   327  			Expect(err).NotTo(HaveOccurred())
   328  
   329  			Expect(io.ReadAll(a)).To(Equal([]byte("test-fixture")))
   330  		})
   331  
   332  		it("modifies request", func() {
   333  			server.AppendHandlers(ghttp.CombineHandlers(
   334  				ghttp.VerifyHeaderKV("User-Agent", "test-user-agent"),
   335  				ghttp.VerifyHeaderKV("Test-Key", "test-value"),
   336  				ghttp.RespondWith(http.StatusOK, "test-fixture"),
   337  			))
   338  
   339  			a, err := dependencyCache.Artifact(dependency, func(request *http.Request) (*http.Request, error) {
   340  				request.Header.Add("Test-Key", "test-value")
   341  				return request, nil
   342  			})
   343  			Expect(err).NotTo(HaveOccurred())
   344  
   345  			Expect(io.ReadAll(a)).To(Equal([]byte("test-fixture")))
   346  		})
   347  	})
   348  }