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 }