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  }