github.com/cloudfoundry-attic/ltc@v0.0.0-20151123212628-098adc7919fc/app_examiner/command_factory/app_examiner_command_factory_test.go (about)

     1  package command_factory_test
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"time"
     8  
     9  	. "github.com/onsi/ginkgo"
    10  	. "github.com/onsi/gomega"
    11  	"github.com/onsi/gomega/gbytes"
    12  
    13  	"github.com/cloudfoundry-incubator/ltc/app_examiner"
    14  	"github.com/cloudfoundry-incubator/ltc/app_examiner/command_factory"
    15  	"github.com/cloudfoundry-incubator/ltc/app_examiner/command_factory/fake_terminal"
    16  	"github.com/cloudfoundry-incubator/ltc/app_examiner/command_factory/graphical/fake_graphical_visualizer"
    17  	"github.com/cloudfoundry-incubator/ltc/app_examiner/fake_app_examiner"
    18  	"github.com/cloudfoundry-incubator/ltc/exit_handler/exit_codes"
    19  	"github.com/cloudfoundry-incubator/ltc/exit_handler/fake_exit_handler"
    20  	"github.com/cloudfoundry-incubator/ltc/route_helpers"
    21  	"github.com/cloudfoundry-incubator/ltc/task_examiner"
    22  	"github.com/cloudfoundry-incubator/ltc/task_examiner/fake_task_examiner"
    23  	"github.com/cloudfoundry-incubator/ltc/terminal"
    24  	"github.com/cloudfoundry-incubator/ltc/terminal/colors"
    25  	"github.com/cloudfoundry-incubator/ltc/terminal/cursor"
    26  	"github.com/cloudfoundry-incubator/ltc/test_helpers"
    27  	"github.com/codegangsta/cli"
    28  	"github.com/pivotal-golang/clock/fakeclock"
    29  )
    30  
    31  const TerminalEsc = "\033["
    32  
    33  var _ = Describe("CommandFactory", func() {
    34  
    35  	var (
    36  		fakeAppExaminer         *fake_app_examiner.FakeAppExaminer
    37  		outputBuffer            *gbytes.Buffer
    38  		terminalUI              terminal.UI
    39  		fakeTerm                *fake_terminal.FakeTerminal
    40  		fakeClock               *fakeclock.FakeClock
    41  		osSignalChan            chan os.Signal
    42  		fakeExitHandler         *fake_exit_handler.FakeExitHandler
    43  		fakeGraphicalVisualizer *fake_graphical_visualizer.FakeGraphicalVisualizer
    44  		fakeTaskExaminer        *fake_task_examiner.FakeTaskExaminer
    45  		systemDomain            string
    46  	)
    47  
    48  	BeforeEach(func() {
    49  		fakeAppExaminer = &fake_app_examiner.FakeAppExaminer{}
    50  		fakeTaskExaminer = &fake_task_examiner.FakeTaskExaminer{}
    51  		outputBuffer = gbytes.NewBuffer()
    52  		terminalUI = terminal.NewUI(nil, outputBuffer, nil)
    53  		fakeTerm = &fake_terminal.FakeTerminal{}
    54  		osSignalChan = make(chan os.Signal, 1)
    55  		location, err := time.LoadLocation("Africa/Djibouti")
    56  		Expect(err).NotTo(HaveOccurred())
    57  		fakeClock = fakeclock.NewFakeClock(time.Date(2012, time.February, 29, 6, 45, 30, 820, location))
    58  		fakeExitHandler = &fake_exit_handler.FakeExitHandler{}
    59  		fakeGraphicalVisualizer = &fake_graphical_visualizer.FakeGraphicalVisualizer{}
    60  		systemDomain = "system.domain"
    61  	})
    62  
    63  	Describe("ListAppsCommand", func() {
    64  		var listAppsCommand cli.Command
    65  
    66  		BeforeEach(func() {
    67  			commandFactory := command_factory.NewAppExaminerCommandFactory(fakeAppExaminer, terminalUI, fakeTerm, fakeClock, fakeExitHandler, nil, fakeTaskExaminer, systemDomain)
    68  			listAppsCommand = commandFactory.MakeListAppCommand()
    69  		})
    70  
    71  		It("displays all the existing apps & tasks, making sure output spacing is correct", func() {
    72  			listApps := []app_examiner.AppInfo{
    73  				{
    74  					ProcessGuid:            "process1",
    75  					DesiredInstances:       21,
    76  					ActualRunningInstances: 0,
    77  					DiskMB:                 100,
    78  					MemoryMB:               50,
    79  					Ports:                  []uint16{54321},
    80  					Routes: route_helpers.Routes{
    81  						AppRoutes: route_helpers.AppRoutes{
    82  							{Hostnames: []string{"alldaylong.com"}, Port: 54321},
    83  						},
    84  					},
    85  				},
    86  				{
    87  					ProcessGuid:            "process2",
    88  					DesiredInstances:       8,
    89  					ActualRunningInstances: 9,
    90  					DiskMB:                 400,
    91  					MemoryMB:               30,
    92  					Ports:                  []uint16{1234},
    93  					Routes: route_helpers.Routes{
    94  						AppRoutes: route_helpers.AppRoutes{
    95  							{Hostnames: []string{"never.io"}, Port: 1234},
    96  						},
    97  					},
    98  				},
    99  				{
   100  					ProcessGuid:            "process3",
   101  					DesiredInstances:       5,
   102  					ActualRunningInstances: 5,
   103  					DiskMB:                 600,
   104  					MemoryMB:               90,
   105  					Ports:                  []uint16{1234},
   106  					Routes: route_helpers.Routes{
   107  						AppRoutes: route_helpers.AppRoutes{
   108  							{Hostnames: []string{"allthetime.com", "herewego.org"}, Port: 1234},
   109  						},
   110  					},
   111  				},
   112  				{
   113  					ProcessGuid:            "process4",
   114  					DesiredInstances:       0,
   115  					ActualRunningInstances: 0,
   116  					DiskMB:                 10,
   117  					MemoryMB:               10,
   118  					Routes:                 route_helpers.Routes{},
   119  				},
   120  			}
   121  			fakeAppExaminer.ListAppsReturns(listApps, nil)
   122  			listTasks := []task_examiner.TaskInfo{
   123  				{
   124  					TaskGuid:      "task-guid-1",
   125  					CellID:        "cell-01",
   126  					Failed:        false,
   127  					FailureReason: "",
   128  					Result:        "Finished",
   129  					State:         "COMPLETED",
   130  				},
   131  				{
   132  					TaskGuid:      "task-guid-2",
   133  					CellID:        "cell-02",
   134  					Failed:        true,
   135  					FailureReason: "No compatible container",
   136  					Result:        "Finished",
   137  					State:         "COMPLETED",
   138  				},
   139  				{
   140  					TaskGuid:      "task-guid-3",
   141  					CellID:        "",
   142  					Failed:        true,
   143  					FailureReason: "",
   144  					Result:        "",
   145  					State:         "COMPLETED",
   146  				},
   147  			}
   148  			fakeTaskExaminer.ListTasksReturns(listTasks, nil)
   149  
   150  			test_helpers.ExecuteCommandWithArgs(listAppsCommand, []string{})
   151  
   152  			Expect(outputBuffer).To(test_helpers.Say(colors.Bold("App Name")))
   153  			Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Instances")))
   154  			Expect(outputBuffer).To(test_helpers.Say(colors.Bold("DiskMB")))
   155  			Expect(outputBuffer).To(test_helpers.Say(colors.Bold("MemoryMB")))
   156  			Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Route")))
   157  
   158  			Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process1")))
   159  			Expect(outputBuffer).To(test_helpers.Say(colors.Red("0/21")))
   160  			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("100")))
   161  			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("50")))
   162  			Expect(outputBuffer).To(test_helpers.Say("alldaylong.com => 54321"))
   163  
   164  			Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process2")))
   165  			Expect(outputBuffer).To(test_helpers.Say(colors.Yellow("9/8")))
   166  			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("400")))
   167  			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("30")))
   168  			Expect(outputBuffer).To(test_helpers.Say("never.io => 1234"))
   169  
   170  			Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process3")))
   171  			Expect(outputBuffer).To(test_helpers.Say(colors.Green("5/5")))
   172  			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("600")))
   173  			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("90")))
   174  			Expect(outputBuffer).To(test_helpers.Say("allthetime.com => 1234, herewego.org => 1234"))
   175  
   176  			Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process4")))
   177  			Expect(outputBuffer).To(test_helpers.Say(colors.Green("0/0")))
   178  			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("10")))
   179  			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("10")))
   180  
   181  			Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Task GUID")))
   182  			Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Cell ID")))
   183  			Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Status")))
   184  			Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Result")))
   185  			Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Failure Reason")))
   186  
   187  			Expect(outputBuffer).To(test_helpers.Say(colors.Bold("task-guid-1")))
   188  			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("cell-01")))
   189  			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("COMPLETED")))
   190  			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("Finished")))
   191  			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("N/A")))
   192  
   193  			Expect(outputBuffer).To(test_helpers.Say(colors.Bold("task-guid-2")))
   194  			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("cell-02")))
   195  			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("COMPLETED")))
   196  			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("Finished")))
   197  			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("No compatible container")))
   198  
   199  			Expect(outputBuffer).To(test_helpers.Say(colors.Bold("task-guid-3")))
   200  			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("N/A")))
   201  			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("COMPLETED")))
   202  			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("N/A")))
   203  			Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("N/A")))
   204  		})
   205  
   206  		It("alerts the user if there are no apps or tasks", func() {
   207  			fakeAppExaminer.ListAppsReturns([]app_examiner.AppInfo{}, nil)
   208  			fakeTaskExaminer.ListTasksReturns([]task_examiner.TaskInfo{}, nil)
   209  
   210  			test_helpers.ExecuteCommandWithArgs(listAppsCommand, []string{})
   211  
   212  			Expect(outputBuffer).To(test_helpers.SayLine("No apps to display."))
   213  			Expect(outputBuffer).To(test_helpers.Say("No tasks to display."))
   214  		})
   215  
   216  		Context("when the app examiner returns an error", func() {
   217  			It("alerts the user fetching the app list returns an error", func() {
   218  				fakeAppExaminer.ListAppsReturns(nil, errors.New("The list was lost"))
   219  				listTasks := []task_examiner.TaskInfo{
   220  					{
   221  						TaskGuid:      "task-guid-1",
   222  						CellID:        "cell-01",
   223  						Failed:        false,
   224  						FailureReason: "",
   225  						Result:        "Finished",
   226  						State:         "COMPLETED",
   227  					},
   228  				}
   229  				fakeTaskExaminer.ListTasksReturns(listTasks, nil)
   230  
   231  				test_helpers.ExecuteCommandWithArgs(listAppsCommand, []string{})
   232  
   233  				Expect(outputBuffer).To(test_helpers.SayLine("Error listing apps: The list was lost"))
   234  
   235  				Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Task GUID")))
   236  				Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Cell ID")))
   237  				Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Status")))
   238  				Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Result")))
   239  				Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Failure Reason")))
   240  
   241  				Expect(outputBuffer).To(test_helpers.Say(colors.Bold("task-guid-1")))
   242  				Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("cell-01")))
   243  				Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("COMPLETED")))
   244  				Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("Finished")))
   245  				Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("N/A")))
   246  
   247  				Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.CommandFailed}))
   248  			})
   249  
   250  			It("alerts the user fetching the task list returns an error", func() {
   251  				listApps := []app_examiner.AppInfo{
   252  					{
   253  						ProcessGuid:            "process1",
   254  						DesiredInstances:       21,
   255  						ActualRunningInstances: 0,
   256  						DiskMB:                 100,
   257  						MemoryMB:               50,
   258  						Ports:                  []uint16{54321},
   259  						Routes: route_helpers.Routes{
   260  							AppRoutes: route_helpers.AppRoutes{
   261  								{
   262  									Hostnames: []string{"alldaylong.com"},
   263  									Port:      54321,
   264  								},
   265  							},
   266  						},
   267  					},
   268  				}
   269  				fakeAppExaminer.ListAppsReturns(listApps, nil)
   270  				fakeTaskExaminer.ListTasksReturns(nil, errors.New("The list was lost"))
   271  
   272  				test_helpers.ExecuteCommandWithArgs(listAppsCommand, []string{})
   273  
   274  				Expect(outputBuffer).To(test_helpers.Say(colors.Bold("App Name")))
   275  				Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Instances")))
   276  				Expect(outputBuffer).To(test_helpers.Say(colors.Bold("DiskMB")))
   277  				Expect(outputBuffer).To(test_helpers.Say(colors.Bold("MemoryMB")))
   278  				Expect(outputBuffer).To(test_helpers.Say(colors.Bold("Route")))
   279  
   280  				Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process1")))
   281  				Expect(outputBuffer).To(test_helpers.Say(colors.Red("0/21")))
   282  				Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("100")))
   283  				Expect(outputBuffer).To(test_helpers.Say(colors.NoColor("50")))
   284  				Expect(outputBuffer).To(test_helpers.Say("alldaylong.com => 54321"))
   285  
   286  				Expect(outputBuffer).To(test_helpers.SayLine("Error listing tasks: The list was lost"))
   287  				Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.CommandFailed}))
   288  			})
   289  		})
   290  
   291  		Context("when app has tcp routes", func() {
   292  			It("displays all tcp routes along with http routes", func() {
   293  				listApps := []app_examiner.AppInfo{
   294  					{
   295  						ProcessGuid:            "process1",
   296  						DesiredInstances:       21,
   297  						ActualRunningInstances: 0,
   298  						DiskMB:                 100,
   299  						MemoryMB:               50,
   300  						Ports:                  []uint16{54321, 5222},
   301  						Routes: route_helpers.Routes{
   302  							AppRoutes: route_helpers.AppRoutes{
   303  								{Hostnames: []string{"alldaylong.com"}, Port: 54321},
   304  							},
   305  							TcpRoutes: route_helpers.TcpRoutes{
   306  								{ExternalPort: 51000, Port: 5222},
   307  							},
   308  						},
   309  					},
   310  					{
   311  						ProcessGuid:            "process2",
   312  						DesiredInstances:       8,
   313  						ActualRunningInstances: 9,
   314  						DiskMB:                 400,
   315  						MemoryMB:               30,
   316  						Ports:                  []uint16{1234, 7400},
   317  						Routes: route_helpers.Routes{
   318  							AppRoutes: route_helpers.AppRoutes{
   319  								{Hostnames: []string{"never.io"}, Port: 1234},
   320  							},
   321  							TcpRoutes: route_helpers.TcpRoutes{
   322  								{ExternalPort: 52000, Port: 7400},
   323  							},
   324  						},
   325  					},
   326  					{
   327  						ProcessGuid:            "process3",
   328  						DesiredInstances:       5,
   329  						ActualRunningInstances: 5,
   330  						DiskMB:                 600,
   331  						MemoryMB:               90,
   332  						Ports:                  []uint16{1234, 1883},
   333  						Routes: route_helpers.Routes{
   334  							AppRoutes: route_helpers.AppRoutes{
   335  								{Hostnames: []string{"allthetime.com", "herewego.org"}, Port: 1234},
   336  							},
   337  							TcpRoutes: route_helpers.TcpRoutes{
   338  								{ExternalPort: 53000, Port: 1883},
   339  							},
   340  						},
   341  					},
   342  					{
   343  						ProcessGuid:            "process4",
   344  						DesiredInstances:       0,
   345  						ActualRunningInstances: 0,
   346  						DiskMB:                 10,
   347  						MemoryMB:               10,
   348  						Routes:                 route_helpers.Routes{},
   349  					},
   350  				}
   351  				fakeAppExaminer.ListAppsReturns(listApps, nil)
   352  
   353  				test_helpers.ExecuteCommandWithArgs(listAppsCommand, []string{})
   354  
   355  				Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process1")))
   356  				Expect(outputBuffer).To(test_helpers.Say("alldaylong.com => 54321, system.domain:51000 => 5222"))
   357  
   358  				Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process2")))
   359  				Expect(outputBuffer).To(test_helpers.Say("never.io => 1234, system.domain:52000 => 7400"))
   360  
   361  				Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process3")))
   362  				Expect(outputBuffer).To(test_helpers.Say("allthetime.com => 1234, herewego.org => 1234, system.domain:53000 => 1883"))
   363  
   364  				Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process4")))
   365  			})
   366  		})
   367  
   368  		Context("when app has routes with container ports not listed in ports section", func() {
   369  			It("displays all tcp routes along with http routes", func() {
   370  				listApps := []app_examiner.AppInfo{
   371  					{
   372  						ProcessGuid:            "process1",
   373  						DesiredInstances:       21,
   374  						ActualRunningInstances: 0,
   375  						DiskMB:                 100,
   376  						MemoryMB:               50,
   377  						Ports:                  []uint16{54321},
   378  						Routes: route_helpers.Routes{
   379  							AppRoutes: route_helpers.AppRoutes{
   380  								{Hostnames: []string{"alldaylong.com"}, Port: 54321},
   381  							},
   382  							TcpRoutes: route_helpers.TcpRoutes{
   383  								{ExternalPort: 51000, Port: 5222},
   384  							},
   385  						},
   386  					},
   387  					{
   388  						ProcessGuid:            "process2",
   389  						DesiredInstances:       8,
   390  						ActualRunningInstances: 9,
   391  						DiskMB:                 400,
   392  						MemoryMB:               30,
   393  						Ports:                  []uint16{7400},
   394  						Routes: route_helpers.Routes{
   395  							AppRoutes: route_helpers.AppRoutes{
   396  								{Hostnames: []string{"never.io"}, Port: 1234},
   397  							},
   398  							TcpRoutes: route_helpers.TcpRoutes{
   399  								{ExternalPort: 52000, Port: 7400},
   400  							},
   401  						},
   402  					},
   403  				}
   404  				fakeAppExaminer.ListAppsReturns(listApps, nil)
   405  
   406  				test_helpers.ExecuteCommandWithArgs(listAppsCommand, []string{})
   407  
   408  				Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process1")))
   409  				Expect(outputBuffer).To(test_helpers.Say("alldaylong.com => 54321"))
   410  
   411  				Expect(outputBuffer).To(test_helpers.Say(colors.Bold("process2")))
   412  				Expect(outputBuffer).To(test_helpers.Say("system.domain:52000 => 7400"))
   413  			})
   414  		})
   415  	})
   416  
   417  	Describe("VisualizeCommand", func() {
   418  		var visualizeCommand cli.Command
   419  
   420  		BeforeEach(func() {
   421  			commandFactory := command_factory.NewAppExaminerCommandFactory(fakeAppExaminer, terminalUI, fakeTerm, fakeClock, fakeExitHandler, fakeGraphicalVisualizer, fakeTaskExaminer, systemDomain)
   422  			visualizeCommand = commandFactory.MakeVisualizeCommand()
   423  		})
   424  
   425  		It("displays a visualization of cells", func() {
   426  			listCells := []app_examiner.CellInfo{
   427  				{CellID: "cell-1", RunningInstances: 3, ClaimedInstances: 2},
   428  				{CellID: "cell-2", RunningInstances: 2, ClaimedInstances: 1},
   429  				{CellID: "cell-3", RunningInstances: 0, ClaimedInstances: 0},
   430  			}
   431  			fakeAppExaminer.ListCellsReturns(listCells, nil)
   432  
   433  			test_helpers.ExecuteCommandWithArgs(visualizeCommand, []string{})
   434  
   435  			Expect(outputBuffer).To(test_helpers.SayLine(colors.Bold("Distribution")))
   436  			Expect(outputBuffer).To(test_helpers.SayLine("cell-1: " + colors.Green("•••") + colors.Yellow("••") + cursor.ClearToEndOfLine()))
   437  			Expect(outputBuffer).To(test_helpers.SayLine("cell-2: " + colors.Green("••") + colors.Yellow("•") + cursor.ClearToEndOfLine()))
   438  			Expect(outputBuffer).To(test_helpers.SayLine("cell-3: " + colors.Red("empty") + cursor.ClearToEndOfLine()))
   439  		})
   440  
   441  		Context("when the app examiner returns an error", func() {
   442  			It("alerts the user fetching the cells returns an error", func() {
   443  				fakeAppExaminer.ListCellsReturns(nil, errors.New("The list was lost"))
   444  
   445  				test_helpers.ExecuteCommandWithArgs(visualizeCommand, []string{})
   446  
   447  				Expect(outputBuffer).To(test_helpers.Say("Error visualizing: The list was lost"))
   448  				Expect(outputBuffer).To(test_helpers.Say(cursor.ClearToEndOfLine()))
   449  				Expect(outputBuffer).To(test_helpers.SayNewLine())
   450  
   451  				// TODO: this should return non-zero, but it's shared with refresh view
   452  				//   which should continue to retry in the event on an error and not exit
   453  			})
   454  		})
   455  
   456  		Context("when a rate flag is provided", func() {
   457  			var closeChan chan struct{}
   458  
   459  			AfterEach(func() {
   460  				go fakeExitHandler.Exit(exit_codes.Signal)
   461  				Eventually(closeChan).Should(BeClosed())
   462  			})
   463  
   464  			Context("when term is set", func() {
   465  				var previousTerm string
   466  
   467  				BeforeEach(func() {
   468  					previousTerm = os.Getenv("TERM")
   469  					os.Setenv("TERM", "xterm")
   470  
   471  				})
   472  
   473  				AfterEach(func() {
   474  					os.Setenv("TERM", previousTerm)
   475  				})
   476  
   477  				It("dynamically displays the visualization", func() {
   478  					setNumberOfRunningInstances := func(count int) {
   479  						fakeAppExaminer.ListCellsReturns([]app_examiner.CellInfo{app_examiner.CellInfo{CellID: "cell-0", RunningInstances: count}, app_examiner.CellInfo{CellID: "cell-1", RunningInstances: count, Missing: true}}, nil)
   480  					}
   481  					setNumberOfRunningInstances(0)
   482  
   483  					closeChan = test_helpers.AsyncExecuteCommandWithArgs(visualizeCommand, []string{"--rate", "2s"})
   484  
   485  					Eventually(outputBuffer).Should(test_helpers.SayLine("cell-0: " + colors.Red("empty") + cursor.ClearToEndOfLine()))
   486  					Eventually(outputBuffer).Should(test_helpers.SayLine("cell-1" + colors.Red("[MISSING]") + ": " + cursor.ClearToEndOfLine()))
   487  
   488  					setNumberOfRunningInstances(2)
   489  
   490  					fakeClock.IncrementBySeconds(1)
   491  
   492  					Consistently(outputBuffer).ShouldNot(test_helpers.Say("cell: \n")) // TODO: how would this happen
   493  
   494  					fakeClock.IncrementBySeconds(1)
   495  
   496  					Eventually(outputBuffer).Should(test_helpers.Say(cursor.Hide()))
   497  					Eventually(outputBuffer).Should(test_helpers.Say(cursor.Up(2)))
   498  					Eventually(outputBuffer).Should(test_helpers.SayLine("cell-0: " + colors.Green("••") + cursor.ClearToEndOfLine()))
   499  					Eventually(outputBuffer).Should(test_helpers.SayLine("cell-1" + colors.Red("[MISSING]") + ": " + colors.Green("••") + cursor.ClearToEndOfLine()))
   500  					Eventually(outputBuffer).Should(test_helpers.Say(cursor.ClearToEndOfDisplay()))
   501  
   502  					Consistently(closeChan).ShouldNot(BeClosed())
   503  				})
   504  
   505  				It("dynamically displays any errors", func() {
   506  					fakeAppExaminer.ListCellsReturns(nil, errors.New("Spilled the Paint"))
   507  
   508  					closeChan = test_helpers.AsyncExecuteCommandWithArgs(visualizeCommand, []string{"--rate", "1s"})
   509  
   510  					Eventually(outputBuffer).Should(test_helpers.SayLine("Error visualizing: Spilled the Paint" + cursor.ClearToEndOfLine()))
   511  
   512  					fakeClock.IncrementBySeconds(1)
   513  
   514  					Eventually(outputBuffer).Should(test_helpers.Say(cursor.Up(1)))
   515  					Eventually(outputBuffer).Should(test_helpers.SayLine("Error visualizing: Spilled the Paint" + cursor.ClearToEndOfLine()))
   516  					Eventually(outputBuffer).Should(test_helpers.Say(cursor.ClearToEndOfDisplay()))
   517  
   518  					Consistently(closeChan).ShouldNot(BeClosed())
   519  				})
   520  
   521  				It("ensures the user's cursor is visible even if they interrupt ltc", func() {
   522  					closeChan = test_helpers.AsyncExecuteCommandWithArgs(visualizeCommand, []string{"--rate=1s"})
   523  
   524  					Eventually(outputBuffer).Should(test_helpers.Say(cursor.Hide()))
   525  					fakeExitHandler.Exit(exit_codes.Signal)
   526  					Eventually(closeChan).Should(BeClosed())
   527  
   528  					Expect(outputBuffer).To(test_helpers.Say(cursor.Show()))
   529  					Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.Signal}))
   530  				})
   531  			})
   532  
   533  			Context("when term is unset", func() {
   534  				var previousTerm string
   535  
   536  				BeforeEach(func() {
   537  					previousTerm = os.Getenv("TERM")
   538  					os.Unsetenv("TERM")
   539  
   540  				})
   541  
   542  				AfterEach(func() {
   543  					os.Setenv("TERM", previousTerm)
   544  				})
   545  
   546  				It("displays the visualization once", func() {
   547  					fakeAppExaminer.ListCellsReturns([]app_examiner.CellInfo{
   548  						app_examiner.CellInfo{
   549  							CellID:           "cell-0",
   550  							RunningInstances: 2,
   551  						},
   552  						app_examiner.CellInfo{
   553  							CellID:           "cell-1",
   554  							RunningInstances: 2,
   555  							Missing:          true,
   556  						},
   557  					}, nil)
   558  
   559  					closeChan = test_helpers.AsyncExecuteCommandWithArgs(visualizeCommand, []string{"--rate", "2s"})
   560  
   561  					Eventually(outputBuffer).Should(test_helpers.SayLine("cell-0: ••"))
   562  					Eventually(outputBuffer).Should(test_helpers.SayLine("cell-1[MISSING]: ••"))
   563  
   564  					Expect(closeChan).To(BeClosed())
   565  				})
   566  			})
   567  		})
   568  
   569  		Context("when the graphical flag is passed", func() {
   570  			It("makes a successful call to the graphical visualizer and returns", func() {
   571  				test_helpers.ExecuteCommandWithArgs(visualizeCommand, []string{"--graphical"})
   572  
   573  				Consistently(outputBuffer).ShouldNot(test_helpers.Say("Distribution"))
   574  
   575  				Expect(fakeGraphicalVisualizer.PrintDistributionChartCallCount()).To(Equal(1))
   576  				Expect(fakeGraphicalVisualizer.PrintDistributionChartArgsForCall(0)).To(BeZero())
   577  			})
   578  
   579  			It("prints the error from an unsuccessful call to the graphical visualizer", func() {
   580  				fakeGraphicalVisualizer.PrintDistributionChartReturns(errors.New("errored"))
   581  
   582  				test_helpers.ExecuteCommandWithArgs(visualizeCommand, []string{"--graphical"})
   583  
   584  				Consistently(outputBuffer).ShouldNot(test_helpers.Say("Distribution"))
   585  				Eventually(outputBuffer).Should(test_helpers.SayLine("Error Visualization: errored"))
   586  
   587  				Expect(fakeGraphicalVisualizer.PrintDistributionChartCallCount()).To(Equal(1))
   588  				Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.CommandFailed}))
   589  			})
   590  
   591  			Context("when the rate flag is also passed", func() {
   592  				It("sets the initial rate when calling the graphical visualizer", func() {
   593  					test_helpers.ExecuteCommandWithArgs(visualizeCommand, []string{"--graphical", "--rate=200ms"})
   594  
   595  					Consistently(outputBuffer).ShouldNot(test_helpers.Say("Distribution"))
   596  
   597  					Expect(fakeGraphicalVisualizer.PrintDistributionChartCallCount()).To(Equal(1))
   598  					duration, err := time.ParseDuration("200ms")
   599  					Expect(err).NotTo(HaveOccurred())
   600  					Expect(fakeGraphicalVisualizer.PrintDistributionChartArgsForCall(0)).To(Equal(duration))
   601  				})
   602  			})
   603  		})
   604  	})
   605  
   606  	Describe("StatusCommand", func() {
   607  		var (
   608  			statusCommand cli.Command
   609  			sampleAppInfo app_examiner.AppInfo
   610  		)
   611  
   612  		epochTime := int64(401120627)
   613  		roundTime := func(now, since time.Time) string {
   614  			uptime := (now.Sub(since) - (now.Sub(since) % time.Second)).String()
   615  			return uptime
   616  		}
   617  
   618  		BeforeEach(func() {
   619  			commandFactory := command_factory.NewAppExaminerCommandFactory(fakeAppExaminer, terminalUI, fakeTerm, fakeClock, fakeExitHandler, nil, fakeTaskExaminer, systemDomain)
   620  			statusCommand = commandFactory.MakeStatusCommand()
   621  
   622  			sampleAppInfo = app_examiner.AppInfo{
   623  				ProcessGuid:            "wompy-app",
   624  				RootFS:                 "preloaded:rootfs2",
   625  				DesiredInstances:       12,
   626  				ActualRunningInstances: 1,
   627  				EnvironmentVariables: []app_examiner.EnvironmentVariable{
   628  					{Name: "WOMPY_APP_PASSWORD", Value: "seekreet pass"},
   629  					{Name: "WOMPY_APP_USERNAME", Value: "mrbigglesworth54"},
   630  				},
   631  				StartTimeout: 600,
   632  				DiskMB:       2048,
   633  				MemoryMB:     256,
   634  				CPUWeight:    100,
   635  				Ports:        []uint16{8887, 9000},
   636  				Routes: route_helpers.Routes{
   637  					AppRoutes: route_helpers.AppRoutes{
   638  						{Hostnames: []string{"route-me.my-fun-domain.com"}, Port: 9000},
   639  						{Hostnames: []string{"wompy-app.my-fun-domain.com", "cranky-app.my-fun-domain.com"}, Port: 8887},
   640  					},
   641  				},
   642  				LogGuid:    "a9s8dfa99023r",
   643  				LogSource:  "wompy-app-logz",
   644  				Annotation: "I love this app. So wompy.",
   645  				ActualInstances: []app_examiner.InstanceInfo{
   646  					app_examiner.InstanceInfo{
   647  						InstanceGuid: "a0s9f-u9a8sf-aasdioasdjoi",
   648  						CellID:       "cell-12",
   649  						Index:        3,
   650  						Ip:           "10.85.12.100",
   651  						Ports: []app_examiner.PortMapping{
   652  							{HostPort: 1234, ContainerPort: 3000},
   653  							{HostPort: 5555, ContainerPort: 6666},
   654  						},
   655  						State:      "RUNNING",
   656  						Since:      epochTime * 1e9,
   657  						HasMetrics: true,
   658  						Metrics: app_examiner.InstanceMetrics{
   659  							CpuPercentage: 23.45678,
   660  							MemoryBytes:   655360,
   661  						},
   662  					},
   663  					app_examiner.InstanceInfo{
   664  						Index:          4,
   665  						State:          "UNCLAIMED",
   666  						PlacementError: "insufficient resources.",
   667  						CrashCount:     2,
   668  						HasMetrics:     false,
   669  					},
   670  					app_examiner.InstanceInfo{
   671  						Index:      5,
   672  						State:      "CRASHED",
   673  						CrashCount: 7,
   674  					},
   675  				},
   676  			}
   677  
   678  			fakeTerm.GetWindowWidthReturns(9999, nil)
   679  		})
   680  
   681  		It("emits a pretty representation of the DesiredLRP", func() {
   682  			fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil)
   683  
   684  			test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"wompy-app"})
   685  
   686  			Expect(fakeAppExaminer.AppStatusCallCount()).To(Equal(1))
   687  			Expect(fakeAppExaminer.AppStatusArgsForCall(0)).To(Equal("wompy-app"))
   688  
   689  			Expect(outputBuffer).To(test_helpers.Say("Instance"))
   690  			Expect(outputBuffer).To(test_helpers.Say("State"))
   691  			Expect(outputBuffer).To(test_helpers.Say("Crashes"))
   692  			Expect(outputBuffer).To(test_helpers.Say("CPU"))
   693  			Expect(outputBuffer).To(test_helpers.Say("Memory"))
   694  			Expect(outputBuffer).To(test_helpers.Say("Uptime"))
   695  			Expect(outputBuffer).To(test_helpers.SayNewLine())
   696  
   697  			Expect(outputBuffer).To(test_helpers.Say("3"))
   698  			Expect(outputBuffer).To(test_helpers.Say("RUNNING"))
   699  			Expect(outputBuffer).To(test_helpers.Say("0"))
   700  			Expect(outputBuffer).To(test_helpers.Say("23.46%"))
   701  			Expect(outputBuffer).To(test_helpers.Say("640K"))
   702  			roundedTimeSince := roundTime(fakeClock.Now(), time.Unix(0, epochTime*1e9))
   703  			Expect(outputBuffer).To(test_helpers.Say(roundedTimeSince))
   704  			Expect(outputBuffer).To(test_helpers.SayNewLine())
   705  
   706  			Expect(outputBuffer).To(test_helpers.Say("4"))
   707  			Expect(outputBuffer).To(test_helpers.Say("UNCLAIMED"))
   708  			Expect(outputBuffer).To(test_helpers.Say("2"))
   709  			Expect(outputBuffer).To(test_helpers.Say("N/A"))
   710  			Expect(outputBuffer).To(test_helpers.Say("N/A"))
   711  			Expect(outputBuffer).To(test_helpers.SayNewLine())
   712  
   713  			Expect(outputBuffer).To(test_helpers.Say("5"))
   714  			Expect(outputBuffer).To(test_helpers.Say("CRASHED"))
   715  			Expect(outputBuffer).To(test_helpers.Say("7"))
   716  			Expect(outputBuffer).To(test_helpers.Say("N/A"))
   717  			Expect(outputBuffer).To(test_helpers.Say("N/A"))
   718  			Expect(outputBuffer).To(test_helpers.SayNewLine())
   719  		})
   720  
   721  		Context("when the --detailed flag is passed", func() {
   722  			It("prints the instance info in full mode", func() {
   723  				fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil)
   724  
   725  				test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"wompy-app", "--detailed"})
   726  
   727  				Expect(fakeAppExaminer.AppStatusCallCount()).To(Equal(1))
   728  				Expect(fakeAppExaminer.AppStatusArgsForCall(0)).To(Equal("wompy-app"))
   729  
   730  				Expect(outputBuffer).To(test_helpers.Say("wompy-app"))
   731  
   732  				Expect(outputBuffer).To(test_helpers.Say("Instances"))
   733  				Expect(outputBuffer).To(test_helpers.Say("1/12"))
   734  				Expect(outputBuffer).To(test_helpers.SayNewLine())
   735  
   736  				Expect(outputBuffer).To(test_helpers.Say("Start Timeout"))
   737  				Expect(outputBuffer).To(test_helpers.SayLine("600"))
   738  
   739  				Expect(outputBuffer).To(test_helpers.Say("DiskMB"))
   740  				Expect(outputBuffer).To(test_helpers.SayLine("2048"))
   741  
   742  				Expect(outputBuffer).To(test_helpers.Say("MemoryMB"))
   743  				Expect(outputBuffer).To(test_helpers.SayLine("256"))
   744  
   745  				Expect(outputBuffer).To(test_helpers.Say("CPUWeight"))
   746  				Expect(outputBuffer).To(test_helpers.SayLine("100"))
   747  
   748  				Expect(outputBuffer).To(test_helpers.Say("Ports"))
   749  				Expect(outputBuffer).To(test_helpers.Say("8887"))
   750  				Expect(outputBuffer).To(test_helpers.Say("9000"))
   751  				Expect(outputBuffer).To(test_helpers.SayNewLine())
   752  
   753  				Expect(outputBuffer).To(test_helpers.Say("Routes"))
   754  				Expect(outputBuffer).To(test_helpers.Say("wompy-app.my-fun-domain.com => 8887"))
   755  				Expect(outputBuffer).To(test_helpers.SayNewLine())
   756  				Expect(outputBuffer).To(test_helpers.Say("cranky-app.my-fun-domain.com => 8887"))
   757  				Expect(outputBuffer).To(test_helpers.SayNewLine())
   758  				Expect(outputBuffer).To(test_helpers.Say("route-me.my-fun-domain.com => 9000"))
   759  				Expect(outputBuffer).To(test_helpers.SayNewLine())
   760  
   761  				Expect(outputBuffer).To(test_helpers.Say("Stack"))
   762  				Expect(outputBuffer).To(test_helpers.SayLine("rootfs2"))
   763  
   764  				Expect(outputBuffer).To(test_helpers.Say("Annotation"))
   765  				Expect(outputBuffer).To(test_helpers.SayLine("I love this app. So wompy."))
   766  
   767  				Expect(outputBuffer).To(test_helpers.SayLine("Environment"))
   768  				Expect(outputBuffer).To(test_helpers.Say(`WOMPY_APP_PASSWORD="seekreet pass"`))
   769  				Expect(outputBuffer).To(test_helpers.SayNewLine())
   770  				Expect(outputBuffer).To(test_helpers.Say(`WOMPY_APP_USERNAME="mrbigglesworth54"`))
   771  				Expect(outputBuffer).To(test_helpers.SayNewLine())
   772  
   773  				Expect(outputBuffer).To(test_helpers.Say("Instance 3"))
   774  				Expect(outputBuffer).To(test_helpers.Say("RUNNING"))
   775  				Expect(outputBuffer).To(test_helpers.SayNewLine())
   776  
   777  				Expect(outputBuffer).To(test_helpers.Say("InstanceGuid"))
   778  				Expect(outputBuffer).To(test_helpers.SayLine("a0s9f-u9a8sf-aasdioasdjoi"))
   779  
   780  				Expect(outputBuffer).To(test_helpers.Say("Cell ID"))
   781  				Expect(outputBuffer).To(test_helpers.SayLine("cell-12"))
   782  
   783  				Expect(outputBuffer).To(test_helpers.Say("Ip"))
   784  				Expect(outputBuffer).To(test_helpers.SayLine("10.85.12.100"))
   785  
   786  				Expect(outputBuffer).To(test_helpers.Say("Port Mapping"))
   787  				Expect(outputBuffer).To(test_helpers.Say("1234:3000"))
   788  				Expect(outputBuffer).To(test_helpers.Say("5555:6666"))
   789  				Expect(outputBuffer).To(test_helpers.SayNewLine())
   790  
   791  				Expect(outputBuffer).To(test_helpers.Say("Uptime"))
   792  				roundedTimeSince := roundTime(fakeClock.Now(), time.Unix(0, epochTime*1e9))
   793  				Expect(outputBuffer).To(test_helpers.Say(roundedTimeSince))
   794  				Expect(outputBuffer).To(test_helpers.SayNewLine())
   795  
   796  				Expect(outputBuffer).To(test_helpers.Say("Crash Count"))
   797  				Expect(outputBuffer).To(test_helpers.Say("0"))
   798  				Expect(outputBuffer).To(test_helpers.SayNewLine())
   799  
   800  				Expect(outputBuffer).To(test_helpers.Say("CPU"))
   801  				Expect(outputBuffer).To(test_helpers.Say("23.46%"))
   802  				Expect(outputBuffer).To(test_helpers.SayNewLine())
   803  
   804  				Expect(outputBuffer).To(test_helpers.Say("Memory"))
   805  				Expect(outputBuffer).To(test_helpers.Say("640K"))
   806  				Expect(outputBuffer).To(test_helpers.SayNewLine())
   807  
   808  				Expect(outputBuffer).To(test_helpers.Say("Instance 4"))
   809  				Expect(outputBuffer).To(test_helpers.Say("UNCLAIMED"))
   810  
   811  				Expect(outputBuffer).NotTo(test_helpers.Say("InstanceGuid"))
   812  				Expect(outputBuffer).To(test_helpers.Say("Placement Error"))
   813  				Expect(outputBuffer).To(test_helpers.Say("insufficient resources."))
   814  				Expect(outputBuffer).To(test_helpers.SayNewLine())
   815  
   816  				Expect(outputBuffer).To(test_helpers.Say("Crash Count"))
   817  				Expect(outputBuffer).To(test_helpers.Say("2"))
   818  				Expect(outputBuffer).To(test_helpers.SayNewLine())
   819  
   820  				Expect(outputBuffer).NotTo(test_helpers.Say("CPU"))
   821  				Expect(outputBuffer).NotTo(test_helpers.Say("Memory"))
   822  
   823  				Expect(outputBuffer).To(test_helpers.Say("Instance 5"))
   824  				Expect(outputBuffer).To(test_helpers.Say("CRASHED"))
   825  				Expect(outputBuffer).To(test_helpers.SayNewLine())
   826  
   827  				Expect(outputBuffer).NotTo(test_helpers.Say("InstanceGuid"))
   828  				Expect(outputBuffer).To(test_helpers.Say("Crash Count"))
   829  				Expect(outputBuffer).To(test_helpers.Say("7"))
   830  				Expect(outputBuffer).To(test_helpers.SayNewLine())
   831  
   832  				Expect(outputBuffer).NotTo(test_helpers.Say("CPU"))
   833  				Expect(outputBuffer).NotTo(test_helpers.Say("Memory"))
   834  			})
   835  		})
   836  
   837  		Context("when the app was launched from a docker image", func() {
   838  			It("emits a pretty representation of the image", func() {
   839  				sampleAppInfo.RootFS = "docker:///wompy/app#latest"
   840  
   841  				fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil)
   842  
   843  				test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"wompy-app"})
   844  
   845  				Expect(fakeAppExaminer.AppStatusCallCount()).To(Equal(1))
   846  				Expect(fakeAppExaminer.AppStatusArgsForCall(0)).To(Equal("wompy-app"))
   847  
   848  				Expect(outputBuffer).To(test_helpers.Say("Docker Image"))
   849  				Expect(outputBuffer).To(test_helpers.SayLine("wompy/app:latest"))
   850  
   851  				Expect(outputBuffer).To(test_helpers.Say("Annotation"))
   852  			})
   853  
   854  			It("emits a pretty representation of the image without its library/ prefix", func() {
   855  				sampleAppInfo.RootFS = "docker:///library/wompy-app#latest"
   856  
   857  				fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil)
   858  
   859  				test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"wompy-app"})
   860  
   861  				Expect(fakeAppExaminer.AppStatusCallCount()).To(Equal(1))
   862  				Expect(fakeAppExaminer.AppStatusArgsForCall(0)).To(Equal("wompy-app"))
   863  
   864  				Expect(outputBuffer).To(test_helpers.Say("Docker Image"))
   865  				Expect(outputBuffer).NotTo(test_helpers.Say("library/"))
   866  				Expect(outputBuffer).To(test_helpers.SayLine("wompy-app:latest"))
   867  
   868  				Expect(outputBuffer).To(test_helpers.Say("Annotation"))
   869  			})
   870  
   871  			It("emits a pretty representation of the image with source", func() {
   872  				sampleAppInfo.RootFS = "docker://dhub/library/wompy-app#latest"
   873  
   874  				fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil)
   875  
   876  				test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"wompy-app"})
   877  
   878  				Expect(fakeAppExaminer.AppStatusCallCount()).To(Equal(1))
   879  				Expect(fakeAppExaminer.AppStatusArgsForCall(0)).To(Equal("wompy-app"))
   880  
   881  				Expect(outputBuffer).To(test_helpers.Say("Docker Image"))
   882  				Expect(outputBuffer).To(test_helpers.SayLine("dhub/library/wompy-app:latest"))
   883  
   884  				Expect(outputBuffer).To(test_helpers.Say("Annotation"))
   885  			})
   886  		})
   887  
   888  		Describe("Monitors", func() {
   889  			It("prints out Monitor None when there is no monitor", func() {
   890  				sampleAppInfo.Monitor = app_examiner.Monitor{}
   891  
   892  				fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil)
   893  
   894  				test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"wompy-app"})
   895  
   896  				Expect(outputBuffer).To(test_helpers.Say("Monitor"))
   897  				Expect(outputBuffer).To(test_helpers.SayLine("None"))
   898  			})
   899  
   900  			It("prints out Monitor Port when there is port-only monitor", func() {
   901  				sampleAppInfo.Monitor = app_examiner.Monitor{
   902  					Port: 1234,
   903  				}
   904  
   905  				fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil)
   906  
   907  				test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"wompy-app"})
   908  
   909  				Expect(outputBuffer).To(test_helpers.Say("Monitor"))
   910  				Expect(outputBuffer).To(test_helpers.SayLine("Port (1234)"))
   911  			})
   912  
   913  			It("prints out Monitor URL when there is a port- and uri- monitor", func() {
   914  				sampleAppInfo.Monitor = app_examiner.Monitor{
   915  					Port: 1234,
   916  					URI:  "/check",
   917  				}
   918  
   919  				fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil)
   920  
   921  				test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"wompy-app"})
   922  
   923  				Expect(outputBuffer).To(test_helpers.Say("Monitor"))
   924  				Expect(outputBuffer).To(test_helpers.SayLine("URL (1234:/check)"))
   925  			})
   926  
   927  			It("prints out Monitor Custom for other kinds of monitors", func() {
   928  				sampleAppInfo.Monitor = app_examiner.Monitor{
   929  					Command:     "/bin/sh",
   930  					CommandArgs: []string{"-c", "healthcheck", "-p"},
   931  				}
   932  
   933  				fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil)
   934  
   935  				test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"wompy-app"})
   936  
   937  				Expect(outputBuffer).To(test_helpers.Say("Monitor"))
   938  				Expect(outputBuffer).To(test_helpers.SayLine("Command (/bin/sh -c healthcheck -p)"))
   939  			})
   940  		})
   941  
   942  		It("prints out an unknown rootfs without parsing", func() {
   943  			sampleAppInfo.RootFS = "wuuuhhhhh"
   944  
   945  			fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil)
   946  
   947  			test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"wompy-app"})
   948  
   949  			Expect(fakeAppExaminer.AppStatusCallCount()).To(Equal(1))
   950  			Expect(fakeAppExaminer.AppStatusArgsForCall(0)).To(Equal("wompy-app"))
   951  
   952  			Expect(outputBuffer).To(test_helpers.Say("Routes"))
   953  			Expect(outputBuffer).To(test_helpers.Say("wompy-app.my-fun-domain.com => 8887"))
   954  			Expect(outputBuffer).To(test_helpers.SayNewLine())
   955  			Expect(outputBuffer).To(test_helpers.Say("cranky-app.my-fun-domain.com => 8887"))
   956  			Expect(outputBuffer).To(test_helpers.SayNewLine())
   957  			Expect(outputBuffer).To(test_helpers.Say("route-me.my-fun-domain.com => 9000"))
   958  			Expect(outputBuffer).To(test_helpers.SayNewLine())
   959  
   960  			Expect(outputBuffer).To(test_helpers.Say("RootFS"))
   961  			Expect(outputBuffer).To(test_helpers.SayLine("wuuuhhhhh"))
   962  
   963  			Expect(outputBuffer).To(test_helpers.Say("Annotation"))
   964  			Expect(outputBuffer).To(test_helpers.SayLine("I love this app. So wompy."))
   965  		})
   966  
   967  		Context("when there are only tcp routes", func() {
   968  			BeforeEach(func() {
   969  				sampleAppInfo.Routes = route_helpers.Routes{
   970  					TcpRoutes: route_helpers.TcpRoutes{
   971  						{ExternalPort: 52000, Port: 9000},
   972  						{ExternalPort: 51000, Port: 8887},
   973  					},
   974  				}
   975  			})
   976  
   977  			It("emits a pretty representation of the DesiredLRP", func() {
   978  				fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil)
   979  
   980  				test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"wompy-app"})
   981  
   982  				Expect(fakeAppExaminer.AppStatusCallCount()).To(Equal(1))
   983  				Expect(fakeAppExaminer.AppStatusArgsForCall(0)).To(Equal("wompy-app"))
   984  
   985  				Expect(outputBuffer).To(test_helpers.Say("Ports"))
   986  				Expect(outputBuffer).To(test_helpers.Say("8887"))
   987  				Expect(outputBuffer).To(test_helpers.Say("9000"))
   988  				Expect(outputBuffer).To(test_helpers.SayNewLine())
   989  
   990  				Expect(outputBuffer).To(test_helpers.Say("Routes"))
   991  				Expect(outputBuffer).To(test_helpers.Say("system.domain:51000 => 8887"))
   992  				Expect(outputBuffer).To(test_helpers.SayNewLine())
   993  				Expect(outputBuffer).To(test_helpers.Say("system.domain:52000 => 9000"))
   994  				Expect(outputBuffer).To(test_helpers.SayNewLine())
   995  			})
   996  		})
   997  
   998  		Context("when there are both http and tcp routes", func() {
   999  			BeforeEach(func() {
  1000  				sampleAppInfo.Routes = route_helpers.Routes{
  1001  					TcpRoutes: route_helpers.TcpRoutes{
  1002  						{ExternalPort: 51000, Port: 8887},
  1003  					},
  1004  					AppRoutes: route_helpers.AppRoutes{
  1005  						{Hostnames: []string{"route-me.my-fun-domain.com"}, Port: 9000},
  1006  					},
  1007  				}
  1008  			})
  1009  
  1010  			It("emits a pretty representation of the DesiredLRP", func() {
  1011  				fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil)
  1012  
  1013  				test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"wompy-app"})
  1014  
  1015  				Expect(fakeAppExaminer.AppStatusCallCount()).To(Equal(1))
  1016  				Expect(fakeAppExaminer.AppStatusArgsForCall(0)).To(Equal("wompy-app"))
  1017  
  1018  				Expect(outputBuffer).To(test_helpers.Say("Ports"))
  1019  				Expect(outputBuffer).To(test_helpers.Say("8887"))
  1020  				Expect(outputBuffer).To(test_helpers.Say("9000"))
  1021  				Expect(outputBuffer).To(test_helpers.SayNewLine())
  1022  
  1023  				Expect(outputBuffer).To(test_helpers.Say("Routes"))
  1024  				Expect(outputBuffer).To(test_helpers.Say("system.domain:51000 => 8887"))
  1025  				Expect(outputBuffer).To(test_helpers.SayNewLine())
  1026  				Expect(outputBuffer).To(test_helpers.Say("route-me.my-fun-domain.com => 9000"))
  1027  				Expect(outputBuffer).To(test_helpers.SayNewLine())
  1028  			})
  1029  		})
  1030  
  1031  		Context("when there is a placement error on an actualLRP", func() {
  1032  			It("Displays UNCLAIMED in red, and outputs only the placement error", func() {
  1033  				fakeAppExaminer.AppStatusReturns(
  1034  					app_examiner.AppInfo{
  1035  						ActualInstances: []app_examiner.InstanceInfo{
  1036  							{
  1037  								Index:          7,
  1038  								State:          "UNCLAIMED",
  1039  								PlacementError: "insufficient resources.",
  1040  							},
  1041  						},
  1042  					}, nil)
  1043  
  1044  				test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"swanky-app", "--detailed"})
  1045  
  1046  				Expect(outputBuffer).To(test_helpers.Say("Instance 7"))
  1047  				Expect(outputBuffer).To(test_helpers.Say("UNCLAIMED"))
  1048  				Expect(outputBuffer).To(test_helpers.SayNewLine())
  1049  
  1050  				Expect(outputBuffer).NotTo(test_helpers.Say("InstanceGuid"))
  1051  				Expect(outputBuffer).NotTo(test_helpers.Say("Cell ID"))
  1052  				Expect(outputBuffer).NotTo(test_helpers.Say("Ip"))
  1053  				Expect(outputBuffer).NotTo(test_helpers.Say("Port Mapping"))
  1054  				Expect(outputBuffer).NotTo(test_helpers.Say("Uptime"))
  1055  
  1056  				Expect(outputBuffer).To(test_helpers.Say("Placement Error"))
  1057  				Expect(outputBuffer).To(test_helpers.Say("insufficient resources."))
  1058  				Expect(outputBuffer).To(test_helpers.SayNewLine())
  1059  			})
  1060  		})
  1061  
  1062  		Context("when a rate flag is passed", func() {
  1063  			var closeChan chan struct{}
  1064  
  1065  			AfterEach(func() {
  1066  				go fakeExitHandler.Exit(exit_codes.Signal)
  1067  				Eventually(closeChan).Should(BeClosed())
  1068  
  1069  				_, err := fmt.Print(cursor.Show())
  1070  				Expect(err).NotTo(HaveOccurred())
  1071  			})
  1072  
  1073  			Context("when term is set", func() {
  1074  				var previousTerm string
  1075  
  1076  				BeforeEach(func() {
  1077  					previousTerm = os.Getenv("TERM")
  1078  					os.Setenv("TERM", "xterm")
  1079  
  1080  				})
  1081  
  1082  				AfterEach(func() {
  1083  					os.Setenv("TERM", previousTerm)
  1084  				})
  1085  
  1086  				It("refreshes for the designated time", func() {
  1087  					fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil)
  1088  
  1089  					closeChan = test_helpers.AsyncExecuteCommandWithArgs(statusCommand, []string{"wompy-app", "--rate", "2s"})
  1090  
  1091  					Consistently(closeChan).ShouldNot(BeClosed())
  1092  					Eventually(outputBuffer).Should(test_helpers.Say("wompy-app"))
  1093  					Expect(outputBuffer).To(test_helpers.SayNewLine())
  1094  
  1095  					roundedTimeSince := roundTime(fakeClock.Now(), time.Unix(0, epochTime*1e9))
  1096  					Expect(outputBuffer).To(test_helpers.Say(roundedTimeSince))
  1097  					Expect(outputBuffer).To(test_helpers.SayNewLine())
  1098  
  1099  					fakeClock.IncrementBySeconds(1)
  1100  
  1101  					Consistently(outputBuffer).ShouldNot(test_helpers.Say("wompy-app"))
  1102  
  1103  					refreshTime := int64(405234567)
  1104  					refreshAppInfo := app_examiner.AppInfo{
  1105  						ProcessGuid:            "wompy-app",
  1106  						DesiredInstances:       1,
  1107  						ActualRunningInstances: 1,
  1108  						ActualInstances: []app_examiner.InstanceInfo{
  1109  							{
  1110  								InstanceGuid: "a0s9f-u9a8sf-aasdioasdjoi",
  1111  								Index:        1,
  1112  								State:        "RUNNING",
  1113  								Since:        refreshTime * 1e9,
  1114  							},
  1115  						},
  1116  						EnvironmentVariables: []app_examiner.EnvironmentVariable{
  1117  							{
  1118  								Name:  "VCAP_APPLICATION",
  1119  								Value: `{"application_name":"latty","application_uris":["latty.192.168.11.11.xip.io","latty-8080.192.168.11.11.xip.io"],"name":"latty","uris":["latty.192.168.11.11.xip.io","latty-8080.192.168.11.11.xip.io"],"limits":{"mem":128}}`,
  1120  							},
  1121  						},
  1122  					}
  1123  
  1124  					fakeAppExaminer.AppStatusReturns(refreshAppInfo, nil)
  1125  
  1126  					Eventually(outputBuffer).Should(test_helpers.Say(cursor.Hide()))
  1127  
  1128  					fakeClock.IncrementBySeconds(1)
  1129  
  1130  					Eventually(outputBuffer).Should(test_helpers.Say(cursor.Up(27)))
  1131  					Eventually(outputBuffer).Should(test_helpers.Say("wompy-app"))
  1132  					Eventually(outputBuffer).Should(test_helpers.SayNewLine())
  1133  					roundedTimeSince = roundTime(fakeClock.Now(), time.Unix(0, refreshTime*1e9))
  1134  					Eventually(outputBuffer).Should(test_helpers.Say(roundedTimeSince))
  1135  
  1136  					Consistently(closeChan).ShouldNot(BeClosed())
  1137  				})
  1138  
  1139  				It("calculates the correct height based on terminal width", func() {
  1140  					fakeTerm.GetWindowWidthReturns(200, nil)
  1141  
  1142  					refreshTime := int64(405234567)
  1143  					wrappedAppInfo := app_examiner.AppInfo{
  1144  						DesiredInstances:       1,
  1145  						ActualRunningInstances: 1,
  1146  						ActualInstances: []app_examiner.InstanceInfo{
  1147  							{
  1148  								InstanceGuid: "a0s9f-u9a8sf-aasdioasdjoi",
  1149  								Index:        1,
  1150  								State:        "RUNNING",
  1151  								Since:        refreshTime * 1e9,
  1152  							},
  1153  						},
  1154  						EnvironmentVariables: []app_examiner.EnvironmentVariable{
  1155  							{
  1156  								Name:  "VCAP_APPLICATION",
  1157  								Value: `{"application_name":"latty","application_uris":["latty.192.168.11.11.xip.io","latty-8080.192.168.11.11.xip.io"],"name":"latty","uris":["latty.192.168.11.11.xip.io","latty-8080.192.168.11.11.xip.io"],"limits":{"mem":128}}`,
  1158  							},
  1159  						},
  1160  					}
  1161  
  1162  					fakeAppExaminer.AppStatusReturns(wrappedAppInfo, nil)
  1163  
  1164  					closeChan = test_helpers.AsyncExecuteCommandWithArgs(statusCommand, []string{"wompy-app", "--rate", "2s"})
  1165  
  1166  					Eventually(outputBuffer).Should(test_helpers.Say(cursor.Hide()))
  1167  
  1168  					fakeClock.IncrementBySeconds(3)
  1169  
  1170  					Eventually(outputBuffer).Should(test_helpers.Say(cursor.Up(20)))
  1171  
  1172  					Consistently(closeChan).ShouldNot(BeClosed())
  1173  				})
  1174  
  1175  				It("dynamically displays any errors", func() {
  1176  					fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil)
  1177  
  1178  					closeChan = test_helpers.AsyncExecuteCommandWithArgs(statusCommand, []string{"wompy-app", "--rate", "1s"})
  1179  
  1180  					Eventually(outputBuffer).Should(test_helpers.Say("wompy-app"))
  1181  					Expect(outputBuffer).NotTo(test_helpers.Say("Error getting status"))
  1182  
  1183  					fakeAppExaminer.AppStatusReturns(app_examiner.AppInfo{}, errors.New("error fetching status"))
  1184  					fakeClock.IncrementBySeconds(1)
  1185  					Eventually(closeChan).Should(BeClosed())
  1186  
  1187  					Expect(outputBuffer).NotTo(test_helpers.Say(TerminalEsc + "\\d+A"))
  1188  					Expect(outputBuffer).To(test_helpers.Say("Error getting status: error fetching status"))
  1189  					Expect(outputBuffer).To(test_helpers.Say(cursor.Show()))
  1190  				})
  1191  
  1192  				Context("when the user interrupts ltc status with ctrl-c", func() {
  1193  					var previousTerm string
  1194  
  1195  					BeforeEach(func() {
  1196  						previousTerm = os.Getenv("TERM")
  1197  						Expect(os.Setenv("TERM", "xterm")).To(Succeed())
  1198  					})
  1199  
  1200  					AfterEach(func() {
  1201  						Expect(os.Setenv("TERM", previousTerm)).To(Succeed())
  1202  					})
  1203  
  1204  					It("ensures the user's cursor is still visible", func() {
  1205  						closeChan = test_helpers.AsyncExecuteCommandWithArgs(statusCommand, []string{"wompy-app", "--rate=1s"})
  1206  
  1207  						Eventually(outputBuffer).Should(test_helpers.Say(cursor.Hide()))
  1208  						fakeExitHandler.Exit(exit_codes.Signal)
  1209  						Eventually(closeChan).Should(BeClosed())
  1210  
  1211  						Expect(outputBuffer).To(test_helpers.Say(cursor.Show()))
  1212  						Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.Signal}))
  1213  					})
  1214  				})
  1215  
  1216  				Context("when the --detailed flag is also passed", func() {
  1217  					It("prints a warning message", func() {
  1218  						fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil)
  1219  
  1220  						closeChan = test_helpers.AsyncExecuteCommandWithArgs(statusCommand, []string{"wompy-app", "--rate", "2s", "--detailed"})
  1221  
  1222  						Consistently(closeChan).ShouldNot(BeClosed())
  1223  						Eventually(outputBuffer).Should(test_helpers.SayLine("WARNING: flags '--detailed' and '--rate' are incompatible."))
  1224  					})
  1225  
  1226  					It("continues as normal with summary output", func() {
  1227  						fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil)
  1228  
  1229  						closeChan = test_helpers.AsyncExecuteCommandWithArgs(statusCommand, []string{"wompy-app", "--rate", "2s", "--detailed"})
  1230  
  1231  						Consistently(closeChan).ShouldNot(BeClosed())
  1232  						Eventually(outputBuffer).Should(test_helpers.Say("wompy-app"))
  1233  						Expect(outputBuffer).To(test_helpers.SayNewLine())
  1234  
  1235  						roundedTimeSince := roundTime(fakeClock.Now(), time.Unix(0, epochTime*1e9))
  1236  						Expect(outputBuffer).To(test_helpers.Say(roundedTimeSince))
  1237  						Expect(outputBuffer).To(test_helpers.SayNewLine())
  1238  
  1239  						fakeClock.IncrementBySeconds(1)
  1240  
  1241  						Consistently(outputBuffer).ShouldNot(test_helpers.Say("wompy-app"))
  1242  
  1243  						refreshTime := int64(405234567)
  1244  						refreshAppInfo := app_examiner.AppInfo{
  1245  							ProcessGuid:            "wompy-app",
  1246  							DesiredInstances:       1,
  1247  							ActualRunningInstances: 1,
  1248  							ActualInstances: []app_examiner.InstanceInfo{
  1249  								{
  1250  									InstanceGuid: "a0s9f-u9a8sf-aasdioasdjoi",
  1251  									Index:        1,
  1252  									State:        "RUNNING",
  1253  									Since:        refreshTime * 1e9,
  1254  								},
  1255  							},
  1256  							EnvironmentVariables: []app_examiner.EnvironmentVariable{
  1257  								{
  1258  									Name:  "VCAP_APPLICATION",
  1259  									Value: `{"application_name":"latty","application_uris":["latty.192.168.11.11.xip.io","latty-8080.192.168.11.11.xip.io"],"name":"latty","uris":["latty.192.168.11.11.xip.io","latty-8080.192.168.11.11.xip.io"],"limits":{"mem":128}}`,
  1260  								},
  1261  							},
  1262  						}
  1263  
  1264  						fakeAppExaminer.AppStatusReturns(refreshAppInfo, nil)
  1265  
  1266  						Eventually(outputBuffer).Should(test_helpers.Say(cursor.Hide()))
  1267  
  1268  						fakeClock.IncrementBySeconds(1)
  1269  
  1270  						Eventually(outputBuffer).Should(test_helpers.Say(cursor.Up(27)))
  1271  						Eventually(outputBuffer).Should(test_helpers.Say("wompy-app"))
  1272  						Eventually(outputBuffer).Should(test_helpers.SayNewLine())
  1273  						roundedTimeSince = roundTime(fakeClock.Now(), time.Unix(0, refreshTime*1e9))
  1274  						Eventually(outputBuffer).Should(test_helpers.Say(roundedTimeSince))
  1275  
  1276  						Consistently(closeChan).ShouldNot(BeClosed())
  1277  					})
  1278  				})
  1279  
  1280  				Context("when term is unset", func() {
  1281  					var previousTerm string
  1282  
  1283  					BeforeEach(func() {
  1284  						previousTerm = os.Getenv("TERM")
  1285  						os.Unsetenv("TERM")
  1286  
  1287  					})
  1288  
  1289  					AfterEach(func() {
  1290  						os.Setenv("TERM", previousTerm)
  1291  					})
  1292  
  1293  					It("prints once", func() {
  1294  						fakeAppExaminer.AppStatusReturns(sampleAppInfo, nil)
  1295  
  1296  						closeChan = test_helpers.AsyncExecuteCommandWithArgs(statusCommand, []string{"wompy-app", "--rate", "2s"})
  1297  
  1298  						Eventually(outputBuffer).Should(test_helpers.Say("wompy-app"))
  1299  						Expect(outputBuffer).To(test_helpers.SayNewLine())
  1300  
  1301  						roundedTimeSince := roundTime(fakeClock.Now(), time.Unix(0, epochTime*1e9))
  1302  						Expect(outputBuffer).To(test_helpers.Say(roundedTimeSince))
  1303  						Expect(outputBuffer).To(test_helpers.SayNewLine())
  1304  
  1305  						Expect(closeChan).To(BeClosed())
  1306  					})
  1307  				})
  1308  			})
  1309  		})
  1310  
  1311  		Context("when annotation is empty", func() {
  1312  			It("omits annotation from the output", func() {
  1313  				fakeAppExaminer.AppStatusReturns(app_examiner.AppInfo{ProcessGuid: "jumpy-app"}, nil)
  1314  
  1315  				test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"jumpy-app"})
  1316  
  1317  				Expect(outputBuffer).NotTo(test_helpers.Say("Annotation"))
  1318  			})
  1319  		})
  1320  
  1321  		Context("when no app name is specified", func() {
  1322  			It("prints usage information", func() {
  1323  				test_helpers.ExecuteCommandWithArgs(statusCommand, []string{})
  1324  
  1325  				Expect(outputBuffer).To(test_helpers.SayIncorrectUsage())
  1326  				Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.InvalidSyntax}))
  1327  			})
  1328  		})
  1329  
  1330  		It("prints any errors from app examiner", func() {
  1331  			fakeAppExaminer.AppStatusReturns(app_examiner.AppInfo{}, errors.New("You want the status?? ...YOU CAN'T HANDLE THE STATUS!!!"))
  1332  
  1333  			test_helpers.ExecuteCommandWithArgs(statusCommand, []string{"zany-app"})
  1334  
  1335  			Expect(outputBuffer).To(test_helpers.SayLine("You want the status?? ...YOU CAN'T HANDLE THE STATUS!!!"))
  1336  			Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.CommandFailed}))
  1337  		})
  1338  	})
  1339  
  1340  	Describe("Cells", func() {
  1341  		var cellsCommand cli.Command
  1342  
  1343  		BeforeEach(func() {
  1344  			commandFactory := command_factory.NewAppExaminerCommandFactory(fakeAppExaminer, terminalUI, fakeTerm, fakeClock, fakeExitHandler, nil, fakeTaskExaminer, systemDomain)
  1345  			cellsCommand = commandFactory.MakeCellsCommand()
  1346  		})
  1347  
  1348  		It("lists the cells", func() {
  1349  			fakeAppExaminer.ListCellsReturns([]app_examiner.CellInfo{
  1350  				{
  1351  					CellID:           "cell-one",
  1352  					RunningInstances: 37,
  1353  					ClaimedInstances: 12,
  1354  					Zone:             "z1",
  1355  					MemoryMB:         1229,
  1356  					DiskMB:           4301,
  1357  					Containers:       256,
  1358  				},
  1359  				{
  1360  					CellID: "cell-two",
  1361  				},
  1362  			}, nil)
  1363  
  1364  			test_helpers.ExecuteCommandWithArgs(cellsCommand, []string{})
  1365  
  1366  			Expect(outputBuffer).To(test_helpers.Say("Cells"))
  1367  			Expect(outputBuffer).To(test_helpers.Say("Zone"))
  1368  			Expect(outputBuffer).To(test_helpers.Say("Memory"))
  1369  			Expect(outputBuffer).To(test_helpers.Say("Disk"))
  1370  			Expect(outputBuffer).To(test_helpers.Say("Apps"))
  1371  			Expect(outputBuffer).To(test_helpers.SayNewLine())
  1372  
  1373  			Expect(outputBuffer).To(test_helpers.Say("cell-one"))
  1374  			Expect(outputBuffer).To(test_helpers.Say("z1"))
  1375  			Expect(outputBuffer).To(test_helpers.Say("1229M"))
  1376  			Expect(outputBuffer).To(test_helpers.Say("4301M"))
  1377  			Expect(outputBuffer).To(test_helpers.Say("37/12"))
  1378  			Expect(outputBuffer).To(test_helpers.SayNewLine())
  1379  
  1380  			Expect(outputBuffer).To(test_helpers.Say("cell-two"))
  1381  			Expect(outputBuffer).To(test_helpers.SayNewLine())
  1382  
  1383  			Expect(fakeAppExaminer.ListCellsCallCount()).To(Equal(1))
  1384  		})
  1385  
  1386  		Context("when the receptor returns an error", func() {
  1387  			It("prints an error", func() {
  1388  				fakeAppExaminer.ListCellsReturns(nil, errors.New("these are not the cells you're looking for"))
  1389  
  1390  				test_helpers.ExecuteCommandWithArgs(cellsCommand, []string{})
  1391  
  1392  				Expect(outputBuffer).To(test_helpers.SayLine("these are not the cells you're looking for"))
  1393  				Expect(outputBuffer).NotTo(test_helpers.Say("Cells"))
  1394  
  1395  				Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.CommandFailed}))
  1396  			})
  1397  		})
  1398  	})
  1399  })