github.com/YousefHaggyHeroku/pack@v1.5.5/internal/builder/writer/human_readable_test.go (about) 1 package writer_test 2 3 import ( 4 "bytes" 5 "errors" 6 "testing" 7 8 "github.com/Masterminds/semver" 9 "github.com/heroku/color" 10 "github.com/sclevine/spec" 11 "github.com/sclevine/spec/report" 12 13 "github.com/buildpacks/lifecycle/api" 14 15 "github.com/YousefHaggyHeroku/pack" 16 pubbldr "github.com/YousefHaggyHeroku/pack/builder" 17 "github.com/YousefHaggyHeroku/pack/internal/builder" 18 "github.com/YousefHaggyHeroku/pack/internal/builder/writer" 19 "github.com/YousefHaggyHeroku/pack/internal/config" 20 "github.com/YousefHaggyHeroku/pack/internal/dist" 21 ilogging "github.com/YousefHaggyHeroku/pack/internal/logging" 22 h "github.com/YousefHaggyHeroku/pack/testhelpers" 23 ) 24 25 func TestHumanReadable(t *testing.T) { 26 color.Disable(true) 27 defer color.Disable(false) 28 spec.Run(t, "Builder Writer", testHumanReadable, spec.Parallel(), spec.Report(report.Terminal{})) 29 } 30 31 func testHumanReadable(t *testing.T, when spec.G, it spec.S) { 32 var ( 33 assert = h.NewAssertionManager(t) 34 outBuf bytes.Buffer 35 36 remoteInfo *pack.BuilderInfo 37 localInfo *pack.BuilderInfo 38 39 expectedRemoteOutput = ` 40 REMOTE: 41 42 Description: Some remote description 43 44 Created By: 45 Name: Pack CLI 46 Version: 1.2.3 47 48 Trusted: No 49 50 Stack: 51 ID: test.stack.id 52 53 Lifecycle: 54 Version: 6.7.8 55 Buildpack APIs: 56 Deprecated: (none) 57 Supported: 1.2, 2.3 58 Platform APIs: 59 Deprecated: 0.1, 1.2 60 Supported: 4.5 61 62 Run Images: 63 first/local (user-configured) 64 second/local (user-configured) 65 some/run-image 66 first/default 67 second/default 68 69 Buildpacks: 70 ID VERSION HOMEPAGE 71 test.top.nested test.top.nested.version 72 test.nested http://geocities.com/top-bp 73 test.bp.one test.bp.one.version http://geocities.com/cool-bp 74 test.bp.two test.bp.two.version 75 test.bp.three test.bp.three.version 76 77 Detection Order: 78 ├ Group #1: 79 │ ├ test.top.nested@test.top.nested.version 80 │ │ └ Group #1: 81 │ │ ├ test.nested 82 │ │ │ └ Group #1: 83 │ │ │ └ test.bp.one@test.bp.one.version (optional) 84 │ │ ├ test.bp.three@test.bp.three.version (optional) 85 │ │ └ test.nested.two@test.nested.two.version 86 │ │ └ Group #2: 87 │ │ └ test.bp.one@test.bp.one.version (optional)[cyclic] 88 │ └ test.bp.two@test.bp.two.version (optional) 89 └ test.bp.three@test.bp.three.version 90 ` 91 92 expectedLocalOutput = ` 93 LOCAL: 94 95 Description: Some local description 96 97 Created By: 98 Name: Pack CLI 99 Version: 4.5.6 100 101 Trusted: No 102 103 Stack: 104 ID: test.stack.id 105 106 Lifecycle: 107 Version: 4.5.6 108 Buildpack APIs: 109 Deprecated: 4.5, 6.7 110 Supported: 8.9, 10.11 111 Platform APIs: 112 Deprecated: (none) 113 Supported: 7.8 114 115 Run Images: 116 first/local (user-configured) 117 second/local (user-configured) 118 some/run-image 119 first/local-default 120 second/local-default 121 122 Buildpacks: 123 ID VERSION HOMEPAGE 124 test.top.nested test.top.nested.version 125 test.nested http://geocities.com/top-bp 126 test.bp.one test.bp.one.version http://geocities.com/cool-bp 127 test.bp.two test.bp.two.version 128 test.bp.three test.bp.three.version 129 130 Detection Order: 131 ├ Group #1: 132 │ ├ test.top.nested@test.top.nested.version 133 │ │ └ Group #1: 134 │ │ ├ test.nested 135 │ │ │ └ Group #1: 136 │ │ │ └ test.bp.one@test.bp.one.version (optional) 137 │ │ ├ test.bp.three@test.bp.three.version (optional) 138 │ │ └ test.nested.two@test.nested.two.version 139 │ │ └ Group #2: 140 │ │ └ test.bp.one@test.bp.one.version (optional)[cyclic] 141 │ └ test.bp.two@test.bp.two.version (optional) 142 └ test.bp.three@test.bp.three.version 143 ` 144 expectedVerboseStack = ` 145 Stack: 146 ID: test.stack.id 147 Mixins: 148 mixin1 149 mixin2 150 build:mixin3 151 build:mixin4 152 ` 153 expectedNilLifecycleVersion = ` 154 Lifecycle: 155 Version: (none) 156 ` 157 expectedEmptyRunImages = ` 158 Run Images: 159 (none) 160 ` 161 expectedEmptyBuildpacks = ` 162 Buildpacks: 163 (none) 164 ` 165 expectedEmptyOrder = ` 166 Detection Order: 167 (none) 168 ` 169 expectedMissingLocalInfo = ` 170 LOCAL: 171 (not present) 172 ` 173 expectedMissingRemoteInfo = ` 174 REMOTE: 175 (not present) 176 ` 177 ) 178 179 when("Print", func() { 180 it.Before(func() { 181 remoteInfo = &pack.BuilderInfo{ 182 Description: "Some remote description", 183 Stack: "test.stack.id", 184 Mixins: []string{"mixin1", "mixin2", "build:mixin3", "build:mixin4"}, 185 RunImage: "some/run-image", 186 RunImageMirrors: []string{"first/default", "second/default"}, 187 Buildpacks: buildpacks, 188 Order: order, 189 BuildpackLayers: dist.BuildpackLayers{}, 190 Lifecycle: builder.LifecycleDescriptor{ 191 Info: builder.LifecycleInfo{ 192 Version: &builder.Version{ 193 Version: *semver.MustParse("6.7.8"), 194 }, 195 }, 196 APIs: builder.LifecycleAPIs{ 197 Buildpack: builder.APIVersions{ 198 Deprecated: nil, 199 Supported: builder.APISet{api.MustParse("1.2"), api.MustParse("2.3")}, 200 }, 201 Platform: builder.APIVersions{ 202 Deprecated: builder.APISet{api.MustParse("0.1"), api.MustParse("1.2")}, 203 Supported: builder.APISet{api.MustParse("4.5")}, 204 }, 205 }, 206 }, 207 CreatedBy: builder.CreatorMetadata{ 208 Name: "Pack CLI", 209 Version: "1.2.3", 210 }, 211 } 212 213 localInfo = &pack.BuilderInfo{ 214 Description: "Some local description", 215 Stack: "test.stack.id", 216 Mixins: []string{"mixin1", "mixin2", "build:mixin3", "build:mixin4"}, 217 RunImage: "some/run-image", 218 RunImageMirrors: []string{"first/local-default", "second/local-default"}, 219 Buildpacks: buildpacks, 220 Order: order, 221 BuildpackLayers: dist.BuildpackLayers{}, 222 Lifecycle: builder.LifecycleDescriptor{ 223 Info: builder.LifecycleInfo{ 224 Version: &builder.Version{ 225 Version: *semver.MustParse("4.5.6"), 226 }, 227 }, 228 APIs: builder.LifecycleAPIs{ 229 Buildpack: builder.APIVersions{ 230 Deprecated: builder.APISet{api.MustParse("4.5"), api.MustParse("6.7")}, 231 Supported: builder.APISet{api.MustParse("8.9"), api.MustParse("10.11")}, 232 }, 233 Platform: builder.APIVersions{ 234 Deprecated: nil, 235 Supported: builder.APISet{api.MustParse("7.8")}, 236 }, 237 }, 238 }, 239 CreatedBy: builder.CreatorMetadata{ 240 Name: "Pack CLI", 241 Version: "4.5.6", 242 }, 243 } 244 245 outBuf = bytes.Buffer{} 246 }) 247 248 it("prints both local and remote builders in a human readable format", func() { 249 humanReadableWriter := writer.NewHumanReadable() 250 251 logger := ilogging.NewLogWithWriters(&outBuf, &outBuf) 252 err := humanReadableWriter.Print(logger, localRunImages, localInfo, remoteInfo, nil, nil, sharedBuilderInfo) 253 assert.Nil(err) 254 255 assert.Contains(outBuf.String(), "Inspecting builder: 'test-builder'") 256 assert.Contains(outBuf.String(), expectedRemoteOutput) 257 assert.Contains(outBuf.String(), expectedLocalOutput) 258 }) 259 260 when("builder is default", func() { 261 it("prints inspecting default builder", func() { 262 defaultSharedBuildInfo := sharedBuilderInfo 263 defaultSharedBuildInfo.IsDefault = true 264 265 humanReadableWriter := writer.NewHumanReadable() 266 267 logger := ilogging.NewLogWithWriters(&outBuf, &outBuf) 268 err := humanReadableWriter.Print(logger, localRunImages, localInfo, remoteInfo, nil, nil, defaultSharedBuildInfo) 269 assert.Nil(err) 270 271 assert.Contains(outBuf.String(), "Inspecting default builder: 'test-builder'") 272 }) 273 }) 274 275 when("builder doesn't exist locally or remotely", func() { 276 it("returns an error", func() { 277 localInfo = nil 278 remoteInfo = nil 279 280 humanReadableWriter := writer.NewHumanReadable() 281 282 logger := ilogging.NewLogWithWriters(&outBuf, &outBuf) 283 err := humanReadableWriter.Print(logger, localRunImages, localInfo, remoteInfo, nil, nil, sharedBuilderInfo) 284 assert.ErrorWithMessage(err, "unable to find builder 'test-builder' locally or remotely") 285 }) 286 }) 287 288 when("builder doesn't exist locally", func() { 289 it("shows not present for local builder, and normal output for remote", func() { 290 localInfo = nil 291 292 humanReadableWriter := writer.NewHumanReadable() 293 294 logger := ilogging.NewLogWithWriters(&outBuf, &outBuf) 295 err := humanReadableWriter.Print(logger, localRunImages, localInfo, remoteInfo, nil, nil, sharedBuilderInfo) 296 assert.Nil(err) 297 298 assert.Contains(outBuf.String(), expectedMissingLocalInfo) 299 assert.Contains(outBuf.String(), expectedRemoteOutput) 300 }) 301 }) 302 303 when("builder doesn't exist remotely", func() { 304 it("shows not present for remote builder, and normal output for local", func() { 305 remoteInfo = nil 306 307 humanReadableWriter := writer.NewHumanReadable() 308 309 logger := ilogging.NewLogWithWriters(&outBuf, &outBuf) 310 err := humanReadableWriter.Print(logger, localRunImages, localInfo, remoteInfo, nil, nil, sharedBuilderInfo) 311 assert.Nil(err) 312 313 assert.Contains(outBuf.String(), expectedMissingRemoteInfo) 314 assert.Contains(outBuf.String(), expectedLocalOutput) 315 }) 316 }) 317 318 when("localErr is an error", func() { 319 it("error is logged, local info is not displayed, but remote info is", func() { 320 errorMessage := "failed to retrieve local info" 321 322 humanReadableWriter := writer.NewHumanReadable() 323 324 logger := ilogging.NewLogWithWriters(&outBuf, &outBuf) 325 err := humanReadableWriter.Print(logger, localRunImages, localInfo, remoteInfo, errors.New(errorMessage), nil, sharedBuilderInfo) 326 assert.Nil(err) 327 328 assert.Contains(outBuf.String(), errorMessage) 329 assert.NotContains(outBuf.String(), expectedLocalOutput) 330 assert.Contains(outBuf.String(), expectedRemoteOutput) 331 }) 332 }) 333 334 when("remoteErr is an error", func() { 335 it("error is logged, remote info is not displayed, but local info is", func() { 336 errorMessage := "failed to retrieve remote info" 337 338 humanReadableWriter := writer.NewHumanReadable() 339 340 logger := ilogging.NewLogWithWriters(&outBuf, &outBuf) 341 err := humanReadableWriter.Print(logger, localRunImages, localInfo, remoteInfo, nil, errors.New(errorMessage), sharedBuilderInfo) 342 assert.Nil(err) 343 344 assert.Contains(outBuf.String(), errorMessage) 345 assert.NotContains(outBuf.String(), expectedRemoteOutput) 346 assert.Contains(outBuf.String(), expectedLocalOutput) 347 }) 348 }) 349 350 when("description is blank", func() { 351 it("doesn't print the description block", func() { 352 localInfo.Description = "" 353 remoteInfo.Description = "" 354 355 humanReadableWriter := writer.NewHumanReadable() 356 357 logger := ilogging.NewLogWithWriters(&outBuf, &outBuf) 358 err := humanReadableWriter.Print(logger, localRunImages, localInfo, remoteInfo, nil, nil, sharedBuilderInfo) 359 assert.Nil(err) 360 361 assert.NotContains(outBuf.String(), "Description:") 362 }) 363 }) 364 365 when("created by name is blank", func() { 366 it("doesn't print created by block", func() { 367 localInfo.CreatedBy.Name = "" 368 remoteInfo.CreatedBy.Name = "" 369 370 humanReadableWriter := writer.NewHumanReadable() 371 372 logger := ilogging.NewLogWithWriters(&outBuf, &outBuf) 373 err := humanReadableWriter.Print(logger, localRunImages, localInfo, remoteInfo, nil, nil, sharedBuilderInfo) 374 assert.Nil(err) 375 376 assert.NotContains(outBuf.String(), "Created By:") 377 }) 378 }) 379 380 when("logger is verbose", func() { 381 it("displays mixins associated with the stack", func() { 382 humanReadableWriter := writer.NewHumanReadable() 383 384 logger := ilogging.NewLogWithWriters(&outBuf, &outBuf, ilogging.WithVerbose()) 385 err := humanReadableWriter.Print(logger, localRunImages, localInfo, remoteInfo, nil, nil, sharedBuilderInfo) 386 assert.Nil(err) 387 388 assert.Contains(outBuf.String(), expectedVerboseStack) 389 }) 390 }) 391 392 when("lifecycle version is not set", func() { 393 it("displays lifecycle version as (none) and warns that version if not set", func() { 394 localInfo.Lifecycle.Info.Version = nil 395 remoteInfo.Lifecycle.Info.Version = nil 396 397 humanReadableWriter := writer.NewHumanReadable() 398 399 logger := ilogging.NewLogWithWriters(&outBuf, &outBuf) 400 err := humanReadableWriter.Print(logger, localRunImages, localInfo, remoteInfo, nil, nil, sharedBuilderInfo) 401 assert.Nil(err) 402 403 assert.Contains(outBuf.String(), expectedNilLifecycleVersion) 404 assert.Contains(outBuf.String(), "test-builder does not specify a Lifecycle version") 405 }) 406 }) 407 408 when("there are no supported buildpack APIs specified", func() { 409 it("prints a warning", func() { 410 localInfo.Lifecycle.APIs.Buildpack.Supported = builder.APISet{} 411 remoteInfo.Lifecycle.APIs.Buildpack.Supported = builder.APISet{} 412 413 humanReadableWriter := writer.NewHumanReadable() 414 415 logger := ilogging.NewLogWithWriters(&outBuf, &outBuf) 416 err := humanReadableWriter.Print(logger, localRunImages, localInfo, remoteInfo, nil, nil, sharedBuilderInfo) 417 assert.Nil(err) 418 419 assert.Contains(outBuf.String(), "test-builder does not specify supported Lifecycle Buildpack APIs") 420 }) 421 }) 422 423 when("there are no supported platform APIs specified", func() { 424 it("prints a warning", func() { 425 localInfo.Lifecycle.APIs.Platform.Supported = builder.APISet{} 426 remoteInfo.Lifecycle.APIs.Platform.Supported = builder.APISet{} 427 428 humanReadableWriter := writer.NewHumanReadable() 429 430 logger := ilogging.NewLogWithWriters(&outBuf, &outBuf) 431 err := humanReadableWriter.Print(logger, localRunImages, localInfo, remoteInfo, nil, nil, sharedBuilderInfo) 432 assert.Nil(err) 433 434 assert.Contains(outBuf.String(), "test-builder does not specify supported Lifecycle Platform APIs") 435 }) 436 }) 437 438 when("no run images are specified", func() { 439 it("displays run images as (none) and warns about unset run image", func() { 440 localInfo.RunImage = "" 441 localInfo.RunImageMirrors = []string{} 442 remoteInfo.RunImage = "" 443 remoteInfo.RunImageMirrors = []string{} 444 emptyLocalRunImages := []config.RunImage{} 445 446 humanReadableWriter := writer.NewHumanReadable() 447 448 logger := ilogging.NewLogWithWriters(&outBuf, &outBuf) 449 err := humanReadableWriter.Print(logger, emptyLocalRunImages, localInfo, remoteInfo, nil, nil, sharedBuilderInfo) 450 assert.Nil(err) 451 452 assert.Contains(outBuf.String(), expectedEmptyRunImages) 453 assert.Contains(outBuf.String(), "test-builder does not specify a run image") 454 assert.Contains(outBuf.String(), "Users must build with an explicitly specified run image") 455 }) 456 }) 457 458 when("no buildpacks are specified", func() { 459 it("displays buildpacks as (none) and prints warnings", func() { 460 localInfo.Buildpacks = []dist.BuildpackInfo{} 461 remoteInfo.Buildpacks = []dist.BuildpackInfo{} 462 463 humanReadableWriter := writer.NewHumanReadable() 464 465 logger := ilogging.NewLogWithWriters(&outBuf, &outBuf) 466 err := humanReadableWriter.Print(logger, localRunImages, localInfo, remoteInfo, nil, nil, sharedBuilderInfo) 467 assert.Nil(err) 468 469 assert.Contains(outBuf.String(), expectedEmptyBuildpacks) 470 assert.Contains(outBuf.String(), "test-builder has no buildpacks") 471 assert.Contains(outBuf.String(), "Users must supply buildpacks from the host machine") 472 }) 473 }) 474 475 when("multiple top level groups", func() { 476 it("displays order correctly", func() { 477 478 }) 479 }) 480 481 when("no detection order is specified", func() { 482 it("displays detection order as (none) and prints warnings", func() { 483 localInfo.Order = pubbldr.DetectionOrder{} 484 remoteInfo.Order = pubbldr.DetectionOrder{} 485 486 humanReadableWriter := writer.NewHumanReadable() 487 488 logger := ilogging.NewLogWithWriters(&outBuf, &outBuf) 489 err := humanReadableWriter.Print(logger, localRunImages, localInfo, remoteInfo, nil, nil, sharedBuilderInfo) 490 assert.Nil(err) 491 492 assert.Contains(outBuf.String(), expectedEmptyOrder) 493 assert.Contains(outBuf.String(), "test-builder has no buildpacks") 494 assert.Contains(outBuf.String(), "Users must build with explicitly specified buildpacks") 495 }) 496 }) 497 }) 498 }