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  })