github.com/dcarley/cf-cli@v6.24.1-0.20170220111324-4225ff346898+incompatible/command/v2/start_command_test.go (about)

     1  package v2_test
     2  
     3  import (
     4  	"errors"
     5  	"time"
     6  
     7  	"code.cloudfoundry.org/cli/actor/sharedaction"
     8  	"code.cloudfoundry.org/cli/actor/v2action"
     9  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv2"
    10  	"code.cloudfoundry.org/cli/command"
    11  	"code.cloudfoundry.org/cli/command/commandfakes"
    12  	"code.cloudfoundry.org/cli/command/v2"
    13  	"code.cloudfoundry.org/cli/command/v2/shared"
    14  	"code.cloudfoundry.org/cli/command/v2/v2fakes"
    15  	"code.cloudfoundry.org/cli/util/configv3"
    16  	"code.cloudfoundry.org/cli/util/ui"
    17  	"github.com/cloudfoundry/bytefmt"
    18  	. "github.com/onsi/ginkgo"
    19  	. "github.com/onsi/gomega"
    20  	. "github.com/onsi/gomega/gbytes"
    21  )
    22  
    23  var _ = Describe("Start Command", func() {
    24  	var (
    25  		cmd             v2.StartCommand
    26  		testUI          *ui.UI
    27  		fakeConfig      *commandfakes.FakeConfig
    28  		fakeSharedActor *commandfakes.FakeSharedActor
    29  		fakeActor       *v2fakes.FakeStartActor
    30  		binaryName      string
    31  		executeErr      error
    32  	)
    33  
    34  	BeforeEach(func() {
    35  		testUI = ui.NewTestUI(nil, NewBuffer(), NewBuffer())
    36  		fakeConfig = new(commandfakes.FakeConfig)
    37  		fakeSharedActor = new(commandfakes.FakeSharedActor)
    38  		fakeActor = new(v2fakes.FakeStartActor)
    39  
    40  		cmd = v2.StartCommand{
    41  			UI:          testUI,
    42  			Config:      fakeConfig,
    43  			SharedActor: fakeSharedActor,
    44  			Actor:       fakeActor,
    45  		}
    46  
    47  		cmd.RequiredArgs.AppName = "some-app"
    48  
    49  		binaryName = "faceman"
    50  		fakeConfig.BinaryNameReturns(binaryName)
    51  
    52  		// TODO: remove when experimental flag is removed
    53  		fakeConfig.ExperimentalReturns(true)
    54  
    55  		var err error
    56  		testUI.TimezoneLocation, err = time.LoadLocation("America/Los_Angeles")
    57  		Expect(err).NotTo(HaveOccurred())
    58  
    59  		fakeActor.StartApplicationStub = func(app v2action.Application, client v2action.NOAAClient, config v2action.Config) (<-chan *v2action.LogMessage, <-chan error, <-chan string, <-chan error) {
    60  			messages := make(chan *v2action.LogMessage)
    61  			logErrs := make(chan error)
    62  			warnings := make(chan string)
    63  			errs := make(chan error)
    64  
    65  			go func() {
    66  				close(messages)
    67  				close(logErrs)
    68  				close(warnings)
    69  				close(errs)
    70  			}()
    71  
    72  			return messages, logErrs, warnings, errs
    73  		}
    74  	})
    75  
    76  	JustBeforeEach(func() {
    77  		executeErr = cmd.Execute(nil)
    78  	})
    79  
    80  	// TODO: remove when experimental flag is removed
    81  	It("Displays the experimental warning message", func() {
    82  		Expect(testUI.Out).To(Say(command.ExperimentalWarning))
    83  	})
    84  
    85  	Context("when checking target fails", func() {
    86  		BeforeEach(func() {
    87  			fakeSharedActor.CheckTargetReturns(sharedaction.NotLoggedInError{BinaryName: binaryName})
    88  		})
    89  
    90  		It("returns an error if the check fails", func() {
    91  			Expect(executeErr).To(MatchError(command.NotLoggedInError{BinaryName: "faceman"}))
    92  
    93  			Expect(fakeSharedActor.CheckTargetCallCount()).To(Equal(1))
    94  			_, checkTargetedOrg, checkTargetedSpace := fakeSharedActor.CheckTargetArgsForCall(0)
    95  			Expect(checkTargetedOrg).To(BeTrue())
    96  			Expect(checkTargetedSpace).To(BeTrue())
    97  		})
    98  	})
    99  
   100  	Context("when the user is logged in, and org and space are targeted", func() {
   101  		BeforeEach(func() {
   102  			fakeConfig.HasTargetedOrganizationReturns(true)
   103  			fakeConfig.TargetedOrganizationReturns(configv3.Organization{Name: "some-org"})
   104  			fakeConfig.HasTargetedSpaceReturns(true)
   105  			fakeConfig.TargetedSpaceReturns(configv3.Space{
   106  				GUID: "some-space-guid",
   107  				Name: "some-space"})
   108  			fakeConfig.CurrentUserReturns(
   109  				configv3.User{Name: "some-user"},
   110  				nil)
   111  		})
   112  
   113  		Context("when getting the current user returns an error", func() {
   114  			var expectedErr error
   115  
   116  			BeforeEach(func() {
   117  				expectedErr = errors.New("getting current user error")
   118  				fakeConfig.CurrentUserReturns(
   119  					configv3.User{},
   120  					expectedErr)
   121  			})
   122  
   123  			It("returns the error", func() {
   124  				Expect(executeErr).To(MatchError(expectedErr))
   125  			})
   126  		})
   127  
   128  		It("displays flavor text", func() {
   129  			Expect(testUI.Out).To(Say("Starting app some-app in org some-org / space some-space as some-user..."))
   130  		})
   131  
   132  		Context("when the app exists", func() {
   133  			Context("when the app is already started", func() {
   134  				BeforeEach(func() {
   135  					fakeActor.GetApplicationByNameAndSpaceReturns(
   136  						v2action.Application{State: ccv2.ApplicationStarted},
   137  						v2action.Warnings{"warning-1", "warning-2"},
   138  						nil,
   139  					)
   140  				})
   141  
   142  				It("short circuits and displays message", func() {
   143  					Expect(executeErr).ToNot(HaveOccurred())
   144  
   145  					Expect(testUI.Out).To(Say("App some-app is already started"))
   146  
   147  					Expect(testUI.Err).To(Say("warning-1"))
   148  					Expect(testUI.Err).To(Say("warning-2"))
   149  
   150  					Expect(fakeActor.StartApplicationCallCount()).To(Equal(0))
   151  				})
   152  			})
   153  
   154  			Context("when the app is not already started", func() {
   155  				BeforeEach(func() {
   156  					fakeActor.GetApplicationByNameAndSpaceReturns(
   157  						v2action.Application{GUID: "app-guid", State: ccv2.ApplicationStopped},
   158  						v2action.Warnings{"warning-1", "warning-2"},
   159  						nil,
   160  					)
   161  				})
   162  
   163  				It("starts the app", func() {
   164  					Expect(executeErr).ToNot(HaveOccurred())
   165  
   166  					Expect(testUI.Err).To(Say("warning-1"))
   167  					Expect(testUI.Err).To(Say("warning-2"))
   168  
   169  					Expect(fakeActor.StartApplicationCallCount()).To(Equal(1))
   170  					app, _, config := fakeActor.StartApplicationArgsForCall(0)
   171  					Expect(app.GUID).To(Equal("app-guid"))
   172  					Expect(config).To(Equal(fakeConfig))
   173  				})
   174  
   175  				Context("when passed a log message", func() {
   176  					BeforeEach(func() {
   177  						fakeActor.StartApplicationStub = func(app v2action.Application, client v2action.NOAAClient, config v2action.Config) (<-chan *v2action.LogMessage, <-chan error, <-chan string, <-chan error) {
   178  							messages := make(chan *v2action.LogMessage)
   179  							logErrs := make(chan error)
   180  							warnings := make(chan string)
   181  							errs := make(chan error)
   182  
   183  							go func() {
   184  								messages <- v2action.NewLogMessage("log message 1", 1, time.Unix(0, 0), "APP", "1")
   185  								messages <- v2action.NewLogMessage("log message 2", 1, time.Unix(0, 0), "APP", "1")
   186  								close(messages)
   187  								close(logErrs)
   188  								close(warnings)
   189  								close(errs)
   190  							}()
   191  
   192  							return messages, logErrs, warnings, errs
   193  						}
   194  					})
   195  
   196  					It("displays the log", func() {
   197  						Expect(executeErr).ToNot(HaveOccurred())
   198  						Expect(testUI.Out).To(Say("log message 1"))
   199  						Expect(testUI.Out).To(Say("log message 2"))
   200  					})
   201  				})
   202  
   203  				Context("when passed an log err", func() {
   204  					var expectedErr error
   205  
   206  					BeforeEach(func() {
   207  						expectedErr = errors.New("err log message")
   208  						fakeActor.StartApplicationStub = func(app v2action.Application, client v2action.NOAAClient, config v2action.Config) (<-chan *v2action.LogMessage, <-chan error, <-chan string, <-chan error) {
   209  							messages := make(chan *v2action.LogMessage)
   210  							logErrs := make(chan error)
   211  							warnings := make(chan string)
   212  							errs := make(chan error)
   213  
   214  							go func() {
   215  								logErrs <- expectedErr
   216  								close(messages)
   217  								close(logErrs)
   218  								close(warnings)
   219  								close(errs)
   220  							}()
   221  
   222  							return messages, logErrs, warnings, errs
   223  						}
   224  					})
   225  
   226  					It("stops logging and returns the error", func() {
   227  						Expect(executeErr).To(MatchError(expectedErr))
   228  					})
   229  				})
   230  
   231  				Context("when passed a warning", func() {
   232  					BeforeEach(func() {
   233  						fakeActor.StartApplicationStub = func(app v2action.Application, client v2action.NOAAClient, config v2action.Config) (<-chan *v2action.LogMessage, <-chan error, <-chan string, <-chan error) {
   234  							messages := make(chan *v2action.LogMessage)
   235  							logErrs := make(chan error)
   236  							warnings := make(chan string)
   237  							errs := make(chan error)
   238  
   239  							go func() {
   240  								warnings <- "warning 1"
   241  								warnings <- "warning 2"
   242  								close(messages)
   243  								close(logErrs)
   244  								close(warnings)
   245  								close(errs)
   246  							}()
   247  
   248  							return messages, logErrs, warnings, errs
   249  						}
   250  					})
   251  
   252  					It("displays the warnings to STDERR", func() {
   253  						Expect(executeErr).ToNot(HaveOccurred())
   254  						Expect(testUI.Err).To(Say("warning 1"))
   255  						Expect(testUI.Err).To(Say("warning 2"))
   256  					})
   257  				})
   258  
   259  				Context("when passed an API err", func() {
   260  					Describe("an unexpected error", func() {
   261  						var expectedErr error
   262  
   263  						BeforeEach(func() {
   264  							expectedErr = errors.New("err log message")
   265  							fakeActor.StartApplicationStub = func(app v2action.Application, client v2action.NOAAClient, config v2action.Config) (<-chan *v2action.LogMessage, <-chan error, <-chan string, <-chan error) {
   266  								messages := make(chan *v2action.LogMessage)
   267  								logErrs := make(chan error)
   268  								warnings := make(chan string)
   269  								errs := make(chan error)
   270  
   271  								go func() {
   272  									errs <- expectedErr
   273  									close(messages)
   274  									close(logErrs)
   275  									close(warnings)
   276  									close(errs)
   277  								}()
   278  
   279  								return messages, logErrs, warnings, errs
   280  							}
   281  						})
   282  
   283  						It("stops logging and returns the error", func() {
   284  							Expect(executeErr).To(MatchError(expectedErr))
   285  						})
   286  					})
   287  
   288  					Describe("staging error", func() {
   289  						BeforeEach(func() {
   290  							fakeActor.StartApplicationStub = func(app v2action.Application, client v2action.NOAAClient, config v2action.Config) (<-chan *v2action.LogMessage, <-chan error, <-chan string, <-chan error) {
   291  								messages := make(chan *v2action.LogMessage)
   292  								logErrs := make(chan error)
   293  								warnings := make(chan string)
   294  								errs := make(chan error)
   295  
   296  								go func() {
   297  									errs <- v2action.StagingFailedError{Reason: "Something, but not nothing"}
   298  									close(messages)
   299  									close(logErrs)
   300  									close(warnings)
   301  									close(errs)
   302  								}()
   303  
   304  								return messages, logErrs, warnings, errs
   305  							}
   306  						})
   307  
   308  						It("stops logging and returns StagingFailedError", func() {
   309  							Expect(executeErr).To(MatchError(shared.StagingFailedError{BinaryName: "faceman", Message: "Something, but not nothing"}))
   310  						})
   311  					})
   312  				})
   313  
   314  				Context("when the app finishes starting", func() {
   315  					var (
   316  						applicationSummary v2action.ApplicationSummary
   317  						warnings           []string
   318  					)
   319  
   320  					BeforeEach(func() {
   321  						applicationSummary = v2action.ApplicationSummary{
   322  							Application: v2action.Application{
   323  								Name:                 "some-app",
   324  								GUID:                 "some-app-guid",
   325  								Instances:            3,
   326  								Memory:               128,
   327  								PackageUpdatedAt:     time.Unix(0, 0),
   328  								DetectedBuildpack:    "some-buildpack",
   329  								State:                "STARTED",
   330  								DetectedStartCommand: "some start command",
   331  							},
   332  							Stack: v2action.Stack{
   333  								Name: "potatos",
   334  							},
   335  							Routes: []v2action.Route{
   336  								{
   337  									Host:   "banana",
   338  									Domain: "fruit.com",
   339  									Path:   "/hi",
   340  								},
   341  								{
   342  									Domain: "foobar.com",
   343  									Port:   13,
   344  								},
   345  							},
   346  						}
   347  						warnings = []string{"app-summary-warning"}
   348  
   349  						applicationSummary.RunningInstances = []v2action.ApplicationInstanceWithStats{
   350  							{
   351  								ID:          0,
   352  								State:       v2action.ApplicationInstanceState(ccv2.ApplicationInstanceRunning),
   353  								Since:       1403140717.984577,
   354  								CPU:         0.73,
   355  								Disk:        50 * bytefmt.MEGABYTE,
   356  								DiskQuota:   2048 * bytefmt.MEGABYTE,
   357  								Memory:      100 * bytefmt.MEGABYTE,
   358  								MemoryQuota: 128 * bytefmt.MEGABYTE,
   359  								Details:     "info from the backend",
   360  							},
   361  						}
   362  
   363  						fakeActor.GetApplicationSummaryByNameAndSpaceReturns(applicationSummary, warnings, nil)
   364  					})
   365  
   366  					It("displays the app summary and it's warnings", func() {
   367  						Expect(executeErr).ToNot(HaveOccurred())
   368  						Expect(testUI.Out).To(Say("Name:\\s+some-app"))
   369  						Expect(testUI.Out).To(Say("Requested state:\\s+started"))
   370  						Expect(testUI.Out).To(Say("Instances:\\s+1\\/3"))
   371  						Expect(testUI.Out).To(Say("Usage:\\s+128M x 3 instances"))
   372  						Expect(testUI.Out).To(Say("Routes:\\s+banana.fruit.com/hi, foobar.com:13"))
   373  						Expect(testUI.Out).To(Say("Last uploaded:\\s+1970-01-01T00:00:00Z"))
   374  						Expect(testUI.Out).To(Say("Stack:\\s+potatos"))
   375  						Expect(testUI.Out).To(Say("Buildpack:\\s+some-buildpack"))
   376  						Expect(testUI.Out).To(Say("Start command:\\s+some start command"))
   377  
   378  						Expect(testUI.Err).To(Say("app-summary-warning"))
   379  					})
   380  
   381  					It("should display the instance table", func() {
   382  						Expect(executeErr).ToNot(HaveOccurred())
   383  						Expect(testUI.Out).To(Say("State\\s+Since\\s+CPU\\s+Memory\\s+Disk"))
   384  						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`))
   385  					})
   386  				})
   387  			})
   388  		})
   389  
   390  		Context("when the app does *not* exists", func() {
   391  			BeforeEach(func() {
   392  				fakeActor.GetApplicationByNameAndSpaceReturns(
   393  					v2action.Application{},
   394  					v2action.Warnings{"warning-1", "warning-2"},
   395  					v2action.ApplicationNotFoundError{Name: "some-app"},
   396  				)
   397  			})
   398  
   399  			It("returns back an error", func() {
   400  				Expect(executeErr).To(MatchError(command.ApplicationNotFoundError{Name: "some-app"}))
   401  
   402  				Expect(testUI.Err).To(Say("warning-1"))
   403  				Expect(testUI.Err).To(Say("warning-2"))
   404  			})
   405  		})
   406  	})
   407  })