github.com/ablease/cli@v6.37.1-0.20180613014814-3adbb7d7fb19+incompatible/command/v2/app_command_test.go (about)

     1  package v2_test
     2  
     3  import (
     4  	"errors"
     5  	"time"
     6  
     7  	"code.cloudfoundry.org/bytefmt"
     8  	"code.cloudfoundry.org/cli/actor/actionerror"
     9  	"code.cloudfoundry.org/cli/actor/v2action"
    10  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv2/constant"
    11  	"code.cloudfoundry.org/cli/command/commandfakes"
    12  	. "code.cloudfoundry.org/cli/command/v2"
    13  	"code.cloudfoundry.org/cli/command/v2/v2fakes"
    14  	"code.cloudfoundry.org/cli/types"
    15  	"code.cloudfoundry.org/cli/util/configv3"
    16  	"code.cloudfoundry.org/cli/util/ui"
    17  	. "github.com/onsi/ginkgo"
    18  	. "github.com/onsi/gomega"
    19  	. "github.com/onsi/gomega/gbytes"
    20  )
    21  
    22  var _ = Describe("App Command", func() {
    23  	var (
    24  		cmd             AppCommand
    25  		testUI          *ui.UI
    26  		fakeConfig      *commandfakes.FakeConfig
    27  		fakeSharedActor *commandfakes.FakeSharedActor
    28  		fakeActor       *v2fakes.FakeAppActor
    29  		binaryName      string
    30  		executeErr      error
    31  	)
    32  
    33  	BeforeEach(func() {
    34  		testUI = ui.NewTestUI(nil, NewBuffer(), NewBuffer())
    35  		fakeConfig = new(commandfakes.FakeConfig)
    36  		fakeSharedActor = new(commandfakes.FakeSharedActor)
    37  		fakeActor = new(v2fakes.FakeAppActor)
    38  
    39  		cmd = AppCommand{
    40  			UI:          testUI,
    41  			Config:      fakeConfig,
    42  			SharedActor: fakeSharedActor,
    43  			Actor:       fakeActor,
    44  		}
    45  
    46  		cmd.RequiredArgs.AppName = "some-app"
    47  
    48  		binaryName = "faceman"
    49  		fakeConfig.BinaryNameReturns(binaryName)
    50  	})
    51  
    52  	JustBeforeEach(func() {
    53  		executeErr = cmd.Execute(nil)
    54  	})
    55  
    56  	Context("when checking target fails", func() {
    57  		BeforeEach(func() {
    58  			fakeSharedActor.CheckTargetReturns(actionerror.NotLoggedInError{BinaryName: binaryName})
    59  		})
    60  
    61  		It("returns an error if the check fails", func() {
    62  			Expect(executeErr).To(MatchError(actionerror.NotLoggedInError{BinaryName: "faceman"}))
    63  
    64  			Expect(fakeSharedActor.CheckTargetCallCount()).To(Equal(1))
    65  			checkTargetedOrg, checkTargetedSpace := fakeSharedActor.CheckTargetArgsForCall(0)
    66  			Expect(checkTargetedOrg).To(BeTrue())
    67  			Expect(checkTargetedSpace).To(BeTrue())
    68  		})
    69  	})
    70  
    71  	Context("when the user is logged in, and org and space are targeted", func() {
    72  		BeforeEach(func() {
    73  			fakeConfig.HasTargetedOrganizationReturns(true)
    74  			fakeConfig.TargetedOrganizationReturns(configv3.Organization{Name: "some-org"})
    75  			fakeConfig.HasTargetedSpaceReturns(true)
    76  			fakeConfig.TargetedSpaceReturns(configv3.Space{
    77  				GUID: "some-space-guid",
    78  				Name: "some-space"})
    79  			fakeConfig.CurrentUserReturns(
    80  				configv3.User{Name: "some-user"},
    81  				nil)
    82  		})
    83  
    84  		Context("when getting the current user returns an error", func() {
    85  			var expectedErr error
    86  
    87  			BeforeEach(func() {
    88  				expectedErr = errors.New("getting current user error")
    89  				fakeConfig.CurrentUserReturns(
    90  					configv3.User{},
    91  					expectedErr)
    92  			})
    93  
    94  			It("returns the error", func() {
    95  				Expect(executeErr).To(MatchError(expectedErr))
    96  			})
    97  		})
    98  
    99  		It("displays flavor text", func() {
   100  			Expect(testUI.Out).To(Say("Showing health and status for app some-app in org some-org / space some-space as some-user..."))
   101  		})
   102  
   103  		Context("when the --guid flag is provided", func() {
   104  			BeforeEach(func() {
   105  				cmd.GUID = true
   106  			})
   107  
   108  			Context("when no errors occur", func() {
   109  				BeforeEach(func() {
   110  					fakeActor.GetApplicationByNameAndSpaceReturns(
   111  						v2action.Application{GUID: "some-guid"},
   112  						v2action.Warnings{"warning-1", "warning-2"},
   113  						nil)
   114  				})
   115  
   116  				It("displays the application guid and all warnings", func() {
   117  					Expect(executeErr).ToNot(HaveOccurred())
   118  
   119  					Expect(testUI.Out).To(Say("some-guid"))
   120  					Expect(testUI.Err).To(Say("warning-1"))
   121  					Expect(testUI.Err).To(Say("warning-2"))
   122  				})
   123  			})
   124  
   125  			Context("when an error is encountered getting the app", func() {
   126  				Context("when the error is translatable", func() {
   127  					BeforeEach(func() {
   128  						fakeActor.GetApplicationByNameAndSpaceReturns(
   129  							v2action.Application{},
   130  							v2action.Warnings{"warning-1", "warning-2"},
   131  							actionerror.ApplicationNotFoundError{Name: "some-app"})
   132  					})
   133  
   134  					It("returns a translatable error and all warnings", func() {
   135  						Expect(executeErr).To(MatchError(actionerror.ApplicationNotFoundError{Name: "some-app"}))
   136  
   137  						Expect(testUI.Err).To(Say("warning-1"))
   138  						Expect(testUI.Err).To(Say("warning-2"))
   139  					})
   140  				})
   141  
   142  				Context("when the error is not translatable", func() {
   143  					var expectedErr error
   144  
   145  					BeforeEach(func() {
   146  						expectedErr = errors.New("get app summary error")
   147  						fakeActor.GetApplicationByNameAndSpaceReturns(
   148  							v2action.Application{},
   149  							v2action.Warnings{"warning-1", "warning-2"},
   150  							expectedErr)
   151  					})
   152  
   153  					It("returns the error and all warnings", func() {
   154  						Expect(executeErr).To(MatchError(expectedErr))
   155  
   156  						Expect(testUI.Err).To(Say("warning-1"))
   157  						Expect(testUI.Err).To(Say("warning-2"))
   158  					})
   159  				})
   160  			})
   161  		})
   162  
   163  		Context("when the --guid flag is not provided", func() {
   164  			Context("when the app is a buildpack app", func() {
   165  				Context("when no errors occur", func() {
   166  					var (
   167  						applicationSummary v2action.ApplicationSummary
   168  						warnings           []string
   169  					)
   170  
   171  					BeforeEach(func() {
   172  						applicationSummary = v2action.ApplicationSummary{
   173  							Application: v2action.Application{
   174  								Name:              "some-app",
   175  								GUID:              "some-app-guid",
   176  								Instances:         types.NullInt{Value: 3, IsSet: true},
   177  								Memory:            types.NullByteSizeInMb{IsSet: true, Value: 128},
   178  								PackageUpdatedAt:  time.Unix(0, 0),
   179  								DetectedBuildpack: types.FilteredString{IsSet: true, Value: "some-buildpack"},
   180  								State:             "STARTED",
   181  							},
   182  							IsolationSegment: "some-isolation-segment",
   183  							Stack: v2action.Stack{
   184  								Name: "potatos",
   185  							},
   186  							Routes: []v2action.Route{
   187  								{
   188  									Host: "banana",
   189  									Domain: v2action.Domain{
   190  										Name: "fruit.com",
   191  									},
   192  									Path: "/hi",
   193  								},
   194  								{
   195  									Domain: v2action.Domain{
   196  										Name: "foobar.com",
   197  									},
   198  									Port: types.NullInt{IsSet: true, Value: 13},
   199  								},
   200  							},
   201  						}
   202  						warnings = []string{"app-summary-warning"}
   203  					})
   204  
   205  					Context("when the app does not have running instances", func() {
   206  						BeforeEach(func() {
   207  							applicationSummary.RunningInstances = []v2action.ApplicationInstanceWithStats{}
   208  							fakeActor.GetApplicationSummaryByNameAndSpaceReturns(applicationSummary, warnings, nil)
   209  						})
   210  
   211  						It("displays the app summary, 'no running instances' message, and all warnings", func() {
   212  							Expect(testUI.Out).To(Say("Showing health and status for app some-app in org some-org / space some-space as some-user..."))
   213  							Expect(testUI.Out).To(Say(""))
   214  
   215  							Expect(testUI.Out).To(Say("name:\\s+some-app"))
   216  							Expect(testUI.Out).To(Say("requested state:\\s+started"))
   217  							Expect(testUI.Out).To(Say("instances:\\s+0\\/3"))
   218  							// Note: in real life, iso segs are tied to *running* instances, so this field
   219  							// would be blank
   220  							Expect(testUI.Out).To(Say("isolation segment:\\s+some-isolation-segment"))
   221  							Expect(testUI.Out).To(Say("usage:\\s+128M x 3 instances"))
   222  							Expect(testUI.Out).To(Say("routes:\\s+banana.fruit.com/hi, foobar.com:13"))
   223  							Expect(testUI.Out).To(Say("last uploaded:\\s+\\w{3} [0-3]\\d \\w{3} [0-2]\\d:[0-5]\\d:[0-5]\\d \\w+ \\d{4}"))
   224  							Expect(testUI.Out).To(Say("stack:\\s+potatos"))
   225  							Expect(testUI.Out).To(Say("buildpack:\\s+some-buildpack"))
   226  							Expect(testUI.Out).To(Say(""))
   227  							Expect(testUI.Out).To(Say("There are no running instances of this app"))
   228  
   229  							Expect(testUI.Err).To(Say("app-summary-warning"))
   230  						})
   231  
   232  						It("should not display the instance table", func() {
   233  							Expect(testUI.Out).NotTo(Say("state\\s+since\\s+cpu\\s+memory\\s+disk"))
   234  						})
   235  					})
   236  
   237  					Context("when the app has running instances", func() {
   238  						BeforeEach(func() {
   239  							applicationSummary.RunningInstances = []v2action.ApplicationInstanceWithStats{
   240  								{
   241  									ID:          0,
   242  									State:       v2action.ApplicationInstanceState(constant.ApplicationInstanceRunning),
   243  									Since:       1403140717.984577,
   244  									CPU:         0.73,
   245  									Disk:        50 * bytefmt.MEGABYTE,
   246  									DiskQuota:   2048 * bytefmt.MEGABYTE,
   247  									Memory:      100 * bytefmt.MEGABYTE,
   248  									MemoryQuota: 128 * bytefmt.MEGABYTE,
   249  									Details:     "info from the backend",
   250  								},
   251  								{
   252  									ID:          1,
   253  									State:       v2action.ApplicationInstanceState(constant.ApplicationInstanceCrashed),
   254  									Since:       1403100000.900000,
   255  									CPU:         0.37,
   256  									Disk:        50 * bytefmt.MEGABYTE,
   257  									DiskQuota:   2048 * bytefmt.MEGABYTE,
   258  									Memory:      100 * bytefmt.MEGABYTE,
   259  									MemoryQuota: 128 * bytefmt.MEGABYTE,
   260  									Details:     "potato",
   261  								},
   262  							}
   263  						})
   264  
   265  						Context("when the isolation segment is not empty", func() {
   266  							BeforeEach(func() {
   267  								fakeActor.GetApplicationSummaryByNameAndSpaceReturns(applicationSummary, warnings, nil)
   268  							})
   269  
   270  							It("displays app summary, instance table, and all warnings", func() {
   271  								Expect(testUI.Out).To(Say("Showing health and status for app some-app in org some-org / space some-space as some-user..."))
   272  								Expect(testUI.Out).To(Say(""))
   273  								Expect(testUI.Out).To(Say("name:\\s+some-app"))
   274  								Expect(testUI.Out).To(Say("requested state:\\s+started"))
   275  								Expect(testUI.Out).To(Say("instances:\\s+1\\/3"))
   276  								Expect(testUI.Out).To(Say("isolation segment:\\s+some-isolation-segment"))
   277  								Expect(testUI.Out).To(Say("usage:\\s+128M x 3 instances"))
   278  								Expect(testUI.Out).To(Say("routes:\\s+banana.fruit.com/hi, foobar.com:13"))
   279  								Expect(testUI.Out).To(Say("last uploaded:\\s+\\w{3} [0-3]\\d \\w{3} [0-2]\\d:[0-5]\\d:[0-5]\\d \\w+ \\d{4}"))
   280  								Expect(testUI.Out).To(Say("stack:\\s+potatos"))
   281  								Expect(testUI.Out).To(Say("buildpack:\\s+some-buildpack"))
   282  								Expect(testUI.Out).To(Say(""))
   283  								Expect(testUI.Out).To(Say("state\\s+since\\s+cpu\\s+memory\\s+disk\\s+details"))
   284  								Expect(testUI.Out).To(Say(`#0\s+running\s+2014-06-19T01:18:37Z\s+73.0%\s+100M of 128M\s+50M of 2G\s+info from the backend`))
   285  								Expect(testUI.Out).To(Say(`#1\s+crashed\s+2014-06-18T14:00:00Z\s+37.0%\s+100M of 128M\s+50M of 2G\s+potato`))
   286  
   287  								Expect(testUI.Err).To(Say("app-summary-warning"))
   288  
   289  								Expect(fakeActor.GetApplicationSummaryByNameAndSpaceCallCount()).To(Equal(1))
   290  								appName, spaceGUID := fakeActor.GetApplicationSummaryByNameAndSpaceArgsForCall(0)
   291  								Expect(appName).To(Equal("some-app"))
   292  								Expect(spaceGUID).To(Equal("some-space-guid"))
   293  							})
   294  						})
   295  
   296  						Context("when the isolation segment is empty", func() {
   297  							BeforeEach(func() {
   298  								applicationSummary.IsolationSegment = ""
   299  								fakeActor.GetApplicationSummaryByNameAndSpaceReturns(applicationSummary, warnings, nil)
   300  							})
   301  
   302  							It("displays app summary, instance table, and all warnings", func() {
   303  								Expect(testUI.Out).To(Say("Showing health and status for app some-app in org some-org / space some-space as some-user..."))
   304  								Expect(testUI.Out).To(Say(""))
   305  								Expect(testUI.Out).To(Say("name:\\s+some-app"))
   306  								Expect(testUI.Out).To(Say("requested state:\\s+started"))
   307  								Expect(testUI.Out).To(Say("instances:\\s+1\\/3"))
   308  								Expect(testUI.Out).ToNot(Say("isolation segment:\\s+"))
   309  								Expect(testUI.Out).To(Say("usage:\\s+128M x 3 instances"))
   310  								Expect(testUI.Out).To(Say("routes:\\s+banana.fruit.com/hi, foobar.com:13"))
   311  								Expect(testUI.Out).To(Say("last uploaded:\\s+\\w{3} [0-3]\\d \\w{3} [0-2]\\d:[0-5]\\d:[0-5]\\d \\w+ \\d{4}"))
   312  								Expect(testUI.Out).To(Say("stack:\\s+potatos"))
   313  								Expect(testUI.Out).To(Say("buildpack:\\s+some-buildpack"))
   314  								Expect(testUI.Out).To(Say(""))
   315  								Expect(testUI.Out).To(Say("state\\s+since\\s+cpu\\s+memory\\s+disk\\s+details"))
   316  								Expect(testUI.Out).To(Say(`#0\s+running\s+2014-06-19T01:18:37Z\s+73.0%\s+100M of 128M\s+50M of 2G\s+info from the backend`))
   317  								Expect(testUI.Out).To(Say(`#1\s+crashed\s+2014-06-18T14:00:00Z\s+37.0%\s+100M of 128M\s+50M of 2G\s+potato`))
   318  
   319  								Expect(testUI.Err).To(Say("app-summary-warning"))
   320  
   321  								Expect(fakeActor.GetApplicationSummaryByNameAndSpaceCallCount()).To(Equal(1))
   322  								appName, spaceGUID := fakeActor.GetApplicationSummaryByNameAndSpaceArgsForCall(0)
   323  								Expect(appName).To(Equal("some-app"))
   324  								Expect(spaceGUID).To(Equal("some-space-guid"))
   325  							})
   326  						})
   327  					})
   328  				})
   329  
   330  				Context("when an error is encountered getting app summary", func() {
   331  					Context("when the error is not translatable", func() {
   332  						var expectedErr error
   333  
   334  						BeforeEach(func() {
   335  							expectedErr = errors.New("get app summary error")
   336  							fakeActor.GetApplicationSummaryByNameAndSpaceReturns(
   337  								v2action.ApplicationSummary{},
   338  								nil,
   339  								expectedErr)
   340  						})
   341  
   342  						It("returns the error", func() {
   343  							Expect(executeErr).To(MatchError(expectedErr))
   344  						})
   345  					})
   346  
   347  					Context("when the error is translatable", func() {
   348  						BeforeEach(func() {
   349  							fakeActor.GetApplicationSummaryByNameAndSpaceReturns(
   350  								v2action.ApplicationSummary{},
   351  								nil,
   352  								actionerror.ApplicationNotFoundError{Name: "some-app"})
   353  						})
   354  
   355  						It("returns a translatable error", func() {
   356  							Expect(executeErr).To(MatchError(actionerror.ApplicationNotFoundError{Name: "some-app"}))
   357  						})
   358  					})
   359  				})
   360  			})
   361  
   362  			Context("when the app is a Docker app", func() {
   363  				var applicationSummary v2action.ApplicationSummary
   364  
   365  				BeforeEach(func() {
   366  					applicationSummary = v2action.ApplicationSummary{
   367  						Application: v2action.Application{
   368  							Name:             "some-app",
   369  							GUID:             "some-app-guid",
   370  							Instances:        types.NullInt{Value: 3, IsSet: true},
   371  							Memory:           types.NullByteSizeInMb{IsSet: true, Value: 128},
   372  							PackageUpdatedAt: time.Unix(0, 0),
   373  							State:            "STARTED",
   374  							DockerImage:      "some-docker-image",
   375  						},
   376  						Stack: v2action.Stack{
   377  							Name: "potatos",
   378  						},
   379  						Routes: []v2action.Route{
   380  							{
   381  								Host: "banana",
   382  								Domain: v2action.Domain{
   383  									Name: "fruit.com",
   384  								},
   385  								Path: "/hi",
   386  							},
   387  							{
   388  								Domain: v2action.Domain{
   389  									Name: "foobar.com",
   390  								},
   391  								Port: types.NullInt{IsSet: true, Value: 13},
   392  							},
   393  						},
   394  					}
   395  					fakeActor.GetApplicationSummaryByNameAndSpaceReturns(applicationSummary, nil, nil)
   396  				})
   397  
   398  				It("displays the Docker image and does not display buildpack", func() {
   399  					Expect(testUI.Out).To(Say("name:\\s+some-app"))
   400  					Expect(testUI.Out).To(Say("docker image:\\s+some-docker-image"))
   401  
   402  					b, ok := testUI.Out.(*Buffer)
   403  					Expect(ok).To(BeTrue())
   404  					Expect(string(b.Contents())).ToNot(MatchRegexp("buildpack:"))
   405  				})
   406  			})
   407  		})
   408  	})
   409  })