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

     1  package commands_test
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"regexp"
     7  	"testing"
     8  
     9  	"github.com/heroku/color"
    10  	"github.com/sclevine/spec"
    11  	"github.com/sclevine/spec/report"
    12  
    13  	"github.com/buildpacks/pack/internal/commands"
    14  	"github.com/buildpacks/pack/internal/config"
    15  	"github.com/buildpacks/pack/pkg/client"
    16  	"github.com/buildpacks/pack/pkg/logging"
    17  	h "github.com/buildpacks/pack/testhelpers"
    18  )
    19  
    20  func TestInspectBuilderCommand(t *testing.T) {
    21  	color.Disable(true)
    22  	defer color.Disable(false)
    23  	spec.Run(t, "InspectBuilderCommand", testInspectBuilderCommand, spec.Parallel(), spec.Report(report.Terminal{}))
    24  }
    25  
    26  func testInspectBuilderCommand(t *testing.T, when spec.G, it spec.S) {
    27  	var (
    28  		logger logging.Logger
    29  		outBuf bytes.Buffer
    30  		cfg    config.Config
    31  	)
    32  
    33  	it.Before(func() {
    34  		cfg = config.Config{
    35  			DefaultBuilder: "default/builder",
    36  			RunImages:      expectedLocalRunImages,
    37  		}
    38  		logger = logging.NewLogWithWriters(&outBuf, &outBuf)
    39  	})
    40  
    41  	when("InspectBuilder", func() {
    42  		var (
    43  			assert = h.NewAssertionManager(t)
    44  		)
    45  
    46  		it("passes output of local and remote builders to correct writer", func() {
    47  			builderInspector := newDefaultBuilderInspector()
    48  			builderWriter := newDefaultBuilderWriter()
    49  			builderWriterFactory := newWriterFactory(returnsForWriter(builderWriter))
    50  
    51  			command := commands.InspectBuilder(logger, cfg, builderInspector, builderWriterFactory)
    52  			command.SetArgs([]string{})
    53  			err := command.Execute()
    54  			assert.Nil(err)
    55  
    56  			assert.Equal(builderWriter.ReceivedInfoForLocal, expectedLocalInfo)
    57  			assert.Equal(builderWriter.ReceivedInfoForRemote, expectedRemoteInfo)
    58  			assert.Equal(builderWriter.ReceivedBuilderInfo, expectedBuilderInfo)
    59  			assert.Equal(builderWriter.ReceivedLocalRunImages, expectedLocalRunImages)
    60  			assert.Equal(builderWriterFactory.ReceivedForKind, "human-readable")
    61  			assert.Equal(builderInspector.ReceivedForLocalName, "default/builder")
    62  			assert.Equal(builderInspector.ReceivedForRemoteName, "default/builder")
    63  			assert.ContainsF(outBuf.String(), "LOCAL:\n%s", expectedLocalDisplay)
    64  			assert.ContainsF(outBuf.String(), "REMOTE:\n%s", expectedRemoteDisplay)
    65  		})
    66  
    67  		when("image name is provided as first arg", func() {
    68  			it("passes that image name to the inspector", func() {
    69  				builderInspector := newDefaultBuilderInspector()
    70  				writer := newDefaultBuilderWriter()
    71  				command := commands.InspectBuilder(logger, cfg, builderInspector, newWriterFactory(returnsForWriter(writer)))
    72  				command.SetArgs([]string{"some/image"})
    73  
    74  				err := command.Execute()
    75  				assert.Nil(err)
    76  
    77  				assert.Equal(builderInspector.ReceivedForLocalName, "some/image")
    78  				assert.Equal(builderInspector.ReceivedForRemoteName, "some/image")
    79  				assert.Equal(writer.ReceivedBuilderInfo.IsDefault, false)
    80  			})
    81  		})
    82  
    83  		when("depth flag is provided", func() {
    84  			it("passes a modifier to the builder inspector", func() {
    85  				builderInspector := newDefaultBuilderInspector()
    86  				command := commands.InspectBuilder(logger, cfg, builderInspector, newDefaultWriterFactory())
    87  				command.SetArgs([]string{"--depth", "5"})
    88  
    89  				err := command.Execute()
    90  				assert.Nil(err)
    91  
    92  				assert.Equal(builderInspector.CalculatedConfigForLocal.OrderDetectionDepth, 5)
    93  				assert.Equal(builderInspector.CalculatedConfigForRemote.OrderDetectionDepth, 5)
    94  			})
    95  		})
    96  
    97  		when("output type is set to json", func() {
    98  			it("passes json to the writer factory", func() {
    99  				writerFactory := newDefaultWriterFactory()
   100  				command := commands.InspectBuilder(logger, cfg, newDefaultBuilderInspector(), writerFactory)
   101  				command.SetArgs([]string{"--output", "json"})
   102  
   103  				err := command.Execute()
   104  				assert.Nil(err)
   105  
   106  				assert.Equal(writerFactory.ReceivedForKind, "json")
   107  			})
   108  		})
   109  
   110  		when("output type is set to toml using the shorthand flag", func() {
   111  			it("passes toml to the writer factory", func() {
   112  				writerFactory := newDefaultWriterFactory()
   113  				command := commands.InspectBuilder(logger, cfg, newDefaultBuilderInspector(), writerFactory)
   114  				command.SetArgs([]string{"-o", "toml"})
   115  
   116  				err := command.Execute()
   117  				assert.Nil(err)
   118  
   119  				assert.Equal(writerFactory.ReceivedForKind, "toml")
   120  			})
   121  		})
   122  
   123  		when("builder inspector returns an error for local builder", func() {
   124  			it("passes that error to the writer to handle appropriately", func() {
   125  				baseError := errors.New("couldn't inspect local")
   126  
   127  				builderInspector := newBuilderInspector(errorsForLocal(baseError))
   128  				builderWriter := newDefaultBuilderWriter()
   129  				builderWriterFactory := newWriterFactory(returnsForWriter(builderWriter))
   130  
   131  				command := commands.InspectBuilder(logger, cfg, builderInspector, builderWriterFactory)
   132  				command.SetArgs([]string{})
   133  				err := command.Execute()
   134  				assert.Nil(err)
   135  
   136  				assert.ErrorWithMessage(builderWriter.ReceivedErrorForLocal, "couldn't inspect local")
   137  			})
   138  		})
   139  
   140  		when("builder inspector returns an error remote builder", func() {
   141  			it("passes that error to the writer to handle appropriately", func() {
   142  				baseError := errors.New("couldn't inspect remote")
   143  
   144  				builderInspector := newBuilderInspector(errorsForRemote(baseError))
   145  				builderWriter := newDefaultBuilderWriter()
   146  				builderWriterFactory := newWriterFactory(returnsForWriter(builderWriter))
   147  
   148  				command := commands.InspectBuilder(logger, cfg, builderInspector, builderWriterFactory)
   149  				command.SetArgs([]string{})
   150  				err := command.Execute()
   151  				assert.Nil(err)
   152  
   153  				assert.ErrorWithMessage(builderWriter.ReceivedErrorForRemote, "couldn't inspect remote")
   154  			})
   155  		})
   156  
   157  		when("image is trusted", func() {
   158  			it("passes builder info with trusted true to the writer's `Print` method", func() {
   159  				cfg.TrustedBuilders = []config.TrustedBuilder{
   160  					{Name: "trusted/builder"},
   161  				}
   162  				writer := newDefaultBuilderWriter()
   163  
   164  				command := commands.InspectBuilder(
   165  					logger,
   166  					cfg,
   167  					newDefaultBuilderInspector(),
   168  					newWriterFactory(returnsForWriter(writer)),
   169  				)
   170  				command.SetArgs([]string{"trusted/builder"})
   171  
   172  				err := command.Execute()
   173  				assert.Nil(err)
   174  
   175  				assert.Equal(writer.ReceivedBuilderInfo.Trusted, true)
   176  			})
   177  		})
   178  
   179  		when("default builder is configured and is the same as specified by the command", func() {
   180  			it("passes builder info with isDefault true to the writer's `Print` method", func() {
   181  				cfg.DefaultBuilder = "the/default-builder"
   182  				writer := newDefaultBuilderWriter()
   183  
   184  				command := commands.InspectBuilder(
   185  					logger,
   186  					cfg,
   187  					newDefaultBuilderInspector(),
   188  					newWriterFactory(returnsForWriter(writer)),
   189  				)
   190  				command.SetArgs([]string{"the/default-builder"})
   191  
   192  				err := command.Execute()
   193  				assert.Nil(err)
   194  
   195  				assert.Equal(writer.ReceivedBuilderInfo.IsDefault, true)
   196  			})
   197  		})
   198  
   199  		when("default builder is empty and no builder is specified in command args", func() {
   200  			it("suggests builders and returns a soft error", func() {
   201  				cfg.DefaultBuilder = ""
   202  
   203  				command := commands.InspectBuilder(logger, cfg, newDefaultBuilderInspector(), newDefaultWriterFactory())
   204  				command.SetArgs([]string{})
   205  
   206  				err := command.Execute()
   207  				assert.Error(err)
   208  				if !errors.Is(err, client.SoftError{}) {
   209  					t.Fatalf("expect a client.SoftError, got: %s", err)
   210  				}
   211  
   212  				assert.Contains(outBuf.String(), `Please select a default builder with:
   213  
   214  	pack config default-builder <builder-image>`)
   215  
   216  				assert.Matches(outBuf.String(), regexp.MustCompile(`Paketo Buildpacks:\s+'paketobuildpacks/builder-jammy-base'`))
   217  				assert.Matches(outBuf.String(), regexp.MustCompile(`Paketo Buildpacks:\s+'paketobuildpacks/builder-jammy-full'`))
   218  				assert.Matches(outBuf.String(), regexp.MustCompile(`Heroku:\s+'heroku/builder:22'`))
   219  			})
   220  		})
   221  
   222  		when("print returns an error", func() {
   223  			it("returns that error", func() {
   224  				baseError := errors.New("couldn't write builder")
   225  
   226  				builderWriter := newBuilderWriter(errorsForPrint(baseError))
   227  				command := commands.InspectBuilder(
   228  					logger,
   229  					cfg,
   230  					newDefaultBuilderInspector(),
   231  					newWriterFactory(returnsForWriter(builderWriter)),
   232  				)
   233  				command.SetArgs([]string{})
   234  
   235  				err := command.Execute()
   236  				assert.ErrorWithMessage(err, "couldn't write builder")
   237  			})
   238  		})
   239  
   240  		when("writer factory returns an error", func() {
   241  			it("returns that error", func() {
   242  				baseError := errors.New("invalid output format")
   243  
   244  				writerFactory := newWriterFactory(errorsForWriter(baseError))
   245  				command := commands.InspectBuilder(logger, cfg, newDefaultBuilderInspector(), writerFactory)
   246  				command.SetArgs([]string{})
   247  
   248  				err := command.Execute()
   249  				assert.ErrorWithMessage(err, "invalid output format")
   250  			})
   251  		})
   252  	})
   253  }