github.com/DaAlbrecht/cf-cli@v0.0.0-20231128151943-1fe19bb400b9/command/v7/shared/app_summary_displayer_test.go (about) 1 package shared_test 2 3 import ( 4 "time" 5 6 "code.cloudfoundry.org/cli/actor/v7action" 7 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" 8 . "code.cloudfoundry.org/cli/command/v7/shared" 9 "code.cloudfoundry.org/cli/integration/helpers" 10 "code.cloudfoundry.org/cli/resources" 11 "code.cloudfoundry.org/cli/types" 12 "code.cloudfoundry.org/cli/util/ui" 13 . "github.com/onsi/ginkgo" 14 . "github.com/onsi/gomega" 15 . "github.com/onsi/gomega/gbytes" 16 ) 17 18 var _ = Describe("app summary displayer", func() { 19 20 const instanceStatsTitles = `state\s+since\s+cpu\s+memory\s+disk\s+logging\s+details` 21 22 var ( 23 appSummaryDisplayer *AppSummaryDisplayer 24 output *Buffer 25 testUI *ui.UI 26 ) 27 28 BeforeEach(func() { 29 output = NewBuffer() 30 testUI = ui.NewTestUI(nil, output, NewBuffer()) 31 32 appSummaryDisplayer = NewAppSummaryDisplayer(testUI) 33 }) 34 35 Describe("AppDisplay", func() { 36 var ( 37 summary v7action.DetailedApplicationSummary 38 displayStartCommand bool 39 ) 40 41 JustBeforeEach(func() { 42 appSummaryDisplayer.AppDisplay(summary, displayStartCommand) 43 }) 44 45 When("the app has instances", func() { 46 When("the process instances are running", func() { 47 var uptime time.Duration 48 49 BeforeEach(func() { 50 uptime = time.Since(time.Unix(267321600, 0)) 51 summary = v7action.DetailedApplicationSummary{ 52 ApplicationSummary: v7action.ApplicationSummary{ 53 Application: resources.Application{ 54 GUID: "some-app-guid", 55 State: constant.ApplicationStarted, 56 }, 57 ProcessSummaries: v7action.ProcessSummaries{ 58 { 59 Process: resources.Process{ 60 Type: constant.ProcessTypeWeb, 61 MemoryInMB: types.NullUint64{Value: 32, IsSet: true}, 62 DiskInMB: types.NullUint64{Value: 1024, IsSet: true}, 63 LogRateLimitInBPS: types.NullInt{Value: 1024 * 5, IsSet: true}, 64 }, 65 Sidecars: []resources.Sidecar{}, 66 InstanceDetails: []v7action.ProcessInstance{ 67 v7action.ProcessInstance{ 68 Index: 0, 69 State: constant.ProcessInstanceRunning, 70 MemoryUsage: 1000000, 71 DiskUsage: 1000000, 72 LogRate: 1024, 73 MemoryQuota: 33554432, 74 DiskQuota: 2000000, 75 LogRateLimit: 1024 * 5, 76 Uptime: uptime, 77 Details: "Some Details 1", 78 }, 79 v7action.ProcessInstance{ 80 Index: 1, 81 State: constant.ProcessInstanceRunning, 82 MemoryUsage: 2000000, 83 DiskUsage: 2000000, 84 LogRate: 1024 * 2, 85 MemoryQuota: 33554432, 86 DiskQuota: 4000000, 87 LogRateLimit: 1024 * 5, 88 Uptime: time.Since(time.Unix(330480000, 0)), 89 Details: "Some Details 2", 90 }, 91 v7action.ProcessInstance{ 92 Index: 2, 93 State: constant.ProcessInstanceRunning, 94 MemoryUsage: 3000000, 95 DiskUsage: 3000000, 96 LogRate: 1024 * 3, 97 MemoryQuota: 33554432, 98 DiskQuota: 6000000, 99 LogRateLimit: 1024 * 5, 100 Uptime: time.Since(time.Unix(1277164800, 0)), 101 }, 102 }, 103 }, 104 { 105 Process: resources.Process{ 106 Type: "console", 107 MemoryInMB: types.NullUint64{Value: 16, IsSet: true}, 108 DiskInMB: types.NullUint64{Value: 512, IsSet: true}, 109 LogRateLimitInBPS: types.NullInt{Value: 256, IsSet: true}, 110 }, 111 Sidecars: []resources.Sidecar{}, 112 InstanceDetails: []v7action.ProcessInstance{ 113 v7action.ProcessInstance{ 114 Index: 0, 115 State: constant.ProcessInstanceRunning, 116 MemoryUsage: 1000000, 117 DiskUsage: 1000000, 118 LogRate: 128, 119 MemoryQuota: 33554432, 120 DiskQuota: 8000000, 121 LogRateLimit: 256, 122 Uptime: time.Since(time.Unix(167572800, 0)), 123 }, 124 }, 125 }, 126 }, 127 }, 128 } 129 }) 130 131 It("lists information for each of the processes", func() { 132 processTable := helpers.ParseV3AppProcessTable(output.Contents()) 133 Expect(len(processTable.Processes)).To(Equal(2)) 134 135 webProcessSummary := processTable.Processes[0] 136 Expect(webProcessSummary.Type).To(Equal("web")) 137 Expect(webProcessSummary.Sidecars).To(Equal("")) 138 Expect(webProcessSummary.InstanceCount).To(Equal("3/3")) 139 Expect(webProcessSummary.MemUsage).To(Equal("32M")) 140 141 Expect(webProcessSummary.Instances[0].Memory).To(Equal("976.6K of 32M")) 142 Expect(webProcessSummary.Instances[0].Since).To(MatchRegexp(`\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z`)) 143 Expect(time.Parse(time.RFC3339, webProcessSummary.Instances[0].Since)).To(BeTemporally("~", time.Now().Add(-uptime), 2*time.Second)) 144 Expect(webProcessSummary.Instances[0].Disk).To(Equal("976.6K of 1.9M")) 145 Expect(webProcessSummary.Instances[0].CPU).To(Equal("0.0%")) 146 Expect(webProcessSummary.Instances[0].LogRate).To(Equal("1K/s of 5K/s")) 147 Expect(webProcessSummary.Instances[0].Details).To(Equal("Some Details 1")) 148 149 Expect(webProcessSummary.Instances[1].Memory).To(Equal("1.9M of 32M")) 150 Expect(webProcessSummary.Instances[1].Disk).To(Equal("1.9M of 3.8M")) 151 Expect(webProcessSummary.Instances[1].CPU).To(Equal("0.0%")) 152 Expect(webProcessSummary.Instances[1].LogRate).To(Equal("2K/s of 5K/s")) 153 Expect(webProcessSummary.Instances[1].Details).To(Equal("Some Details 2")) 154 155 Expect(webProcessSummary.Instances[2].Memory).To(Equal("2.9M of 32M")) 156 Expect(webProcessSummary.Instances[2].Disk).To(Equal("2.9M of 5.7M")) 157 Expect(webProcessSummary.Instances[2].CPU).To(Equal("0.0%")) 158 Expect(webProcessSummary.Instances[2].LogRate).To(Equal("3K/s of 5K/s")) 159 160 consoleProcessSummary := processTable.Processes[1] 161 Expect(consoleProcessSummary.Type).To(Equal("console")) 162 Expect(consoleProcessSummary.Sidecars).To(Equal("")) 163 Expect(consoleProcessSummary.InstanceCount).To(Equal("1/1")) 164 Expect(consoleProcessSummary.MemUsage).To(Equal("16M")) 165 166 Expect(consoleProcessSummary.Instances[0].Memory).To(Equal("976.6K of 32M")) 167 Expect(consoleProcessSummary.Instances[0].Disk).To(Equal("976.6K of 7.6M")) 168 Expect(consoleProcessSummary.Instances[0].CPU).To(Equal("0.0%")) 169 Expect(consoleProcessSummary.Instances[0].LogRate).To(Equal("128B/s of 256B/s")) 170 }) 171 }) 172 173 When("the log rate is unlimited", func() { 174 BeforeEach(func() { 175 summary = v7action.DetailedApplicationSummary{ 176 ApplicationSummary: v7action.ApplicationSummary{ 177 Application: resources.Application{ 178 GUID: "some-app-guid", 179 State: constant.ApplicationStarted, 180 }, 181 ProcessSummaries: v7action.ProcessSummaries{ 182 { 183 Process: resources.Process{ 184 Type: constant.ProcessTypeWeb, 185 MemoryInMB: types.NullUint64{Value: 32, IsSet: true}, 186 DiskInMB: types.NullUint64{Value: 1024, IsSet: true}, 187 }, 188 Sidecars: []resources.Sidecar{}, 189 InstanceDetails: []v7action.ProcessInstance{ 190 v7action.ProcessInstance{ 191 Index: 0, 192 State: constant.ProcessInstanceRunning, 193 LogRate: 1024, 194 LogRateLimit: -1, 195 }, 196 }, 197 }, 198 }, 199 }, 200 } 201 }) 202 203 It("renders unlimited log rate limits correctly", func() { 204 processTable := helpers.ParseV3AppProcessTable(output.Contents()) 205 webProcessSummary := processTable.Processes[0] 206 207 Expect(webProcessSummary.Instances[0].LogRate).To(Equal("1K/s of unlimited")) 208 }) 209 }) 210 211 When("some processes have > 0 instances and others have 0 instances", func() { 212 BeforeEach(func() { 213 summary = v7action.DetailedApplicationSummary{ 214 ApplicationSummary: v7action.ApplicationSummary{ 215 Application: resources.Application{ 216 GUID: "some-app-guid", 217 State: constant.ApplicationStarted, 218 }, 219 ProcessSummaries: v7action.ProcessSummaries{ 220 { 221 Process: resources.Process{ 222 Type: constant.ProcessTypeWeb, 223 MemoryInMB: types.NullUint64{Value: 32, IsSet: true}, 224 DiskInMB: types.NullUint64{Value: 1024, IsSet: true}, 225 }, 226 Sidecars: []resources.Sidecar{}, 227 InstanceDetails: []v7action.ProcessInstance{ 228 v7action.ProcessInstance{ 229 Index: 0, 230 State: constant.ProcessInstanceRunning, 231 MemoryUsage: 1000000, 232 DiskUsage: 1000000, 233 MemoryQuota: 33554432, 234 DiskQuota: 2000000, 235 Uptime: time.Since(time.Unix(267321600, 0)), 236 }, 237 }, 238 }, 239 { 240 Process: resources.Process{ 241 Type: "console", 242 MemoryInMB: types.NullUint64{Value: 16, IsSet: true}, 243 DiskInMB: types.NullUint64{Value: 512, IsSet: true}, 244 }, 245 Sidecars: []resources.Sidecar{}, 246 }, 247 }, 248 }, 249 } 250 }) 251 252 It("lists instance stats for process types that have > 0 instances", func() { 253 Expect(testUI.Out).To(Say(`type:\s+web`)) 254 Expect(testUI.Out).To(Say(`sidecars: `)) 255 Expect(testUI.Out).To(Say(instanceStatsTitles)) 256 }) 257 258 It("does not show the instance stats table for process types with 0 instances", func() { 259 Expect(testUI.Out).To(Say(`type:\s+console`)) 260 Expect(testUI.Out).To(Say(`sidecars: `)) 261 Expect(testUI.Out).To(Say("There are no running instances of this process.")) 262 }) 263 }) 264 265 When("all the instances for a process are down (but scaled to > 0 instances)", func() { 266 BeforeEach(func() { 267 summary = v7action.DetailedApplicationSummary{ 268 ApplicationSummary: v7action.ApplicationSummary{ 269 ProcessSummaries: []v7action.ProcessSummary{ 270 { 271 Process: resources.Process{ 272 Type: constant.ProcessTypeWeb, 273 MemoryInMB: types.NullUint64{Value: 32, IsSet: true}, 274 }, 275 Sidecars: []resources.Sidecar{}, 276 InstanceDetails: []v7action.ProcessInstance{{State: constant.ProcessInstanceDown}}, 277 }}, 278 }, 279 } 280 }) 281 282 It("displays the instances table", func() { 283 Expect(testUI.Out).To(Say(`type:\s+web`)) 284 Expect(testUI.Out).To(Say(`sidecars: `)) 285 Expect(testUI.Out).To(Say(instanceStatsTitles)) 286 }) 287 }) 288 289 Describe("start command", func() { 290 BeforeEach(func() { 291 summary = v7action.DetailedApplicationSummary{ 292 ApplicationSummary: v7action.ApplicationSummary{ 293 Application: resources.Application{ 294 GUID: "some-app-guid", 295 State: constant.ApplicationStarted, 296 }, 297 ProcessSummaries: v7action.ProcessSummaries{ 298 { 299 Process: resources.Process{ 300 Type: constant.ProcessTypeWeb, 301 Command: *types.NewFilteredString("some-command-1"), 302 }, 303 Sidecars: []resources.Sidecar{}, 304 }, 305 { 306 Process: resources.Process{ 307 Type: "console", 308 Command: *types.NewFilteredString("some-command-2"), 309 }, 310 Sidecars: []resources.Sidecar{}, 311 }, 312 { 313 Process: resources.Process{ 314 Type: "random", 315 }, 316 Sidecars: []resources.Sidecar{}, 317 }, 318 }, 319 }, 320 } 321 }) 322 323 When("displayStartCommand is true", func() { 324 BeforeEach(func() { 325 displayStartCommand = true 326 }) 327 328 It("displays the non-empty start command for each process", func() { 329 Expect(testUI.Out).To(Say(`type:\s+web`)) 330 Expect(testUI.Out).To(Say(`sidecars: `)) 331 Expect(testUI.Out).To(Say(`start command:\s+some-command-1`)) 332 333 Expect(testUI.Out).To(Say(`type:\s+console`)) 334 Expect(testUI.Out).To(Say(`sidecars: `)) 335 Expect(testUI.Out).To(Say(`start command:\s+some-command-2`)) 336 337 Expect(testUI.Out).To(Say(`type:\s+random`)) 338 Expect(testUI.Out).To(Say(`sidecars: `)) 339 Expect(testUI.Out).ToNot(Say("start command:")) 340 }) 341 }) 342 343 When("displayStartCommand is false", func() { 344 BeforeEach(func() { 345 displayStartCommand = false 346 }) 347 348 It("hides the start command", func() { 349 Expect(testUI.Out).ToNot(Say("start command:")) 350 }) 351 }) 352 }) 353 }) 354 355 When("the app has no instances", func() { 356 BeforeEach(func() { 357 summary = v7action.DetailedApplicationSummary{ 358 ApplicationSummary: v7action.ApplicationSummary{ 359 Application: resources.Application{ 360 GUID: "some-app-guid", 361 State: constant.ApplicationStarted, 362 }, 363 ProcessSummaries: v7action.ProcessSummaries{ 364 { 365 Process: resources.Process{ 366 Type: constant.ProcessTypeWeb, 367 MemoryInMB: types.NullUint64{Value: 32, IsSet: true}, 368 DiskInMB: types.NullUint64{Value: 1024, IsSet: true}, 369 }, 370 Sidecars: []resources.Sidecar{}, 371 }, 372 { 373 Process: resources.Process{ 374 Type: "console", 375 MemoryInMB: types.NullUint64{Value: 16, IsSet: true}, 376 DiskInMB: types.NullUint64{Value: 512, IsSet: true}, 377 }, 378 Sidecars: []resources.Sidecar{}, 379 }, 380 }, 381 }, 382 } 383 }) 384 385 It("lists information for each of the processes", func() { 386 Expect(testUI.Out).To(Say(`type:\s+web`)) 387 Expect(testUI.Out).To(Say(`sidecars: `)) 388 Expect(testUI.Out).To(Say(`instances:\s+0/0`)) 389 Expect(testUI.Out).To(Say(`memory usage:\s+32M`)) 390 Expect(testUI.Out).To(Say("There are no running instances of this process.")) 391 392 Expect(testUI.Out).To(Say(`type:\s+console`)) 393 Expect(testUI.Out).To(Say(`sidecars: `)) 394 Expect(testUI.Out).To(Say(`instances:\s+0/0`)) 395 Expect(testUI.Out).To(Say(`memory usage:\s+16M`)) 396 Expect(testUI.Out).To(Say("There are no running instances of this process.")) 397 }) 398 399 It("does not display the instance table", func() { 400 Expect(testUI.Out).NotTo(Say(instanceStatsTitles)) 401 }) 402 }) 403 404 When("the app has sidecars", func() { 405 BeforeEach(func() { 406 summary = v7action.DetailedApplicationSummary{ 407 ApplicationSummary: v7action.ApplicationSummary{ 408 Application: resources.Application{ 409 GUID: "some-app-guid", 410 State: constant.ApplicationStarted, 411 }, 412 ProcessSummaries: v7action.ProcessSummaries{ 413 { 414 Process: resources.Process{ 415 Type: constant.ProcessTypeWeb, 416 MemoryInMB: types.NullUint64{Value: 32, IsSet: true}, 417 DiskInMB: types.NullUint64{Value: 1024, IsSet: true}, 418 }, 419 Sidecars: []resources.Sidecar{ 420 {Name: "authenticator"}, 421 {Name: "clock"}, 422 }, 423 }, 424 }, 425 }, 426 } 427 }) 428 429 It("lists information for each of the processes", func() { 430 Expect(testUI.Out).To(Say(`type:\s+web`)) 431 Expect(testUI.Out).To(Say(`sidecars:\s+authenticator, clock`)) 432 Expect(testUI.Out).To(Say(`instances:\s+0/0`)) 433 Expect(testUI.Out).To(Say(`memory usage:\s+32M`)) 434 Expect(testUI.Out).To(Say("There are no running instances of this process.")) 435 }) 436 437 It("does not display the instance table", func() { 438 Expect(testUI.Out).NotTo(Say(instanceStatsTitles)) 439 }) 440 }) 441 442 When("the app is stopped", func() { 443 BeforeEach(func() { 444 summary = v7action.DetailedApplicationSummary{ 445 ApplicationSummary: v7action.ApplicationSummary{ 446 Application: resources.Application{ 447 GUID: "some-app-guid", 448 State: constant.ApplicationStopped, 449 }, 450 ProcessSummaries: v7action.ProcessSummaries{ 451 { 452 Process: resources.Process{ 453 Type: constant.ProcessTypeWeb, 454 }, 455 Sidecars: []resources.Sidecar{}, 456 }, 457 { 458 Process: resources.Process{ 459 Type: "console", 460 }, 461 Sidecars: []resources.Sidecar{}, 462 }, 463 }, 464 }, 465 } 466 }) 467 468 It("lists information for each of the processes", func() { 469 Expect(testUI.Out).To(Say(`type:\s+web`)) 470 Expect(testUI.Out).To(Say(`sidecars: `)) 471 Expect(testUI.Out).To(Say("There are no running instances of this process.")) 472 473 Expect(testUI.Out).To(Say(`type:\s+console`)) 474 Expect(testUI.Out).To(Say(`sidecars: `)) 475 Expect(testUI.Out).To(Say("There are no running instances of this process.")) 476 }) 477 478 It("does not display the instance table", func() { 479 Expect(testUI.Out).NotTo(Say(instanceStatsTitles)) 480 }) 481 }) 482 483 Describe("isolation segments", func() { 484 When("the isolation segment name is provided", func() { 485 var isolationSegmentName string 486 BeforeEach(func() { 487 isolationSegmentName = "potato beans" 488 summary.ProcessSummaries = v7action.ProcessSummaries{ 489 v7action.ProcessSummary{ 490 InstanceDetails: []v7action.ProcessInstance{ 491 {IsolationSegment: isolationSegmentName}, 492 }, 493 }, 494 } 495 }) 496 497 It("should output the isolation segment name", func() { 498 Expect(testUI.Out).To(Say(`isolation segment:\s+%s`, isolationSegmentName)) 499 }) 500 }) 501 502 When("the application summary has no isolation segment information", func() { 503 BeforeEach(func() { 504 summary = v7action.DetailedApplicationSummary{ 505 ApplicationSummary: v7action.ApplicationSummary{ 506 Application: resources.Application{ 507 GUID: "some-app-guid", 508 State: constant.ApplicationStopped, 509 }, 510 }, 511 } 512 }) 513 514 It("should not output isolation segment header", func() { 515 Expect(testUI.Out).ToNot(Say("isolation segment:")) 516 }) 517 }) 518 }) 519 520 Describe("last upload time", func() { 521 When("the application has a last uploaded time", func() { 522 var createdTime string 523 524 BeforeEach(func() { 525 createdTime = "2006-01-02T15:04:05-07:00" 526 summary.CurrentDroplet.CreatedAt = createdTime 527 }) 528 529 It("displays the uploaded time", func() { 530 t, err := time.Parse(time.RFC3339, createdTime) 531 Expect(err).To(Not(HaveOccurred())) 532 533 time := t.Local().Format("Mon 02 Jan 15:04:05 MST 2006") 534 Expect(testUI.Out).To(Say(`last uploaded:\s+%s`, time)) 535 }) 536 }) 537 538 When("the application does not have a last uploaded time", func() { 539 BeforeEach(func() { 540 summary.CurrentDroplet.CreatedAt = "" 541 }) 542 543 It("leaves last uploaded blank", func() { 544 Expect(testUI.Out).To(Say(`(?m)last uploaded:\s*\n`)) 545 }) 546 }) 547 }) 548 549 When("the application has routes", func() { 550 BeforeEach(func() { 551 summary.Routes = []resources.Route{ 552 {Host: "route1", URL: "route1.example.com"}, 553 {Host: "route2", URL: "route2.example.com"}, 554 } 555 }) 556 557 It("displays routes", func() { 558 Expect(testUI.Out).To(Say(`routes:\s+%s, %s`, "route1.example.com", "route2.example.com")) 559 }) 560 }) 561 562 When("the application has a stack", func() { 563 BeforeEach(func() { 564 summary.CurrentDroplet.Stack = "some-stack" 565 }) 566 567 It("displays stack", func() { 568 Expect(testUI.Out).To(Say(`stack:\s+some-stack`)) 569 }) 570 }) 571 572 When("the application is a docker app", func() { 573 BeforeEach(func() { 574 summary = v7action.DetailedApplicationSummary{ 575 ApplicationSummary: v7action.ApplicationSummary{ 576 Application: resources.Application{ 577 GUID: "some-guid", 578 Name: "some-app", 579 State: constant.ApplicationStarted, 580 LifecycleType: constant.AppLifecycleTypeDocker, 581 }, 582 }, 583 CurrentDroplet: resources.Droplet{ 584 Image: "docker/some-image", 585 }, 586 } 587 }) 588 589 It("displays the app information", func() { 590 Expect(testUI.Out).To(Say(`name:\s+some-app`)) 591 Expect(testUI.Out).To(Say(`requested state:\s+started`)) 592 Expect(testUI.Out).To(Say(`routes:\s+\n`)) 593 Expect(testUI.Out).To(Say(`stack:\s+\n`)) 594 Expect(testUI.Out).To(Say(`(?m)docker image:\s+docker/some-image$\n`)) 595 }) 596 597 It("does not display the buildpack info for docker apps", func() { 598 Expect(testUI.Out).ToNot(Say("buildpacks:")) 599 }) 600 }) 601 602 When("the application is a buildpack app", func() { 603 BeforeEach(func() { 604 summary = v7action.DetailedApplicationSummary{ 605 ApplicationSummary: v7action.ApplicationSummary{ 606 Application: resources.Application{ 607 LifecycleType: constant.AppLifecycleTypeBuildpack, 608 }, 609 }, 610 CurrentDroplet: resources.Droplet{ 611 Stack: "cflinuxfs2", 612 Buildpacks: []resources.DropletBuildpack{ 613 { 614 Name: "ruby_buildpack", 615 BuildpackName: "ruby_buildpack_name", 616 DetectOutput: "some-detect-output", 617 Version: "0.0.1", 618 }, 619 { 620 Name: "go_buildpack_without_detect_output", 621 BuildpackName: "go_buildpack_name", 622 DetectOutput: "", 623 Version: "0.0.2", 624 }, 625 { 626 Name: "go_buildpack_without_version", 627 BuildpackName: "go_buildpack_name", 628 DetectOutput: "", 629 Version: "", 630 }, 631 { 632 Name: "some-buildpack", 633 DetectOutput: "", 634 }, 635 }, 636 }, 637 } 638 }) 639 640 It("displays stack and buildpacks", func() { 641 Expect(testUI.Out).To(Say(`stack:\s+cflinuxfs2\n`)) 642 Expect(testUI.Out).To(Say(`buildpacks:\s+\n`)) 643 Expect(testUI.Out).To(Say(`name\s+version\s+detect output\s+buildpack name\n`)) 644 Expect(testUI.Out).To(Say(`ruby_buildpack\s+0.0.1\s+some-detect-output\s+ruby_buildpack_name\n`)) 645 Expect(testUI.Out).To(Say(`some-buildpack`)) 646 }) 647 }) 648 }) 649 })