github.com/liamawhite/cli-with-i18n@v6.32.1-0.20171122084555-dede0a5c3448+incompatible/command/v2/v2_push_command_test.go (about)

     1  package v2_test
     2  
     3  import (
     4  	"errors"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  	"regexp"
     9  	"time"
    10  
    11  	"github.com/liamawhite/cli-with-i18n/actor/pushaction"
    12  	"github.com/liamawhite/cli-with-i18n/actor/sharedaction"
    13  	"github.com/liamawhite/cli-with-i18n/actor/v2action"
    14  	"github.com/liamawhite/cli-with-i18n/api/cloudcontroller/ccv2"
    15  	"github.com/liamawhite/cli-with-i18n/command/commandfakes"
    16  	"github.com/liamawhite/cli-with-i18n/command/flag"
    17  	"github.com/liamawhite/cli-with-i18n/command/translatableerror"
    18  	. "github.com/liamawhite/cli-with-i18n/command/v2"
    19  	"github.com/liamawhite/cli-with-i18n/command/v2/v2fakes"
    20  	"github.com/liamawhite/cli-with-i18n/types"
    21  	"github.com/liamawhite/cli-with-i18n/util/configv3"
    22  	"github.com/liamawhite/cli-with-i18n/util/manifest"
    23  	"github.com/liamawhite/cli-with-i18n/util/ui"
    24  	. "github.com/onsi/ginkgo"
    25  	. "github.com/onsi/gomega"
    26  	. "github.com/onsi/gomega/gbytes"
    27  )
    28  
    29  var _ = Describe("v2-push Command", func() {
    30  	var (
    31  		cmd              V2PushCommand
    32  		testUI           *ui.UI
    33  		fakeConfig       *commandfakes.FakeConfig
    34  		fakeSharedActor  *commandfakes.FakeSharedActor
    35  		fakeActor        *v2fakes.FakeV2PushActor
    36  		fakeRestartActor *v2fakes.FakeRestartActor
    37  		fakeProgressBar  *v2fakes.FakeProgressBar
    38  		input            *Buffer
    39  		binaryName       string
    40  
    41  		appName    string
    42  		executeErr error
    43  		pwd        string
    44  	)
    45  
    46  	BeforeEach(func() {
    47  		input = NewBuffer()
    48  		testUI = ui.NewTestUI(input, NewBuffer(), NewBuffer())
    49  		fakeConfig = new(commandfakes.FakeConfig)
    50  		fakeSharedActor = new(commandfakes.FakeSharedActor)
    51  		fakeActor = new(v2fakes.FakeV2PushActor)
    52  		fakeRestartActor = new(v2fakes.FakeRestartActor)
    53  		fakeProgressBar = new(v2fakes.FakeProgressBar)
    54  
    55  		cmd = V2PushCommand{
    56  			UI:           testUI,
    57  			Config:       fakeConfig,
    58  			SharedActor:  fakeSharedActor,
    59  			Actor:        fakeActor,
    60  			RestartActor: fakeRestartActor,
    61  			ProgressBar:  fakeProgressBar,
    62  		}
    63  
    64  		appName = "some-app"
    65  		cmd.OptionalArgs.AppName = appName
    66  		binaryName = "faceman"
    67  		fakeConfig.BinaryNameReturns(binaryName)
    68  
    69  		var err error
    70  		pwd, err = os.Getwd()
    71  		Expect(err).ToNot(HaveOccurred())
    72  	})
    73  
    74  	JustBeforeEach(func() {
    75  		executeErr = cmd.Execute(nil)
    76  	})
    77  
    78  	Context("when checking target fails", func() {
    79  		BeforeEach(func() {
    80  			fakeSharedActor.CheckTargetReturns(sharedaction.NotLoggedInError{BinaryName: binaryName})
    81  		})
    82  
    83  		It("returns an error", func() {
    84  			Expect(executeErr).To(MatchError(translatableerror.NotLoggedInError{BinaryName: binaryName}))
    85  
    86  			Expect(fakeSharedActor.CheckTargetCallCount()).To(Equal(1))
    87  			_, checkTargetedOrg, checkTargetedSpace := fakeSharedActor.CheckTargetArgsForCall(0)
    88  			Expect(checkTargetedOrg).To(BeTrue())
    89  			Expect(checkTargetedSpace).To(BeTrue())
    90  		})
    91  	})
    92  
    93  	Context("when the user is logged in, and org and space are targeted", func() {
    94  		BeforeEach(func() {
    95  			fakeConfig.HasTargetedOrganizationReturns(true)
    96  			fakeConfig.TargetedOrganizationReturns(configv3.Organization{GUID: "some-org-guid", Name: "some-org"})
    97  			fakeConfig.HasTargetedSpaceReturns(true)
    98  			fakeConfig.TargetedSpaceReturns(configv3.Space{GUID: "some-space-guid", Name: "some-space"})
    99  			fakeConfig.CurrentUserReturns(configv3.User{Name: "some-user"}, nil)
   100  		})
   101  
   102  		Context("when the push settings are valid", func() {
   103  			var appManifests []manifest.Application
   104  
   105  			BeforeEach(func() {
   106  				appManifests = []manifest.Application{
   107  					{
   108  						Name: appName,
   109  						Path: pwd,
   110  					},
   111  				}
   112  				fakeActor.MergeAndValidateSettingsAndManifestsReturns(appManifests, nil)
   113  			})
   114  
   115  			Context("when the settings can be converted to a valid config", func() {
   116  				var appConfigs []pushaction.ApplicationConfig
   117  
   118  				BeforeEach(func() {
   119  					appConfigs = []pushaction.ApplicationConfig{
   120  						{
   121  							CurrentApplication: pushaction.Application{Application: v2action.Application{Name: appName, State: ccv2.ApplicationStarted}},
   122  							DesiredApplication: pushaction.Application{Application: v2action.Application{Name: appName}},
   123  							CurrentRoutes: []v2action.Route{
   124  								{Host: "route1", Domain: v2action.Domain{Name: "example.com"}},
   125  								{Host: "route2", Domain: v2action.Domain{Name: "example.com"}},
   126  							},
   127  							DesiredRoutes: []v2action.Route{
   128  								{Host: "route3", Domain: v2action.Domain{Name: "example.com"}},
   129  								{Host: "route4", Domain: v2action.Domain{Name: "example.com"}},
   130  							},
   131  							TargetedSpaceGUID: "some-space-guid",
   132  							Path:              pwd,
   133  						},
   134  					}
   135  					fakeActor.ConvertToApplicationConfigsReturns(appConfigs, pushaction.Warnings{"some-config-warnings"}, nil)
   136  				})
   137  
   138  				Context("when the apply is successful", func() {
   139  					var updatedConfig pushaction.ApplicationConfig
   140  
   141  					BeforeEach(func() {
   142  						fakeActor.ApplyStub = func(_ pushaction.ApplicationConfig, _ pushaction.ProgressBar) (<-chan pushaction.ApplicationConfig, <-chan pushaction.Event, <-chan pushaction.Warnings, <-chan error) {
   143  							configStream := make(chan pushaction.ApplicationConfig, 1)
   144  							eventStream := make(chan pushaction.Event)
   145  							warningsStream := make(chan pushaction.Warnings)
   146  							errorStream := make(chan error)
   147  
   148  							updatedConfig = pushaction.ApplicationConfig{
   149  								CurrentApplication: pushaction.Application{Application: v2action.Application{Name: appName, GUID: "some-app-guid"}},
   150  								DesiredApplication: pushaction.Application{Application: v2action.Application{Name: appName, GUID: "some-app-guid"}},
   151  								TargetedSpaceGUID:  "some-space-guid",
   152  								Path:               pwd,
   153  							}
   154  
   155  							go func() {
   156  								defer GinkgoRecover()
   157  
   158  								Eventually(eventStream).Should(BeSent(pushaction.SettingUpApplication))
   159  								Eventually(eventStream).Should(BeSent(pushaction.CreatedApplication))
   160  								Eventually(eventStream).Should(BeSent(pushaction.UpdatedApplication))
   161  								Eventually(eventStream).Should(BeSent(pushaction.ConfiguringRoutes))
   162  								Eventually(eventStream).Should(BeSent(pushaction.CreatedRoutes))
   163  								Eventually(eventStream).Should(BeSent(pushaction.BoundRoutes))
   164  								Eventually(eventStream).Should(BeSent(pushaction.ConfiguringServices))
   165  								Eventually(eventStream).Should(BeSent(pushaction.BoundServices))
   166  								Eventually(eventStream).Should(BeSent(pushaction.ResourceMatching))
   167  								Eventually(eventStream).Should(BeSent(pushaction.CreatingArchive))
   168  								Eventually(eventStream).Should(BeSent(pushaction.UploadingApplication))
   169  								Eventually(fakeProgressBar.ReadyCallCount).Should(Equal(1))
   170  								Eventually(eventStream).Should(BeSent(pushaction.RetryUpload))
   171  								Eventually(eventStream).Should(BeSent(pushaction.UploadComplete))
   172  								Eventually(fakeProgressBar.CompleteCallCount).Should(Equal(1))
   173  								Eventually(configStream).Should(BeSent(updatedConfig))
   174  								Eventually(eventStream).Should(BeSent(pushaction.Complete))
   175  								Eventually(warningsStream).Should(BeSent(pushaction.Warnings{"apply-1", "apply-2"}))
   176  								close(configStream)
   177  								close(eventStream)
   178  								close(warningsStream)
   179  								close(errorStream)
   180  							}()
   181  
   182  							return configStream, eventStream, warningsStream, errorStream
   183  						}
   184  
   185  						fakeRestartActor.RestartApplicationStub = func(app v2action.Application, client v2action.NOAAClient, config v2action.Config) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) {
   186  							messages := make(chan *v2action.LogMessage)
   187  							logErrs := make(chan error)
   188  							appState := make(chan v2action.ApplicationStateChange)
   189  							warnings := make(chan string)
   190  							errs := make(chan error)
   191  
   192  							go func() {
   193  								messages <- v2action.NewLogMessage("log message 1", 1, time.Unix(0, 0), "STG", "1")
   194  								messages <- v2action.NewLogMessage("log message 2", 1, time.Unix(0, 0), "STG", "1")
   195  								appState <- v2action.ApplicationStateStopping
   196  								appState <- v2action.ApplicationStateStaging
   197  								appState <- v2action.ApplicationStateStarting
   198  								close(messages)
   199  								close(logErrs)
   200  								close(appState)
   201  								close(warnings)
   202  								close(errs)
   203  							}()
   204  
   205  							return messages, logErrs, appState, warnings, errs
   206  						}
   207  
   208  						applicationSummary := v2action.ApplicationSummary{
   209  							Application: v2action.Application{
   210  								DetectedBuildpack:    types.FilteredString{IsSet: true, Value: "some-buildpack"},
   211  								DetectedStartCommand: types.FilteredString{IsSet: true, Value: "some start command"},
   212  								GUID:                 "some-app-guid",
   213  								Instances:            types.NullInt{Value: 3, IsSet: true},
   214  								Memory:               128,
   215  								Name:                 appName,
   216  								PackageUpdatedAt:     time.Unix(0, 0),
   217  								State:                "STARTED",
   218  							},
   219  							Stack: v2action.Stack{
   220  								Name: "potatos",
   221  							},
   222  							Routes: []v2action.Route{
   223  								{
   224  									Host: "banana",
   225  									Domain: v2action.Domain{
   226  										Name: "fruit.com",
   227  									},
   228  									Path: "/hi",
   229  								},
   230  								{
   231  									Domain: v2action.Domain{
   232  										Name: "foobar.com",
   233  									},
   234  									Port: types.NullInt{IsSet: true, Value: 13},
   235  								},
   236  							},
   237  						}
   238  						warnings := []string{"app-summary-warning"}
   239  
   240  						applicationSummary.RunningInstances = []v2action.ApplicationInstanceWithStats{{State: "RUNNING"}}
   241  
   242  						fakeRestartActor.GetApplicationSummaryByNameAndSpaceReturns(applicationSummary, warnings, nil)
   243  					})
   244  
   245  					Context("when no manifest is provided", func() {
   246  						It("passes through the command line flags", func() {
   247  							Expect(executeErr).ToNot(HaveOccurred())
   248  
   249  							Expect(fakeActor.MergeAndValidateSettingsAndManifestsCallCount()).To(Equal(1))
   250  							cmdSettings, _ := fakeActor.MergeAndValidateSettingsAndManifestsArgsForCall(0)
   251  							Expect(cmdSettings).To(Equal(pushaction.CommandLineSettings{
   252  								Name:             appName,
   253  								CurrentDirectory: pwd,
   254  							}))
   255  						})
   256  					})
   257  
   258  					Context("when a manifest is provided", func() {
   259  						var (
   260  							tmpDir         string
   261  							pathToManifest string
   262  
   263  							originalDir string
   264  						)
   265  
   266  						BeforeEach(func() {
   267  							var err error
   268  							tmpDir, err = ioutil.TempDir("", "v2-push-command-test")
   269  							Expect(err).ToNot(HaveOccurred())
   270  
   271  							// OS X uses weird symlinks that causes problems for some tests
   272  							tmpDir, err = filepath.EvalSymlinks(tmpDir)
   273  							Expect(err).ToNot(HaveOccurred())
   274  
   275  							originalDir, err = os.Getwd()
   276  							Expect(err).ToNot(HaveOccurred())
   277  
   278  							cmd.OptionalArgs.AppName = ""
   279  						})
   280  
   281  						AfterEach(func() {
   282  							Expect(os.Chdir(originalDir)).ToNot(HaveOccurred())
   283  							Expect(os.RemoveAll(tmpDir)).ToNot(HaveOccurred())
   284  						})
   285  
   286  						Context("via a manfiest.yml in the current directory", func() {
   287  							var expectedApps []manifest.Application
   288  
   289  							BeforeEach(func() {
   290  								err := os.Chdir(tmpDir)
   291  								Expect(err).ToNot(HaveOccurred())
   292  
   293  								pathToManifest = filepath.Join(tmpDir, "manifest.yml")
   294  								err = ioutil.WriteFile(pathToManifest, []byte("some manfiest file"), 0666)
   295  								Expect(err).ToNot(HaveOccurred())
   296  
   297  								expectedApps = []manifest.Application{{Name: "some-app"}, {Name: "some-other-app"}}
   298  								fakeActor.ReadManifestReturns(expectedApps, nil)
   299  							})
   300  
   301  							Context("when reading the manifest file is successful", func() {
   302  								It("merges app manifest and flags", func() {
   303  									Expect(executeErr).ToNot(HaveOccurred())
   304  
   305  									Expect(fakeActor.ReadManifestCallCount()).To(Equal(1))
   306  									Expect(fakeActor.ReadManifestArgsForCall(0)).To(Equal(pathToManifest))
   307  
   308  									Expect(fakeActor.MergeAndValidateSettingsAndManifestsCallCount()).To(Equal(1))
   309  									cmdSettings, manifestApps := fakeActor.MergeAndValidateSettingsAndManifestsArgsForCall(0)
   310  									Expect(cmdSettings).To(Equal(pushaction.CommandLineSettings{
   311  										CurrentDirectory: tmpDir,
   312  									}))
   313  									Expect(manifestApps).To(Equal(expectedApps))
   314  								})
   315  							})
   316  
   317  							Context("when reading manifest file errors", func() {
   318  								var expectedErr error
   319  
   320  								BeforeEach(func() {
   321  									expectedErr = errors.New("I am an error!!!")
   322  
   323  									fakeActor.ReadManifestReturns(nil, expectedErr)
   324  								})
   325  
   326  								It("returns the error", func() {
   327  									Expect(executeErr).To(MatchError(expectedErr))
   328  								})
   329  							})
   330  
   331  							Context("when --no-manifest is specified", func() {
   332  								BeforeEach(func() {
   333  									cmd.NoManifest = true
   334  								})
   335  
   336  								It("ignores the manifest file", func() {
   337  									Expect(executeErr).ToNot(HaveOccurred())
   338  
   339  									Expect(fakeActor.MergeAndValidateSettingsAndManifestsCallCount()).To(Equal(1))
   340  									cmdSettings, manifestApps := fakeActor.MergeAndValidateSettingsAndManifestsArgsForCall(0)
   341  									Expect(cmdSettings).To(Equal(pushaction.CommandLineSettings{
   342  										CurrentDirectory: tmpDir,
   343  									}))
   344  									Expect(manifestApps).To(BeNil())
   345  								})
   346  							})
   347  						})
   348  
   349  						Context("via a manfiest.yaml in the current directory", func() {
   350  							BeforeEach(func() {
   351  								err := os.Chdir(tmpDir)
   352  								Expect(err).ToNot(HaveOccurred())
   353  
   354  								pathToManifest = filepath.Join(tmpDir, "manifest.yaml")
   355  								err = ioutil.WriteFile(pathToManifest, []byte("some manfiest file"), 0666)
   356  								Expect(err).ToNot(HaveOccurred())
   357  							})
   358  
   359  							It("should read the manifest.yml", func() {
   360  								Expect(executeErr).ToNot(HaveOccurred())
   361  
   362  								Expect(fakeActor.ReadManifestCallCount()).To(Equal(1))
   363  								Expect(fakeActor.ReadManifestArgsForCall(0)).To(Equal(pathToManifest))
   364  							})
   365  						})
   366  
   367  						Context("via the -f flag", func() {
   368  							BeforeEach(func() {
   369  								pathToManifest = filepath.Join(tmpDir, "manifest.yaml")
   370  								err := ioutil.WriteFile(pathToManifest, []byte("some manfiest file"), 0666)
   371  								Expect(err).ToNot(HaveOccurred())
   372  
   373  								cmd.PathToManifest = flag.PathWithExistenceCheck(pathToManifest)
   374  							})
   375  
   376  							It("should read the manifest.yml", func() {
   377  								Expect(executeErr).ToNot(HaveOccurred())
   378  
   379  								Expect(fakeActor.ReadManifestCallCount()).To(Equal(1))
   380  								Expect(fakeActor.ReadManifestArgsForCall(0)).To(Equal(pathToManifest))
   381  							})
   382  						})
   383  					})
   384  
   385  					It("converts the manifests to app configs and outputs config warnings", func() {
   386  						Expect(executeErr).ToNot(HaveOccurred())
   387  
   388  						Expect(testUI.Err).To(Say("some-config-warnings"))
   389  
   390  						Expect(fakeActor.ConvertToApplicationConfigsCallCount()).To(Equal(1))
   391  						orgGUID, spaceGUID, noStart, manifests := fakeActor.ConvertToApplicationConfigsArgsForCall(0)
   392  						Expect(orgGUID).To(Equal("some-org-guid"))
   393  						Expect(spaceGUID).To(Equal("some-space-guid"))
   394  						Expect(noStart).To(BeFalse())
   395  						Expect(manifests).To(Equal(appManifests))
   396  					})
   397  
   398  					It("outputs flavor text prior to generating app configuration", func() {
   399  						Expect(executeErr).ToNot(HaveOccurred())
   400  						Expect(testUI.Out).To(Say("Getting app info\\.\\.\\."))
   401  					})
   402  
   403  					It("applies each of the application configurations", func() {
   404  						Expect(executeErr).ToNot(HaveOccurred())
   405  
   406  						Expect(fakeActor.ApplyCallCount()).To(Equal(1))
   407  						config, progressBar := fakeActor.ApplyArgsForCall(0)
   408  						Expect(config).To(Equal(appConfigs[0]))
   409  						Expect(progressBar).To(Equal(fakeProgressBar))
   410  					})
   411  
   412  					It("display diff of changes", func() {
   413  						Expect(executeErr).ToNot(HaveOccurred())
   414  
   415  						Expect(testUI.Out).To(Say("\\s+name:\\s+%s", appName))
   416  						Expect(testUI.Out).To(Say("\\s+path:\\s+%s", regexp.QuoteMeta(appConfigs[0].Path)))
   417  						Expect(testUI.Out).To(Say("\\s+routes:"))
   418  						for _, route := range appConfigs[0].CurrentRoutes {
   419  							Expect(testUI.Out).To(Say(route.String()))
   420  						}
   421  						for _, route := range appConfigs[0].DesiredRoutes {
   422  							Expect(testUI.Out).To(Say(route.String()))
   423  						}
   424  					})
   425  
   426  					Context("when the app starts", func() {
   427  						It("displays app events and warnings", func() {
   428  							Expect(executeErr).ToNot(HaveOccurred())
   429  
   430  							Expect(testUI.Out).To(Say("Creating app with these attributes\\.\\.\\."))
   431  							Expect(testUI.Out).To(Say("Mapping routes\\.\\.\\."))
   432  							Expect(testUI.Out).To(Say("Binding services\\.\\.\\."))
   433  							Expect(testUI.Out).To(Say("Comparing local files to remote cache\\.\\.\\."))
   434  							Expect(testUI.Out).To(Say("Packaging files to upload\\.\\.\\."))
   435  							Expect(testUI.Out).To(Say("Uploading files\\.\\.\\."))
   436  							Expect(testUI.Out).To(Say("Retrying upload due to an error\\.\\.\\."))
   437  							Expect(testUI.Out).To(Say("Waiting for API to complete processing files\\.\\.\\."))
   438  							Expect(testUI.Out).To(Say("Stopping app\\.\\.\\."))
   439  
   440  							Expect(testUI.Err).To(Say("some-config-warnings"))
   441  							Expect(testUI.Err).To(Say("apply-1"))
   442  							Expect(testUI.Err).To(Say("apply-2"))
   443  						})
   444  
   445  						It("displays app staging logs", func() {
   446  							Expect(executeErr).ToNot(HaveOccurred())
   447  
   448  							Expect(testUI.Out).To(Say("log message 1"))
   449  							Expect(testUI.Out).To(Say("log message 2"))
   450  
   451  							Expect(fakeRestartActor.RestartApplicationCallCount()).To(Equal(1))
   452  							appConfig, _, _ := fakeRestartActor.RestartApplicationArgsForCall(0)
   453  							Expect(appConfig).To(Equal(updatedConfig.CurrentApplication.Application))
   454  						})
   455  
   456  						It("displays the app summary with isolation segments as well as warnings", func() {
   457  							Expect(executeErr).ToNot(HaveOccurred())
   458  							Expect(testUI.Out).To(Say("name:\\s+%s", appName))
   459  							Expect(testUI.Out).To(Say("requested state:\\s+started"))
   460  							Expect(testUI.Out).To(Say("instances:\\s+1\\/3"))
   461  							Expect(testUI.Out).To(Say("usage:\\s+128M x 3 instances"))
   462  							Expect(testUI.Out).To(Say("routes:\\s+banana.fruit.com/hi, foobar.com:13"))
   463  							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}"))
   464  							Expect(testUI.Out).To(Say("stack:\\s+potatos"))
   465  							Expect(testUI.Out).To(Say("buildpack:\\s+some-buildpack"))
   466  							Expect(testUI.Out).To(Say("start command:\\s+some start command"))
   467  
   468  							Expect(testUI.Err).To(Say("app-summary-warning"))
   469  						})
   470  
   471  						Context("when the start command is explicitly set", func() {
   472  							BeforeEach(func() {
   473  								applicationSummary := v2action.ApplicationSummary{
   474  									Application: v2action.Application{
   475  										Command:              types.FilteredString{IsSet: true, Value: "a-different-start-command"},
   476  										DetectedBuildpack:    types.FilteredString{IsSet: true, Value: "some-buildpack"},
   477  										DetectedStartCommand: types.FilteredString{IsSet: true, Value: "some start command"},
   478  										GUID:                 "some-app-guid",
   479  										Instances:            types.NullInt{Value: 3, IsSet: true},
   480  										Memory:               128,
   481  										Name:                 appName,
   482  										PackageUpdatedAt:     time.Unix(0, 0),
   483  										State:                "STARTED",
   484  									},
   485  									Stack: v2action.Stack{
   486  										Name: "potatos",
   487  									},
   488  									Routes: []v2action.Route{
   489  										{
   490  											Host: "banana",
   491  											Domain: v2action.Domain{
   492  												Name: "fruit.com",
   493  											},
   494  											Path: "/hi",
   495  										},
   496  										{
   497  											Domain: v2action.Domain{
   498  												Name: "foobar.com",
   499  											},
   500  											Port: types.NullInt{IsSet: true, Value: 13},
   501  										},
   502  									},
   503  								}
   504  								warnings := []string{"app-summary-warning"}
   505  
   506  								applicationSummary.RunningInstances = []v2action.ApplicationInstanceWithStats{{State: "RUNNING"}}
   507  
   508  								fakeRestartActor.GetApplicationSummaryByNameAndSpaceReturns(applicationSummary, warnings, nil)
   509  							})
   510  
   511  							It("displays the correct start command", func() {
   512  								Expect(executeErr).ToNot(HaveOccurred())
   513  								Expect(testUI.Out).To(Say("name:\\s+%s", appName))
   514  								Expect(testUI.Out).To(Say("start command:\\s+a-different-start-command"))
   515  							})
   516  						})
   517  					})
   518  
   519  					Context("when no-start is set", func() {
   520  						BeforeEach(func() {
   521  							cmd.NoStart = true
   522  
   523  							applicationSummary := v2action.ApplicationSummary{
   524  								Application: v2action.Application{
   525  									Command:              types.FilteredString{IsSet: true, Value: "a-different-start-command"},
   526  									DetectedBuildpack:    types.FilteredString{IsSet: true, Value: "some-buildpack"},
   527  									DetectedStartCommand: types.FilteredString{IsSet: true, Value: "some start command"},
   528  									GUID:                 "some-app-guid",
   529  									Instances:            types.NullInt{Value: 3, IsSet: true},
   530  									Memory:               128,
   531  									Name:                 appName,
   532  									PackageUpdatedAt:     time.Unix(0, 0),
   533  									State:                "STOPPED",
   534  								},
   535  								Stack: v2action.Stack{
   536  									Name: "potatos",
   537  								},
   538  								Routes: []v2action.Route{
   539  									{
   540  										Host: "banana",
   541  										Domain: v2action.Domain{
   542  											Name: "fruit.com",
   543  										},
   544  										Path: "/hi",
   545  									},
   546  									{
   547  										Domain: v2action.Domain{
   548  											Name: "foobar.com",
   549  										},
   550  										Port: types.NullInt{IsSet: true, Value: 13},
   551  									},
   552  								},
   553  							}
   554  							warnings := []string{"app-summary-warning"}
   555  
   556  							fakeRestartActor.GetApplicationSummaryByNameAndSpaceReturns(applicationSummary, warnings, nil)
   557  						})
   558  
   559  						Context("when the app is not running", func() {
   560  							It("does not start the app", func() {
   561  								Expect(executeErr).ToNot(HaveOccurred())
   562  								Expect(testUI.Out).To(Say("Waiting for API to complete processing files\\.\\.\\."))
   563  								Expect(testUI.Out).To(Say("name:\\s+%s", appName))
   564  								Expect(testUI.Out).To(Say("requested state:\\s+stopped"))
   565  
   566  								Expect(fakeRestartActor.RestartApplicationCallCount()).To(Equal(0))
   567  							})
   568  						})
   569  					})
   570  				})
   571  
   572  				Context("when the apply errors", func() {
   573  					var expectedErr error
   574  
   575  					BeforeEach(func() {
   576  						expectedErr = errors.New("no wayz dude")
   577  						fakeActor.ApplyStub = func(_ pushaction.ApplicationConfig, _ pushaction.ProgressBar) (<-chan pushaction.ApplicationConfig, <-chan pushaction.Event, <-chan pushaction.Warnings, <-chan error) {
   578  							configStream := make(chan pushaction.ApplicationConfig)
   579  							eventStream := make(chan pushaction.Event)
   580  							warningsStream := make(chan pushaction.Warnings)
   581  							errorStream := make(chan error)
   582  
   583  							go func() {
   584  								defer GinkgoRecover()
   585  
   586  								Eventually(warningsStream).Should(BeSent(pushaction.Warnings{"apply-1", "apply-2"}))
   587  								Eventually(errorStream).Should(BeSent(expectedErr))
   588  								close(configStream)
   589  								close(eventStream)
   590  								close(warningsStream)
   591  								close(errorStream)
   592  							}()
   593  
   594  							return configStream, eventStream, warningsStream, errorStream
   595  						}
   596  					})
   597  
   598  					It("outputs the warnings and returns the error", func() {
   599  						Expect(executeErr).To(MatchError(expectedErr))
   600  
   601  						Expect(testUI.Err).To(Say("some-config-warnings"))
   602  						Expect(testUI.Err).To(Say("apply-1"))
   603  						Expect(testUI.Err).To(Say("apply-2"))
   604  					})
   605  				})
   606  			})
   607  
   608  			Context("when there is an error converting the app setting into a config", func() {
   609  				var expectedErr error
   610  
   611  				BeforeEach(func() {
   612  					expectedErr = errors.New("no wayz dude")
   613  					fakeActor.ConvertToApplicationConfigsReturns(nil, pushaction.Warnings{"some-config-warnings"}, expectedErr)
   614  				})
   615  
   616  				It("outputs the warnings and returns the error", func() {
   617  					Expect(executeErr).To(MatchError(expectedErr))
   618  
   619  					Expect(testUI.Err).To(Say("some-config-warnings"))
   620  				})
   621  			})
   622  		})
   623  
   624  		Context("when the push settings are invalid", func() {
   625  			var expectedErr error
   626  
   627  			BeforeEach(func() {
   628  				expectedErr = errors.New("no wayz dude")
   629  				fakeActor.MergeAndValidateSettingsAndManifestsReturns(nil, expectedErr)
   630  			})
   631  
   632  			It("returns the error", func() {
   633  				Expect(executeErr).To(MatchError(expectedErr))
   634  			})
   635  		})
   636  	})
   637  
   638  	Describe("GetCommandLineSettings", func() {
   639  		var (
   640  			settings   pushaction.CommandLineSettings
   641  			executeErr error
   642  		)
   643  
   644  		JustBeforeEach(func() {
   645  			settings, executeErr = cmd.GetCommandLineSettings()
   646  		})
   647  
   648  		Context("when passed app related flags", func() {
   649  			BeforeEach(func() {
   650  				cmd.Buildpack = flag.Buildpack{FilteredString: types.FilteredString{Value: "some-buildpack", IsSet: true}}
   651  				cmd.Command = flag.Command{FilteredString: types.FilteredString{IsSet: true, Value: "echo foo bar baz"}}
   652  				cmd.DiskQuota = flag.Megabytes{NullUint64: types.NullUint64{Value: 1024, IsSet: true}}
   653  				cmd.HealthCheckTimeout = 14
   654  				cmd.HealthCheckType = flag.HealthCheckType{Type: "http"}
   655  				cmd.Instances = flag.Instances{NullInt: types.NullInt{Value: 12, IsSet: true}}
   656  				cmd.Memory = flag.Megabytes{NullUint64: types.NullUint64{Value: 100, IsSet: true}}
   657  				cmd.StackName = "some-stack"
   658  			})
   659  
   660  			It("sets them on the command line settings", func() {
   661  				Expect(executeErr).ToNot(HaveOccurred())
   662  				Expect(settings.Buildpack).To(Equal(types.FilteredString{Value: "some-buildpack", IsSet: true}))
   663  				Expect(settings.Command).To(Equal(types.FilteredString{IsSet: true, Value: "echo foo bar baz"}))
   664  				Expect(settings.DiskQuota).To(Equal(uint64(1024)))
   665  				Expect(settings.HealthCheckTimeout).To(Equal(14))
   666  				Expect(settings.HealthCheckType).To(Equal("http"))
   667  				Expect(settings.Instances).To(Equal(types.NullInt{Value: 12, IsSet: true}))
   668  				Expect(settings.Memory).To(Equal(uint64(100)))
   669  				Expect(settings.StackName).To(Equal("some-stack"))
   670  			})
   671  		})
   672  
   673  		Context("when the -o and -p flags are both given", func() {
   674  			BeforeEach(func() {
   675  				cmd.DockerImage.Path = "some-docker-image"
   676  				cmd.AppPath = "some-directory-path"
   677  			})
   678  
   679  			It("returns an error", func() {
   680  				Expect(executeErr).To(MatchError(translatableerror.ArgumentCombinationError{
   681  					Args: []string{"--docker-image, -o", "-p"},
   682  				}))
   683  			})
   684  		})
   685  
   686  		Context("when only -f and --no-manifest flags are passed", func() {
   687  			BeforeEach(func() {
   688  				cmd.PathToManifest = "/some/path.yml"
   689  				cmd.NoManifest = true
   690  			})
   691  
   692  			It("returns an ArgumentCombinationError", func() {
   693  				Expect(executeErr).To(MatchError(translatableerror.ArgumentCombinationError{
   694  					Args: []string{"-f", "--no-manifest"},
   695  				}))
   696  			})
   697  		})
   698  
   699  		Context("when only -o flag is passed", func() {
   700  			BeforeEach(func() {
   701  				cmd.DockerImage.Path = "some-docker-image-path"
   702  			})
   703  
   704  			It("creates command line setting from command line arguments", func() {
   705  				Expect(executeErr).ToNot(HaveOccurred())
   706  				Expect(settings.Name).To(Equal(appName))
   707  				Expect(settings.DockerImage).To(Equal("some-docker-image-path"))
   708  			})
   709  		})
   710  
   711  		Context("when only --docker-username flag are passed", func() {
   712  			BeforeEach(func() {
   713  				cmd.DockerUsername = "some-docker-username"
   714  			})
   715  
   716  			It("returns an error", func() {
   717  				Expect(executeErr).To(MatchError(translatableerror.RequiredFlagsError{
   718  					Arg1: "--docker-image, -o",
   719  					Arg2: "--docker-username",
   720  				}))
   721  			})
   722  		})
   723  
   724  		Context("when the -o, --docker-username flags are passed", func() {
   725  			BeforeEach(func() {
   726  				cmd.DockerImage.Path = "some-docker-image-path"
   727  				cmd.DockerUsername = "some-docker-username"
   728  			})
   729  
   730  			Context("the docker password environment variable is set", func() {
   731  				BeforeEach(func() {
   732  					fakeConfig.DockerPasswordReturns("some-docker-password")
   733  				})
   734  
   735  				It("creates command line setting from command line arguments and config", func() {
   736  					Expect(executeErr).ToNot(HaveOccurred())
   737  					Expect(settings.Name).To(Equal(appName))
   738  					Expect(settings.DockerImage).To(Equal("some-docker-image-path"))
   739  					Expect(settings.DockerUsername).To(Equal("some-docker-username"))
   740  					Expect(settings.DockerPassword).To(Equal("some-docker-password"))
   741  				})
   742  			})
   743  
   744  			Context("the docker password environment variable is *not* set", func() {
   745  				It("returns an error", func() {
   746  					Expect(executeErr).To(MatchError(translatableerror.DockerPasswordNotSetError{}))
   747  				})
   748  			})
   749  		})
   750  
   751  		Context("when only -p flag is passed", func() {
   752  			BeforeEach(func() {
   753  				cmd.AppPath = "some-directory-path"
   754  			})
   755  
   756  			It("creates command line setting from command line arguments", func() {
   757  				Expect(executeErr).ToNot(HaveOccurred())
   758  				Expect(settings.Name).To(Equal(appName))
   759  				Expect(settings.ProvidedAppPath).To(Equal("some-directory-path"))
   760  			})
   761  		})
   762  
   763  		Context("when -o and -b flags are passed", func() {
   764  			BeforeEach(func() {
   765  				cmd.DockerImage.Path = "some-docker-image"
   766  				cmd.Buildpack = flag.Buildpack{FilteredString: types.FilteredString{Value: "some-buildpack", IsSet: true}}
   767  			})
   768  
   769  			It("returns an error", func() {
   770  				Expect(executeErr).To(MatchError(translatableerror.ArgumentCombinationError{
   771  					Args: []string{"-b", "--docker-image, -o"},
   772  				}))
   773  			})
   774  		})
   775  	})
   776  })