github.com/arunkumar7540/cli@v6.45.0+incompatible/command/v7/push_command_test.go (about)

     1  package v7_test
     2  
     3  import (
     4  	"errors"
     5  	"time"
     6  
     7  	. "github.com/onsi/gomega/gstruct"
     8  
     9  	"code.cloudfoundry.org/cli/actor/actionerror"
    10  	"code.cloudfoundry.org/cli/actor/v7action"
    11  	"code.cloudfoundry.org/cli/actor/v7action/v7actionfakes"
    12  	"code.cloudfoundry.org/cli/actor/v7pushaction"
    13  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
    14  	"code.cloudfoundry.org/cli/command/commandfakes"
    15  	"code.cloudfoundry.org/cli/command/flag"
    16  	"code.cloudfoundry.org/cli/command/translatableerror"
    17  	"code.cloudfoundry.org/cli/command/v6/v6fakes"
    18  	. "code.cloudfoundry.org/cli/command/v7"
    19  	"code.cloudfoundry.org/cli/command/v7/v7fakes"
    20  	"code.cloudfoundry.org/cli/types"
    21  	"code.cloudfoundry.org/cli/util/configv3"
    22  	"code.cloudfoundry.org/cli/util/ui"
    23  	"github.com/cloudfoundry/bosh-cli/director/template"
    24  	. "github.com/onsi/ginkgo"
    25  	. "github.com/onsi/ginkgo/extensions/table"
    26  	. "github.com/onsi/gomega"
    27  	. "github.com/onsi/gomega/gbytes"
    28  )
    29  
    30  type Step struct {
    31  	Error    error
    32  	Event    v7pushaction.Event
    33  	Warnings v7pushaction.Warnings
    34  }
    35  
    36  func FillInEvents(tuples []Step) (<-chan v7pushaction.Event, <-chan v7pushaction.Warnings, <-chan error) {
    37  	eventStream := make(chan v7pushaction.Event)
    38  	warningsStream := make(chan v7pushaction.Warnings)
    39  	errorStream := make(chan error)
    40  
    41  	go func() {
    42  		defer close(eventStream)
    43  		defer close(warningsStream)
    44  		defer close(errorStream)
    45  
    46  		for _, tuple := range tuples {
    47  			warningsStream <- tuple.Warnings
    48  			if tuple.Error != nil {
    49  				errorStream <- tuple.Error
    50  				return
    51  			} else {
    52  				eventStream <- tuple.Event
    53  			}
    54  		}
    55  	}()
    56  
    57  	return eventStream, warningsStream, errorStream
    58  }
    59  
    60  func FillInValues(tuples []Step, state v7pushaction.PushPlan) func(v7pushaction.PushPlan, v7pushaction.ProgressBar) (<-chan v7pushaction.PushPlan, <-chan v7pushaction.Event, <-chan v7pushaction.Warnings, <-chan error) {
    61  	return func(v7pushaction.PushPlan, v7pushaction.ProgressBar) (<-chan v7pushaction.PushPlan, <-chan v7pushaction.Event, <-chan v7pushaction.Warnings, <-chan error) {
    62  		stateStream := make(chan v7pushaction.PushPlan)
    63  
    64  		eventStream := make(chan v7pushaction.Event)
    65  		warningsStream := make(chan v7pushaction.Warnings)
    66  		errorStream := make(chan error)
    67  
    68  		go func() {
    69  			defer close(stateStream)
    70  			defer close(eventStream)
    71  			defer close(warningsStream)
    72  			defer close(errorStream)
    73  
    74  			for _, tuple := range tuples {
    75  				warningsStream <- tuple.Warnings
    76  				if tuple.Error != nil {
    77  					errorStream <- tuple.Error
    78  					return
    79  				} else {
    80  					eventStream <- tuple.Event
    81  				}
    82  			}
    83  
    84  			stateStream <- state
    85  			eventStream <- v7pushaction.Complete
    86  		}()
    87  
    88  		return stateStream, eventStream, warningsStream, errorStream
    89  	}
    90  }
    91  
    92  type LogEvent struct {
    93  	Log   *v7action.LogMessage
    94  	Error error
    95  }
    96  
    97  func ReturnLogs(logevents []LogEvent, passedWarnings v7action.Warnings, passedError error) func(appName string, spaceGUID string, client v7action.NOAAClient) (<-chan *v7action.LogMessage, <-chan error, v7action.Warnings, error) {
    98  	return func(appName string, spaceGUID string, client v7action.NOAAClient) (<-chan *v7action.LogMessage, <-chan error, v7action.Warnings, error) {
    99  		logStream := make(chan *v7action.LogMessage)
   100  		errStream := make(chan error)
   101  		go func() {
   102  			defer close(logStream)
   103  			defer close(errStream)
   104  
   105  			for _, log := range logevents {
   106  				if log.Log != nil {
   107  					logStream <- log.Log
   108  				}
   109  				if log.Error != nil {
   110  					errStream <- log.Error
   111  				}
   112  			}
   113  		}()
   114  
   115  		return logStream, errStream, passedWarnings, passedError
   116  	}
   117  }
   118  
   119  var _ = Describe("push Command", func() {
   120  	var (
   121  		cmd                 PushCommand
   122  		input               *Buffer
   123  		testUI              *ui.UI
   124  		fakeConfig          *commandfakes.FakeConfig
   125  		fakeSharedActor     *commandfakes.FakeSharedActor
   126  		fakeActor           *v7fakes.FakePushActor
   127  		fakeVersionActor    *v7fakes.FakeV7ActorForPush
   128  		fakeProgressBar     *v6fakes.FakeProgressBar
   129  		fakeNOAAClient      *v7actionfakes.FakeNOAAClient
   130  		fakeManifestLocator *v7fakes.FakeManifestLocator
   131  		binaryName          string
   132  		executeErr          error
   133  
   134  		appName1           string
   135  		appName2           string
   136  		userName           string
   137  		spaceName          string
   138  		orgName            string
   139  		pwd                string
   140  		fakeManifestParser *v7fakes.FakeManifestParser
   141  	)
   142  
   143  	BeforeEach(func() {
   144  		input = NewBuffer()
   145  		testUI = ui.NewTestUI(input, NewBuffer(), NewBuffer())
   146  		fakeConfig = new(commandfakes.FakeConfig)
   147  		fakeSharedActor = new(commandfakes.FakeSharedActor)
   148  		fakeActor = new(v7fakes.FakePushActor)
   149  		fakeVersionActor = new(v7fakes.FakeV7ActorForPush)
   150  		fakeProgressBar = new(v6fakes.FakeProgressBar)
   151  		fakeNOAAClient = new(v7actionfakes.FakeNOAAClient)
   152  
   153  		appName1 = "first-app"
   154  		appName2 = "second-app"
   155  		userName = "some-user"
   156  		spaceName = "some-space"
   157  		orgName = "some-org"
   158  		pwd = "/push/cmd/test"
   159  		fakeManifestLocator = new(v7fakes.FakeManifestLocator)
   160  		fakeManifestParser = new(v7fakes.FakeManifestParser)
   161  
   162  		binaryName = "faceman"
   163  		fakeConfig.BinaryNameReturns(binaryName)
   164  		fakeConfig.ExperimentalReturns(true) // TODO: Delete once we remove the experimental flag
   165  
   166  		cmd = PushCommand{
   167  			UI:              testUI,
   168  			Config:          fakeConfig,
   169  			Actor:           fakeActor,
   170  			VersionActor:    fakeVersionActor,
   171  			SharedActor:     fakeSharedActor,
   172  			ProgressBar:     fakeProgressBar,
   173  			NOAAClient:      fakeNOAAClient,
   174  			PWD:             pwd,
   175  			ManifestLocator: fakeManifestLocator,
   176  			ManifestParser:  fakeManifestParser,
   177  		}
   178  	})
   179  
   180  	Describe("Execute", func() {
   181  		JustBeforeEach(func() {
   182  			executeErr = cmd.Execute(nil)
   183  		})
   184  
   185  		BeforeEach(func() {
   186  			pushPlanChannel := make(chan []v7pushaction.PushPlan, 1)
   187  			pushPlanChannel <- []v7pushaction.PushPlan{
   188  				{Application: v7action.Application{Name: appName1}},
   189  				{Application: v7action.Application{Name: appName2}},
   190  			}
   191  			close(pushPlanChannel)
   192  			events, warnings, errors := FillInEvents([]Step{
   193  				{
   194  					Warnings: v7pushaction.Warnings{"some-warning-1"},
   195  					Event:    v7pushaction.ApplyManifest,
   196  				},
   197  				{
   198  					Warnings: v7pushaction.Warnings{"some-warning-2"},
   199  					Event:    v7pushaction.ApplyManifestComplete,
   200  				},
   201  			})
   202  
   203  			fakeActor.PrepareSpaceReturns(pushPlanChannel, events, warnings, errors)
   204  
   205  			fakeActor.ActualizeStub = FillInValues([]Step{{}}, v7pushaction.PushPlan{})
   206  		})
   207  
   208  		When("checking target fails", func() {
   209  			BeforeEach(func() {
   210  				fakeSharedActor.CheckTargetReturns(actionerror.NoOrganizationTargetedError{BinaryName: binaryName})
   211  			})
   212  
   213  			It("returns an error", func() {
   214  				Expect(executeErr).To(MatchError(actionerror.NoOrganizationTargetedError{BinaryName: binaryName}))
   215  
   216  				Expect(fakeSharedActor.CheckTargetCallCount()).To(Equal(1))
   217  				checkTargetedOrg, checkTargetedSpace := fakeSharedActor.CheckTargetArgsForCall(0)
   218  				Expect(checkTargetedOrg).To(BeTrue())
   219  				Expect(checkTargetedSpace).To(BeTrue())
   220  			})
   221  		})
   222  
   223  		When("checking target fails because the user is not logged in", func() {
   224  			BeforeEach(func() {
   225  				fakeSharedActor.CheckTargetReturns(actionerror.NotLoggedInError{BinaryName: binaryName})
   226  			})
   227  
   228  			It("returns an error", func() {
   229  				Expect(executeErr).To(MatchError(actionerror.NotLoggedInError{BinaryName: binaryName}))
   230  
   231  				Expect(fakeSharedActor.CheckTargetCallCount()).To(Equal(1))
   232  				checkTargetedOrg, checkTargetedSpace := fakeSharedActor.CheckTargetArgsForCall(0)
   233  				Expect(checkTargetedOrg).To(BeTrue())
   234  				Expect(checkTargetedSpace).To(BeTrue())
   235  			})
   236  		})
   237  
   238  		When("the user is logged in, and org and space are targeted", func() {
   239  			BeforeEach(func() {
   240  				fakeConfig.CurrentUserReturns(configv3.User{Name: userName}, nil)
   241  
   242  				fakeConfig.TargetedOrganizationReturns(configv3.Organization{
   243  					Name: orgName,
   244  					GUID: "some-org-guid",
   245  				})
   246  				fakeConfig.TargetedSpaceReturns(configv3.Space{
   247  					Name: spaceName,
   248  					GUID: "some-space-guid",
   249  				})
   250  			})
   251  
   252  			It("displays the experimental warning", func() {
   253  				Expect(testUI.Err).To(Say("This command is in EXPERIMENTAL stage and may change without notice"))
   254  			})
   255  
   256  			When("invalid flags are passed", func() {
   257  				BeforeEach(func() {
   258  					cmd.DockerUsername = "some-docker-username"
   259  				})
   260  
   261  				It("returns a validation error", func() {
   262  					Expect(executeErr).To(MatchError(translatableerror.RequiredFlagsError{Arg1: "--docker-image, -o", Arg2: "--docker-username"}))
   263  				})
   264  			})
   265  
   266  			Describe("reading manifest", func() {
   267  				BeforeEach(func() {
   268  					fakeManifestLocator.PathReturns("", true, nil)
   269  				})
   270  
   271  				When("Reading the manifest fails", func() {
   272  					BeforeEach(func() {
   273  						fakeManifestParser.InterpolateAndParseReturns(errors.New("oh no"))
   274  					})
   275  					It("returns the error", func() {
   276  						Expect(executeErr).To(MatchError("oh no"))
   277  					})
   278  				})
   279  
   280  				When("Reading the manifest succeeds", func() {
   281  					It("interpolates the manifest", func() {
   282  						Expect(executeErr).ToNot(HaveOccurred())
   283  						Expect(fakeManifestParser.InterpolateAndParseCallCount()).To(Equal(1))
   284  					})
   285  
   286  					When("the manifest contains private docker images", func() {
   287  						It("returns docker password", func() {
   288  							Expect(executeErr).ToNot(HaveOccurred())
   289  							Expect(fakeManifestParser.ContainsPrivateDockerImagesCallCount()).To(Equal(1))
   290  						})
   291  					})
   292  				})
   293  
   294  				When("no manifest flag", func() {
   295  					BeforeEach(func() {
   296  						cmd.NoManifest = true
   297  					})
   298  
   299  					It("does not read the manifest", func() {
   300  						Expect(executeErr).ToNot(HaveOccurred())
   301  
   302  						Expect(fakeManifestParser.InterpolateAndParseCallCount()).To(Equal(0))
   303  					})
   304  				})
   305  
   306  				When("multi app manifest + flag overrides", func() {
   307  					BeforeEach(func() {
   308  						fakeManifestParser.ContainsMultipleAppsReturns(true)
   309  						cmd.NoRoute = true
   310  					})
   311  
   312  					It("returns an error", func() {
   313  						Expect(executeErr).To(MatchError(translatableerror.CommandLineArgsWithMultipleAppsError{}))
   314  					})
   315  				})
   316  			})
   317  
   318  			Describe("delegating to Actor.CreatePushPlans", func() {
   319  				BeforeEach(func() {
   320  					cmd.OptionalArgs.AppName = appName1
   321  				})
   322  
   323  				It("delegates the correct values", func() {
   324  					Expect(fakeActor.CreatePushPlansCallCount()).To(Equal(1))
   325  					actualAppName, actualSpaceGUID, actualOrgGUID, _, _ := fakeActor.CreatePushPlansArgsForCall(0)
   326  
   327  					Expect(actualAppName).To(Equal(appName1))
   328  					Expect(actualSpaceGUID).To(Equal("some-space-guid"))
   329  					Expect(actualOrgGUID).To(Equal("some-org-guid"))
   330  				})
   331  
   332  				When("Creating the pushPlans errors", func() {
   333  					BeforeEach(func() {
   334  						fakeActor.CreatePushPlansReturns(nil, errors.New("panic"))
   335  					})
   336  
   337  					It("passes up the error", func() {
   338  						Expect(executeErr).To(MatchError(errors.New("panic")))
   339  						Expect(fakeActor.PrepareSpaceCallCount()).To(Equal(0))
   340  					})
   341  				})
   342  
   343  				When("creating push plans succeeds", func() {
   344  					BeforeEach(func() {
   345  						fakeActor.CreatePushPlansReturns(
   346  							[]v7pushaction.PushPlan{
   347  								{Application: v7action.Application{Name: appName1}, SpaceGUID: "some-space-guid"},
   348  								{Application: v7action.Application{Name: appName2}, SpaceGUID: "some-space-guid"},
   349  							}, nil,
   350  						)
   351  					})
   352  
   353  					Describe("delegating to Actor.PrepareSpace", func() {
   354  						It("delegates to PrepareSpace", func() {
   355  							actualPushPlans, actualParser := fakeActor.PrepareSpaceArgsForCall(0)
   356  							Expect(actualPushPlans).To(ConsistOf(
   357  								v7pushaction.PushPlan{Application: v7action.Application{Name: appName1}, SpaceGUID: "some-space-guid"},
   358  								v7pushaction.PushPlan{Application: v7action.Application{Name: appName2}, SpaceGUID: "some-space-guid"},
   359  							))
   360  							Expect(actualParser).To(Equal(fakeManifestParser))
   361  						})
   362  
   363  						When("Actor.PrepareSpace has no errors", func() {
   364  							Describe("delegating to Actor.UpdateApplicationSettings", func() {
   365  								When("there are no flag overrides", func() {
   366  									BeforeEach(func() {
   367  										fakeActor.UpdateApplicationSettingsReturns(
   368  											[]v7pushaction.PushPlan{
   369  												{Application: v7action.Application{Name: appName1}},
   370  												{Application: v7action.Application{Name: appName2}},
   371  											},
   372  											v7pushaction.Warnings{"conceptualize-warning-1"}, nil)
   373  									})
   374  
   375  									It("generates a push plan with the specified app path", func() {
   376  										Expect(executeErr).ToNot(HaveOccurred())
   377  										Expect(testUI.Out).To(Say(
   378  											"Pushing apps %s, %s to org some-org / space some-space as some-user",
   379  											appName1,
   380  											appName2,
   381  										))
   382  										Expect(testUI.Out).To(Say(`Getting app info\.\.\.`))
   383  										Expect(testUI.Err).To(Say("conceptualize-warning-1"))
   384  
   385  										Expect(fakeActor.UpdateApplicationSettingsCallCount()).To(Equal(1))
   386  										actualPushPlans := fakeActor.UpdateApplicationSettingsArgsForCall(0)
   387  										Expect(actualPushPlans).To(ConsistOf(
   388  											v7pushaction.PushPlan{Application: v7action.Application{Name: appName1}, SpaceGUID: "some-space-guid"},
   389  											v7pushaction.PushPlan{Application: v7action.Application{Name: appName2}, SpaceGUID: "some-space-guid"},
   390  										))
   391  									})
   392  
   393  									Describe("delegating to Actor.Actualize", func() {
   394  										When("Actualize returns success", func() {
   395  											BeforeEach(func() {
   396  												fakeActor.ActualizeStub = FillInValues([]Step{
   397  													{},
   398  												}, v7pushaction.PushPlan{Application: v7action.Application{GUID: "potato"}})
   399  											})
   400  
   401  											Describe("actualize events", func() {
   402  												BeforeEach(func() {
   403  													fakeActor.ActualizeStub = FillInValues([]Step{
   404  														{
   405  															Event:    v7pushaction.SkippingApplicationCreation,
   406  															Warnings: v7pushaction.Warnings{"skipping app creation warnings"},
   407  														},
   408  														{
   409  															Event:    v7pushaction.CreatingApplication,
   410  															Warnings: v7pushaction.Warnings{"app creation warnings"},
   411  														},
   412  														{
   413  															Event: v7pushaction.CreatingAndMappingRoutes,
   414  														},
   415  														{
   416  															Event:    v7pushaction.CreatedRoutes,
   417  															Warnings: v7pushaction.Warnings{"routes warnings"},
   418  														},
   419  														{
   420  															Event: v7pushaction.CreatingArchive,
   421  														},
   422  														{
   423  															Event:    v7pushaction.UploadingApplicationWithArchive,
   424  															Warnings: v7pushaction.Warnings{"upload app archive warning"},
   425  														},
   426  														{
   427  															Event:    v7pushaction.RetryUpload,
   428  															Warnings: v7pushaction.Warnings{"retry upload warning"},
   429  														},
   430  														{
   431  															Event: v7pushaction.UploadWithArchiveComplete,
   432  														},
   433  														{
   434  															Event: v7pushaction.RestartingApplication,
   435  														},
   436  													}, v7pushaction.PushPlan{})
   437  												})
   438  
   439  												It("actualizes the application and displays events/warnings", func() {
   440  													Expect(executeErr).ToNot(HaveOccurred())
   441  
   442  													Expect(fakeProgressBar.ReadyCallCount()).Should(Equal(2))
   443  													Expect(fakeProgressBar.CompleteCallCount()).Should(Equal(2))
   444  
   445  													Expect(testUI.Out).To(Say("Updating app first-app..."))
   446  													Expect(testUI.Err).To(Say("skipping app creation warnings"))
   447  
   448  													Expect(testUI.Out).To(Say("Creating app first-app..."))
   449  													Expect(testUI.Err).To(Say("app creation warnings"))
   450  
   451  													Expect(testUI.Out).To(Say("Mapping routes..."))
   452  													Expect(testUI.Err).To(Say("routes warnings"))
   453  
   454  													Expect(testUI.Out).To(Say("Packaging files to upload..."))
   455  
   456  													Expect(testUI.Out).To(Say("Uploading files..."))
   457  													Expect(testUI.Err).To(Say("upload app archive warning"))
   458  
   459  													Expect(testUI.Out).To(Say("Retrying upload due to an error..."))
   460  													Expect(testUI.Err).To(Say("retry upload warning"))
   461  
   462  													Expect(testUI.Out).To(Say("Waiting for API to complete processing files..."))
   463  
   464  													Expect(testUI.Out).To(Say("Waiting for app first-app to start..."))
   465  
   466  													Expect(testUI.Out).To(Say("Updating app second-app..."))
   467  													Expect(testUI.Err).To(Say("skipping app creation warnings"))
   468  
   469  													Expect(testUI.Out).To(Say("Creating app second-app..."))
   470  													Expect(testUI.Err).To(Say("app creation warnings"))
   471  
   472  													Expect(testUI.Out).To(Say("Mapping routes..."))
   473  													Expect(testUI.Err).To(Say("routes warnings"))
   474  
   475  													Expect(testUI.Out).To(Say("Packaging files to upload..."))
   476  
   477  													Expect(testUI.Out).To(Say("Uploading files..."))
   478  													Expect(testUI.Err).To(Say("upload app archive warning"))
   479  
   480  													Expect(testUI.Out).To(Say("Retrying upload due to an error..."))
   481  													Expect(testUI.Err).To(Say("retry upload warning"))
   482  
   483  													Expect(testUI.Out).To(Say("Waiting for API to complete processing files..."))
   484  
   485  													Expect(testUI.Out).To(Say("Waiting for app second-app to start..."))
   486  												})
   487  											})
   488  
   489  											Describe("staging logs", func() {
   490  												BeforeEach(func() {
   491  													fakeActor.ActualizeStub = FillInValues([]Step{
   492  														{
   493  															Event: v7pushaction.StartingStaging,
   494  														},
   495  													}, v7pushaction.PushPlan{})
   496  												})
   497  
   498  												When("there are no logging errors", func() {
   499  													BeforeEach(func() {
   500  														fakeVersionActor.GetStreamingLogsForApplicationByNameAndSpaceStub = ReturnLogs(
   501  															[]LogEvent{
   502  																{Log: v7action.NewLogMessage("log-message-1", 1, time.Now(), v7action.StagingLog, "source-instance")},
   503  																{Log: v7action.NewLogMessage("log-message-2", 1, time.Now(), v7action.StagingLog, "source-instance")},
   504  																{Log: v7action.NewLogMessage("log-message-3", 1, time.Now(), "potato", "source-instance")},
   505  															},
   506  															v7action.Warnings{"log-warning-1", "log-warning-2"},
   507  															nil,
   508  														)
   509  													})
   510  
   511  													It("displays the staging logs and warnings", func() {
   512  														Expect(testUI.Out).To(Say("Staging app and tracing logs..."))
   513  
   514  														Expect(testUI.Err).To(Say("log-warning-1"))
   515  														Expect(testUI.Err).To(Say("log-warning-2"))
   516  
   517  														Eventually(testUI.Out).Should(Say("log-message-1"))
   518  														Eventually(testUI.Out).Should(Say("log-message-2"))
   519  														Eventually(testUI.Out).ShouldNot(Say("log-message-3"))
   520  
   521  														Expect(fakeVersionActor.GetStreamingLogsForApplicationByNameAndSpaceCallCount()).To(Equal(2))
   522  														passedAppName, spaceGUID, _ := fakeVersionActor.GetStreamingLogsForApplicationByNameAndSpaceArgsForCall(0)
   523  														Expect(passedAppName).To(Equal(appName1))
   524  														Expect(spaceGUID).To(Equal("some-space-guid"))
   525  														passedAppName, spaceGUID, _ = fakeVersionActor.GetStreamingLogsForApplicationByNameAndSpaceArgsForCall(1)
   526  														Expect(passedAppName).To(Equal(appName2))
   527  														Expect(spaceGUID).To(Equal("some-space-guid"))
   528  
   529  													})
   530  												})
   531  
   532  												When("there are logging errors", func() {
   533  													BeforeEach(func() {
   534  														fakeVersionActor.GetStreamingLogsForApplicationByNameAndSpaceStub = ReturnLogs(
   535  															[]LogEvent{
   536  																{Error: errors.New("some-random-err")},
   537  																{Error: actionerror.NOAATimeoutError{}},
   538  																{Log: v7action.NewLogMessage("log-message-1", 1, time.Now(), v7action.StagingLog, "source-instance")},
   539  															},
   540  															v7action.Warnings{"log-warning-1", "log-warning-2"},
   541  															nil,
   542  														)
   543  													})
   544  
   545  													It("displays the errors as warnings", func() {
   546  														Expect(testUI.Out).To(Say("Staging app and tracing logs..."))
   547  
   548  														Expect(testUI.Err).To(Say("log-warning-1"))
   549  														Expect(testUI.Err).To(Say("log-warning-2"))
   550  														Eventually(testUI.Err).Should(Say("some-random-err"))
   551  														Eventually(testUI.Err).Should(Say("timeout connecting to log server, no log will be shown"))
   552  
   553  														Eventually(testUI.Out).Should(Say("log-message-1"))
   554  													})
   555  												})
   556  											})
   557  
   558  											When("when getting the application summary succeeds", func() {
   559  												BeforeEach(func() {
   560  													summary := v7action.ApplicationSummary{
   561  														Application:      v7action.Application{},
   562  														CurrentDroplet:   v7action.Droplet{},
   563  														ProcessSummaries: v7action.ProcessSummaries{},
   564  													}
   565  													fakeVersionActor.GetApplicationSummaryByNameAndSpaceReturnsOnCall(0, summary, v7action.Warnings{"app-1-summary-warning-1", "app-1-summary-warning-2"}, nil)
   566  													fakeVersionActor.GetApplicationSummaryByNameAndSpaceReturnsOnCall(1, summary, v7action.Warnings{"app-2-summary-warning-1", "app-2-summary-warning-2"}, nil)
   567  												})
   568  
   569  												// TODO: Don't test the shared.AppSummaryDisplayer.AppDisplay method.
   570  												// Use DI to pass in a new AppSummaryDisplayer to the Command instead.
   571  												It("displays the app summary", func() {
   572  													Expect(executeErr).ToNot(HaveOccurred())
   573  													Expect(fakeVersionActor.GetApplicationSummaryByNameAndSpaceCallCount()).To(Equal(2))
   574  												})
   575  											})
   576  
   577  											When("getting the application summary fails", func() {
   578  												BeforeEach(func() {
   579  													fakeVersionActor.GetApplicationSummaryByNameAndSpaceReturns(
   580  														v7action.ApplicationSummary{},
   581  														v7action.Warnings{"get-application-summary-warnings"},
   582  														errors.New("get-application-summary-error"),
   583  													)
   584  												})
   585  
   586  												It("does not display the app summary", func() {
   587  													Expect(testUI.Out).ToNot(Say(`requested state:`))
   588  												})
   589  
   590  												It("returns the error from GetApplicationSummaryByNameAndSpace", func() {
   591  													Expect(executeErr).To(MatchError("get-application-summary-error"))
   592  												})
   593  
   594  												It("prints the warnings", func() {
   595  													Expect(testUI.Err).To(Say("get-application-summary-warnings"))
   596  												})
   597  											})
   598  
   599  										})
   600  
   601  										When("actualize returns an error", func() {
   602  											When("the error is generic", func() {
   603  												BeforeEach(func() {
   604  													fakeActor.ActualizeStub = FillInValues([]Step{
   605  														{
   606  															Error: errors.New("anti avant garde naming"),
   607  														},
   608  													}, v7pushaction.PushPlan{})
   609  												})
   610  
   611  												It("returns the error", func() {
   612  													Expect(executeErr).To(MatchError("anti avant garde naming"))
   613  												})
   614  											})
   615  
   616  											When("the error is a startup timeout error", func() {
   617  												BeforeEach(func() {
   618  													fakeActor.ActualizeStub = FillInValues([]Step{
   619  														{
   620  															Error: actionerror.StartupTimeoutError{},
   621  														},
   622  													}, v7pushaction.PushPlan{})
   623  												})
   624  
   625  												It("returns the StartupTimeoutError and prints warnings", func() {
   626  													Expect(executeErr).To(MatchError(translatableerror.StartupTimeoutError{
   627  														AppName:    "first-app",
   628  														BinaryName: binaryName,
   629  													}))
   630  												})
   631  											})
   632  
   633  											When("the error is a process crashed error", func() {
   634  												BeforeEach(func() {
   635  													fakeActor.ActualizeStub = FillInValues([]Step{
   636  														{
   637  															Error: actionerror.AllInstancesCrashedError{},
   638  														},
   639  													}, v7pushaction.PushPlan{})
   640  												})
   641  
   642  												It("returns the ApplicationUnableToStartError", func() {
   643  													Expect(executeErr).To(MatchError(translatableerror.ApplicationUnableToStartError{
   644  														AppName:    "first-app",
   645  														BinaryName: binaryName,
   646  													}))
   647  												})
   648  
   649  												It("displays the app summary", func() {
   650  													Expect(executeErr).To(HaveOccurred())
   651  													Expect(fakeVersionActor.GetApplicationSummaryByNameAndSpaceCallCount()).To(Equal(1))
   652  												})
   653  											})
   654  										})
   655  									})
   656  								})
   657  
   658  								When("flag overrides are specified", func() {
   659  									BeforeEach(func() {
   660  										cmd.AppPath = "some/app/path"
   661  									})
   662  
   663  									It("generates a push plan with the specified flag overrides", func() {
   664  										Expect(fakeActor.CreatePushPlansCallCount()).To(Equal(1))
   665  										_, _, _, _, overrides := fakeActor.CreatePushPlansArgsForCall(0)
   666  										Expect(overrides).To(MatchFields(IgnoreExtras, Fields{
   667  											"ProvidedAppPath": Equal("some/app/path"),
   668  										}))
   669  									})
   670  								})
   671  
   672  								When("conceptualize returns an error", func() {
   673  									var expectedErr error
   674  
   675  									BeforeEach(func() {
   676  										expectedErr = errors.New("some-error")
   677  										fakeActor.UpdateApplicationSettingsReturns(nil, v7pushaction.Warnings{"some-warning-1"}, expectedErr)
   678  									})
   679  
   680  									It("generates a push plan with the specified app path", func() {
   681  										Expect(executeErr).To(MatchError(expectedErr))
   682  										Expect(testUI.Err).To(Say("some-warning-1"))
   683  									})
   684  								})
   685  							})
   686  						})
   687  
   688  						When("Actor.PrepareSpace has an error", func() {
   689  							var pushPlansChannel chan []v7pushaction.PushPlan
   690  
   691  							BeforeEach(func() {
   692  								pushPlansChannel = make(chan []v7pushaction.PushPlan)
   693  								close(pushPlansChannel)
   694  								events, warnings, errors := FillInEvents([]Step{
   695  									{
   696  										Warnings: v7pushaction.Warnings{"prepare-space-warning-1"},
   697  										Error:    errors.New("prepare-space-error-1"),
   698  									},
   699  								})
   700  
   701  								fakeActor.PrepareSpaceReturns(pushPlansChannel, events, warnings, errors)
   702  							})
   703  
   704  							It("returns the error", func() {
   705  								Expect(executeErr).To(MatchError(errors.New("prepare-space-error-1")))
   706  								Expect(testUI.Err).To(Say("prepare-space-warning-1"))
   707  							})
   708  
   709  							It("does not delegate to UpdateApplicationSettings", func() {
   710  								Expect(fakeActor.UpdateApplicationSettingsCallCount()).To(Equal(0))
   711  							})
   712  
   713  							It("does not delegate to Actualize", func() {
   714  								Expect(fakeActor.ActualizeCallCount()).To(Equal(0))
   715  							})
   716  						})
   717  
   718  						When("Actor.PrepareSpace has no errors but returns no apps", func() {
   719  							var pushPlansChannel chan []v7pushaction.PushPlan
   720  
   721  							BeforeEach(func() {
   722  								pushPlansChannel = make(chan []v7pushaction.PushPlan)
   723  								close(pushPlansChannel)
   724  								events, warnings, errors := FillInEvents([]Step{
   725  									{
   726  										Warnings: v7pushaction.Warnings{"prepare-no-app-or-manifest-space-warning"},
   727  										Error:    nil,
   728  									},
   729  								})
   730  
   731  								fakeActor.PrepareSpaceReturns(pushPlansChannel, events, warnings, errors)
   732  							})
   733  
   734  							It("returns the error", func() {
   735  								Expect(executeErr).To(MatchError(translatableerror.AppNameOrManifestRequiredError{}))
   736  								Expect(testUI.Err).To(Say("prepare-no-app-or-manifest-space-warning"))
   737  							})
   738  
   739  							It("does not delegate to UpdateApplicationSettings", func() {
   740  								Expect(fakeActor.UpdateApplicationSettingsCallCount()).To(Equal(0))
   741  							})
   742  
   743  							It("does not delegate to Actualize", func() {
   744  								Expect(fakeActor.ActualizeCallCount()).To(Equal(0))
   745  							})
   746  
   747  						})
   748  					})
   749  				})
   750  			})
   751  		})
   752  	})
   753  
   754  	Describe("ValidateAllowedFlagsForMultipleApps", func() {
   755  		When("manifest contains a single app", func() {
   756  			DescribeTable("returns nil when",
   757  				func(setup func()) {
   758  					setup()
   759  					Expect(cmd.ValidateAllowedFlagsForMultipleApps(false)).ToNot(HaveOccurred())
   760  				},
   761  				Entry("buildpacks is specified",
   762  					func() {
   763  						cmd.Buildpacks = []string{"buildpack-1", "buildpack-2"}
   764  					}),
   765  				Entry("disk is specified",
   766  					func() {
   767  						cmd.Disk = flag.Megabytes{NullUint64: types.NullUint64{IsSet: true}}
   768  					}),
   769  				Entry("droplet is specified",
   770  					func() {
   771  						cmd.DropletPath = "some-droplet.tgz"
   772  					}),
   773  			)
   774  		})
   775  
   776  		When("manifest contains multiple apps", func() {
   777  			DescribeTable("throws an error when",
   778  				func(setup func()) {
   779  					setup()
   780  					Expect(cmd.ValidateAllowedFlagsForMultipleApps(true)).To(MatchError(translatableerror.CommandLineArgsWithMultipleAppsError{}))
   781  				},
   782  
   783  				Entry("buildpacks is specified",
   784  					func() {
   785  						cmd.Buildpacks = []string{"buildpack-1", "buildpack-2"}
   786  					}),
   787  				Entry("disk is specified",
   788  					func() {
   789  						cmd.Disk = flag.Megabytes{NullUint64: types.NullUint64{IsSet: true}}
   790  					}),
   791  				Entry("droplet is specified",
   792  					func() {
   793  						cmd.DropletPath = "some-droplet.tgz"
   794  					}),
   795  				Entry("docker image is specified",
   796  					func() {
   797  						cmd.DockerImage = flag.DockerImage{Path: "some-docker"}
   798  					}),
   799  				Entry("docker username is specified",
   800  					func() {
   801  						fakeConfig.DockerPasswordReturns("some-password")
   802  						cmd.DockerUsername = "docker-username"
   803  					}),
   804  				Entry("health check type is specified",
   805  					func() {
   806  						cmd.HealthCheckType = flag.HealthCheckType{Type: constant.HTTP}
   807  					}),
   808  				Entry("health check HTTP endpoint is specified",
   809  					func() {
   810  						cmd.HealthCheckHTTPEndpoint = "some-endpoint"
   811  					}),
   812  				Entry("health check timeout is specified",
   813  					func() {
   814  						cmd.HealthCheckTimeout = flag.PositiveInteger{Value: 5}
   815  					}),
   816  				Entry("instances is specified",
   817  					func() {
   818  						cmd.Instances = flag.Instances{NullInt: types.NullInt{IsSet: true}}
   819  					}),
   820  				Entry("stack is specified",
   821  					func() {
   822  						cmd.Stack = "some-stack"
   823  					}),
   824  				Entry("memory is specified",
   825  					func() {
   826  						cmd.Memory = flag.Megabytes{NullUint64: types.NullUint64{IsSet: true}}
   827  					}),
   828  				Entry("provided app path is specified",
   829  					func() {
   830  						cmd.AppPath = "some-app-path"
   831  					}),
   832  				Entry("skip route creation is specified",
   833  					func() {
   834  						cmd.NoRoute = true
   835  					}),
   836  				Entry("start command is specified",
   837  					func() {
   838  						cmd.StartCommand = flag.Command{FilteredString: types.FilteredString{IsSet: true}}
   839  					}),
   840  			)
   841  
   842  			DescribeTable("is nil when",
   843  				func(setup func()) {
   844  					setup()
   845  					Expect(cmd.ValidateAllowedFlagsForMultipleApps(true)).ToNot(HaveOccurred())
   846  				},
   847  				Entry("no flags are specified", func() {}),
   848  				Entry("path is specified",
   849  					func() {
   850  						cmd.PathToManifest = flag.PathWithExistenceCheck("/some/path")
   851  					}),
   852  				Entry("no-start is specified",
   853  					func() {
   854  						cmd.NoStart = true
   855  					}),
   856  				Entry("single app name is specified, even with disallowed flags",
   857  					func() {
   858  						cmd.OptionalArgs.AppName = "some-app-name"
   859  
   860  						cmd.Stack = "some-stack"
   861  						cmd.NoRoute = true
   862  						cmd.DockerImage = flag.DockerImage{Path: "some-docker"}
   863  						cmd.Instances = flag.Instances{NullInt: types.NullInt{IsSet: true}}
   864  					}),
   865  			)
   866  		})
   867  	})
   868  
   869  	Describe("GetFlagOverrides", func() {
   870  		var (
   871  			overrides    v7pushaction.FlagOverrides
   872  			overridesErr error
   873  		)
   874  
   875  		BeforeEach(func() {
   876  			cmd.Buildpacks = []string{"buildpack-1", "buildpack-2"}
   877  			cmd.Stack = "validStack"
   878  			cmd.HealthCheckType = flag.HealthCheckType{Type: constant.Port}
   879  			cmd.HealthCheckHTTPEndpoint = "/health-check-http-endpoint"
   880  			cmd.HealthCheckTimeout = flag.PositiveInteger{Value: 7}
   881  			cmd.Memory = flag.Megabytes{NullUint64: types.NullUint64{Value: 100, IsSet: true}}
   882  			cmd.Disk = flag.Megabytes{NullUint64: types.NullUint64{Value: 1024, IsSet: true}}
   883  			cmd.DropletPath = flag.PathWithExistenceCheck("some-droplet.tgz")
   884  			cmd.StartCommand = flag.Command{FilteredString: types.FilteredString{IsSet: true, Value: "some-start-command"}}
   885  			cmd.NoRoute = true
   886  			cmd.NoStart = true
   887  			cmd.Instances = flag.Instances{NullInt: types.NullInt{Value: 10, IsSet: true}}
   888  		})
   889  
   890  		JustBeforeEach(func() {
   891  			overrides, overridesErr = cmd.GetFlagOverrides()
   892  			Expect(overridesErr).ToNot(HaveOccurred())
   893  		})
   894  
   895  		It("sets them on the flag overrides", func() {
   896  			Expect(overridesErr).ToNot(HaveOccurred())
   897  			Expect(overrides.Buildpacks).To(ConsistOf("buildpack-1", "buildpack-2"))
   898  			Expect(overrides.DropletPath).To(Equal("some-droplet.tgz"))
   899  			Expect(overrides.Stack).To(Equal("validStack"))
   900  			Expect(overrides.HealthCheckType).To(Equal(constant.Port))
   901  			Expect(overrides.HealthCheckEndpoint).To(Equal("/health-check-http-endpoint"))
   902  			Expect(overrides.HealthCheckTimeout).To(BeEquivalentTo(7))
   903  			Expect(overrides.Memory).To(Equal(types.NullUint64{Value: 100, IsSet: true}))
   904  			Expect(overrides.Disk).To(Equal(types.NullUint64{Value: 1024, IsSet: true}))
   905  			Expect(overrides.StartCommand).To(Equal(types.FilteredString{IsSet: true, Value: "some-start-command"}))
   906  			Expect(overrides.SkipRouteCreation).To(BeTrue())
   907  			Expect(overrides.NoStart).To(BeTrue())
   908  			Expect(overrides.Instances).To(Equal(types.NullInt{Value: 10, IsSet: true}))
   909  		})
   910  
   911  		When("a docker image is provided", func() {
   912  			BeforeEach(func() {
   913  				cmd.DockerImage = flag.DockerImage{Path: "some-docker-image"}
   914  			})
   915  
   916  			It("sets docker image on the flag overrides", func() {
   917  				Expect(overridesErr).ToNot(HaveOccurred())
   918  				Expect(overrides.DockerImage).To(Equal("some-docker-image"))
   919  			})
   920  		})
   921  	})
   922  
   923  	Describe("ReadManifest", func() {
   924  		var (
   925  			somePath   string
   926  			executeErr error
   927  		)
   928  
   929  		BeforeEach(func() {
   930  			somePath = "/some/path"
   931  		})
   932  
   933  		JustBeforeEach(func() {
   934  			executeErr = cmd.ReadManifest()
   935  		})
   936  
   937  		When("No path is provided", func() {
   938  			BeforeEach(func() {
   939  				cmd.PWD = somePath
   940  			})
   941  
   942  			When("a manifest exists in the current dir", func() {
   943  				BeforeEach(func() {
   944  					fakeManifestLocator.PathReturns("/manifest/path", true, nil)
   945  				})
   946  
   947  				It("uses the manifest in the current directory", func() {
   948  					Expect(executeErr).ToNot(HaveOccurred())
   949  
   950  					Expect(fakeManifestLocator.PathCallCount()).To(Equal(1))
   951  					Expect(fakeManifestLocator.PathArgsForCall(0)).To(Equal(cmd.PWD))
   952  
   953  					Expect(fakeManifestParser.InterpolateAndParseCallCount()).To(Equal(1))
   954  					actualManifestPath, _, _ := fakeManifestParser.InterpolateAndParseArgsForCall(0)
   955  					Expect(actualManifestPath).To(Equal("/manifest/path"))
   956  				})
   957  			})
   958  
   959  			When("there is not a manifest in the current dir", func() {
   960  				BeforeEach(func() {
   961  					fakeManifestLocator.PathReturns("", false, nil)
   962  				})
   963  
   964  				It("ignores the file not found error", func() {
   965  					Expect(executeErr).ToNot(HaveOccurred())
   966  
   967  					Expect(fakeManifestParser.InterpolateAndParseCallCount()).To(Equal(0))
   968  				})
   969  			})
   970  
   971  			When("when there is an error locating the manifest in the current directory", func() {
   972  				BeforeEach(func() {
   973  					fakeManifestLocator.PathReturns("", false, errors.New("err-location"))
   974  				})
   975  
   976  				It("ignores the file not found error", func() {
   977  					Expect(executeErr).To(MatchError("err-location"))
   978  
   979  					Expect(fakeManifestParser.InterpolateAndParseCallCount()).To(Equal(0))
   980  				})
   981  			})
   982  		})
   983  
   984  		When("The -f flag is specified", func() {
   985  			BeforeEach(func() {
   986  				cmd.PathToManifest = flag.PathWithExistenceCheck(somePath)
   987  				fakeManifestLocator.PathReturns("/manifest/path", true, nil)
   988  			})
   989  
   990  			It("reads the manifest and passes through to PrepareSpace", func() {
   991  				Expect(executeErr).ToNot(HaveOccurred())
   992  
   993  				Expect(fakeManifestLocator.PathCallCount()).To(Equal(1))
   994  				Expect(fakeManifestLocator.PathArgsForCall(0)).To(Equal(somePath))
   995  
   996  				Expect(fakeManifestParser.InterpolateAndParseCallCount()).To(Equal(1))
   997  				actualManifestPath, _, _ := fakeManifestParser.InterpolateAndParseArgsForCall(0)
   998  				Expect(actualManifestPath).To(Equal("/manifest/path"))
   999  			})
  1000  		})
  1001  
  1002  		When("--vars-files are specified", func() {
  1003  			var varsFiles []string
  1004  
  1005  			BeforeEach(func() {
  1006  				fakeManifestLocator.PathReturns("/manifest/path", true, nil)
  1007  				varsFiles = []string{"path1", "path2"}
  1008  				for _, path := range varsFiles {
  1009  					cmd.PathsToVarsFiles = append(cmd.PathsToVarsFiles, flag.PathWithExistenceCheck(path))
  1010  				}
  1011  			})
  1012  
  1013  			It("passes vars files to the manifest parser", func() {
  1014  				Expect(executeErr).ToNot(HaveOccurred())
  1015  
  1016  				Expect(fakeManifestParser.InterpolateAndParseCallCount()).To(Equal(1))
  1017  				_, actualVarsFiles, _ := fakeManifestParser.InterpolateAndParseArgsForCall(0)
  1018  				Expect(actualVarsFiles).To(Equal(varsFiles))
  1019  			})
  1020  		})
  1021  
  1022  		When("The --var flag is provided", func() {
  1023  			var vars []template.VarKV
  1024  
  1025  			BeforeEach(func() {
  1026  				fakeManifestLocator.PathReturns("/manifest/path", true, nil)
  1027  				vars = []template.VarKV{
  1028  					{Name: "put-var-here", Value: "turtle"},
  1029  				}
  1030  				cmd.Vars = vars
  1031  			})
  1032  
  1033  			It("passes vars files to the manifest parser", func() {
  1034  				Expect(executeErr).ToNot(HaveOccurred())
  1035  
  1036  				Expect(fakeManifestParser.InterpolateAndParseCallCount()).To(Equal(1))
  1037  				_, _, actualVars := fakeManifestParser.InterpolateAndParseArgsForCall(0)
  1038  				Expect(actualVars).To(Equal(vars))
  1039  			})
  1040  		})
  1041  	})
  1042  
  1043  	DescribeTable("ValidateFlags returns an error",
  1044  		func(setup func(), expectedErr error) {
  1045  			setup()
  1046  			err := cmd.ValidateFlags()
  1047  			if expectedErr == nil {
  1048  				Expect(err).To(BeNil())
  1049  			} else {
  1050  				Expect(err).To(MatchError(expectedErr))
  1051  			}
  1052  		},
  1053  
  1054  		Entry("when docker username flag is passed *without* docker flag",
  1055  			func() {
  1056  				cmd.DockerUsername = "some-docker-username"
  1057  			},
  1058  			translatableerror.RequiredFlagsError{Arg1: "--docker-image, -o", Arg2: "--docker-username"}),
  1059  
  1060  		Entry("when docker and buildpacks flags are passed",
  1061  			func() {
  1062  				cmd.DockerImage.Path = "some-docker-image"
  1063  				cmd.Buildpacks = []string{"some-buildpack"}
  1064  			},
  1065  			translatableerror.ArgumentCombinationError{Args: []string{"--buildpack, -b", "--docker-image, -o"}}),
  1066  
  1067  		Entry("when docker and stack flags are passed",
  1068  			func() {
  1069  				cmd.DockerImage.Path = "some-docker-image"
  1070  				cmd.Stack = "validStack"
  1071  			},
  1072  			translatableerror.ArgumentCombinationError{Args: []string{"--stack, -s", "--docker-image, -o"}}),
  1073  
  1074  		Entry("when docker and path flags are passed",
  1075  			func() {
  1076  				cmd.DockerImage.Path = "some-docker-image"
  1077  				cmd.AppPath = "some-directory-path"
  1078  			},
  1079  			translatableerror.ArgumentCombinationError{Args: []string{"--docker-image, -o", "--path, -p"}}),
  1080  
  1081  		Entry("when -u http does not have a matching --endpoint",
  1082  			func() {
  1083  				cmd.HealthCheckType.Type = constant.HTTP
  1084  			},
  1085  			translatableerror.RequiredFlagsError{Arg1: "--endpoint", Arg2: "--health-check-type=http, -u=http"}),
  1086  
  1087  		Entry("when --endpoint does not have a matching -u",
  1088  			func() {
  1089  				cmd.HealthCheckHTTPEndpoint = "/health"
  1090  			},
  1091  			translatableerror.RequiredFlagsError{Arg1: "--health-check-type=http, -u=http", Arg2: "--endpoint"}),
  1092  
  1093  		Entry("when --endpoint has a matching -u=process instead of a -u=http",
  1094  			func() {
  1095  				cmd.HealthCheckHTTPEndpoint = "/health"
  1096  				cmd.HealthCheckType.Type = constant.Process
  1097  			},
  1098  			translatableerror.RequiredFlagsError{Arg1: "--health-check-type=http, -u=http", Arg2: "--endpoint"}),
  1099  
  1100  		Entry("when --endpoint has a matching -u=port instead of a -u=http",
  1101  			func() {
  1102  				cmd.HealthCheckHTTPEndpoint = "/health"
  1103  				cmd.HealthCheckType.Type = constant.Port
  1104  			},
  1105  			translatableerror.RequiredFlagsError{Arg1: "--health-check-type=http, -u=http", Arg2: "--endpoint"}),
  1106  
  1107  		Entry("when -u http does have a matching --endpoint",
  1108  			func() {
  1109  				cmd.HealthCheckType.Type = constant.HTTP
  1110  				cmd.HealthCheckHTTPEndpoint = "/health"
  1111  			},
  1112  			nil),
  1113  
  1114  		Entry("when droplet and path flags are passed",
  1115  			func() {
  1116  				cmd.DropletPath = "some-droplet.tgz"
  1117  				cmd.AppPath = "/my/app"
  1118  			},
  1119  			translatableerror.ArgumentCombinationError{
  1120  				Args: []string{
  1121  					"--droplet", "--docker-image, -o", "--docker-username", "-p",
  1122  				},
  1123  			}),
  1124  
  1125  		Entry("when droplet and docker image flags are passed",
  1126  			func() {
  1127  				cmd.DropletPath = "some-droplet.tgz"
  1128  				cmd.DockerImage.Path = "docker-image"
  1129  			},
  1130  			translatableerror.ArgumentCombinationError{
  1131  				Args: []string{
  1132  					"--droplet", "--docker-image, -o", "--docker-username", "-p",
  1133  				},
  1134  			}),
  1135  
  1136  		Entry("when droplet, docker image, and docker username flags are passed",
  1137  			func() {
  1138  				cmd.DropletPath = "some-droplet.tgz"
  1139  				cmd.DockerImage.Path = "docker-image"
  1140  				cmd.DockerUsername = "docker-username"
  1141  			},
  1142  			translatableerror.ArgumentCombinationError{
  1143  				Args: []string{
  1144  					"--droplet", "--docker-image, -o", "--docker-username", "-p",
  1145  				},
  1146  			}),
  1147  	)
  1148  })