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