github.com/orange-cloudfoundry/cli@v7.1.0+incompatible/command/v6/shared/app_summary_displayer_refactored_test.go (about)

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