github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/internal/commands/buildpack_inspect_test.go (about)

     1  package commands_test
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/buildpacks/lifecycle/api"
     9  	"github.com/golang/mock/gomock"
    10  	"github.com/heroku/color"
    11  	"github.com/pkg/errors"
    12  	"github.com/sclevine/spec"
    13  	"github.com/sclevine/spec/report"
    14  	"github.com/spf13/cobra"
    15  
    16  	"github.com/buildpacks/pack/internal/commands"
    17  	"github.com/buildpacks/pack/internal/commands/testmocks"
    18  	"github.com/buildpacks/pack/internal/config"
    19  	"github.com/buildpacks/pack/pkg/buildpack"
    20  	"github.com/buildpacks/pack/pkg/client"
    21  	"github.com/buildpacks/pack/pkg/dist"
    22  	"github.com/buildpacks/pack/pkg/image"
    23  	"github.com/buildpacks/pack/pkg/logging"
    24  	h "github.com/buildpacks/pack/testhelpers"
    25  )
    26  
    27  const complexOutputSection = `Stacks:
    28    ID: io.buildpacks.stacks.first-stack
    29      Mixins:
    30        (omitted)
    31    ID: io.buildpacks.stacks.second-stack
    32      Mixins:
    33        (omitted)
    34  
    35  Buildpacks:
    36    ID                                 NAME        VERSION        HOMEPAGE
    37    some/first-inner-buildpack         -           1.0.0          first-inner-buildpack-homepage
    38    some/second-inner-buildpack        -           2.0.0          second-inner-buildpack-homepage
    39    some/third-inner-buildpack         -           3.0.0          third-inner-buildpack-homepage
    40    some/top-buildpack                 top         0.0.1          top-buildpack-homepage
    41  
    42  Detection Order:
    43   └ Group #1:
    44      └ some/top-buildpack@0.0.1
    45         ├ Group #1:
    46         │  ├ some/first-inner-buildpack@1.0.0
    47         │  │  ├ Group #1:
    48         │  │  │  ├ some/first-inner-buildpack@1.0.0    [cyclic]
    49         │  │  │  └ some/third-inner-buildpack@3.0.0
    50         │  │  └ Group #2:
    51         │  │     └ some/third-inner-buildpack@3.0.0
    52         │  └ some/second-inner-buildpack@2.0.0
    53         └ Group #2:
    54            └ some/first-inner-buildpack@1.0.0
    55               ├ Group #1:
    56               │  ├ some/first-inner-buildpack@1.0.0    [cyclic]
    57               │  └ some/third-inner-buildpack@3.0.0
    58               └ Group #2:
    59                  └ some/third-inner-buildpack@3.0.0`
    60  
    61  const simpleOutputSection = `Stacks:
    62    ID: io.buildpacks.stacks.first-stack
    63      Mixins:
    64        (omitted)
    65    ID: io.buildpacks.stacks.second-stack
    66      Mixins:
    67        (omitted)
    68  
    69  Buildpacks:
    70    ID                                NAME        VERSION        HOMEPAGE
    71    some/single-buildpack             some        0.0.1          single-buildpack-homepage
    72    some/buildpack-no-homepage        -           0.0.2          -
    73  
    74  Detection Order:
    75   └ Group #1:
    76      └ some/single-buildpack@0.0.1`
    77  
    78  const inspectOutputTemplate = `Inspecting buildpack: '%s'
    79  
    80  %s
    81  
    82  %s
    83  `
    84  
    85  const depthOutputSection = `
    86  Detection Order:
    87   └ Group #1:
    88      └ some/top-buildpack@0.0.1
    89         ├ Group #1:
    90         │  ├ some/first-inner-buildpack@1.0.0
    91         │  └ some/second-inner-buildpack@2.0.0
    92         └ Group #2:
    93            └ some/first-inner-buildpack@1.0.0`
    94  
    95  const simpleMixinOutputSection = `
    96    ID: io.buildpacks.stacks.first-stack
    97      Mixins:
    98        mixin1
    99        mixin2
   100        build:mixin3
   101        build:mixin4
   102    ID: io.buildpacks.stacks.second-stack
   103      Mixins:
   104        mixin1
   105        mixin2`
   106  
   107  func TestBuildpackInspectCommand(t *testing.T) {
   108  	color.Disable(true)
   109  	defer color.Disable(false)
   110  	spec.Run(t, "BuildpackInspectCommand", testBuildpackInspectCommand, spec.Sequential(), spec.Report(report.Terminal{}))
   111  }
   112  
   113  func testBuildpackInspectCommand(t *testing.T, when spec.G, it spec.S) {
   114  	apiVersion, err := api.NewVersion("0.2")
   115  	if err != nil {
   116  		t.Fail()
   117  	}
   118  
   119  	var (
   120  		command        *cobra.Command
   121  		logger         logging.Logger
   122  		outBuf         bytes.Buffer
   123  		mockController *gomock.Controller
   124  		mockClient     *testmocks.MockPackClient
   125  		cfg            config.Config
   126  		complexInfo    *client.BuildpackInfo
   127  		simpleInfo     *client.BuildpackInfo
   128  		assert         = h.NewAssertionManager(t)
   129  	)
   130  
   131  	it.Before(func() {
   132  		mockController = gomock.NewController(t)
   133  		mockClient = testmocks.NewMockPackClient(mockController)
   134  		logger = logging.NewLogWithWriters(&outBuf, &outBuf)
   135  
   136  		cfg = config.Config{
   137  			DefaultRegistryName: "default-registry",
   138  		}
   139  
   140  		complexInfo = &client.BuildpackInfo{
   141  			BuildpackMetadata: buildpack.Metadata{
   142  				ModuleInfo: dist.ModuleInfo{
   143  					ID:       "some/top-buildpack",
   144  					Version:  "0.0.1",
   145  					Homepage: "top-buildpack-homepage",
   146  					Name:     "top",
   147  				},
   148  				Stacks: []dist.Stack{
   149  					{ID: "io.buildpacks.stacks.first-stack", Mixins: []string{"mixin1", "mixin2", "build:mixin3", "build:mixin4"}},
   150  					{ID: "io.buildpacks.stacks.second-stack", Mixins: []string{"mixin1", "mixin2"}},
   151  				},
   152  			},
   153  			Buildpacks: []dist.ModuleInfo{
   154  				{
   155  					ID:       "some/first-inner-buildpack",
   156  					Version:  "1.0.0",
   157  					Homepage: "first-inner-buildpack-homepage",
   158  				},
   159  				{
   160  					ID:       "some/second-inner-buildpack",
   161  					Version:  "2.0.0",
   162  					Homepage: "second-inner-buildpack-homepage",
   163  				},
   164  				{
   165  					ID:       "some/third-inner-buildpack",
   166  					Version:  "3.0.0",
   167  					Homepage: "third-inner-buildpack-homepage",
   168  				},
   169  				{
   170  					ID:       "some/top-buildpack",
   171  					Version:  "0.0.1",
   172  					Homepage: "top-buildpack-homepage",
   173  					Name:     "top",
   174  				},
   175  			},
   176  			Order: dist.Order{
   177  				{
   178  					Group: []dist.ModuleRef{
   179  						{
   180  							ModuleInfo: dist.ModuleInfo{
   181  								ID:       "some/top-buildpack",
   182  								Version:  "0.0.1",
   183  								Homepage: "top-buildpack-homepage",
   184  								Name:     "top",
   185  							},
   186  							Optional: false,
   187  						},
   188  					},
   189  				},
   190  			},
   191  			BuildpackLayers: dist.ModuleLayers{
   192  				"some/first-inner-buildpack": {
   193  					"1.0.0": {
   194  						API: apiVersion,
   195  						Stacks: []dist.Stack{
   196  							{ID: "io.buildpacks.stacks.first-stack", Mixins: []string{"mixin1", "mixin2", "build:mixin3", "build:mixin4"}},
   197  							{ID: "io.buildpacks.stacks.second-stack", Mixins: []string{"mixin1", "mixin2"}},
   198  						},
   199  						Order: dist.Order{
   200  							{
   201  								Group: []dist.ModuleRef{
   202  									{
   203  										ModuleInfo: dist.ModuleInfo{
   204  											ID:      "some/first-inner-buildpack",
   205  											Version: "1.0.0",
   206  										},
   207  										Optional: false,
   208  									},
   209  									{
   210  										ModuleInfo: dist.ModuleInfo{
   211  											ID:      "some/third-inner-buildpack",
   212  											Version: "3.0.0",
   213  										},
   214  										Optional: false,
   215  									},
   216  								},
   217  							},
   218  							{
   219  								Group: []dist.ModuleRef{
   220  									{
   221  										ModuleInfo: dist.ModuleInfo{
   222  											ID:      "some/third-inner-buildpack",
   223  											Version: "3.0.0",
   224  										},
   225  										Optional: false,
   226  									},
   227  								},
   228  							},
   229  						},
   230  						LayerDiffID: "sha256:first-inner-buildpack-diff-id",
   231  						Homepage:    "first-inner-buildpack-homepage",
   232  					},
   233  				},
   234  				"some/second-inner-buildpack": {
   235  					"2.0.0": {
   236  						API: apiVersion,
   237  						Stacks: []dist.Stack{
   238  							{ID: "io.buildpacks.stacks.first-stack", Mixins: []string{"mixin1", "mixin2", "build:mixin3", "build:mixin4"}},
   239  							{ID: "io.buildpacks.stacks.second-stack", Mixins: []string{"mixin1", "mixin2"}},
   240  						},
   241  						LayerDiffID: "sha256:second-inner-buildpack-diff-id",
   242  						Homepage:    "second-inner-buildpack-homepage",
   243  					},
   244  				},
   245  				"some/third-inner-buildpack": {
   246  					"3.0.0": {
   247  						API: apiVersion,
   248  						Stacks: []dist.Stack{
   249  							{ID: "io.buildpacks.stacks.first-stack", Mixins: []string{"mixin1", "mixin2", "build:mixin3", "build:mixin4"}},
   250  							{ID: "io.buildpacks.stacks.second-stack", Mixins: []string{"mixin1", "mixin2"}},
   251  						},
   252  						LayerDiffID: "sha256:third-inner-buildpack-diff-id",
   253  						Homepage:    "third-inner-buildpack-homepage",
   254  					},
   255  				},
   256  				"some/top-buildpack": {
   257  					"0.0.1": {
   258  						API: apiVersion,
   259  						Order: dist.Order{
   260  							{
   261  								Group: []dist.ModuleRef{
   262  									{
   263  										ModuleInfo: dist.ModuleInfo{
   264  											ID:      "some/first-inner-buildpack",
   265  											Version: "1.0.0",
   266  										},
   267  										Optional: false,
   268  									},
   269  									{
   270  										ModuleInfo: dist.ModuleInfo{
   271  											ID:      "some/second-inner-buildpack",
   272  											Version: "2.0.0",
   273  										},
   274  										Optional: false,
   275  									},
   276  								},
   277  							},
   278  							{
   279  								Group: []dist.ModuleRef{
   280  									{
   281  										ModuleInfo: dist.ModuleInfo{
   282  											ID:      "some/first-inner-buildpack",
   283  											Version: "1.0.0",
   284  										},
   285  										Optional: false,
   286  									},
   287  								},
   288  							},
   289  						},
   290  						LayerDiffID: "sha256:top-buildpack-diff-id",
   291  						Homepage:    "top-buildpack-homepage",
   292  						Name:        "top",
   293  					},
   294  				},
   295  			},
   296  		}
   297  
   298  		simpleInfo = &client.BuildpackInfo{
   299  			BuildpackMetadata: buildpack.Metadata{
   300  				ModuleInfo: dist.ModuleInfo{
   301  					ID:       "some/single-buildpack",
   302  					Version:  "0.0.1",
   303  					Homepage: "single-homepage-homepace",
   304  					Name:     "some",
   305  				},
   306  				Stacks: []dist.Stack{
   307  					{ID: "io.buildpacks.stacks.first-stack", Mixins: []string{"mixin1", "mixin2", "build:mixin3", "build:mixin4"}},
   308  					{ID: "io.buildpacks.stacks.second-stack", Mixins: []string{"mixin1", "mixin2"}},
   309  				},
   310  			},
   311  			Buildpacks: []dist.ModuleInfo{
   312  				{
   313  					ID:       "some/single-buildpack",
   314  					Version:  "0.0.1",
   315  					Name:     "some",
   316  					Homepage: "single-buildpack-homepage",
   317  				},
   318  				{
   319  					ID:      "some/buildpack-no-homepage",
   320  					Version: "0.0.2",
   321  				},
   322  			},
   323  			Order: dist.Order{
   324  				{
   325  					Group: []dist.ModuleRef{
   326  						{
   327  							ModuleInfo: dist.ModuleInfo{
   328  								ID:       "some/single-buildpack",
   329  								Version:  "0.0.1",
   330  								Homepage: "single-buildpack-homepage",
   331  							},
   332  							Optional: false,
   333  						},
   334  					},
   335  				},
   336  			},
   337  			BuildpackLayers: dist.ModuleLayers{
   338  				"some/single-buildpack": {
   339  					"0.0.1": {
   340  						API: apiVersion,
   341  						Stacks: []dist.Stack{
   342  							{ID: "io.buildpacks.stacks.first-stack", Mixins: []string{"mixin1", "mixin2", "build:mixin3", "build:mixin4"}},
   343  							{ID: "io.buildpacks.stacks.second-stack", Mixins: []string{"mixin1", "mixin2"}},
   344  						},
   345  						LayerDiffID: "sha256:single-buildpack-diff-id",
   346  						Homepage:    "single-buildpack-homepage",
   347  						Name:        "some",
   348  					},
   349  				},
   350  			},
   351  		}
   352  
   353  		command = commands.BuildpackInspect(logger, cfg, mockClient)
   354  	})
   355  
   356  	when("BuildpackInspect", func() {
   357  		when("inspecting an image", func() {
   358  			when("both remote and local image are present", func() {
   359  				it.Before(func() {
   360  					complexInfo.Location = buildpack.PackageLocator
   361  					simpleInfo.Location = buildpack.PackageLocator
   362  
   363  					mockClient.EXPECT().InspectBuildpack(client.InspectBuildpackOptions{
   364  						BuildpackName: "test/buildpack",
   365  						Daemon:        true,
   366  						Registry:      "default-registry",
   367  					}).Return(complexInfo, nil)
   368  
   369  					mockClient.EXPECT().InspectBuildpack(client.InspectBuildpackOptions{
   370  						BuildpackName: "test/buildpack",
   371  						Daemon:        false,
   372  						Registry:      "default-registry",
   373  					}).Return(simpleInfo, nil)
   374  				})
   375  
   376  				it("succeeds", func() {
   377  					command.SetArgs([]string{"test/buildpack"})
   378  					assert.Nil(command.Execute())
   379  
   380  					localOutputSection := fmt.Sprintf(inspectOutputTemplate,
   381  						"test/buildpack",
   382  						"LOCAL IMAGE:",
   383  						complexOutputSection)
   384  
   385  					remoteOutputSection := fmt.Sprintf("%s\n\n%s",
   386  						"REMOTE IMAGE:",
   387  						simpleOutputSection)
   388  
   389  					assert.AssertTrimmedContains(outBuf.String(), localOutputSection)
   390  					assert.AssertTrimmedContains(outBuf.String(), remoteOutputSection)
   391  				})
   392  			})
   393  
   394  			when("only a local image is present", func() {
   395  				it.Before(func() {
   396  					complexInfo.Location = buildpack.PackageLocator
   397  					simpleInfo.Location = buildpack.PackageLocator
   398  
   399  					mockClient.EXPECT().InspectBuildpack(client.InspectBuildpackOptions{
   400  						BuildpackName: "only-local-test/buildpack",
   401  						Daemon:        true,
   402  						Registry:      "default-registry",
   403  					}).Return(complexInfo, nil)
   404  
   405  					mockClient.EXPECT().InspectBuildpack(client.InspectBuildpackOptions{
   406  						BuildpackName: "only-local-test/buildpack",
   407  						Daemon:        false,
   408  						Registry:      "default-registry",
   409  					}).Return(nil, errors.Wrap(image.ErrNotFound, "remote image not found!"))
   410  				})
   411  
   412  				it("displays output for local image", func() {
   413  					command.SetArgs([]string{"only-local-test/buildpack"})
   414  					assert.Nil(command.Execute())
   415  
   416  					expectedOutput := fmt.Sprintf(inspectOutputTemplate,
   417  						"only-local-test/buildpack",
   418  						"LOCAL IMAGE:",
   419  						complexOutputSection)
   420  
   421  					assert.AssertTrimmedContains(outBuf.String(), expectedOutput)
   422  				})
   423  			})
   424  
   425  			when("only a remote image is present", func() {
   426  				it.Before(func() {
   427  					complexInfo.Location = buildpack.PackageLocator
   428  					simpleInfo.Location = buildpack.PackageLocator
   429  
   430  					mockClient.EXPECT().InspectBuildpack(client.InspectBuildpackOptions{
   431  						BuildpackName: "only-remote-test/buildpack",
   432  						Daemon:        false,
   433  						Registry:      "default-registry",
   434  					}).Return(complexInfo, nil)
   435  
   436  					mockClient.EXPECT().InspectBuildpack(client.InspectBuildpackOptions{
   437  						BuildpackName: "only-remote-test/buildpack",
   438  						Daemon:        true,
   439  						Registry:      "default-registry",
   440  					}).Return(nil, errors.Wrap(image.ErrNotFound, "remote image not found!"))
   441  				})
   442  
   443  				it("displays output for remote image", func() {
   444  					command.SetArgs([]string{"only-remote-test/buildpack"})
   445  					assert.Nil(command.Execute())
   446  
   447  					expectedOutput := fmt.Sprintf(inspectOutputTemplate,
   448  						"only-remote-test/buildpack",
   449  						"REMOTE IMAGE:",
   450  						complexOutputSection)
   451  
   452  					assert.AssertTrimmedContains(outBuf.String(), expectedOutput)
   453  				})
   454  			})
   455  		})
   456  
   457  		when("inspecting a buildpack uri", func() {
   458  			it.Before(func() {
   459  				simpleInfo.Location = buildpack.URILocator
   460  			})
   461  
   462  			when("uri is a local path", func() {
   463  				it.Before(func() {
   464  					mockClient.EXPECT().InspectBuildpack(client.InspectBuildpackOptions{
   465  						BuildpackName: "/path/to/test/buildpack",
   466  						Daemon:        true,
   467  						Registry:      "default-registry",
   468  					}).Return(simpleInfo, nil)
   469  				})
   470  
   471  				it("succeeds", func() {
   472  					command.SetArgs([]string{"/path/to/test/buildpack"})
   473  					assert.Nil(command.Execute())
   474  
   475  					expectedOutput := fmt.Sprintf(inspectOutputTemplate,
   476  						"/path/to/test/buildpack",
   477  						"LOCAL ARCHIVE:",
   478  						simpleOutputSection)
   479  
   480  					assert.TrimmedEq(outBuf.String(), expectedOutput)
   481  				})
   482  			})
   483  
   484  			when("uri is an http or https location", func() {
   485  				it.Before(func() {
   486  					simpleInfo.Location = buildpack.URILocator
   487  				})
   488  				when("uri is a local path", func() {
   489  					it.Before(func() {
   490  						mockClient.EXPECT().InspectBuildpack(client.InspectBuildpackOptions{
   491  							BuildpackName: "https://path/to/test/buildpack",
   492  							Daemon:        true,
   493  							Registry:      "default-registry",
   494  						}).Return(simpleInfo, nil)
   495  					})
   496  
   497  					it("succeeds", func() {
   498  						command.SetArgs([]string{"https://path/to/test/buildpack"})
   499  						assert.Nil(command.Execute())
   500  
   501  						expectedOutput := fmt.Sprintf(inspectOutputTemplate,
   502  							"https://path/to/test/buildpack",
   503  							"REMOTE ARCHIVE:",
   504  							simpleOutputSection)
   505  
   506  						assert.TrimmedEq(outBuf.String(), expectedOutput)
   507  					})
   508  				})
   509  			})
   510  		})
   511  
   512  		when("inspecting a buildpack on the registry", func() {
   513  			it.Before(func() {
   514  				simpleInfo.Location = buildpack.RegistryLocator
   515  			})
   516  
   517  			when("using the default registry", func() {
   518  				it.Before(func() {
   519  					mockClient.EXPECT().InspectBuildpack(client.InspectBuildpackOptions{
   520  						BuildpackName: "urn:cnb:registry:test/buildpack",
   521  						Daemon:        true,
   522  						Registry:      "default-registry",
   523  					}).Return(simpleInfo, nil)
   524  				})
   525  				it("succeeds", func() {
   526  					command.SetArgs([]string{"urn:cnb:registry:test/buildpack"})
   527  					assert.Nil(command.Execute())
   528  
   529  					expectedOutput := fmt.Sprintf(inspectOutputTemplate,
   530  						"urn:cnb:registry:test/buildpack",
   531  						"REGISTRY IMAGE:",
   532  						simpleOutputSection)
   533  
   534  					assert.TrimmedEq(outBuf.String(), expectedOutput)
   535  				})
   536  			})
   537  
   538  			when("using a user provided registry", func() {
   539  				it.Before(func() {
   540  					mockClient.EXPECT().InspectBuildpack(client.InspectBuildpackOptions{
   541  						BuildpackName: "urn:cnb:registry:test/buildpack",
   542  						Daemon:        true,
   543  						Registry:      "some-registry",
   544  					}).Return(simpleInfo, nil)
   545  				})
   546  
   547  				it("succeeds", func() {
   548  					command.SetArgs([]string{"urn:cnb:registry:test/buildpack", "-r", "some-registry"})
   549  					assert.Nil(command.Execute())
   550  
   551  					expectedOutput := fmt.Sprintf(inspectOutputTemplate,
   552  						"urn:cnb:registry:test/buildpack",
   553  						"REGISTRY IMAGE:",
   554  						simpleOutputSection)
   555  
   556  					assert.TrimmedEq(outBuf.String(), expectedOutput)
   557  				})
   558  			})
   559  		})
   560  
   561  		when("a depth flag is passed", func() {
   562  			it.Before(func() {
   563  				complexInfo.Location = buildpack.URILocator
   564  
   565  				mockClient.EXPECT().InspectBuildpack(client.InspectBuildpackOptions{
   566  					BuildpackName: "/other/path/to/test/buildpack",
   567  					Daemon:        true,
   568  					Registry:      "default-registry",
   569  				}).Return(complexInfo, nil)
   570  			})
   571  
   572  			it("displays detection order to specified depth", func() {
   573  				command.SetArgs([]string{"/other/path/to/test/buildpack", "-d", "2"})
   574  				assert.Nil(command.Execute())
   575  
   576  				assert.AssertTrimmedContains(outBuf.String(), depthOutputSection)
   577  			})
   578  		})
   579  	})
   580  
   581  	when("verbose flag is passed", func() {
   582  		it.Before(func() {
   583  			simpleInfo.Location = buildpack.URILocator
   584  			mockClient.EXPECT().InspectBuildpack(client.InspectBuildpackOptions{
   585  				BuildpackName: "/another/path/to/test/buildpack",
   586  				Daemon:        true,
   587  				Registry:      "default-registry",
   588  			}).Return(simpleInfo, nil)
   589  		})
   590  
   591  		it("displays all mixins", func() {
   592  			command.SetArgs([]string{"/another/path/to/test/buildpack", "-v"})
   593  			assert.Nil(command.Execute())
   594  
   595  			assert.AssertTrimmedContains(outBuf.String(), simpleMixinOutputSection)
   596  		})
   597  	})
   598  
   599  	when("failure cases", func() {
   600  		when("unable to inspect buildpack image", func() {
   601  			it.Before(func() {
   602  				mockClient.EXPECT().InspectBuildpack(client.InspectBuildpackOptions{
   603  					BuildpackName: "failure-case/buildpack",
   604  					Daemon:        true,
   605  					Registry:      "default-registry",
   606  				}).Return(&client.BuildpackInfo{}, errors.Wrap(image.ErrNotFound, "unable to inspect local failure-case/buildpack"))
   607  
   608  				mockClient.EXPECT().InspectBuildpack(client.InspectBuildpackOptions{
   609  					BuildpackName: "failure-case/buildpack",
   610  					Daemon:        false,
   611  					Registry:      "default-registry",
   612  				}).Return(&client.BuildpackInfo{}, errors.Wrap(image.ErrNotFound, "unable to inspect remote failure-case/buildpack"))
   613  			})
   614  
   615  			it("errors", func() {
   616  				command.SetArgs([]string{"failure-case/buildpack"})
   617  				err := command.Execute()
   618  				assert.Error(err)
   619  			})
   620  		})
   621  		when("unable to inspect buildpack archive", func() {
   622  			it.Before(func() {
   623  				mockClient.EXPECT().InspectBuildpack(client.InspectBuildpackOptions{
   624  					BuildpackName: "http://path/to/failure-case/buildpack",
   625  					Daemon:        true,
   626  					Registry:      "default-registry",
   627  				}).Return(&client.BuildpackInfo{}, errors.New("error inspecting local archive"))
   628  
   629  				it("errors", func() {
   630  					command.SetArgs([]string{"http://path/to/failure-case/buildpack"})
   631  					err := command.Execute()
   632  
   633  					assert.Error(err)
   634  					assert.Contains(err.Error(), "error inspecting local archive")
   635  				})
   636  			})
   637  		})
   638  		when("unable to inspect both remote and local images", func() {
   639  			it.Before(func() {
   640  				mockClient.EXPECT().InspectBuildpack(client.InspectBuildpackOptions{
   641  					BuildpackName: "image-failure-case/buildpack",
   642  					Daemon:        true,
   643  					Registry:      "default-registry",
   644  				}).Return(&client.BuildpackInfo{}, errors.Wrap(image.ErrNotFound, "error inspecting local archive"))
   645  
   646  				mockClient.EXPECT().InspectBuildpack(client.InspectBuildpackOptions{
   647  					BuildpackName: "image-failure-case/buildpack",
   648  					Daemon:        false,
   649  					Registry:      "default-registry",
   650  				}).Return(&client.BuildpackInfo{}, errors.Wrap(image.ErrNotFound, "error inspecting remote archive"))
   651  			})
   652  
   653  			it("errors", func() {
   654  				command.SetArgs([]string{"image-failure-case/buildpack"})
   655  				err := command.Execute()
   656  
   657  				assert.Error(err)
   658  				assert.Contains(err.Error(), "error writing buildpack output: \"error inspecting local archive: not found, error inspecting remote archive: not found\"")
   659  			})
   660  		})
   661  
   662  		when("unable to inspect buildpack on registry", func() {
   663  			it.Before(func() {
   664  				mockClient.EXPECT().InspectBuildpack(client.InspectBuildpackOptions{
   665  					BuildpackName: "urn:cnb:registry:registry-failure/buildpack",
   666  					Daemon:        true,
   667  					Registry:      "some-registry",
   668  				}).Return(&client.BuildpackInfo{}, errors.New("error inspecting registry image"))
   669  
   670  				mockClient.EXPECT().InspectBuildpack(client.InspectBuildpackOptions{
   671  					BuildpackName: "urn:cnb:registry:registry-failure/buildpack",
   672  					Daemon:        false,
   673  					Registry:      "some-registry",
   674  				}).Return(&client.BuildpackInfo{}, errors.New("error inspecting registry image"))
   675  			})
   676  
   677  			it("errors", func() {
   678  				command.SetArgs([]string{"urn:cnb:registry:registry-failure/buildpack", "-r", "some-registry"})
   679  
   680  				err := command.Execute()
   681  				assert.Error(err)
   682  				assert.Contains(err.Error(), "error inspecting registry image")
   683  			})
   684  		})
   685  	})
   686  }