github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/internal/inspectimage/writer/human_readable_test.go (about) 1 package writer_test 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "testing" 8 9 "github.com/buildpacks/lifecycle/buildpack" 10 "github.com/buildpacks/lifecycle/launch" 11 "github.com/buildpacks/lifecycle/platform/files" 12 "github.com/heroku/color" 13 "github.com/sclevine/spec" 14 "github.com/sclevine/spec/report" 15 16 "github.com/buildpacks/pack/internal/config" 17 "github.com/buildpacks/pack/internal/inspectimage" 18 "github.com/buildpacks/pack/internal/inspectimage/writer" 19 "github.com/buildpacks/pack/pkg/client" 20 "github.com/buildpacks/pack/pkg/logging" 21 h "github.com/buildpacks/pack/testhelpers" 22 ) 23 24 func TestHumanReadable(t *testing.T) { 25 color.Disable(true) 26 defer color.Disable(false) 27 spec.Run(t, "Human Readable Writer", testHumanReadable, spec.Parallel(), spec.Report(report.Terminal{})) 28 } 29 30 func testHumanReadable(t *testing.T, when spec.G, it spec.S) { 31 var ( 32 assert = h.NewAssertionManager(t) 33 outBuf bytes.Buffer 34 35 remoteInfo *client.ImageInfo 36 remoteWithExtensionInfo *client.ImageInfo 37 remoteInfoNoRebasable *client.ImageInfo 38 39 localInfo *client.ImageInfo 40 localWithExtensionInfo *client.ImageInfo 41 localInfoNoRebasable *client.ImageInfo 42 43 expectedRemoteOutput = `REMOTE: 44 45 Stack: test.stack.id.remote 46 47 Base Image: 48 Reference: some-remote-run-image-reference 49 Top Layer: some-remote-top-layer 50 51 Run Images: 52 user-configured-mirror-for-remote (user-configured) 53 some-remote-run-image 54 some-remote-mirror 55 other-remote-mirror 56 57 Rebasable: true 58 59 Buildpacks: 60 ID VERSION HOMEPAGE 61 test.bp.one.remote 1.0.0 https://some-homepage-one 62 test.bp.two.remote 2.0.0 https://some-homepage-two 63 test.bp.three.remote 3.0.0 - 64 65 Processes: 66 TYPE SHELL COMMAND ARGS WORK DIR 67 some-remote-type (default) bash /some/remote command some remote args /some-test-work-dir 68 other-remote-type /other/remote/command other remote args /other-test-work-dir` 69 expectedRemoteNoRebasableOutput = `REMOTE: 70 71 Stack: test.stack.id.remote 72 73 Base Image: 74 Reference: some-remote-run-image-reference 75 Top Layer: some-remote-top-layer 76 77 Run Images: 78 user-configured-mirror-for-remote (user-configured) 79 some-remote-run-image 80 some-remote-mirror 81 other-remote-mirror 82 83 Rebasable: false 84 85 Buildpacks: 86 ID VERSION HOMEPAGE 87 test.bp.one.remote 1.0.0 https://some-homepage-one 88 test.bp.two.remote 2.0.0 https://some-homepage-two 89 test.bp.three.remote 3.0.0 - 90 91 Processes: 92 TYPE SHELL COMMAND ARGS WORK DIR 93 some-remote-type (default) bash /some/remote command some remote args /some-test-work-dir 94 other-remote-type /other/remote/command other remote args /other-test-work-dir` 95 96 expectedRemoteWithExtensionOutput = `REMOTE: 97 98 Stack: test.stack.id.remote 99 100 Base Image: 101 Reference: some-remote-run-image-reference 102 Top Layer: some-remote-top-layer 103 104 Run Images: 105 user-configured-mirror-for-remote (user-configured) 106 some-remote-run-image 107 some-remote-mirror 108 other-remote-mirror 109 110 Rebasable: true 111 112 Buildpacks: 113 ID VERSION HOMEPAGE 114 test.bp.one.remote 1.0.0 https://some-homepage-one 115 test.bp.two.remote 2.0.0 https://some-homepage-two 116 test.bp.three.remote 3.0.0 - 117 118 Extensions: 119 ID VERSION HOMEPAGE 120 test.bp.one.remote 1.0.0 https://some-homepage-one 121 test.bp.two.remote 2.0.0 https://some-homepage-two 122 test.bp.three.remote 3.0.0 - 123 124 Processes: 125 TYPE SHELL COMMAND ARGS WORK DIR 126 some-remote-type (default) bash /some/remote command some remote args /some-test-work-dir 127 other-remote-type /other/remote/command other remote args /other-test-work-dir` 128 129 expectedLocalOutput = `LOCAL: 130 131 Stack: test.stack.id.local 132 133 Base Image: 134 Reference: some-local-run-image-reference 135 Top Layer: some-local-top-layer 136 137 Run Images: 138 user-configured-mirror-for-local (user-configured) 139 some-local-run-image 140 some-local-mirror 141 other-local-mirror 142 143 Rebasable: true 144 145 Buildpacks: 146 ID VERSION HOMEPAGE 147 test.bp.one.local 1.0.0 https://some-homepage-one 148 test.bp.two.local 2.0.0 https://some-homepage-two 149 test.bp.three.local 3.0.0 - 150 151 Processes: 152 TYPE SHELL COMMAND ARGS WORK DIR 153 some-local-type (default) bash /some/local command some local args /some-test-work-dir 154 other-local-type /other/local/command other local args /other-test-work-dir` 155 expectedLocalNoRebasableOutput = `LOCAL: 156 157 Stack: test.stack.id.local 158 159 Base Image: 160 Reference: some-local-run-image-reference 161 Top Layer: some-local-top-layer 162 163 Run Images: 164 user-configured-mirror-for-local (user-configured) 165 some-local-run-image 166 some-local-mirror 167 other-local-mirror 168 169 Rebasable: false 170 171 Buildpacks: 172 ID VERSION HOMEPAGE 173 test.bp.one.local 1.0.0 https://some-homepage-one 174 test.bp.two.local 2.0.0 https://some-homepage-two 175 test.bp.three.local 3.0.0 - 176 177 Processes: 178 TYPE SHELL COMMAND ARGS WORK DIR 179 some-local-type (default) bash /some/local command some local args /some-test-work-dir 180 other-local-type /other/local/command other local args /other-test-work-dir` 181 182 expectedLocalWithExtensionOutput = `LOCAL: 183 184 Stack: test.stack.id.local 185 186 Base Image: 187 Reference: some-local-run-image-reference 188 Top Layer: some-local-top-layer 189 190 Run Images: 191 user-configured-mirror-for-local (user-configured) 192 some-local-run-image 193 some-local-mirror 194 other-local-mirror 195 196 Rebasable: true 197 198 Buildpacks: 199 ID VERSION HOMEPAGE 200 test.bp.one.local 1.0.0 https://some-homepage-one 201 test.bp.two.local 2.0.0 https://some-homepage-two 202 test.bp.three.local 3.0.0 - 203 204 Extensions: 205 ID VERSION HOMEPAGE 206 test.bp.one.local 1.0.0 https://some-homepage-one 207 test.bp.two.local 2.0.0 https://some-homepage-two 208 test.bp.three.local 3.0.0 - 209 210 Processes: 211 TYPE SHELL COMMAND ARGS WORK DIR 212 some-local-type (default) bash /some/local command some local args /some-test-work-dir 213 other-local-type /other/local/command other local args /other-test-work-dir` 214 ) 215 216 when("Print", func() { 217 it.Before(func() { 218 remoteInfo = getRemoteBasicImageInfo(t) 219 remoteWithExtensionInfo = getRemoteImageInfoWithExtension(t) 220 remoteInfoNoRebasable = getRemoteImageInfoNoRebasable(t) 221 222 localInfo = getBasicLocalImageInfo(t) 223 localWithExtensionInfo = getLocalImageInfoWithExtension(t) 224 localInfoNoRebasable = getLocalImageInfoNoRebasable(t) 225 226 outBuf = bytes.Buffer{} 227 }) 228 229 when("local and remote image exits", func() { 230 it("prints both local and remote image info in a human readable format", func() { 231 runImageMirrors := []config.RunImage{ 232 { 233 Image: "un-used-run-image", 234 Mirrors: []string{"un-used"}, 235 }, 236 { 237 Image: "some-local-run-image", 238 Mirrors: []string{"user-configured-mirror-for-local"}, 239 }, 240 { 241 Image: "some-remote-run-image", 242 Mirrors: []string{"user-configured-mirror-for-remote"}, 243 }, 244 } 245 sharedImageInfo := inspectimage.GeneralInfo{ 246 Name: "test-image", 247 RunImageMirrors: runImageMirrors, 248 } 249 humanReadableWriter := writer.NewHumanReadable() 250 251 logger := logging.NewLogWithWriters(&outBuf, &outBuf) 252 err := humanReadableWriter.Print(logger, sharedImageInfo, localInfo, remoteInfo, nil, nil) 253 assert.Nil(err) 254 255 assert.Contains(outBuf.String(), expectedLocalOutput) 256 assert.Contains(outBuf.String(), expectedRemoteOutput) 257 }) 258 }) 259 260 when("localWithExtension and remoteWithExtension image exits", func() { 261 it("prints both localWithExtension and remoteWithExtension image info in a human readable format", func() { 262 runImageMirrors := []config.RunImage{ 263 { 264 Image: "un-used-run-image", 265 Mirrors: []string{"un-used"}, 266 }, 267 { 268 Image: "some-local-run-image", 269 Mirrors: []string{"user-configured-mirror-for-local"}, 270 }, 271 { 272 Image: "some-remote-run-image", 273 Mirrors: []string{"user-configured-mirror-for-remote"}, 274 }, 275 } 276 sharedImageInfo := inspectimage.GeneralInfo{ 277 Name: "test-image", 278 RunImageMirrors: runImageMirrors, 279 } 280 humanReadableWriter := writer.NewHumanReadable() 281 282 logger := logging.NewLogWithWriters(&outBuf, &outBuf) 283 err := humanReadableWriter.Print(logger, sharedImageInfo, localWithExtensionInfo, remoteWithExtensionInfo, nil, nil) 284 assert.Nil(err) 285 286 assert.Contains(outBuf.String(), expectedLocalWithExtensionOutput) 287 assert.Contains(outBuf.String(), expectedRemoteWithExtensionOutput) 288 }) 289 }) 290 291 when("only local image exists", func() { 292 it("prints local image info in a human readable format", func() { 293 runImageMirrors := []config.RunImage{ 294 { 295 Image: "un-used-run-image", 296 Mirrors: []string{"un-used"}, 297 }, 298 { 299 Image: "some-local-run-image", 300 Mirrors: []string{"user-configured-mirror-for-local"}, 301 }, 302 { 303 Image: "some-remote-run-image", 304 Mirrors: []string{"user-configured-mirror-for-remote"}, 305 }, 306 } 307 sharedImageInfo := inspectimage.GeneralInfo{ 308 Name: "test-image", 309 RunImageMirrors: runImageMirrors, 310 } 311 humanReadableWriter := writer.NewHumanReadable() 312 313 logger := logging.NewLogWithWriters(&outBuf, &outBuf) 314 err := humanReadableWriter.Print(logger, sharedImageInfo, localInfo, nil, nil, nil) 315 assert.Nil(err) 316 317 assert.Contains(outBuf.String(), expectedLocalOutput) 318 assert.NotContains(outBuf.String(), expectedRemoteOutput) 319 }) 320 it("prints local no rebasable image info in a human readable format", func() { 321 runImageMirrors := []config.RunImage{ 322 { 323 Image: "un-used-run-image", 324 Mirrors: []string{"un-used"}, 325 }, 326 { 327 Image: "some-local-run-image", 328 Mirrors: []string{"user-configured-mirror-for-local"}, 329 }, 330 { 331 Image: "some-remote-run-image", 332 Mirrors: []string{"user-configured-mirror-for-remote"}, 333 }, 334 } 335 sharedImageInfo := inspectimage.GeneralInfo{ 336 Name: "test-image", 337 RunImageMirrors: runImageMirrors, 338 } 339 humanReadableWriter := writer.NewHumanReadable() 340 341 logger := logging.NewLogWithWriters(&outBuf, &outBuf) 342 err := humanReadableWriter.Print(logger, sharedImageInfo, localInfoNoRebasable, nil, nil, nil) 343 assert.Nil(err) 344 345 assert.Contains(outBuf.String(), expectedLocalNoRebasableOutput) 346 assert.NotContains(outBuf.String(), expectedRemoteOutput) 347 }) 348 }) 349 350 when("only localWithExtension image exists", func() { 351 it("prints localWithExtension image info in a human readable format", func() { 352 runImageMirrors := []config.RunImage{ 353 { 354 Image: "un-used-run-image", 355 Mirrors: []string{"un-used"}, 356 }, 357 { 358 Image: "some-local-run-image", 359 Mirrors: []string{"user-configured-mirror-for-local"}, 360 }, 361 { 362 Image: "some-remote-run-image", 363 Mirrors: []string{"user-configured-mirror-for-remote"}, 364 }, 365 } 366 sharedImageInfo := inspectimage.GeneralInfo{ 367 Name: "test-image", 368 RunImageMirrors: runImageMirrors, 369 } 370 humanReadableWriter := writer.NewHumanReadable() 371 372 logger := logging.NewLogWithWriters(&outBuf, &outBuf) 373 err := humanReadableWriter.Print(logger, sharedImageInfo, localWithExtensionInfo, nil, nil, nil) 374 assert.Nil(err) 375 376 assert.Contains(outBuf.String(), expectedLocalWithExtensionOutput) 377 assert.NotContains(outBuf.String(), expectedRemoteWithExtensionOutput) 378 }) 379 }) 380 381 when("only remote image exists", func() { 382 it("prints remote image info in a human readable format", func() { 383 runImageMirrors := []config.RunImage{ 384 { 385 Image: "un-used-run-image", 386 Mirrors: []string{"un-used"}, 387 }, 388 { 389 Image: "some-local-run-image", 390 Mirrors: []string{"user-configured-mirror-for-local"}, 391 }, 392 { 393 Image: "some-remote-run-image", 394 Mirrors: []string{"user-configured-mirror-for-remote"}, 395 }, 396 } 397 sharedImageInfo := inspectimage.GeneralInfo{ 398 Name: "test-image", 399 RunImageMirrors: runImageMirrors, 400 } 401 humanReadableWriter := writer.NewHumanReadable() 402 403 logger := logging.NewLogWithWriters(&outBuf, &outBuf) 404 err := humanReadableWriter.Print(logger, sharedImageInfo, nil, remoteInfo, nil, nil) 405 assert.Nil(err) 406 407 assert.NotContains(outBuf.String(), expectedLocalOutput) 408 assert.Contains(outBuf.String(), expectedRemoteOutput) 409 }) 410 it("prints remote no rebasable image info in a human readable format", func() { 411 runImageMirrors := []config.RunImage{ 412 { 413 Image: "un-used-run-image", 414 Mirrors: []string{"un-used"}, 415 }, 416 { 417 Image: "some-local-run-image", 418 Mirrors: []string{"user-configured-mirror-for-local"}, 419 }, 420 { 421 Image: "some-remote-run-image", 422 Mirrors: []string{"user-configured-mirror-for-remote"}, 423 }, 424 } 425 sharedImageInfo := inspectimage.GeneralInfo{ 426 Name: "test-image", 427 RunImageMirrors: runImageMirrors, 428 } 429 humanReadableWriter := writer.NewHumanReadable() 430 431 logger := logging.NewLogWithWriters(&outBuf, &outBuf) 432 err := humanReadableWriter.Print(logger, sharedImageInfo, nil, remoteInfoNoRebasable, nil, nil) 433 assert.Nil(err) 434 435 assert.NotContains(outBuf.String(), expectedLocalOutput) 436 assert.Contains(outBuf.String(), expectedRemoteNoRebasableOutput) 437 }) 438 439 when("buildpack metadata is missing", func() { 440 it.Before(func() { 441 remoteInfo.Buildpacks = []buildpack.GroupElement{} 442 }) 443 it("displays a message indicating missing metadata", func() { 444 sharedImageInfo := inspectimage.GeneralInfo{ 445 Name: "test-image", 446 RunImageMirrors: []config.RunImage{}, 447 } 448 449 humanReadableWriter := writer.NewHumanReadable() 450 451 logger := logging.NewLogWithWriters(&outBuf, &outBuf) 452 err := humanReadableWriter.Print(logger, sharedImageInfo, nil, remoteInfo, nil, nil) 453 assert.Nil(err) 454 455 assert.Contains(outBuf.String(), "(buildpack metadata not present)") 456 }) 457 }) 458 459 when("there are no run images", func() { 460 it.Before(func() { 461 remoteInfo.Stack = files.Stack{} 462 }) 463 it("displays a message indicating missing run images", func() { 464 sharedImageInfo := inspectimage.GeneralInfo{ 465 Name: "test-image", 466 RunImageMirrors: []config.RunImage{}, 467 } 468 469 humanReadableWriter := writer.NewHumanReadable() 470 471 logger := logging.NewLogWithWriters(&outBuf, &outBuf) 472 err := humanReadableWriter.Print(logger, sharedImageInfo, nil, remoteInfo, nil, nil) 473 assert.Nil(err) 474 475 assert.Contains(outBuf.String(), "Run Images:\n (none)") 476 }) 477 }) 478 }) 479 480 when("only remoteWithExtension image exists", func() { 481 it("prints remoteWithExtension image info in a human readable format", func() { 482 runImageMirrors := []config.RunImage{ 483 { 484 Image: "un-used-run-image", 485 Mirrors: []string{"un-used"}, 486 }, 487 { 488 Image: "some-local-run-image", 489 Mirrors: []string{"user-configured-mirror-for-local"}, 490 }, 491 { 492 Image: "some-remote-run-image", 493 Mirrors: []string{"user-configured-mirror-for-remote"}, 494 }, 495 } 496 sharedImageInfo := inspectimage.GeneralInfo{ 497 Name: "test-image", 498 RunImageMirrors: runImageMirrors, 499 } 500 humanReadableWriter := writer.NewHumanReadable() 501 502 logger := logging.NewLogWithWriters(&outBuf, &outBuf) 503 err := humanReadableWriter.Print(logger, sharedImageInfo, nil, remoteWithExtensionInfo, nil, nil) 504 assert.Nil(err) 505 506 assert.NotContains(outBuf.String(), expectedLocalWithExtensionOutput) 507 assert.Contains(outBuf.String(), expectedRemoteWithExtensionOutput) 508 }) 509 510 when("buildpack metadata is missing", func() { 511 it.Before(func() { 512 remoteWithExtensionInfo.Buildpacks = []buildpack.GroupElement{} 513 }) 514 it("displays a message indicating missing metadata", func() { 515 sharedImageInfo := inspectimage.GeneralInfo{ 516 Name: "test-image", 517 RunImageMirrors: []config.RunImage{}, 518 } 519 520 humanReadableWriter := writer.NewHumanReadable() 521 522 logger := logging.NewLogWithWriters(&outBuf, &outBuf) 523 err := humanReadableWriter.Print(logger, sharedImageInfo, nil, remoteWithExtensionInfo, nil, nil) 524 assert.Nil(err) 525 526 assert.Contains(outBuf.String(), "(buildpack metadata not present)") 527 }) 528 }) 529 530 when("there are no run images", func() { 531 it.Before(func() { 532 remoteWithExtensionInfo.Stack = files.Stack{} 533 }) 534 it("displays a message indicating missing run images", func() { 535 sharedImageInfo := inspectimage.GeneralInfo{ 536 Name: "test-image", 537 RunImageMirrors: []config.RunImage{}, 538 } 539 540 humanReadableWriter := writer.NewHumanReadable() 541 542 logger := logging.NewLogWithWriters(&outBuf, &outBuf) 543 err := humanReadableWriter.Print(logger, sharedImageInfo, nil, remoteWithExtensionInfo, nil, nil) 544 assert.Nil(err) 545 546 assert.Contains(outBuf.String(), "Run Images:\n (none)") 547 }) 548 }) 549 }) 550 551 when("error handled cases", func() { 552 when("there is a remoteErr", func() { 553 var remoteErr error 554 it.Before(func() { 555 remoteErr = errors.New("some remote error") 556 }) 557 it("displays the remote error and local info", func() { 558 runImageMirrors := []config.RunImage{ 559 { 560 Image: "un-used-run-image", 561 Mirrors: []string{"un-used"}, 562 }, 563 { 564 Image: "some-local-run-image", 565 Mirrors: []string{"user-configured-mirror-for-local"}, 566 }, 567 { 568 Image: "some-remote-run-image", 569 Mirrors: []string{"user-configured-mirror-for-remote"}, 570 }, 571 } 572 sharedImageInfo := inspectimage.GeneralInfo{ 573 Name: "test-image", 574 RunImageMirrors: runImageMirrors, 575 } 576 humanReadableWriter := writer.NewHumanReadable() 577 578 logger := logging.NewLogWithWriters(&outBuf, &outBuf) 579 err := humanReadableWriter.Print(logger, sharedImageInfo, localInfo, remoteInfo, nil, remoteErr) 580 assert.Nil(err) 581 582 assert.Contains(outBuf.String(), expectedLocalOutput) 583 assert.Contains(outBuf.String(), "some remote error") 584 }) 585 }) 586 587 when("there is a localErr", func() { 588 var localErr error 589 it.Before(func() { 590 localErr = errors.New("some local error") 591 }) 592 it("displays the remote info and local error", func() { 593 runImageMirrors := []config.RunImage{ 594 { 595 Image: "un-used-run-image", 596 Mirrors: []string{"un-used"}, 597 }, 598 { 599 Image: "some-local-run-image", 600 Mirrors: []string{"user-configured-mirror-for-local"}, 601 }, 602 { 603 Image: "some-remote-run-image", 604 Mirrors: []string{"user-configured-mirror-for-remote"}, 605 }, 606 } 607 sharedImageInfo := inspectimage.GeneralInfo{ 608 Name: "test-image", 609 RunImageMirrors: runImageMirrors, 610 } 611 humanReadableWriter := writer.NewHumanReadable() 612 613 logger := logging.NewLogWithWriters(&outBuf, &outBuf) 614 err := humanReadableWriter.Print(logger, sharedImageInfo, localInfo, remoteInfo, localErr, nil) 615 assert.Nil(err) 616 617 assert.Contains(outBuf.String(), expectedRemoteOutput) 618 assert.Contains(outBuf.String(), "some local error") 619 }) 620 }) 621 622 when("error handled cases", func() { 623 when("there is a remoteErr", func() { 624 var remoteErr error 625 it.Before(func() { 626 remoteErr = errors.New("some remote error") 627 }) 628 it("displays the remote error and local info", func() { 629 runImageMirrors := []config.RunImage{ 630 { 631 Image: "un-used-run-image", 632 Mirrors: []string{"un-used"}, 633 }, 634 { 635 Image: "some-local-run-image", 636 Mirrors: []string{"user-configured-mirror-for-local"}, 637 }, 638 { 639 Image: "some-remote-run-image", 640 Mirrors: []string{"user-configured-mirror-for-remote"}, 641 }, 642 } 643 sharedImageInfo := inspectimage.GeneralInfo{ 644 Name: "test-image", 645 RunImageMirrors: runImageMirrors, 646 } 647 humanReadableWriter := writer.NewHumanReadable() 648 649 logger := logging.NewLogWithWriters(&outBuf, &outBuf) 650 err := humanReadableWriter.Print(logger, sharedImageInfo, localWithExtensionInfo, remoteWithExtensionInfo, nil, remoteErr) 651 assert.Nil(err) 652 653 assert.Contains(outBuf.String(), expectedLocalWithExtensionOutput) 654 assert.Contains(outBuf.String(), "some remote error") 655 }) 656 }) 657 658 when("there is a localErr", func() { 659 var localErr error 660 it.Before(func() { 661 localErr = errors.New("some local error") 662 }) 663 it("displays the remote info and local error", func() { 664 runImageMirrors := []config.RunImage{ 665 { 666 Image: "un-used-run-image", 667 Mirrors: []string{"un-used"}, 668 }, 669 { 670 Image: "some-local-run-image", 671 Mirrors: []string{"user-configured-mirror-for-local"}, 672 }, 673 { 674 Image: "some-remote-run-image", 675 Mirrors: []string{"user-configured-mirror-for-remote"}, 676 }, 677 } 678 sharedImageInfo := inspectimage.GeneralInfo{ 679 Name: "test-image", 680 RunImageMirrors: runImageMirrors, 681 } 682 humanReadableWriter := writer.NewHumanReadable() 683 684 logger := logging.NewLogWithWriters(&outBuf, &outBuf) 685 err := humanReadableWriter.Print(logger, sharedImageInfo, localWithExtensionInfo, remoteWithExtensionInfo, localErr, nil) 686 assert.Nil(err) 687 688 assert.Contains(outBuf.String(), expectedRemoteWithExtensionOutput) 689 assert.Contains(outBuf.String(), "some local error") 690 }) 691 }) 692 }) 693 694 when("error cases", func() { 695 when("both localInfo and remoteInfo are nil", func() { 696 it("displays a 'missing image' error message", func() { 697 humanReadableWriter := writer.NewHumanReadable() 698 699 logger := logging.NewLogWithWriters(&outBuf, &outBuf) 700 err := humanReadableWriter.Print(logger, inspectimage.GeneralInfo{Name: "missing-image"}, nil, nil, nil, nil) 701 assert.ErrorWithMessage(err, fmt.Sprintf("unable to find image '%s' locally or remotely", "missing-image")) 702 }) 703 }) 704 }) 705 }) 706 }) 707 } 708 709 func getRemoteBasicImageInfo(t testing.TB) *client.ImageInfo { 710 t.Helper() 711 return getRemoteImageInfo(t, false, true) 712 } 713 func getRemoteImageInfoWithExtension(t testing.TB) *client.ImageInfo { 714 t.Helper() 715 return getRemoteImageInfo(t, true, true) 716 } 717 718 func getRemoteImageInfoNoRebasable(t testing.TB) *client.ImageInfo { 719 t.Helper() 720 return getRemoteImageInfo(t, false, false) 721 } 722 723 func getRemoteImageInfo(t testing.TB, extension bool, rebasable bool) *client.ImageInfo { 724 t.Helper() 725 726 mockedStackID := "test.stack.id.remote" 727 728 mockedBuildpacks := []buildpack.GroupElement{ 729 {ID: "test.bp.one.remote", Version: "1.0.0", Homepage: "https://some-homepage-one"}, 730 {ID: "test.bp.two.remote", Version: "2.0.0", Homepage: "https://some-homepage-two"}, 731 {ID: "test.bp.three.remote", Version: "3.0.0"}, 732 } 733 734 mockedBase := files.RunImageForRebase{ 735 TopLayer: "some-remote-top-layer", 736 Reference: "some-remote-run-image-reference", 737 } 738 739 mockedStack := files.Stack{ 740 RunImage: files.RunImageForExport{ 741 Image: "some-remote-run-image", 742 Mirrors: []string{"some-remote-mirror", "other-remote-mirror"}, 743 }, 744 } 745 746 type someData struct { 747 String string 748 Bool bool 749 Int int 750 Nested struct { 751 String string 752 } 753 } 754 mockedMetadata := map[string]interface{}{ 755 "RemoteData": someData{ 756 String: "aString", 757 Bool: true, 758 Int: 123, 759 Nested: struct { 760 String string 761 }{ 762 String: "anotherString", 763 }, 764 }, 765 } 766 767 mockedBOM := []buildpack.BOMEntry{{ 768 Require: buildpack.Require{ 769 Name: "name-1", 770 Metadata: mockedMetadata, 771 }, 772 Buildpack: buildpack.GroupElement{ID: "test.bp.one.remote", Version: "1.0.0"}, 773 }} 774 775 mockedProcesses := client.ProcessDetails{ 776 DefaultProcess: &launch.Process{ 777 Type: "some-remote-type", 778 Command: launch.RawCommand{Entries: []string{"/some/remote command"}}, 779 Args: []string{"some", "remote", "args"}, 780 Direct: false, 781 WorkingDirectory: "/some-test-work-dir", 782 }, 783 OtherProcesses: []launch.Process{ 784 { 785 Type: "other-remote-type", 786 Command: launch.RawCommand{Entries: []string{"/other/remote/command"}}, 787 Args: []string{"other", "remote", "args"}, 788 Direct: true, 789 WorkingDirectory: "/other-test-work-dir", 790 }, 791 }, 792 } 793 794 mockedExtension := []buildpack.GroupElement{ 795 {ID: "test.bp.one.remote", Version: "1.0.0", Homepage: "https://some-homepage-one"}, 796 {ID: "test.bp.two.remote", Version: "2.0.0", Homepage: "https://some-homepage-two"}, 797 {ID: "test.bp.three.remote", Version: "3.0.0"}, 798 } 799 800 imageInfo := &client.ImageInfo{ 801 StackID: mockedStackID, 802 Buildpacks: mockedBuildpacks, 803 Base: mockedBase, 804 Stack: mockedStack, 805 BOM: mockedBOM, 806 Processes: mockedProcesses, 807 Rebasable: rebasable, 808 } 809 810 if extension { 811 imageInfo.Extensions = mockedExtension 812 } 813 814 return imageInfo 815 } 816 817 func getBasicLocalImageInfo(t testing.TB) *client.ImageInfo { 818 t.Helper() 819 return getLocalImageInfo(t, false, true) 820 } 821 822 func getLocalImageInfoWithExtension(t testing.TB) *client.ImageInfo { 823 t.Helper() 824 return getLocalImageInfo(t, true, true) 825 } 826 827 func getLocalImageInfoNoRebasable(t testing.TB) *client.ImageInfo { 828 t.Helper() 829 return getLocalImageInfo(t, false, false) 830 } 831 832 func getLocalImageInfo(t testing.TB, extension bool, rebasable bool) *client.ImageInfo { 833 t.Helper() 834 835 mockedStackID := "test.stack.id.local" 836 837 mockedBuildpacks := []buildpack.GroupElement{ 838 {ID: "test.bp.one.local", Version: "1.0.0", Homepage: "https://some-homepage-one"}, 839 {ID: "test.bp.two.local", Version: "2.0.0", Homepage: "https://some-homepage-two"}, 840 {ID: "test.bp.three.local", Version: "3.0.0"}, 841 } 842 843 mockedBase := files.RunImageForRebase{ 844 TopLayer: "some-local-top-layer", 845 Reference: "some-local-run-image-reference", 846 } 847 848 mockedPlatform := files.Stack{ 849 RunImage: files.RunImageForExport{ 850 Image: "some-local-run-image", 851 Mirrors: []string{"some-local-mirror", "other-local-mirror"}, 852 }, 853 } 854 855 type someData struct { 856 String string 857 Bool bool 858 Int int 859 Nested struct { 860 String string 861 } 862 } 863 mockedMetadata := map[string]interface{}{ 864 "LocalData": someData{ 865 Bool: false, 866 Int: 456, 867 }, 868 } 869 870 mockedBOM := []buildpack.BOMEntry{{ 871 Require: buildpack.Require{ 872 Name: "name-1", 873 Version: "version-1", 874 Metadata: mockedMetadata, 875 }, 876 Buildpack: buildpack.GroupElement{ID: "test.bp.one.remote", Version: "1.0.0"}, 877 }} 878 879 mockedProcesses := client.ProcessDetails{ 880 DefaultProcess: &launch.Process{ 881 Type: "some-local-type", 882 Command: launch.RawCommand{Entries: []string{"/some/local command"}}, 883 Args: []string{"some", "local", "args"}, 884 Direct: false, 885 WorkingDirectory: "/some-test-work-dir", 886 }, 887 OtherProcesses: []launch.Process{ 888 { 889 Type: "other-local-type", 890 Command: launch.RawCommand{Entries: []string{"/other/local/command"}}, 891 Args: []string{"other", "local", "args"}, 892 Direct: true, 893 WorkingDirectory: "/other-test-work-dir", 894 }, 895 }, 896 } 897 898 mockedExtension := []buildpack.GroupElement{ 899 {ID: "test.bp.one.local", Version: "1.0.0", Homepage: "https://some-homepage-one"}, 900 {ID: "test.bp.two.local", Version: "2.0.0", Homepage: "https://some-homepage-two"}, 901 {ID: "test.bp.three.local", Version: "3.0.0"}, 902 } 903 904 imageInfo := &client.ImageInfo{ 905 StackID: mockedStackID, 906 Buildpacks: mockedBuildpacks, 907 Base: mockedBase, 908 Stack: mockedPlatform, 909 BOM: mockedBOM, 910 Processes: mockedProcesses, 911 Rebasable: rebasable, 912 } 913 914 if extension { 915 imageInfo.Extensions = mockedExtension 916 } 917 918 return imageInfo 919 }