github.com/YousefHaggyHeroku/pack@v1.5.5/internal/builder/inspect_test.go (about) 1 package builder_test 2 3 import ( 4 "errors" 5 "testing" 6 7 "github.com/buildpacks/lifecycle/api" 8 9 pubbldr "github.com/YousefHaggyHeroku/pack/builder" 10 "github.com/YousefHaggyHeroku/pack/config" 11 "github.com/YousefHaggyHeroku/pack/internal/builder" 12 "github.com/YousefHaggyHeroku/pack/internal/builder/fakes" 13 "github.com/YousefHaggyHeroku/pack/internal/dist" 14 h "github.com/YousefHaggyHeroku/pack/testhelpers" 15 16 "github.com/heroku/color" 17 "github.com/sclevine/spec" 18 "github.com/sclevine/spec/report" 19 ) 20 21 const ( 22 testBuilderName = "test-builder" 23 testBuilderDescription = "Test Builder Description" 24 testStackID = "test-builder-stack-id" 25 testRunImage = "test/run-image" 26 ) 27 28 var ( 29 testTopNestedBuildpack = dist.BuildpackInfo{ 30 ID: "test.top.nested", 31 Version: "test.top.nested.version", 32 } 33 testNestedBuildpack = dist.BuildpackInfo{ 34 ID: "test.nested", 35 Version: "test.nested.version", 36 Homepage: "http://geocities.com/top-bp", 37 } 38 testBuildpack = dist.BuildpackInfo{ 39 ID: "test.bp.two", 40 Version: "test.bp.two.version", 41 } 42 testBuildpacks = []dist.BuildpackInfo{ 43 testTopNestedBuildpack, 44 testNestedBuildpack, 45 testBuildpack, 46 } 47 testLifecycleInfo = builder.LifecycleInfo{ 48 Version: builder.VersionMustParse("1.2.3"), 49 } 50 testBuildpackVersions = builder.APIVersions{ 51 Deprecated: builder.APISet{api.MustParse("0.1")}, 52 Supported: builder.APISet{api.MustParse("1.2"), api.MustParse("1.3")}, 53 } 54 testPlatformVersions = builder.APIVersions{ 55 Supported: builder.APISet{api.MustParse("2.3"), api.MustParse("2.4")}, 56 } 57 inspectTestLifecycle = builder.LifecycleMetadata{ 58 LifecycleInfo: testLifecycleInfo, 59 APIs: builder.LifecycleAPIs{ 60 Buildpack: testBuildpackVersions, 61 Platform: testPlatformVersions, 62 }, 63 } 64 testCreatorData = builder.CreatorMetadata{ 65 Name: "pack", 66 Version: "1.2.3", 67 } 68 testMetadata = builder.Metadata{ 69 Description: testBuilderDescription, 70 Buildpacks: testBuildpacks, 71 Stack: testStack, 72 Lifecycle: inspectTestLifecycle, 73 CreatedBy: testCreatorData, 74 } 75 testMixins = []string{"build:mixinA", "mixinX", "mixinY"} 76 expectedTestMixins = []string{"mixinX", "mixinY", "build:mixinA"} 77 testRunImageMirrors = []string{"test/first-run-image-mirror", "test/second-run-image-mirror"} 78 testStack = builder.StackMetadata{ 79 RunImage: builder.RunImageMetadata{ 80 Image: testRunImage, 81 Mirrors: testRunImageMirrors, 82 }, 83 } 84 testOrder = dist.Order{ 85 dist.OrderEntry{Group: []dist.BuildpackRef{ 86 {BuildpackInfo: testBuildpack, Optional: false}, 87 }}, 88 dist.OrderEntry{Group: []dist.BuildpackRef{ 89 {BuildpackInfo: testNestedBuildpack, Optional: false}, 90 {BuildpackInfo: testTopNestedBuildpack, Optional: true}, 91 }}, 92 } 93 testLayers = dist.BuildpackLayers{ 94 "test.top.nested": { 95 "test.top.nested.version": { 96 API: api.MustParse("0.2"), 97 Order: testOrder, 98 LayerDiffID: "sha256:test.top.nested.sha256", 99 Homepage: "http://geocities.com/top-bp", 100 }, 101 }, 102 "test.bp.two": { 103 "test.bp.two.version": { 104 API: api.MustParse("0.2"), 105 Stacks: []dist.Stack{{ID: "test.stack.id"}}, 106 LayerDiffID: "sha256:test.bp.two.sha256", 107 Homepage: "http://geocities.com/cool-bp", 108 }, 109 }, 110 } 111 expectedTestLifecycle = builder.LifecycleDescriptor{ 112 Info: testLifecycleInfo, 113 API: builder.LifecycleAPI{ 114 BuildpackVersion: api.MustParse("0.1"), 115 PlatformVersion: api.MustParse("2.3"), 116 }, 117 APIs: builder.LifecycleAPIs{ 118 Buildpack: testBuildpackVersions, 119 Platform: testPlatformVersions, 120 }, 121 } 122 expectedDetectionTestOrder = pubbldr.DetectionOrder{ 123 { 124 BuildpackRef: dist.BuildpackRef{ 125 BuildpackInfo: testBuildpack, 126 }, 127 }, 128 { 129 BuildpackRef: dist.BuildpackRef{ 130 BuildpackInfo: testTopNestedBuildpack, 131 }, 132 GroupDetectionOrder: pubbldr.DetectionOrder{ 133 { 134 BuildpackRef: dist.BuildpackRef{ 135 BuildpackInfo: testNestedBuildpack, 136 }, 137 }, 138 }, 139 }, 140 } 141 ) 142 143 func TestInspect(t *testing.T) { 144 color.Disable(true) 145 defer color.Disable(false) 146 spec.Run(t, "testInspect", testInspect, spec.Parallel(), spec.Report(report.Terminal{})) 147 } 148 149 func testInspect(t *testing.T, when spec.G, it spec.S) { 150 when("Inspect", func() { 151 var assert = h.NewAssertionManager(t) 152 153 it("calls Fetch on inspectableFetcher with expected arguments", func() { 154 fetcher := newDefaultInspectableFetcher() 155 156 inspector := builder.NewInspector(fetcher, newDefaultLabelManagerFactory(), newDefaultDetectionCalculator()) 157 _, err := inspector.Inspect(testBuilderName, true, pubbldr.OrderDetectionNone) 158 assert.Nil(err) 159 160 assert.Equal(fetcher.CallCount, 1) 161 assert.Equal(fetcher.ReceivedName, testBuilderName) 162 assert.Equal(fetcher.ReceivedDaemon, true) 163 assert.Equal(fetcher.ReceivedPullPolicy, config.PullNever) 164 }) 165 166 it("instantiates a builder label manager with the correct inspectable", func() { 167 inspectable := newNoOpInspectable() 168 169 fetcher := &fakes.FakeInspectableFetcher{ 170 InspectableToReturn: inspectable, 171 } 172 173 labelManagerFactory := newDefaultLabelManagerFactory() 174 175 inspector := builder.NewInspector(fetcher, labelManagerFactory, newDefaultDetectionCalculator()) 176 _, err := inspector.Inspect(testBuilderName, true, pubbldr.OrderDetectionNone) 177 assert.Nil(err) 178 179 assert.Equal(labelManagerFactory.ReceivedInspectable, inspectable) 180 }) 181 182 it("calls `Order` on detectionCalculator with expected arguments", func() { 183 detectionOrderCalculator := newDefaultDetectionCalculator() 184 185 inspector := builder.NewInspector( 186 newDefaultInspectableFetcher(), 187 newDefaultLabelManagerFactory(), 188 detectionOrderCalculator, 189 ) 190 _, err := inspector.Inspect(testBuilderName, true, 3) 191 assert.Nil(err) 192 193 assert.Equal(detectionOrderCalculator.ReceivedTopOrder, testOrder) 194 assert.Equal(detectionOrderCalculator.ReceivedLayers, testLayers) 195 assert.Equal(detectionOrderCalculator.ReceivedDepth, 3) 196 }) 197 198 it("returns Info object with expected fields", func() { 199 fetcher := newDefaultInspectableFetcher() 200 201 inspector := builder.NewInspector(fetcher, newDefaultLabelManagerFactory(), newDefaultDetectionCalculator()) 202 info, err := inspector.Inspect(testBuilderName, true, pubbldr.OrderDetectionNone) 203 assert.Nil(err) 204 205 assert.Equal(info.Description, testBuilderDescription) 206 assert.Equal(info.StackID, testStackID) 207 assert.Equal(info.Mixins, expectedTestMixins) 208 assert.Equal(info.RunImage, testRunImage) 209 assert.Equal(info.RunImageMirrors, testRunImageMirrors) 210 assert.Equal(info.Buildpacks, testBuildpacks) 211 assert.Equal(info.Order, expectedDetectionTestOrder) 212 assert.Equal(info.BuildpackLayers, testLayers) 213 assert.Equal(info.Lifecycle, expectedTestLifecycle) 214 assert.Equal(info.CreatedBy, testCreatorData) 215 }) 216 217 when("there are duplicated buildpacks in metadata", func() { 218 it("returns deduplicated buildpacks", func() { 219 metadata := builder.Metadata{ 220 Description: testBuilderDescription, 221 Buildpacks: []dist.BuildpackInfo{ 222 testTopNestedBuildpack, 223 testNestedBuildpack, 224 testTopNestedBuildpack, 225 }, 226 } 227 labelManager := newLabelManager(returnForMetadata(metadata)) 228 229 inspector := builder.NewInspector( 230 newDefaultInspectableFetcher(), 231 newLabelManagerFactory(labelManager), 232 newDefaultDetectionCalculator(), 233 ) 234 info, err := inspector.Inspect(testBuilderName, true, pubbldr.OrderDetectionNone) 235 236 assert.Nil(err) 237 assert.Equal(info.Buildpacks, []dist.BuildpackInfo{testTopNestedBuildpack, testNestedBuildpack}) 238 }) 239 }) 240 241 when("label manager returns an error for `Metadata`", func() { 242 it("returns the wrapped error", func() { 243 expectedBaseError := errors.New("failed to parse") 244 245 labelManager := newLabelManager(errorForMetadata(expectedBaseError)) 246 247 inspector := builder.NewInspector( 248 newDefaultInspectableFetcher(), 249 newLabelManagerFactory(labelManager), 250 newDefaultDetectionCalculator(), 251 ) 252 _, err := inspector.Inspect(testBuilderName, true, pubbldr.OrderDetectionNone) 253 254 assert.ErrorWithMessage(err, "reading image metadata: failed to parse") 255 }) 256 }) 257 258 when("label manager returns an error for `StackID`", func() { 259 it("returns the wrapped error", func() { 260 expectedBaseError := errors.New("label not found") 261 262 labelManager := newLabelManager(errorForStackID(expectedBaseError)) 263 264 inspector := builder.NewInspector( 265 newDefaultInspectableFetcher(), 266 newLabelManagerFactory(labelManager), 267 newDefaultDetectionCalculator(), 268 ) 269 _, err := inspector.Inspect(testBuilderName, true, pubbldr.OrderDetectionNone) 270 271 assert.ErrorWithMessage(err, "reading image stack id: label not found") 272 }) 273 }) 274 275 when("label manager returns an error for `Mixins`", func() { 276 it("returns the wrapped error", func() { 277 expectedBaseError := errors.New("label not found") 278 279 labelManager := newLabelManager(errorForMixins(expectedBaseError)) 280 281 inspector := builder.NewInspector( 282 newDefaultInspectableFetcher(), 283 newLabelManagerFactory(labelManager), 284 newDefaultDetectionCalculator(), 285 ) 286 _, err := inspector.Inspect(testBuilderName, true, pubbldr.OrderDetectionNone) 287 288 assert.ErrorWithMessage(err, "reading image mixins: label not found") 289 }) 290 }) 291 292 when("label manager returns an error for `Order`", func() { 293 it("returns the wrapped error", func() { 294 expectedBaseError := errors.New("label not found") 295 296 labelManager := newLabelManager(errorForOrder(expectedBaseError)) 297 298 inspector := builder.NewInspector( 299 newDefaultInspectableFetcher(), 300 newLabelManagerFactory(labelManager), 301 newDefaultDetectionCalculator(), 302 ) 303 _, err := inspector.Inspect(testBuilderName, true, pubbldr.OrderDetectionNone) 304 305 assert.ErrorWithMessage(err, "reading image order: label not found") 306 }) 307 }) 308 309 when("label manager returns an error for `BuildpackLayers`", func() { 310 it("returns the wrapped error", func() { 311 expectedBaseError := errors.New("label not found") 312 313 labelManager := newLabelManager(errorForBuildpackLayers(expectedBaseError)) 314 315 inspector := builder.NewInspector( 316 newDefaultInspectableFetcher(), 317 newLabelManagerFactory(labelManager), 318 newDefaultDetectionCalculator(), 319 ) 320 _, err := inspector.Inspect(testBuilderName, true, pubbldr.OrderDetectionNone) 321 322 assert.ErrorWithMessage(err, "reading image buildpack layers: label not found") 323 }) 324 }) 325 326 when("detection calculator returns an error for `Order`", func() { 327 it("returns the wrapped error", func() { 328 expectedBaseError := errors.New("couldn't read label") 329 330 inspector := builder.NewInspector( 331 newDefaultInspectableFetcher(), 332 newDefaultLabelManagerFactory(), 333 newDetectionCalculator(errorForDetectionOrder(expectedBaseError)), 334 ) 335 _, err := inspector.Inspect(testBuilderName, true, pubbldr.OrderDetectionMaxDepth) 336 337 assert.ErrorWithMessage(err, "calculating detection order: couldn't read label") 338 }) 339 }) 340 }) 341 } 342 343 func newDefaultInspectableFetcher() *fakes.FakeInspectableFetcher { 344 return &fakes.FakeInspectableFetcher{ 345 InspectableToReturn: newNoOpInspectable(), 346 } 347 } 348 349 func newNoOpInspectable() *fakes.FakeInspectable { 350 return &fakes.FakeInspectable{} 351 } 352 353 func newDefaultLabelManagerFactory() *fakes.FakeLabelManagerFactory { 354 return newLabelManagerFactory(newDefaultLabelManager()) 355 } 356 357 func newLabelManagerFactory(manager builder.LabelInspector) *fakes.FakeLabelManagerFactory { 358 return fakes.NewFakeLabelManagerFactory(manager) 359 } 360 361 func newDefaultLabelManager() *fakes.FakeLabelManager { 362 return &fakes.FakeLabelManager{ 363 ReturnForMetadata: testMetadata, 364 ReturnForStackID: testStackID, 365 ReturnForMixins: testMixins, 366 ReturnForOrder: testOrder, 367 ReturnForBuildpackLayers: testLayers, 368 } 369 } 370 371 type labelManagerModifier func(manager *fakes.FakeLabelManager) 372 373 func returnForMetadata(metadata builder.Metadata) labelManagerModifier { 374 return func(manager *fakes.FakeLabelManager) { 375 manager.ReturnForMetadata = metadata 376 } 377 } 378 379 func errorForMetadata(err error) labelManagerModifier { 380 return func(manager *fakes.FakeLabelManager) { 381 manager.ErrorForMetadata = err 382 } 383 } 384 385 func errorForStackID(err error) labelManagerModifier { 386 return func(manager *fakes.FakeLabelManager) { 387 manager.ErrorForStackID = err 388 } 389 } 390 391 func errorForMixins(err error) labelManagerModifier { 392 return func(manager *fakes.FakeLabelManager) { 393 manager.ErrorForMixins = err 394 } 395 } 396 397 func errorForOrder(err error) labelManagerModifier { 398 return func(manager *fakes.FakeLabelManager) { 399 manager.ErrorForOrder = err 400 } 401 } 402 403 func errorForBuildpackLayers(err error) labelManagerModifier { 404 return func(manager *fakes.FakeLabelManager) { 405 manager.ErrorForBuildpackLayers = err 406 } 407 } 408 409 func newLabelManager(modifiers ...labelManagerModifier) *fakes.FakeLabelManager { 410 manager := newDefaultLabelManager() 411 412 for _, mod := range modifiers { 413 mod(manager) 414 } 415 416 return manager 417 } 418 419 func newDefaultDetectionCalculator() *fakes.FakeDetectionCalculator { 420 return &fakes.FakeDetectionCalculator{ 421 ReturnForOrder: expectedDetectionTestOrder, 422 } 423 } 424 425 type detectionCalculatorModifier func(calculator *fakes.FakeDetectionCalculator) 426 427 func errorForDetectionOrder(err error) detectionCalculatorModifier { 428 return func(calculator *fakes.FakeDetectionCalculator) { 429 calculator.ErrorForOrder = err 430 } 431 } 432 433 func newDetectionCalculator(modifiers ...detectionCalculatorModifier) *fakes.FakeDetectionCalculator { 434 calculator := newDefaultDetectionCalculator() 435 436 for _, mod := range modifiers { 437 mod(calculator) 438 } 439 440 return calculator 441 }