github.com/jenspinney/cli@v6.42.1-0.20190207184520-7450c600020e+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/v2action"
     7  	"code.cloudfoundry.org/cli/actor/v7action"
     8  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
     9  	. "code.cloudfoundry.org/cli/command/v7/shared"
    10  	"code.cloudfoundry.org/cli/integration/helpers"
    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.ApplicationSummary
    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.Now().Sub(time.Unix(267321600, 0))
    48  					summary = v7action.ApplicationSummary{
    49  						Application: v7action.Application{
    50  							GUID:  "some-app-guid",
    51  							State: constant.ApplicationStarted,
    52  						},
    53  						ProcessSummaries: v7action.ProcessSummaries{
    54  							{
    55  								Process: v7action.Process{
    56  									Type:       constant.ProcessTypeWeb,
    57  									MemoryInMB: types.NullUint64{Value: 32, IsSet: true},
    58  									DiskInMB:   types.NullUint64{Value: 1024, IsSet: true},
    59  								},
    60  								InstanceDetails: []v7action.ProcessInstance{
    61  									v7action.ProcessInstance{
    62  										Index:       0,
    63  										State:       constant.ProcessInstanceRunning,
    64  										MemoryUsage: 1000000,
    65  										DiskUsage:   1000000,
    66  										MemoryQuota: 33554432,
    67  										DiskQuota:   2000000,
    68  										Uptime:      uptime,
    69  										Details:     "Some Details 1",
    70  									},
    71  									v7action.ProcessInstance{
    72  										Index:       1,
    73  										State:       constant.ProcessInstanceRunning,
    74  										MemoryUsage: 2000000,
    75  										DiskUsage:   2000000,
    76  										MemoryQuota: 33554432,
    77  										DiskQuota:   4000000,
    78  										Uptime:      time.Now().Sub(time.Unix(330480000, 0)),
    79  										Details:     "Some Details 2",
    80  									},
    81  									v7action.ProcessInstance{
    82  										Index:       2,
    83  										State:       constant.ProcessInstanceRunning,
    84  										MemoryUsage: 3000000,
    85  										DiskUsage:   3000000,
    86  										MemoryQuota: 33554432,
    87  										DiskQuota:   6000000,
    88  										Uptime:      time.Now().Sub(time.Unix(1277164800, 0)),
    89  									},
    90  								},
    91  							},
    92  							{
    93  								Process: v7action.Process{
    94  									Type:       "console",
    95  									MemoryInMB: types.NullUint64{Value: 16, IsSet: true},
    96  									DiskInMB:   types.NullUint64{Value: 512, IsSet: true},
    97  								},
    98  								InstanceDetails: []v7action.ProcessInstance{
    99  									v7action.ProcessInstance{
   100  										Index:       0,
   101  										State:       constant.ProcessInstanceRunning,
   102  										MemoryUsage: 1000000,
   103  										DiskUsage:   1000000,
   104  										MemoryQuota: 33554432,
   105  										DiskQuota:   8000000,
   106  										Uptime:      time.Now().Sub(time.Unix(167572800, 0)),
   107  									},
   108  								},
   109  							},
   110  						},
   111  					}
   112  				})
   113  
   114  				It("lists information for each of the processes", func() {
   115  					processTable := helpers.ParseV3AppProcessTable(output.Contents())
   116  					Expect(len(processTable.Processes)).To(Equal(2))
   117  
   118  					webProcessSummary := processTable.Processes[0]
   119  					Expect(webProcessSummary.Type).To(Equal("web"))
   120  					Expect(webProcessSummary.InstanceCount).To(Equal("3/3"))
   121  					Expect(webProcessSummary.MemUsage).To(Equal("32M"))
   122  
   123  					Expect(webProcessSummary.Instances[0].Memory).To(Equal("976.6K of 32M"))
   124  					Expect(webProcessSummary.Instances[0].Since).To(MatchRegexp(`\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z`))
   125  					Expect(time.Parse(time.RFC3339, webProcessSummary.Instances[0].Since)).To(BeTemporally("~", time.Now().Add(-uptime), 2*time.Second))
   126  					Expect(webProcessSummary.Instances[0].Disk).To(Equal("976.6K of 1.9M"))
   127  					Expect(webProcessSummary.Instances[0].CPU).To(Equal("0.0%"))
   128  					Expect(webProcessSummary.Instances[0].Details).To(Equal("Some Details 1"))
   129  
   130  					Expect(webProcessSummary.Instances[1].Memory).To(Equal("1.9M of 32M"))
   131  					Expect(webProcessSummary.Instances[1].Disk).To(Equal("1.9M of 3.8M"))
   132  					Expect(webProcessSummary.Instances[1].CPU).To(Equal("0.0%"))
   133  					Expect(webProcessSummary.Instances[1].Details).To(Equal("Some Details 2"))
   134  
   135  					Expect(webProcessSummary.Instances[2].Memory).To(Equal("2.9M of 32M"))
   136  					Expect(webProcessSummary.Instances[2].Disk).To(Equal("2.9M of 5.7M"))
   137  					Expect(webProcessSummary.Instances[2].CPU).To(Equal("0.0%"))
   138  
   139  					consoleProcessSummary := processTable.Processes[1]
   140  					Expect(consoleProcessSummary.Type).To(Equal("console"))
   141  					Expect(consoleProcessSummary.InstanceCount).To(Equal("1/1"))
   142  					Expect(consoleProcessSummary.MemUsage).To(Equal("16M"))
   143  
   144  					Expect(consoleProcessSummary.Instances[0].Memory).To(Equal("976.6K of 32M"))
   145  					Expect(consoleProcessSummary.Instances[0].Disk).To(Equal("976.6K of 7.6M"))
   146  					Expect(consoleProcessSummary.Instances[0].CPU).To(Equal("0.0%"))
   147  				})
   148  			})
   149  
   150  			When("some processes have > 0 instances and others have 0 instances", func() {
   151  				BeforeEach(func() {
   152  					summary = v7action.ApplicationSummary{
   153  						Application: v7action.Application{
   154  							GUID:  "some-app-guid",
   155  							State: constant.ApplicationStarted,
   156  						},
   157  						ProcessSummaries: v7action.ProcessSummaries{
   158  							{
   159  								Process: v7action.Process{
   160  									Type:       constant.ProcessTypeWeb,
   161  									MemoryInMB: types.NullUint64{Value: 32, IsSet: true},
   162  									DiskInMB:   types.NullUint64{Value: 1024, IsSet: true},
   163  								},
   164  								InstanceDetails: []v7action.ProcessInstance{
   165  									v7action.ProcessInstance{
   166  										Index:       0,
   167  										State:       constant.ProcessInstanceRunning,
   168  										MemoryUsage: 1000000,
   169  										DiskUsage:   1000000,
   170  										MemoryQuota: 33554432,
   171  										DiskQuota:   2000000,
   172  										Uptime:      time.Now().Sub(time.Unix(267321600, 0)),
   173  									},
   174  								},
   175  							},
   176  							{
   177  								Process: v7action.Process{
   178  									Type:       "console",
   179  									MemoryInMB: types.NullUint64{Value: 16, IsSet: true},
   180  									DiskInMB:   types.NullUint64{Value: 512, IsSet: true},
   181  								},
   182  							},
   183  						},
   184  					}
   185  				})
   186  
   187  				It("lists instance stats for process types that have > 0 instances", func() {
   188  					Expect(testUI.Out).To(Say(`type:\s+web`))
   189  					Expect(testUI.Out).To(Say(`state\s+since\s+cpu\s+memory\s+disk\s+details`))
   190  				})
   191  
   192  				It("does not show the instance stats table for process types with 0 instances", func() {
   193  					Expect(testUI.Out).To(Say(`type:\s+console`))
   194  					Expect(testUI.Out).To(Say("There are no running instances of this process."))
   195  				})
   196  			})
   197  
   198  			When("all the instances for a process are down (but scaled to > 0 instances)", func() {
   199  				BeforeEach(func() {
   200  					summary = v7action.ApplicationSummary{
   201  
   202  						ProcessSummaries: []v7action.ProcessSummary{
   203  							{
   204  								Process: v7action.Process{
   205  									Type:       constant.ProcessTypeWeb,
   206  									MemoryInMB: types.NullUint64{Value: 32, IsSet: true},
   207  								},
   208  								InstanceDetails: []v7action.ProcessInstance{{State: constant.ProcessInstanceDown}},
   209  							},
   210  						},
   211  					}
   212  				})
   213  
   214  				It("displays the instances table", func() {
   215  					Expect(testUI.Out).To(Say(`type:\s+web`))
   216  					Expect(testUI.Out).To(Say(`state\s+since\s+cpu\s+memory\s+disk\s+details`))
   217  				})
   218  			})
   219  
   220  			Describe("start command", func() {
   221  				BeforeEach(func() {
   222  					summary = v7action.ApplicationSummary{
   223  						Application: v7action.Application{
   224  							GUID:  "some-app-guid",
   225  							State: constant.ApplicationStarted,
   226  						},
   227  						ProcessSummaries: v7action.ProcessSummaries{
   228  							{
   229  								Process: v7action.Process{
   230  									Type:    constant.ProcessTypeWeb,
   231  									Command: *types.NewFilteredString("some-command-1"),
   232  								},
   233  							},
   234  							{
   235  								Process: v7action.Process{
   236  									Type:    "console",
   237  									Command: *types.NewFilteredString("some-command-2"),
   238  								},
   239  							},
   240  							{
   241  								Process: v7action.Process{
   242  									Type: "random",
   243  								},
   244  							},
   245  						},
   246  					}
   247  				})
   248  
   249  				When("displayStartCommand is true", func() {
   250  					BeforeEach(func() {
   251  						displayStartCommand = true
   252  					})
   253  
   254  					It("displays the non-empty start command for each process", func() {
   255  						Expect(testUI.Out).To(Say(`type:\s+web`))
   256  						Expect(testUI.Out).To(Say(`start command:\s+some-command-1`))
   257  
   258  						Expect(testUI.Out).To(Say(`type:\s+console`))
   259  						Expect(testUI.Out).To(Say(`start command:\s+some-command-2`))
   260  
   261  						Expect(testUI.Out).To(Say(`type:\s+random`))
   262  						Expect(testUI.Out).ToNot(Say("start command:"))
   263  					})
   264  				})
   265  
   266  				When("displayStartCommand is false", func() {
   267  					BeforeEach(func() {
   268  						displayStartCommand = false
   269  					})
   270  
   271  					It("hides the start command", func() {
   272  						Expect(testUI.Out).ToNot(Say("start command:"))
   273  					})
   274  				})
   275  			})
   276  		})
   277  
   278  		When("the app has no instances", func() {
   279  			BeforeEach(func() {
   280  				summary = v7action.ApplicationSummary{
   281  					Application: v7action.Application{
   282  						GUID:  "some-app-guid",
   283  						State: constant.ApplicationStarted,
   284  					},
   285  					ProcessSummaries: v7action.ProcessSummaries{
   286  						{
   287  							Process: v7action.Process{
   288  								Type:       constant.ProcessTypeWeb,
   289  								MemoryInMB: types.NullUint64{Value: 32, IsSet: true},
   290  								DiskInMB:   types.NullUint64{Value: 1024, IsSet: true},
   291  							},
   292  						},
   293  						{
   294  							Process: v7action.Process{
   295  								Type:       "console",
   296  								MemoryInMB: types.NullUint64{Value: 16, IsSet: true},
   297  								DiskInMB:   types.NullUint64{Value: 512, IsSet: true},
   298  							},
   299  						},
   300  					},
   301  				}
   302  			})
   303  
   304  			It("lists information for each of the processes", func() {
   305  				Expect(testUI.Out).To(Say(`type:\s+web`))
   306  				Expect(testUI.Out).To(Say(`instances:\s+0/0`))
   307  				Expect(testUI.Out).To(Say(`memory usage:\s+32M`))
   308  				Expect(testUI.Out).To(Say("There are no running instances of this process."))
   309  
   310  				Expect(testUI.Out).To(Say(`type:\s+console`))
   311  				Expect(testUI.Out).To(Say(`instances:\s+0/0`))
   312  				Expect(testUI.Out).To(Say(`memory usage:\s+16M`))
   313  				Expect(testUI.Out).To(Say("There are no running instances of this process."))
   314  			})
   315  
   316  			It("does not display the instance table", func() {
   317  				Expect(testUI.Out).NotTo(Say(`state\s+since\s+cpu\s+memory\s+disk\s+details`))
   318  			})
   319  		})
   320  
   321  		When("the app is stopped", func() {
   322  			BeforeEach(func() {
   323  				summary = v7action.ApplicationSummary{
   324  					Application: v7action.Application{
   325  						GUID:  "some-app-guid",
   326  						State: constant.ApplicationStopped,
   327  					},
   328  					ProcessSummaries: v7action.ProcessSummaries{
   329  						{
   330  							Process: v7action.Process{
   331  								Type: constant.ProcessTypeWeb,
   332  							},
   333  						},
   334  						{
   335  							Process: v7action.Process{
   336  								Type: "console",
   337  							},
   338  						},
   339  					},
   340  				}
   341  			})
   342  
   343  			It("lists information for each of the processes", func() {
   344  				Expect(testUI.Out).To(Say(`type:\s+web`))
   345  				Expect(testUI.Out).To(Say("There are no running instances of this process."))
   346  
   347  				Expect(testUI.Out).To(Say(`type:\s+console`))
   348  				Expect(testUI.Out).To(Say("There are no running instances of this process."))
   349  			})
   350  
   351  			It("does not display the instance table", func() {
   352  				Expect(testUI.Out).NotTo(Say(`state\s+since\s+cpu\s+memory\s+disk\s+details`))
   353  			})
   354  		})
   355  
   356  		Describe("isolation segments", func() {
   357  			When("the isolation segment name is provided", func() {
   358  				var isolationSegmentName string
   359  				BeforeEach(func() {
   360  					isolationSegmentName = "potato beans"
   361  					summary.ProcessSummaries = v7action.ProcessSummaries{
   362  						v7action.ProcessSummary{
   363  							InstanceDetails: []v7action.ProcessInstance{
   364  								{IsolationSegment: isolationSegmentName},
   365  							},
   366  						},
   367  					}
   368  				})
   369  
   370  				It("should output the isolation segment name", func() {
   371  					Expect(testUI.Out).To(Say(`isolation segment:\s+%s`, isolationSegmentName))
   372  				})
   373  			})
   374  
   375  			When("the application summary has no isolation segment information", func() {
   376  				BeforeEach(func() {
   377  					summary = v7action.ApplicationSummary{
   378  						Application: v7action.Application{
   379  							GUID:  "some-app-guid",
   380  							State: constant.ApplicationStopped,
   381  						},
   382  					}
   383  				})
   384  
   385  				It("should not output isolation segment header", func() {
   386  					Expect(testUI.Out).ToNot(Say("isolation segment:"))
   387  				})
   388  			})
   389  		})
   390  
   391  		Describe("last upload time", func() {
   392  			When("the application has a last uploaded time", func() {
   393  				var createdTime string
   394  
   395  				BeforeEach(func() {
   396  					createdTime = "2006-01-02T15:04:05-07:00"
   397  					summary.CurrentDroplet.CreatedAt = createdTime
   398  				})
   399  
   400  				It("displays the uploaded time", func() {
   401  					t, err := time.Parse(time.RFC3339, createdTime)
   402  					Expect(err).To(Not(HaveOccurred()))
   403  
   404  					time := t.Local().Format("Mon 02 Jan 15:04:05 MST 2006")
   405  					Expect(testUI.Out).To(Say(`last uploaded:\s+%s`, time))
   406  				})
   407  			})
   408  
   409  			When("the application does not have a last uploaded time", func() {
   410  				BeforeEach(func() {
   411  					summary.CurrentDroplet.CreatedAt = ""
   412  				})
   413  
   414  				It("leaves last uploaded blank", func() {
   415  					Expect(testUI.Out).To(Say(`(?m)last uploaded:\s*\n`))
   416  				})
   417  			})
   418  		})
   419  
   420  		When("the application has routes", func() {
   421  			BeforeEach(func() {
   422  				summary.Routes = []v2action.Route{
   423  					{Host: "route1", Domain: v2action.Domain{Name: "example.com"}},
   424  					{Host: "route2", Domain: v2action.Domain{Name: "example.com"}},
   425  				}
   426  			})
   427  
   428  			It("displays routes", func() {
   429  				Expect(testUI.Out).To(Say(`routes:\s+%s, %s`, "route1.example.com", "route2.example.com"))
   430  			})
   431  		})
   432  
   433  		When("the application has a stack", func() {
   434  			BeforeEach(func() {
   435  				summary.CurrentDroplet.Stack = "some-stack"
   436  			})
   437  
   438  			It("displays stack", func() {
   439  				Expect(testUI.Out).To(Say(`stack:\s+some-stack`))
   440  			})
   441  		})
   442  
   443  		When("the application is a docker app", func() {
   444  			BeforeEach(func() {
   445  				summary = v7action.ApplicationSummary{
   446  					Application: v7action.Application{
   447  						GUID:          "some-guid",
   448  						Name:          "some-app",
   449  						State:         constant.ApplicationStarted,
   450  						LifecycleType: constant.AppLifecycleTypeDocker,
   451  					},
   452  					CurrentDroplet: v7action.Droplet{
   453  						Image: "docker/some-image",
   454  					},
   455  				}
   456  			})
   457  
   458  			It("displays the app information", func() {
   459  				Expect(testUI.Out).To(Say(`name:\s+some-app`))
   460  				Expect(testUI.Out).To(Say(`requested state:\s+started`))
   461  				Expect(testUI.Out).To(Say(`routes:\s+\n`))
   462  				Expect(testUI.Out).To(Say(`stack:\s+\n`))
   463  				Expect(testUI.Out).To(Say(`(?m)docker image:\s+docker/some-image$\n`))
   464  			})
   465  		})
   466  
   467  		When("the application is a buildpack app", func() {
   468  			BeforeEach(func() {
   469  				summary = v7action.ApplicationSummary{
   470  					CurrentDroplet: v7action.Droplet{
   471  						Stack: "cflinuxfs2",
   472  						Buildpacks: []v7action.DropletBuildpack{
   473  							{
   474  								Name:         "ruby_buildpack",
   475  								DetectOutput: "some-detect-output",
   476  							},
   477  							{
   478  								Name:         "some-buildpack",
   479  								DetectOutput: "",
   480  							},
   481  						},
   482  					},
   483  				}
   484  			})
   485  
   486  			It("displays stack and buildpacks", func() {
   487  				Expect(testUI.Out).To(Say(`stack:\s+cflinuxfs2`))
   488  				Expect(testUI.Out).To(Say(`buildpacks:\s+some-detect-output, some-buildpack`))
   489  			})
   490  		})
   491  	})
   492  })