github.com/cloudfoundry-attic/cli-with-i18n@v6.32.1-0.20171002233121-7401370d3b85+incompatible/command/v3/v3_push_command_test.go (about)

     1  package v3_test
     2  
     3  import (
     4  	"errors"
     5  	"time"
     6  
     7  	"code.cloudfoundry.org/cli/actor/pushaction"
     8  	"code.cloudfoundry.org/cli/actor/sharedaction"
     9  	"code.cloudfoundry.org/cli/actor/v2action"
    10  	"code.cloudfoundry.org/cli/actor/v3action"
    11  	"code.cloudfoundry.org/cli/actor/v3action/v3actionfakes"
    12  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccversion"
    13  	"code.cloudfoundry.org/cli/command/commandfakes"
    14  	"code.cloudfoundry.org/cli/command/flag"
    15  	"code.cloudfoundry.org/cli/command/translatableerror"
    16  	"code.cloudfoundry.org/cli/command/v3"
    17  	"code.cloudfoundry.org/cli/command/v3/shared"
    18  	"code.cloudfoundry.org/cli/command/v3/shared/sharedfakes"
    19  	"code.cloudfoundry.org/cli/command/v3/v3fakes"
    20  	"code.cloudfoundry.org/cli/types"
    21  	"code.cloudfoundry.org/cli/util/configv3"
    22  	"code.cloudfoundry.org/cli/util/ui"
    23  	. "github.com/onsi/ginkgo"
    24  	. "github.com/onsi/ginkgo/extensions/table"
    25  	. "github.com/onsi/gomega"
    26  	. "github.com/onsi/gomega/gbytes"
    27  )
    28  
    29  var _ = Describe("v3-push Command", func() {
    30  	var (
    31  		cmd             v3.V3PushCommand
    32  		testUI          *ui.UI
    33  		fakeConfig      *commandfakes.FakeConfig
    34  		fakeSharedActor *commandfakes.FakeSharedActor
    35  		fakeNOAAClient  *v3actionfakes.FakeNOAAClient
    36  		fakeActor       *v3fakes.FakeV3PushActor
    37  		fakeV2PushActor *v3fakes.FakeV2PushActor
    38  		fakeV2AppActor  *sharedfakes.FakeV2AppRouteActor
    39  		binaryName      string
    40  		executeErr      error
    41  		app             string
    42  		userName        string
    43  		spaceName       string
    44  		orgName         string
    45  	)
    46  
    47  	BeforeEach(func() {
    48  		testUI = ui.NewTestUI(nil, NewBuffer(), NewBuffer())
    49  		fakeConfig = new(commandfakes.FakeConfig)
    50  		fakeSharedActor = new(commandfakes.FakeSharedActor)
    51  		fakeActor = new(v3fakes.FakeV3PushActor)
    52  		fakeV2PushActor = new(v3fakes.FakeV2PushActor)
    53  		fakeV2AppActor = new(sharedfakes.FakeV2AppRouteActor)
    54  		fakeNOAAClient = new(v3actionfakes.FakeNOAAClient)
    55  
    56  		fakeConfig.StagingTimeoutReturns(10 * time.Minute)
    57  
    58  		binaryName = "faceman"
    59  		fakeConfig.BinaryNameReturns(binaryName)
    60  		app = "some-app"
    61  		userName = "banana"
    62  		spaceName = "some-space"
    63  		orgName = "some-org"
    64  
    65  		appSummaryDisplayer := shared.AppSummaryDisplayer{
    66  			UI:              testUI,
    67  			Config:          fakeConfig,
    68  			Actor:           fakeActor,
    69  			V2AppRouteActor: fakeV2AppActor,
    70  			AppName:         app,
    71  		}
    72  		packageDisplayer := shared.NewPackageDisplayer(
    73  			testUI,
    74  			fakeConfig,
    75  		)
    76  
    77  		cmd = v3.V3PushCommand{
    78  			RequiredArgs: flag.AppName{AppName: app},
    79  
    80  			UI:          testUI,
    81  			Config:      fakeConfig,
    82  			SharedActor: fakeSharedActor,
    83  			Actor:       fakeActor,
    84  			V2PushActor: fakeV2PushActor,
    85  
    86  			NOAAClient:          fakeNOAAClient,
    87  			AppSummaryDisplayer: appSummaryDisplayer,
    88  			PackageDisplayer:    packageDisplayer,
    89  		}
    90  		fakeActor.CloudControllerAPIVersionReturns(ccversion.MinVersionV3)
    91  
    92  		// we stub out StagePackage out here so the happy paths below don't hang
    93  		fakeActor.StagePackageStub = func(_ string, _ string) (<-chan v3action.Droplet, <-chan v3action.Warnings, <-chan error) {
    94  			dropletStream := make(chan v3action.Droplet)
    95  			warningsStream := make(chan v3action.Warnings)
    96  			errorStream := make(chan error)
    97  
    98  			go func() {
    99  				defer close(dropletStream)
   100  				defer close(warningsStream)
   101  				defer close(errorStream)
   102  			}()
   103  
   104  			return dropletStream, warningsStream, errorStream
   105  		}
   106  	})
   107  
   108  	JustBeforeEach(func() {
   109  		executeErr = cmd.Execute(nil)
   110  	})
   111  
   112  	Context("when the API version is below the minimum", func() {
   113  		BeforeEach(func() {
   114  			fakeActor.CloudControllerAPIVersionReturns("0.0.0")
   115  		})
   116  
   117  		It("returns a MinimumAPIVersionNotMetError", func() {
   118  			Expect(executeErr).To(MatchError(translatableerror.MinimumAPIVersionNotMetError{
   119  				CurrentVersion: "0.0.0",
   120  				MinimumVersion: ccversion.MinVersionV3,
   121  			}))
   122  		})
   123  	})
   124  
   125  	DescribeTable("argument combinations",
   126  		func(dockerImage string, dockerUsername string, dockerPassword string,
   127  			buildpacks []string, appPath string,
   128  			expectedErr error) {
   129  			cmd.DockerImage.Path = dockerImage
   130  			cmd.DockerUsername = dockerUsername
   131  			fakeConfig.DockerPasswordReturns(dockerPassword)
   132  			cmd.Buildpacks = buildpacks
   133  			cmd.AppPath = flag.PathWithExistenceCheck(appPath)
   134  			Expect(cmd.Execute(nil)).To(MatchError(expectedErr))
   135  		},
   136  		Entry("docker username",
   137  			"", "some-docker-username", "", []string{}, "",
   138  			translatableerror.RequiredFlagsError{
   139  				Arg1: "--docker-image, -o",
   140  				Arg2: "--docker-username",
   141  			}),
   142  		Entry("docker username, password",
   143  			"", "some-docker-username", "my-password", []string{}, "",
   144  			translatableerror.RequiredFlagsError{
   145  				Arg1: "--docker-image, -o",
   146  				Arg2: "--docker-username",
   147  			}),
   148  		Entry("docker username, app path",
   149  			"", "some-docker-username", "", []string{}, "some/app/path",
   150  			translatableerror.RequiredFlagsError{
   151  				Arg1: "--docker-image, -o",
   152  				Arg2: "--docker-username",
   153  			}),
   154  		Entry("docker username, buildpacks",
   155  			"", "some-docker-username", "", []string{"ruby_buildpack"}, "",
   156  			translatableerror.RequiredFlagsError{
   157  				Arg1: "--docker-image, -o",
   158  				Arg2: "--docker-username",
   159  			}),
   160  		Entry("docker image, docker username",
   161  			"some-docker-image", "some-docker-username", "", []string{}, "",
   162  			translatableerror.DockerPasswordNotSetError{}),
   163  		Entry("docker image, app path",
   164  			"some-docker-image", "", "", []string{}, "some/app/path",
   165  			translatableerror.ArgumentCombinationError{
   166  				Args: []string{"--docker-image", "-o", "-p"},
   167  			}),
   168  		Entry("docker image, buildpacks",
   169  			"some-docker-image", "", "", []string{"ruby_buildpack"}, "",
   170  			translatableerror.ArgumentCombinationError{
   171  				Args: []string{"-b", "--docker-image", "-o"},
   172  			}),
   173  	)
   174  
   175  	Context("when checking target fails", func() {
   176  		BeforeEach(func() {
   177  			fakeSharedActor.CheckTargetReturns(sharedaction.NotLoggedInError{BinaryName: binaryName})
   178  		})
   179  
   180  		It("returns an error", func() {
   181  			Expect(executeErr).To(MatchError(translatableerror.NotLoggedInError{BinaryName: binaryName}))
   182  
   183  			Expect(fakeSharedActor.CheckTargetCallCount()).To(Equal(1))
   184  			_, checkTargetedOrg, checkTargetedSpace := fakeSharedActor.CheckTargetArgsForCall(0)
   185  			Expect(checkTargetedOrg).To(BeTrue())
   186  			Expect(checkTargetedSpace).To(BeTrue())
   187  		})
   188  	})
   189  
   190  	Context("when the user is logged in", func() {
   191  		BeforeEach(func() {
   192  			fakeConfig.CurrentUserReturns(configv3.User{Name: userName}, nil)
   193  			fakeConfig.TargetedSpaceReturns(configv3.Space{Name: spaceName, GUID: "some-space-guid"})
   194  			fakeConfig.TargetedOrganizationReturns(configv3.Organization{Name: orgName, GUID: "some-org-guid"})
   195  
   196  		})
   197  
   198  		Context("when looking up the application returns some api error", func() {
   199  			BeforeEach(func() {
   200  				fakeActor.GetApplicationByNameAndSpaceReturns(v3action.Application{}, v3action.Warnings{"get-warning"}, errors.New("some-error"))
   201  			})
   202  
   203  			It("returns the error and displays all warnings", func() {
   204  				Expect(executeErr).To(MatchError("some-error"))
   205  
   206  				Expect(testUI.Err).To(Say("get-warning"))
   207  			})
   208  		})
   209  
   210  		Context("when looking up the application returns an application not found error", func() {
   211  			BeforeEach(func() {
   212  				fakeActor.GetApplicationByNameAndSpaceReturns(v3action.Application{}, v3action.Warnings{"get-warning"}, v3action.ApplicationNotFoundError{Name: "some-app"})
   213  			})
   214  
   215  			Context("when creating the application returns an error", func() {
   216  				var expectedErr error
   217  
   218  				BeforeEach(func() {
   219  					expectedErr = errors.New("I am an error")
   220  					fakeActor.CreateApplicationInSpaceReturns(v3action.Application{}, v3action.Warnings{"I am a warning", "I am also a warning"}, expectedErr)
   221  				})
   222  
   223  				It("displays the warnings and error", func() {
   224  					Expect(executeErr).To(MatchError(expectedErr))
   225  
   226  					Expect(testUI.Err).To(Say("I am a warning"))
   227  					Expect(testUI.Err).To(Say("I am also a warning"))
   228  					Expect(testUI.Out).ToNot(Say("app some-app in org some-org / space some-space as banana..."))
   229  				})
   230  			})
   231  
   232  			Context("when creating the application does not error", func() {
   233  				BeforeEach(func() {
   234  					fakeActor.CreateApplicationInSpaceReturns(v3action.Application{Name: "some-app", GUID: "some-app-guid"}, v3action.Warnings{"I am a warning", "I am also a warning"}, nil)
   235  				})
   236  
   237  				It("calls CreateApplication", func() {
   238  					Expect(fakeActor.CreateApplicationInSpaceCallCount()).To(Equal(1), "Expected CreateApplicationInSpace to be called once")
   239  					createApp, createSpaceGUID := fakeActor.CreateApplicationInSpaceArgsForCall(0)
   240  					Expect(createApp).To(Equal(v3action.Application{
   241  						Name: "some-app",
   242  						Lifecycle: v3action.AppLifecycle{
   243  							Type: v3action.BuildpackAppLifecycleType,
   244  						},
   245  					}))
   246  					Expect(createSpaceGUID).To(Equal("some-space-guid"))
   247  				})
   248  
   249  				Context("when creating the package fails", func() {
   250  					var expectedErr error
   251  
   252  					BeforeEach(func() {
   253  						expectedErr = errors.New("I am an error")
   254  						fakeActor.CreateAndUploadBitsPackageByApplicationNameAndSpaceReturns(v3action.Package{}, v3action.Warnings{"I am a package warning", "I am also a package warning"}, expectedErr)
   255  					})
   256  
   257  					It("displays the header and error", func() {
   258  						Expect(executeErr).To(MatchError(expectedErr))
   259  
   260  						Expect(testUI.Out).To(Say("Uploading and creating bits package for app some-app in org some-org / space some-space as banana..."))
   261  
   262  						Expect(testUI.Err).To(Say("I am a package warning"))
   263  						Expect(testUI.Err).To(Say("I am also a package warning"))
   264  
   265  						Expect(testUI.Out).ToNot(Say("Staging package for %s in org some-org / space some-space as banana...", app))
   266  					})
   267  				})
   268  
   269  				Context("when creating the package succeeds", func() {
   270  					BeforeEach(func() {
   271  						fakeActor.CreateAndUploadBitsPackageByApplicationNameAndSpaceReturns(v3action.Package{GUID: "some-guid"}, v3action.Warnings{"I am a package warning", "I am also a package warning"}, nil)
   272  					})
   273  
   274  					Context("when the -p flag is provided", func() {
   275  						BeforeEach(func() {
   276  							cmd.AppPath = "some-app-path"
   277  						})
   278  
   279  						It("creates the package with the provided path", func() {
   280  							Expect(testUI.Out).To(Say("Uploading and creating bits package for app %s in org %s / space %s as %s", app, orgName, spaceName, userName))
   281  							Expect(testUI.Err).To(Say("I am a package warning"))
   282  							Expect(testUI.Err).To(Say("I am also a package warning"))
   283  							Expect(testUI.Out).To(Say("OK"))
   284  							Expect(testUI.Out).To(Say("Staging package for app %s in org some-org / space some-space as banana...", app))
   285  
   286  							Expect(fakeActor.CreateAndUploadBitsPackageByApplicationNameAndSpaceCallCount()).To(Equal(1))
   287  							_, _, appPath := fakeActor.CreateAndUploadBitsPackageByApplicationNameAndSpaceArgsForCall(0)
   288  
   289  							Expect(appPath).To(Equal("some-app-path"))
   290  						})
   291  					})
   292  
   293  					Context("when the -o flag is provided", func() {
   294  						BeforeEach(func() {
   295  							cmd.DockerImage.Path = "example.com/docker/docker/docker:docker"
   296  							fakeActor.CreateDockerPackageByApplicationNameAndSpaceReturns(v3action.Package{GUID: "some-guid"}, v3action.Warnings{"I am a docker package warning", "I am also a docker package warning"}, nil)
   297  						})
   298  
   299  						It("creates a docker package with the provided image path", func() {
   300  
   301  							Expect(testUI.Out).To(Say("Creating docker package for app %s in org %s / space %s as %s", app, orgName, spaceName, userName))
   302  							Expect(testUI.Err).To(Say("I am a docker package warning"))
   303  							Expect(testUI.Err).To(Say("I am also a docker package warning"))
   304  							Expect(testUI.Out).To(Say("OK"))
   305  							Expect(testUI.Out).To(Say("Staging package for app %s in org some-org / space some-space as banana...", app))
   306  
   307  							Expect(fakeActor.CreateDockerPackageByApplicationNameAndSpaceCallCount()).To(Equal(1))
   308  							_, _, dockerImageCredentials := fakeActor.CreateDockerPackageByApplicationNameAndSpaceArgsForCall(0)
   309  
   310  							Expect(dockerImageCredentials.Path).To(Equal("example.com/docker/docker/docker:docker"))
   311  						})
   312  					})
   313  
   314  					Context("when neither -p nor -o flags are provided", func() {
   315  						It("calls CreateAndUploadBitsPackageByApplicationNameAndSpace with empty string", func() {
   316  							Expect(testUI.Out).To(Say("Uploading and creating bits package for app %s in org %s / space %s as %s", app, orgName, spaceName, userName))
   317  
   318  							Expect(fakeActor.CreateAndUploadBitsPackageByApplicationNameAndSpaceCallCount()).To(Equal(1))
   319  							_, _, appPath := fakeActor.CreateAndUploadBitsPackageByApplicationNameAndSpaceArgsForCall(0)
   320  
   321  							Expect(appPath).To(BeEmpty())
   322  						})
   323  					})
   324  
   325  					Context("when getting streaming logs fails", func() {
   326  						var expectedErr error
   327  						BeforeEach(func() {
   328  							expectedErr = errors.New("something is wrong!")
   329  							fakeActor.GetStreamingLogsForApplicationByNameAndSpaceReturns(nil, nil, v3action.Warnings{"some-logging-warning", "some-other-logging-warning"}, expectedErr)
   330  						})
   331  
   332  						It("returns the error and displays warnings", func() {
   333  							Expect(executeErr).To(Equal(expectedErr))
   334  
   335  							Expect(testUI.Out).To(Say("Staging package for app %s in org some-org / space some-space as banana...", app))
   336  
   337  							Expect(testUI.Err).To(Say("some-logging-warning"))
   338  							Expect(testUI.Err).To(Say("some-other-logging-warning"))
   339  
   340  						})
   341  					})
   342  
   343  					Context("when the logging does not error", func() {
   344  						var allLogsWritten chan bool
   345  
   346  						BeforeEach(func() {
   347  							allLogsWritten = make(chan bool)
   348  							fakeActor.GetStreamingLogsForApplicationByNameAndSpaceStub = func(appName string, spaceGUID string, client v3action.NOAAClient) (<-chan *v3action.LogMessage, <-chan error, v3action.Warnings, error) {
   349  								logStream := make(chan *v3action.LogMessage)
   350  								errorStream := make(chan error)
   351  
   352  								go func() {
   353  									logStream <- v3action.NewLogMessage("Here are some staging logs!", 1, time.Now(), v3action.StagingLog, "sourceInstance")
   354  									logStream <- v3action.NewLogMessage("Here are some other staging logs!", 1, time.Now(), v3action.StagingLog, "sourceInstance")
   355  									logStream <- v3action.NewLogMessage("not from staging", 1, time.Now(), "potato", "sourceInstance")
   356  									allLogsWritten <- true
   357  								}()
   358  
   359  								return logStream, errorStream, v3action.Warnings{"steve for all I care"}, nil
   360  							}
   361  						})
   362  
   363  						Context("when the staging returns an error", func() {
   364  							var expectedErr error
   365  
   366  							BeforeEach(func() {
   367  								expectedErr = errors.New("any gibberish")
   368  								fakeActor.StagePackageStub = func(packageGUID string, _ string) (<-chan v3action.Droplet, <-chan v3action.Warnings, <-chan error) {
   369  									dropletStream := make(chan v3action.Droplet)
   370  									warningsStream := make(chan v3action.Warnings)
   371  									errorStream := make(chan error)
   372  
   373  									go func() {
   374  										<-allLogsWritten
   375  										defer close(dropletStream)
   376  										defer close(warningsStream)
   377  										defer close(errorStream)
   378  										warningsStream <- v3action.Warnings{"some-staging-warning", "some-other-staging-warning"}
   379  										errorStream <- expectedErr
   380  									}()
   381  
   382  									return dropletStream, warningsStream, errorStream
   383  								}
   384  							})
   385  
   386  							It("returns the error and displays warnings", func() {
   387  								Expect(executeErr).To(Equal(expectedErr))
   388  
   389  								Expect(testUI.Out).To(Say("Staging package for app %s in org some-org / space some-space as banana...", app))
   390  
   391  								Expect(testUI.Err).To(Say("some-staging-warning"))
   392  								Expect(testUI.Err).To(Say("some-other-staging-warning"))
   393  
   394  								Expect(testUI.Out).ToNot(Say("Setting app some-app to droplet some-droplet-guid in org some-org / space some-space as banana..."))
   395  							})
   396  						})
   397  
   398  						Context("when the staging is successful", func() {
   399  							BeforeEach(func() {
   400  								fakeActor.StagePackageStub = func(packageGUID string, _ string) (<-chan v3action.Droplet, <-chan v3action.Warnings, <-chan error) {
   401  									dropletStream := make(chan v3action.Droplet)
   402  									warningsStream := make(chan v3action.Warnings)
   403  									errorStream := make(chan error)
   404  
   405  									go func() {
   406  										<-allLogsWritten
   407  										defer close(dropletStream)
   408  										defer close(warningsStream)
   409  										defer close(errorStream)
   410  										warningsStream <- v3action.Warnings{"some-staging-warning", "some-other-staging-warning"}
   411  										dropletStream <- v3action.Droplet{GUID: "some-droplet-guid"}
   412  									}()
   413  
   414  									return dropletStream, warningsStream, errorStream
   415  								}
   416  							})
   417  
   418  							It("outputs the staging message and warnings", func() {
   419  								Expect(executeErr).ToNot(HaveOccurred())
   420  
   421  								Expect(testUI.Out).To(Say("Staging package for app %s in org some-org / space some-space as banana...", app))
   422  								Expect(testUI.Out).To(Say("OK"))
   423  
   424  								Expect(testUI.Err).To(Say("some-staging-warning"))
   425  								Expect(testUI.Err).To(Say("some-other-staging-warning"))
   426  							})
   427  
   428  							It("stages the package", func() {
   429  								Expect(executeErr).ToNot(HaveOccurred())
   430  								Expect(fakeActor.StagePackageCallCount()).To(Equal(1))
   431  								guidArg, _ := fakeActor.StagePackageArgsForCall(0)
   432  								Expect(guidArg).To(Equal("some-guid"))
   433  							})
   434  
   435  							It("displays staging logs and their warnings", func() {
   436  								Expect(testUI.Out).To(Say("Here are some staging logs!"))
   437  								Expect(testUI.Out).To(Say("Here are some other staging logs!"))
   438  								Expect(testUI.Out).ToNot(Say("not from staging"))
   439  
   440  								Expect(testUI.Err).To(Say("steve for all I care"))
   441  
   442  								Expect(fakeActor.GetStreamingLogsForApplicationByNameAndSpaceCallCount()).To(Equal(1))
   443  								appName, spaceGUID, noaaClient := fakeActor.GetStreamingLogsForApplicationByNameAndSpaceArgsForCall(0)
   444  								Expect(appName).To(Equal(app))
   445  								Expect(spaceGUID).To(Equal("some-space-guid"))
   446  								Expect(noaaClient).To(Equal(fakeNOAAClient))
   447  
   448  								guidArg, _ := fakeActor.StagePackageArgsForCall(0)
   449  								Expect(guidArg).To(Equal("some-guid"))
   450  							})
   451  
   452  							Context("when setting the droplet fails", func() {
   453  								BeforeEach(func() {
   454  									fakeActor.SetApplicationDropletReturns(v3action.Warnings{"droplet-warning-1", "droplet-warning-2"}, errors.New("some-error"))
   455  								})
   456  
   457  								It("returns the error", func() {
   458  									Expect(executeErr).To(Equal(errors.New("some-error")))
   459  
   460  									Expect(testUI.Out).To(Say("Setting app some-app to droplet some-droplet-guid in org some-org / space some-space as banana..."))
   461  
   462  									Expect(testUI.Err).To(Say("droplet-warning-1"))
   463  									Expect(testUI.Err).To(Say("droplet-warning-2"))
   464  
   465  									Expect(testUI.Out).ToNot(Say("Starting app some-app in org some-org / space some-space as banana\\.\\.\\."))
   466  								})
   467  							})
   468  
   469  							Context("when setting the application droplet is successful", func() {
   470  								BeforeEach(func() {
   471  									fakeActor.SetApplicationDropletReturns(v3action.Warnings{"droplet-warning-1", "droplet-warning-2"}, nil)
   472  								})
   473  
   474  								It("displays that the droplet was assigned", func() {
   475  									Expect(testUI.Out).To(Say("Staging package for app %s in org some-org / space some-space as banana...", app))
   476  									Expect(testUI.Out).To(Say("OK"))
   477  
   478  									Expect(testUI.Out).ToNot(Say("Stopping .*"))
   479  
   480  									Expect(testUI.Out).To(Say("Setting app some-app to droplet some-droplet-guid in org some-org / space some-space as banana..."))
   481  
   482  									Expect(testUI.Err).To(Say("droplet-warning-1"))
   483  									Expect(testUI.Err).To(Say("droplet-warning-2"))
   484  									Expect(testUI.Out).To(Say("OK"))
   485  
   486  									Expect(fakeActor.SetApplicationDropletCallCount()).To(Equal(1))
   487  									appName, spaceGUID, dropletGUID := fakeActor.SetApplicationDropletArgsForCall(0)
   488  									Expect(appName).To(Equal("some-app"))
   489  									Expect(spaceGUID).To(Equal("some-space-guid"))
   490  									Expect(dropletGUID).To(Equal("some-droplet-guid"))
   491  								})
   492  
   493  								Context("when --no-route flag is set to true", func() {
   494  									BeforeEach(func() {
   495  										cmd.NoRoute = true
   496  									})
   497  
   498  									It("does not create any routes", func() {
   499  										Expect(fakeV2PushActor.CreateAndBindApplicationRoutesCallCount()).To(Equal(0))
   500  
   501  										Expect(fakeActor.StartApplicationCallCount()).To(Equal(1))
   502  									})
   503  								})
   504  
   505  								Context("when buildpack(s) are provided via -b flag", func() {
   506  									BeforeEach(func() {
   507  										cmd.Buildpacks = []string{"some-buildpack"}
   508  									})
   509  
   510  									It("creates the app with the specified buildpack and prints the buildpack name in the summary", func() {
   511  										Expect(fakeActor.CreateApplicationInSpaceCallCount()).To(Equal(1), "Expected CreateApplicationInSpace to be called once")
   512  										createApp, createSpaceGUID := fakeActor.CreateApplicationInSpaceArgsForCall(0)
   513  										Expect(createApp).To(Equal(v3action.Application{
   514  											Name: "some-app",
   515  											Lifecycle: v3action.AppLifecycle{
   516  												Type: v3action.BuildpackAppLifecycleType,
   517  												Data: v3action.AppLifecycleData{
   518  													Buildpacks: []string{"some-buildpack"},
   519  												},
   520  											},
   521  										}))
   522  										Expect(createSpaceGUID).To(Equal("some-space-guid"))
   523  									})
   524  								})
   525  
   526  								Context("when a docker image is specified", func() {
   527  									BeforeEach(func() {
   528  										cmd.DockerImage.Path = "example.com/docker/docker/docker:docker"
   529  									})
   530  
   531  									It("creates the app with a docker lifecycle", func() {
   532  										Expect(fakeActor.CreateApplicationInSpaceCallCount()).To(Equal(1), "Expected CreateApplicationInSpace to be called once")
   533  										createApp, createSpaceGUID := fakeActor.CreateApplicationInSpaceArgsForCall(0)
   534  										Expect(createApp).To(Equal(v3action.Application{
   535  											Name: "some-app",
   536  											Lifecycle: v3action.AppLifecycle{
   537  												Type: v3action.DockerAppLifecycleType,
   538  												Data: v3action.AppLifecycleData{},
   539  											},
   540  										}))
   541  										Expect(createSpaceGUID).To(Equal("some-space-guid"))
   542  									})
   543  								})
   544  
   545  								Context("when mapping routes fails", func() {
   546  									BeforeEach(func() {
   547  										fakeV2PushActor.CreateAndBindApplicationRoutesReturns(pushaction.Warnings{"route-warning"}, errors.New("some-error"))
   548  									})
   549  
   550  									It("returns the error", func() {
   551  										Expect(executeErr).To(MatchError("some-error"))
   552  										Expect(testUI.Out).To(Say("Mapping routes\\.\\.\\."))
   553  										Expect(testUI.Err).To(Say("route-warning"))
   554  
   555  										Expect(fakeActor.StartApplicationCallCount()).To(Equal(0))
   556  									})
   557  								})
   558  
   559  								Context("when mapping routes succeeds", func() {
   560  									BeforeEach(func() {
   561  										fakeV2PushActor.CreateAndBindApplicationRoutesReturns(pushaction.Warnings{"route-warning"}, nil)
   562  									})
   563  
   564  									It("displays the header and OK", func() {
   565  										Expect(testUI.Out).To(Say("Mapping routes\\.\\.\\."))
   566  										Expect(testUI.Out).To(Say("OK"))
   567  
   568  										Expect(testUI.Err).To(Say("route-warning"))
   569  
   570  										Expect(fakeV2PushActor.CreateAndBindApplicationRoutesCallCount()).To(Equal(1), "Expected CreateAndBindApplicationRoutes to be called")
   571  										orgArg, spaceArg, appArg := fakeV2PushActor.CreateAndBindApplicationRoutesArgsForCall(0)
   572  										Expect(orgArg).To(Equal("some-org-guid"))
   573  										Expect(spaceArg).To(Equal("some-space-guid"))
   574  										Expect(appArg).To(Equal(v2action.Application{Name: "some-app", GUID: "some-app-guid"}))
   575  
   576  										Expect(fakeActor.StartApplicationCallCount()).To(Equal(1))
   577  									})
   578  
   579  									Context("when starting the application fails", func() {
   580  										BeforeEach(func() {
   581  											fakeActor.StartApplicationReturns(v3action.Application{}, v3action.Warnings{"start-warning-1", "start-warning-2"}, errors.New("some-error"))
   582  										})
   583  
   584  										It("says that the app failed to start", func() {
   585  											Expect(executeErr).To(Equal(errors.New("some-error")))
   586  											Expect(testUI.Out).To(Say("Starting app some-app in org some-org / space some-space as banana\\.\\.\\."))
   587  
   588  											Expect(testUI.Err).To(Say("start-warning-1"))
   589  											Expect(testUI.Err).To(Say("start-warning-2"))
   590  
   591  											Expect(testUI.Out).ToNot(Say("Showing health and status for app some-app in org some-org / space some-space as banana\\.\\.\\."))
   592  										})
   593  									})
   594  
   595  									Context("when starting the application succeeds", func() {
   596  										BeforeEach(func() {
   597  											fakeActor.StartApplicationReturns(v3action.Application{GUID: "some-app-guid"}, v3action.Warnings{"start-warning-1", "start-warning-2"}, nil)
   598  										})
   599  
   600  										It("says that the app was started and outputs warnings", func() {
   601  											Expect(testUI.Out).To(Say("Starting app some-app in org some-org / space some-space as banana\\.\\.\\."))
   602  
   603  											Expect(testUI.Err).To(Say("start-warning-1"))
   604  											Expect(testUI.Err).To(Say("start-warning-2"))
   605  											Expect(testUI.Out).To(Say("OK"))
   606  
   607  											Expect(fakeActor.StartApplicationCallCount()).To(Equal(1))
   608  											appGUID := fakeActor.StartApplicationArgsForCall(0)
   609  											Expect(appGUID).To(Equal("some-app-guid"))
   610  										})
   611  									})
   612  
   613  									Context("when polling the start fails", func() {
   614  										BeforeEach(func() {
   615  											fakeActor.PollStartStub = func(appGUID string, warnings chan<- v3action.Warnings) error {
   616  												warnings <- v3action.Warnings{"some-poll-warning-1", "some-poll-warning-2"}
   617  												return errors.New("some-error")
   618  											}
   619  										})
   620  
   621  										It("displays all warnings and fails", func() {
   622  											Expect(testUI.Out).To(Say("Waiting for app to start\\.\\.\\."))
   623  
   624  											Expect(testUI.Err).To(Say("some-poll-warning-1"))
   625  											Expect(testUI.Err).To(Say("some-poll-warning-2"))
   626  
   627  											Expect(executeErr).To(MatchError("some-error"))
   628  										})
   629  									})
   630  
   631  									Context("when polling times out", func() {
   632  										BeforeEach(func() {
   633  											fakeActor.PollStartReturns(v3action.StartupTimeoutError{})
   634  										})
   635  
   636  										It("returns the StartupTimeoutError", func() {
   637  											Expect(executeErr).To(MatchError(translatableerror.StartupTimeoutError{
   638  												AppName:    "some-app",
   639  												BinaryName: binaryName,
   640  											}))
   641  										})
   642  									})
   643  
   644  									Context("when polling the start succeeds", func() {
   645  										BeforeEach(func() {
   646  											fakeActor.PollStartStub = func(appGUID string, warnings chan<- v3action.Warnings) error {
   647  												warnings <- v3action.Warnings{"some-poll-warning-1", "some-poll-warning-2"}
   648  												return nil
   649  											}
   650  										})
   651  
   652  										It("displays all warnings", func() {
   653  											Expect(testUI.Out).To(Say("Waiting for app to start\\.\\.\\."))
   654  
   655  											Expect(testUI.Err).To(Say("some-poll-warning-1"))
   656  											Expect(testUI.Err).To(Say("some-poll-warning-2"))
   657  
   658  											Expect(executeErr).ToNot(HaveOccurred())
   659  										})
   660  
   661  										Context("when displaying the application info fails", func() {
   662  											BeforeEach(func() {
   663  												var expectedErr error
   664  												expectedErr = v3action.ApplicationNotFoundError{Name: app}
   665  												fakeActor.GetApplicationSummaryByNameAndSpaceReturns(v3action.ApplicationSummary{}, v3action.Warnings{"display-warning-1", "display-warning-2"}, expectedErr)
   666  											})
   667  
   668  											It("returns the error and prints warnings", func() {
   669  												Expect(executeErr).To(Equal(translatableerror.ApplicationNotFoundError{Name: app}))
   670  
   671  												Expect(testUI.Out).To(Say("Showing health and status for app some-app in org some-org / space some-space as banana\\.\\.\\."))
   672  
   673  												Expect(testUI.Err).To(Say("display-warning-1"))
   674  												Expect(testUI.Err).To(Say("display-warning-2"))
   675  
   676  												Expect(testUI.Out).ToNot(Say("name:\\s+some-app"))
   677  											})
   678  										})
   679  
   680  										Context("when getting the application summary is successful", func() {
   681  											BeforeEach(func() {
   682  												summary := v3action.ApplicationSummary{
   683  													Application: v3action.Application{
   684  														Name:  "some-app",
   685  														GUID:  "some-app-guid",
   686  														State: "started",
   687  													},
   688  													CurrentDroplet: v3action.Droplet{
   689  														Stack: "cflinuxfs2",
   690  														Buildpacks: []v3action.Buildpack{
   691  															{
   692  																Name:         "ruby_buildpack",
   693  																DetectOutput: "some-detect-output",
   694  															},
   695  														},
   696  													},
   697  													ProcessSummaries: []v3action.ProcessSummary{
   698  														{
   699  															Process: v3action.Process{
   700  																Type:       "worker",
   701  																MemoryInMB: types.NullUint64{Value: 64, IsSet: true},
   702  															},
   703  															InstanceDetails: []v3action.Instance{
   704  																v3action.Instance{
   705  																	Index:       0,
   706  																	State:       "RUNNING",
   707  																	MemoryUsage: 4000000,
   708  																	DiskUsage:   4000000,
   709  																	MemoryQuota: 67108864,
   710  																	DiskQuota:   8000000,
   711  																	Uptime:      int(time.Now().Sub(time.Unix(1371859200, 0)).Seconds()),
   712  																},
   713  															},
   714  														},
   715  													},
   716  												}
   717  
   718  												fakeActor.GetApplicationSummaryByNameAndSpaceReturns(summary, v3action.Warnings{"display-warning-1", "display-warning-2"}, nil)
   719  											})
   720  
   721  											Context("when getting the application routes fails", func() {
   722  												BeforeEach(func() {
   723  													fakeV2AppActor.GetApplicationRoutesReturns([]v2action.Route{},
   724  														v2action.Warnings{"route-warning-1", "route-warning-2"}, errors.New("some-error"))
   725  												})
   726  
   727  												It("displays all warnings and returns the error", func() {
   728  													Expect(executeErr).To(MatchError("some-error"))
   729  
   730  													Expect(testUI.Out).To(Say("Showing health and status for app some-app in org some-org / space some-space as banana\\.\\.\\."))
   731  
   732  													Expect(testUI.Err).To(Say("display-warning-1"))
   733  													Expect(testUI.Err).To(Say("display-warning-2"))
   734  													Expect(testUI.Err).To(Say("route-warning-1"))
   735  													Expect(testUI.Err).To(Say("route-warning-2"))
   736  
   737  													Expect(testUI.Out).ToNot(Say("name:\\s+some-app"))
   738  												})
   739  											})
   740  
   741  											Context("when getting the application routes is successful", func() {
   742  												BeforeEach(func() {
   743  													fakeV2AppActor.GetApplicationRoutesReturns([]v2action.Route{
   744  														{Domain: v2action.Domain{Name: "some-other-domain"}}, {
   745  															Domain: v2action.Domain{Name: "some-domain"}}},
   746  														v2action.Warnings{"route-warning-1", "route-warning-2"}, nil)
   747  												})
   748  
   749  												It("prints the application summary and outputs warnings", func() {
   750  													Expect(executeErr).ToNot(HaveOccurred())
   751  
   752  													Expect(testUI.Out).To(Say("(?m)Showing health and status for app some-app in org some-org / space some-space as banana\\.\\.\\.\n\n"))
   753  													Expect(testUI.Out).To(Say("name:\\s+some-app"))
   754  													Expect(testUI.Out).To(Say("requested state:\\s+started"))
   755  													Expect(testUI.Out).To(Say("processes:\\s+worker:1/1"))
   756  													Expect(testUI.Out).To(Say("memory usage:\\s+64M x 1"))
   757  													Expect(testUI.Out).To(Say("routes:\\s+some-other-domain, some-domain"))
   758  													Expect(testUI.Out).To(Say("stack:\\s+cflinuxfs2"))
   759  													Expect(testUI.Out).To(Say("(?m)buildpacks:\\s+some-detect-output\n\n"))
   760  
   761  													Expect(testUI.Out).To(Say("worker:1/1"))
   762  													Expect(testUI.Out).To(Say("\\s+state\\s+since\\s+cpu\\s+memory\\s+disk"))
   763  													Expect(testUI.Out).To(Say("#0\\s+running\\s+2013-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} [AP]M\\s+0.0%\\s+3.8M of 64M\\s+3.8M of 7.6M"))
   764  
   765  													Expect(testUI.Err).To(Say("display-warning-1"))
   766  													Expect(testUI.Err).To(Say("display-warning-2"))
   767  													Expect(testUI.Err).To(Say("route-warning-1"))
   768  													Expect(testUI.Err).To(Say("route-warning-2"))
   769  
   770  													Expect(fakeActor.GetApplicationSummaryByNameAndSpaceCallCount()).To(Equal(1))
   771  													appName, spaceGUID := fakeActor.GetApplicationSummaryByNameAndSpaceArgsForCall(0)
   772  													Expect(appName).To(Equal("some-app"))
   773  													Expect(spaceGUID).To(Equal("some-space-guid"))
   774  
   775  													Expect(fakeV2AppActor.GetApplicationRoutesCallCount()).To(Equal(1))
   776  													Expect(fakeV2AppActor.GetApplicationRoutesArgsForCall(0)).To(Equal("some-app-guid"))
   777  												})
   778  											})
   779  										})
   780  									})
   781  								})
   782  							})
   783  						})
   784  					})
   785  				})
   786  			})
   787  		})
   788  
   789  		Context("when looking up the application succeeds", func() {
   790  			BeforeEach(func() {
   791  				fakeActor.GetApplicationByNameAndSpaceReturns(v3action.Application{
   792  					Name: "some-app",
   793  					GUID: "some-app-guid",
   794  				}, v3action.Warnings{"get-warning"}, nil)
   795  			})
   796  
   797  			It("updates the application", func() {
   798  				Expect(fakeActor.CreateApplicationInSpaceCallCount()).To(Equal(0))
   799  				Expect(fakeActor.UpdateApplicationCallCount()).To(Equal(1))
   800  			})
   801  
   802  			Context("when a docker image is provided", func() {
   803  				BeforeEach(func() {
   804  					cmd.DockerImage.Path = "example.com/docker/docker/docker:docker"
   805  					cmd.DockerUsername = "username"
   806  					fakeConfig.DockerPasswordReturns("password")
   807  				})
   808  
   809  				Context("when a username/password are provided", func() {
   810  					It("updates the app with the provided credentials", func() {
   811  						appName, spaceGuid, dockerImageCredentials := fakeActor.CreateDockerPackageByApplicationNameAndSpaceArgsForCall(0)
   812  						Expect(appName).To(Equal("some-app"))
   813  						Expect(spaceGuid).To(Equal("some-space-guid"))
   814  						Expect(dockerImageCredentials.Path).To(Equal(cmd.DockerImage.Path))
   815  						Expect(dockerImageCredentials.Username).To(Equal("username"))
   816  						Expect(dockerImageCredentials.Password).To(Equal("password"))
   817  					})
   818  				})
   819  
   820  				It("updates the app with a docker lifecycle", func() {
   821  					Expect(fakeActor.UpdateApplicationCallCount()).To(Equal(1), "Expected UpdateApplication to be called once")
   822  					updateApp := fakeActor.UpdateApplicationArgsForCall(0)
   823  					Expect(updateApp).To(Equal(v3action.Application{
   824  						GUID: "some-app-guid",
   825  						Lifecycle: v3action.AppLifecycle{
   826  							Type: v3action.DockerAppLifecycleType,
   827  							Data: v3action.AppLifecycleData{},
   828  						},
   829  					}))
   830  				})
   831  			})
   832  			Context("when updating the application fails", func() {
   833  				BeforeEach(func() {
   834  					fakeActor.UpdateApplicationReturns(v3action.Application{}, v3action.Warnings{"update-warning-1"}, errors.New("some-error"))
   835  				})
   836  
   837  				It("returns the error and displays warnings", func() {
   838  					Expect(executeErr).To(MatchError("some-error"))
   839  
   840  					Expect(testUI.Err).To(Say("get-warning"))
   841  					Expect(testUI.Err).To(Say("update-warning"))
   842  				})
   843  			})
   844  
   845  			Context("when the app has a buildpack lifecycle", func() {
   846  				Context("when a buildpack was not provided", func() {
   847  					BeforeEach(func() {
   848  						cmd.Buildpacks = []string{}
   849  					})
   850  
   851  					It("does not update the buildpack", func() {
   852  						appArg := fakeActor.UpdateApplicationArgsForCall(0)
   853  						Expect(appArg).To(Equal(v3action.Application{
   854  							GUID: "some-app-guid",
   855  							Lifecycle: v3action.AppLifecycle{
   856  								Type: v3action.BuildpackAppLifecycleType,
   857  								Data: v3action.AppLifecycleData{
   858  									Buildpacks: []string{},
   859  								},
   860  							},
   861  						}))
   862  					})
   863  				})
   864  
   865  				Context("when a buildpack was provided", func() {
   866  					BeforeEach(func() {
   867  						cmd.Buildpacks = []string{"some-buildpack"}
   868  					})
   869  
   870  					It("updates the buildpack", func() {
   871  						appArg := fakeActor.UpdateApplicationArgsForCall(0)
   872  						Expect(appArg).To(Equal(v3action.Application{
   873  							GUID: "some-app-guid",
   874  							Lifecycle: v3action.AppLifecycle{
   875  								Type: v3action.BuildpackAppLifecycleType,
   876  								Data: v3action.AppLifecycleData{
   877  									Buildpacks: []string{"some-buildpack"},
   878  								},
   879  							},
   880  						}))
   881  					})
   882  				})
   883  
   884  				Context("when multiple buildpacks are provided", func() {
   885  					BeforeEach(func() {
   886  						cmd.Buildpacks = []string{"some-buildpack-1", "some-buildpack-2"}
   887  					})
   888  
   889  					It("updates the buildpacks", func() {
   890  						appArg := fakeActor.UpdateApplicationArgsForCall(0)
   891  						Expect(appArg).To(Equal(v3action.Application{
   892  							GUID: "some-app-guid",
   893  							Lifecycle: v3action.AppLifecycle{
   894  								Type: v3action.BuildpackAppLifecycleType,
   895  								Data: v3action.AppLifecycleData{
   896  									Buildpacks: []string{"some-buildpack-1", "some-buildpack-2"},
   897  								},
   898  							},
   899  						}))
   900  					})
   901  
   902  					Context("when default was also provided", func() {
   903  						BeforeEach(func() {
   904  							cmd.Buildpacks = []string{"default", "some-buildpack-2"}
   905  						})
   906  
   907  						It("returns the ConflictingBuildpacksError", func() {
   908  							Expect(executeErr).To(Equal(translatableerror.ConflictingBuildpacksError{}))
   909  							Expect(fakeActor.UpdateApplicationCallCount()).To(Equal(0))
   910  						})
   911  					})
   912  
   913  					Context("when null was also provided", func() {
   914  						BeforeEach(func() {
   915  							cmd.Buildpacks = []string{"null", "some-buildpack-2"}
   916  						})
   917  
   918  						It("returns the ConflictingBuildpacksError", func() {
   919  							Expect(executeErr).To(Equal(translatableerror.ConflictingBuildpacksError{}))
   920  							Expect(fakeActor.UpdateApplicationCallCount()).To(Equal(0))
   921  						})
   922  					})
   923  				})
   924  			})
   925  
   926  			Context("when updating the application succeeds", func() {
   927  				Context("when the application is stopped", func() {
   928  					BeforeEach(func() {
   929  						fakeActor.UpdateApplicationReturns(v3action.Application{GUID: "some-app-guid", State: "STOPPED"}, v3action.Warnings{"update-warning"}, nil)
   930  					})
   931  
   932  					It("skips stopping the application and pushes it", func() {
   933  						Expect(executeErr).ToNot(HaveOccurred())
   934  
   935  						Expect(testUI.Err).To(Say("get-warning"))
   936  						Expect(testUI.Err).To(Say("update-warning"))
   937  
   938  						Expect(testUI.Out).ToNot(Say("Stopping"))
   939  
   940  						Expect(fakeActor.StopApplicationCallCount()).To(Equal(0), "Expected StopApplication to not be called")
   941  
   942  						Expect(fakeActor.StartApplicationCallCount()).To(Equal(1), "Expected StartApplication to be called")
   943  					})
   944  				})
   945  
   946  				Context("when the application is started", func() {
   947  					BeforeEach(func() {
   948  						fakeActor.UpdateApplicationReturns(v3action.Application{GUID: "some-app-guid", State: "STARTED"}, nil, nil)
   949  					})
   950  
   951  					It("stops the application and pushes it", func() {
   952  						Expect(executeErr).ToNot(HaveOccurred())
   953  						Expect(testUI.Out).To(Say("Stopping app some-app in org some-org / space some-space as banana..."))
   954  
   955  						Expect(fakeActor.StopApplicationCallCount()).To(Equal(1))
   956  
   957  						Expect(fakeActor.StartApplicationCallCount()).To(Equal(1), "Expected StartApplication to be called")
   958  					})
   959  				})
   960  			})
   961  		})
   962  	})
   963  })