github.com/BarDweller/libpak@v0.0.0-20230630201634-8dd5cfc15ec9/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/BarDweller/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.BuildModuleDependency 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.BuildModuleDependency{ 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.BuildModuleDependencyLicense{ 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 }