github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/internal/inspectimage/writer/json_test.go (about) 1 package writer_test 2 3 import ( 4 "bytes" 5 "testing" 6 7 "github.com/buildpacks/lifecycle/buildpack" 8 "github.com/buildpacks/lifecycle/launch" 9 "github.com/buildpacks/lifecycle/platform/files" 10 "github.com/heroku/color" 11 "github.com/sclevine/spec" 12 "github.com/sclevine/spec/report" 13 14 "github.com/buildpacks/pack/internal/config" 15 "github.com/buildpacks/pack/internal/inspectimage" 16 "github.com/buildpacks/pack/internal/inspectimage/writer" 17 "github.com/buildpacks/pack/pkg/client" 18 "github.com/buildpacks/pack/pkg/logging" 19 h "github.com/buildpacks/pack/testhelpers" 20 ) 21 22 func TestJSON(t *testing.T) { 23 color.Disable(true) 24 defer color.Disable(false) 25 spec.Run(t, "JSON Writer", testJSON, spec.Parallel(), spec.Report(report.Terminal{})) 26 } 27 28 func testJSON(t *testing.T, when spec.G, it spec.S) { 29 var ( 30 assert = h.NewAssertionManager(t) 31 outBuf bytes.Buffer 32 33 remoteInfo *client.ImageInfo 34 remoteInfoNoRebasable *client.ImageInfo 35 localInfo *client.ImageInfo 36 localInfoNoRebasable *client.ImageInfo 37 38 expectedLocalOutput = `{ 39 "local_info": { 40 "stack": "test.stack.id.local", 41 "rebasable": true, 42 "base_image": { 43 "top_layer": "some-local-top-layer", 44 "reference": "some-local-run-image-reference" 45 }, 46 "run_images": [ 47 { 48 "name": "user-configured-mirror-for-local", 49 "user_configured": true 50 }, 51 { 52 "name": "some-local-run-image" 53 }, 54 { 55 "name": "some-local-mirror" 56 }, 57 { 58 "name": "other-local-mirror" 59 } 60 ], 61 "buildpacks": [ 62 { 63 "homepage": "https://some-homepage-one", 64 "id": "test.bp.one.local", 65 "version": "1.0.0" 66 }, 67 { 68 "homepage": "https://some-homepage-two", 69 "id": "test.bp.two.local", 70 "version": "2.0.0" 71 } 72 ], 73 "extensions": null, 74 "processes": [ 75 { 76 "type": "some-local-type", 77 "shell": "bash", 78 "command": "/some/local command", 79 "default": true, 80 "args": [ 81 "some", 82 "local", 83 "args" 84 ], 85 "working-dir": "/some-test-work-dir" 86 }, 87 { 88 "type": "other-local-type", 89 "shell": "", 90 "command": "/other/local/command", 91 "default": false, 92 "args": [ 93 "other", 94 "local", 95 "args" 96 ], 97 "working-dir": "/other-test-work-dir" 98 } 99 ] 100 } 101 }` 102 expectedLocalNoRebasableOutput = `{ 103 "local_info": { 104 "stack": "test.stack.id.local", 105 "rebasable": false, 106 "base_image": { 107 "top_layer": "some-local-top-layer", 108 "reference": "some-local-run-image-reference" 109 }, 110 "run_images": [ 111 { 112 "name": "user-configured-mirror-for-local", 113 "user_configured": true 114 }, 115 { 116 "name": "some-local-run-image" 117 }, 118 { 119 "name": "some-local-mirror" 120 }, 121 { 122 "name": "other-local-mirror" 123 } 124 ], 125 "buildpacks": [ 126 { 127 "homepage": "https://some-homepage-one", 128 "id": "test.bp.one.local", 129 "version": "1.0.0" 130 }, 131 { 132 "homepage": "https://some-homepage-two", 133 "id": "test.bp.two.local", 134 "version": "2.0.0" 135 } 136 ], 137 "extensions": null, 138 "processes": [ 139 { 140 "type": "some-local-type", 141 "shell": "bash", 142 "command": "/some/local command", 143 "default": true, 144 "args": [ 145 "some", 146 "local", 147 "args" 148 ], 149 "working-dir": "/some-test-work-dir" 150 }, 151 { 152 "type": "other-local-type", 153 "shell": "", 154 "command": "/other/local/command", 155 "default": false, 156 "args": [ 157 "other", 158 "local", 159 "args" 160 ], 161 "working-dir": "/other-test-work-dir" 162 } 163 ] 164 } 165 }` 166 expectedRemoteOutput = `{ 167 "remote_info": { 168 "stack": "test.stack.id.remote", 169 "rebasable": true, 170 "base_image": { 171 "top_layer": "some-remote-top-layer", 172 "reference": "some-remote-run-image-reference" 173 }, 174 "run_images": [ 175 { 176 "name": "user-configured-mirror-for-remote", 177 "user_configured": true 178 }, 179 { 180 "name": "some-remote-run-image" 181 }, 182 { 183 "name": "some-remote-mirror" 184 }, 185 { 186 "name": "other-remote-mirror" 187 } 188 ], 189 "buildpacks": [ 190 { 191 "id": "test.bp.one.remote", 192 "version": "1.0.0", 193 "homepage": "https://some-homepage-one" 194 }, 195 { 196 "id": "test.bp.two.remote", 197 "version": "2.0.0", 198 "homepage": "https://some-homepage-two" 199 } 200 ], 201 "extensions": null, 202 "processes": [ 203 { 204 "type": "some-remote-type", 205 "shell": "bash", 206 "command": "/some/remote command", 207 "default": true, 208 "args": [ 209 "some", 210 "remote", 211 "args" 212 ], 213 "working-dir": "/some-test-work-dir" 214 }, 215 { 216 "type": "other-remote-type", 217 "shell": "", 218 "command": "/other/remote/command", 219 "default": false, 220 "args": [ 221 "other", 222 "remote", 223 "args" 224 ], 225 "working-dir": "/other-test-work-dir" 226 } 227 ] 228 } 229 }` 230 expectedRemoteNoRebasableOutput = `{ 231 "remote_info": { 232 "stack": "test.stack.id.remote", 233 "rebasable": false, 234 "base_image": { 235 "top_layer": "some-remote-top-layer", 236 "reference": "some-remote-run-image-reference" 237 }, 238 "run_images": [ 239 { 240 "name": "user-configured-mirror-for-remote", 241 "user_configured": true 242 }, 243 { 244 "name": "some-remote-run-image" 245 }, 246 { 247 "name": "some-remote-mirror" 248 }, 249 { 250 "name": "other-remote-mirror" 251 } 252 ], 253 "buildpacks": [ 254 { 255 "id": "test.bp.one.remote", 256 "version": "1.0.0", 257 "homepage": "https://some-homepage-one" 258 }, 259 { 260 "id": "test.bp.two.remote", 261 "version": "2.0.0", 262 "homepage": "https://some-homepage-two" 263 } 264 ], 265 "extensions": null, 266 "processes": [ 267 { 268 "type": "some-remote-type", 269 "shell": "bash", 270 "command": "/some/remote command", 271 "default": true, 272 "args": [ 273 "some", 274 "remote", 275 "args" 276 ], 277 "working-dir": "/some-test-work-dir" 278 }, 279 { 280 "type": "other-remote-type", 281 "shell": "", 282 "command": "/other/remote/command", 283 "default": false, 284 "args": [ 285 "other", 286 "remote", 287 "args" 288 ], 289 "working-dir": "/other-test-work-dir" 290 } 291 ] 292 } 293 }` 294 ) 295 296 when("Print", func() { 297 it.Before(func() { 298 type someData struct { 299 String string 300 Bool bool 301 Int int 302 Nested struct { 303 String string 304 } 305 } 306 307 remoteInfo = &client.ImageInfo{ 308 StackID: "test.stack.id.remote", 309 Buildpacks: []buildpack.GroupElement{ 310 {ID: "test.bp.one.remote", Version: "1.0.0", Homepage: "https://some-homepage-one"}, 311 {ID: "test.bp.two.remote", Version: "2.0.0", Homepage: "https://some-homepage-two"}, 312 }, 313 Base: files.RunImageForRebase{ 314 TopLayer: "some-remote-top-layer", 315 Reference: "some-remote-run-image-reference", 316 }, 317 Stack: files.Stack{ 318 RunImage: files.RunImageForExport{ 319 Image: "some-remote-run-image", 320 Mirrors: []string{"some-remote-mirror", "other-remote-mirror"}, 321 }, 322 }, 323 BOM: []buildpack.BOMEntry{{ 324 Require: buildpack.Require{ 325 Name: "name-1", 326 Version: "version-1", 327 Metadata: map[string]interface{}{ 328 "RemoteData": someData{ 329 String: "aString", 330 Bool: true, 331 Int: 123, 332 Nested: struct { 333 String string 334 }{ 335 String: "anotherString", 336 }, 337 }, 338 }, 339 }, 340 Buildpack: buildpack.GroupElement{ID: "test.bp.one.remote", Version: "1.0.0", Homepage: "https://some-homepage-one"}, 341 }}, 342 Processes: client.ProcessDetails{ 343 DefaultProcess: &launch.Process{ 344 Type: "some-remote-type", 345 Command: launch.RawCommand{Entries: []string{"/some/remote command"}}, 346 Args: []string{"some", "remote", "args"}, 347 Direct: false, 348 WorkingDirectory: "/some-test-work-dir", 349 }, 350 OtherProcesses: []launch.Process{ 351 { 352 Type: "other-remote-type", 353 Command: launch.RawCommand{Entries: []string{"/other/remote/command"}}, 354 Args: []string{"other", "remote", "args"}, 355 Direct: true, 356 WorkingDirectory: "/other-test-work-dir", 357 }, 358 }, 359 }, 360 Rebasable: true, 361 } 362 remoteInfoNoRebasable = &client.ImageInfo{ 363 StackID: "test.stack.id.remote", 364 Buildpacks: []buildpack.GroupElement{ 365 {ID: "test.bp.one.remote", Version: "1.0.0", Homepage: "https://some-homepage-one"}, 366 {ID: "test.bp.two.remote", Version: "2.0.0", Homepage: "https://some-homepage-two"}, 367 }, 368 Base: files.RunImageForRebase{ 369 TopLayer: "some-remote-top-layer", 370 Reference: "some-remote-run-image-reference", 371 }, 372 Stack: files.Stack{ 373 RunImage: files.RunImageForExport{ 374 Image: "some-remote-run-image", 375 Mirrors: []string{"some-remote-mirror", "other-remote-mirror"}, 376 }, 377 }, 378 BOM: []buildpack.BOMEntry{{ 379 Require: buildpack.Require{ 380 Name: "name-1", 381 Version: "version-1", 382 Metadata: map[string]interface{}{ 383 "RemoteData": someData{ 384 String: "aString", 385 Bool: true, 386 Int: 123, 387 Nested: struct { 388 String string 389 }{ 390 String: "anotherString", 391 }, 392 }, 393 }, 394 }, 395 Buildpack: buildpack.GroupElement{ID: "test.bp.one.remote", Version: "1.0.0", Homepage: "https://some-homepage-one"}, 396 }}, 397 Processes: client.ProcessDetails{ 398 DefaultProcess: &launch.Process{ 399 Type: "some-remote-type", 400 Command: launch.RawCommand{Entries: []string{"/some/remote command"}}, 401 Args: []string{"some", "remote", "args"}, 402 Direct: false, 403 WorkingDirectory: "/some-test-work-dir", 404 }, 405 OtherProcesses: []launch.Process{ 406 { 407 Type: "other-remote-type", 408 Command: launch.RawCommand{Entries: []string{"/other/remote/command"}}, 409 Args: []string{"other", "remote", "args"}, 410 Direct: true, 411 WorkingDirectory: "/other-test-work-dir", 412 }, 413 }, 414 }, 415 Rebasable: false, 416 } 417 418 localInfo = &client.ImageInfo{ 419 StackID: "test.stack.id.local", 420 Buildpacks: []buildpack.GroupElement{ 421 {ID: "test.bp.one.local", Version: "1.0.0", Homepage: "https://some-homepage-one"}, 422 {ID: "test.bp.two.local", Version: "2.0.0", Homepage: "https://some-homepage-two"}, 423 }, 424 Base: files.RunImageForRebase{ 425 TopLayer: "some-local-top-layer", 426 Reference: "some-local-run-image-reference", 427 }, 428 Stack: files.Stack{ 429 RunImage: files.RunImageForExport{ 430 Image: "some-local-run-image", 431 Mirrors: []string{"some-local-mirror", "other-local-mirror"}, 432 }, 433 }, 434 BOM: []buildpack.BOMEntry{{ 435 Require: buildpack.Require{ 436 Name: "name-1", 437 Version: "version-1", 438 Metadata: map[string]interface{}{ 439 "LocalData": someData{ 440 Bool: false, 441 Int: 456, 442 }, 443 }, 444 }, 445 Buildpack: buildpack.GroupElement{ID: "test.bp.one.remote", Version: "1.0.0", Homepage: "https://some-homepage-one"}, 446 }}, 447 Processes: client.ProcessDetails{ 448 DefaultProcess: &launch.Process{ 449 Type: "some-local-type", 450 Command: launch.RawCommand{Entries: []string{"/some/local command"}}, 451 Args: []string{"some", "local", "args"}, 452 Direct: false, 453 WorkingDirectory: "/some-test-work-dir", 454 }, 455 OtherProcesses: []launch.Process{ 456 { 457 Type: "other-local-type", 458 Command: launch.RawCommand{Entries: []string{"/other/local/command"}}, 459 Args: []string{"other", "local", "args"}, 460 Direct: true, 461 WorkingDirectory: "/other-test-work-dir", 462 }, 463 }, 464 }, 465 Rebasable: true, 466 } 467 localInfoNoRebasable = &client.ImageInfo{ 468 StackID: "test.stack.id.local", 469 Buildpacks: []buildpack.GroupElement{ 470 {ID: "test.bp.one.local", Version: "1.0.0", Homepage: "https://some-homepage-one"}, 471 {ID: "test.bp.two.local", Version: "2.0.0", Homepage: "https://some-homepage-two"}, 472 }, 473 Base: files.RunImageForRebase{ 474 TopLayer: "some-local-top-layer", 475 Reference: "some-local-run-image-reference", 476 }, 477 Stack: files.Stack{ 478 RunImage: files.RunImageForExport{ 479 Image: "some-local-run-image", 480 Mirrors: []string{"some-local-mirror", "other-local-mirror"}, 481 }, 482 }, 483 BOM: []buildpack.BOMEntry{{ 484 Require: buildpack.Require{ 485 Name: "name-1", 486 Version: "version-1", 487 Metadata: map[string]interface{}{ 488 "LocalData": someData{ 489 Bool: false, 490 Int: 456, 491 }, 492 }, 493 }, 494 Buildpack: buildpack.GroupElement{ID: "test.bp.one.remote", Version: "1.0.0", Homepage: "https://some-homepage-one"}, 495 }}, 496 Processes: client.ProcessDetails{ 497 DefaultProcess: &launch.Process{ 498 Type: "some-local-type", 499 Command: launch.RawCommand{Entries: []string{"/some/local command"}}, 500 Args: []string{"some", "local", "args"}, 501 Direct: false, 502 WorkingDirectory: "/some-test-work-dir", 503 }, 504 OtherProcesses: []launch.Process{ 505 { 506 Type: "other-local-type", 507 Command: launch.RawCommand{Entries: []string{"/other/local/command"}}, 508 Args: []string{"other", "local", "args"}, 509 Direct: true, 510 WorkingDirectory: "/other-test-work-dir", 511 }, 512 }, 513 }, 514 Rebasable: false, 515 } 516 517 outBuf = bytes.Buffer{} 518 }) 519 520 when("local and remote image exits", func() { 521 it("prints both local and remote image info in a JSON format", func() { 522 runImageMirrors := []config.RunImage{ 523 { 524 Image: "un-used-run-image", 525 Mirrors: []string{"un-used"}, 526 }, 527 { 528 Image: "some-local-run-image", 529 Mirrors: []string{"user-configured-mirror-for-local"}, 530 }, 531 { 532 Image: "some-remote-run-image", 533 Mirrors: []string{"user-configured-mirror-for-remote"}, 534 }, 535 } 536 sharedImageInfo := inspectimage.GeneralInfo{ 537 Name: "test-image", 538 RunImageMirrors: runImageMirrors, 539 } 540 jsonWriter := writer.NewJSON() 541 542 logger := logging.NewLogWithWriters(&outBuf, &outBuf) 543 err := jsonWriter.Print(logger, sharedImageInfo, localInfo, remoteInfo, nil, nil) 544 assert.Nil(err) 545 546 assert.ContainsJSON(outBuf.String(), `{ "image_name": "test-image" }`) 547 assert.ContainsJSON(outBuf.String(), expectedLocalOutput) 548 assert.ContainsJSON(outBuf.String(), expectedRemoteOutput) 549 }) 550 it("prints both local and remote no rebasable images info in a JSON format", func() { 551 runImageMirrors := []config.RunImage{ 552 { 553 Image: "un-used-run-image", 554 Mirrors: []string{"un-used"}, 555 }, 556 { 557 Image: "some-local-run-image", 558 Mirrors: []string{"user-configured-mirror-for-local"}, 559 }, 560 { 561 Image: "some-remote-run-image", 562 Mirrors: []string{"user-configured-mirror-for-remote"}, 563 }, 564 } 565 sharedImageInfo := inspectimage.GeneralInfo{ 566 Name: "test-image", 567 RunImageMirrors: runImageMirrors, 568 } 569 jsonWriter := writer.NewJSON() 570 571 logger := logging.NewLogWithWriters(&outBuf, &outBuf) 572 err := jsonWriter.Print(logger, sharedImageInfo, localInfoNoRebasable, remoteInfoNoRebasable, nil, nil) 573 assert.Nil(err) 574 575 assert.ContainsJSON(outBuf.String(), `{ "image_name": "test-image" }`) 576 assert.ContainsJSON(outBuf.String(), expectedLocalNoRebasableOutput) 577 assert.ContainsJSON(outBuf.String(), expectedRemoteNoRebasableOutput) 578 }) 579 }) 580 581 when("only local image exists", func() { 582 it("prints local image info in JSON format", func() { 583 runImageMirrors := []config.RunImage{ 584 { 585 Image: "un-used-run-image", 586 Mirrors: []string{"un-used"}, 587 }, 588 { 589 Image: "some-local-run-image", 590 Mirrors: []string{"user-configured-mirror-for-local"}, 591 }, 592 { 593 Image: "some-remote-run-image", 594 Mirrors: []string{"user-configured-mirror-for-remote"}, 595 }, 596 } 597 sharedImageInfo := inspectimage.GeneralInfo{ 598 Name: "test-image", 599 RunImageMirrors: runImageMirrors, 600 } 601 jsonWriter := writer.NewJSON() 602 603 logger := logging.NewLogWithWriters(&outBuf, &outBuf) 604 err := jsonWriter.Print(logger, sharedImageInfo, localInfo, nil, nil, nil) 605 assert.Nil(err) 606 607 assert.ContainsJSON(outBuf.String(), `{ "image_name": "test-image" }`) 608 assert.ContainsJSON(outBuf.String(), expectedLocalOutput) 609 610 assert.NotContains(outBuf.String(), "test.stack.id.remote") 611 assert.ContainsJSON(outBuf.String(), expectedLocalOutput) 612 }) 613 }) 614 615 when("only remote image exists", func() { 616 it("prints remote image info in JSON format", func() { 617 runImageMirrors := []config.RunImage{ 618 { 619 Image: "un-used-run-image", 620 Mirrors: []string{"un-used"}, 621 }, 622 { 623 Image: "some-local-run-image", 624 Mirrors: []string{"user-configured-mirror-for-local"}, 625 }, 626 { 627 Image: "some-remote-run-image", 628 Mirrors: []string{"user-configured-mirror-for-remote"}, 629 }, 630 } 631 sharedImageInfo := inspectimage.GeneralInfo{ 632 Name: "test-image", 633 RunImageMirrors: runImageMirrors, 634 } 635 jsonWriter := writer.NewJSON() 636 637 logger := logging.NewLogWithWriters(&outBuf, &outBuf) 638 err := jsonWriter.Print(logger, sharedImageInfo, nil, remoteInfo, nil, nil) 639 assert.Nil(err) 640 641 assert.ContainsJSON(outBuf.String(), `{ "image_name": "test-image" }`) 642 assert.NotContains(outBuf.String(), "test.stack.id.local") 643 assert.ContainsJSON(outBuf.String(), expectedRemoteOutput) 644 }) 645 }) 646 }) 647 }