github.com/wanddynosios/cli/v8@v8.7.9-0.20240221182337-1a92e3a7017f/command/v7/scale_command_test.go (about)

     1  package v7_test
     2  
     3  import (
     4  	"errors"
     5  	"time"
     6  
     7  	"code.cloudfoundry.org/cli/actor/actionerror"
     8  	"code.cloudfoundry.org/cli/actor/v7action"
     9  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
    10  	"code.cloudfoundry.org/cli/command/commandfakes"
    11  	"code.cloudfoundry.org/cli/command/translatableerror"
    12  	. "code.cloudfoundry.org/cli/command/v7"
    13  	"code.cloudfoundry.org/cli/command/v7/v7fakes"
    14  	"code.cloudfoundry.org/cli/integration/helpers"
    15  	"code.cloudfoundry.org/cli/resources"
    16  	"code.cloudfoundry.org/cli/types"
    17  	"code.cloudfoundry.org/cli/util/configv3"
    18  	"code.cloudfoundry.org/cli/util/ui"
    19  	. "github.com/onsi/ginkgo"
    20  	. "github.com/onsi/gomega"
    21  	. "github.com/onsi/gomega/gbytes"
    22  )
    23  
    24  var _ = Describe("scale Command", func() {
    25  	var (
    26  		cmd             ScaleCommand
    27  		input           *Buffer
    28  		output          *Buffer
    29  		testUI          *ui.UI
    30  		fakeConfig      *commandfakes.FakeConfig
    31  		fakeSharedActor *commandfakes.FakeSharedActor
    32  		fakeActor       *v7fakes.FakeActor
    33  		app             resources.Application
    34  		binaryName      string
    35  		executeErr      error
    36  	)
    37  
    38  	BeforeEach(func() {
    39  		input = NewBuffer()
    40  		output = NewBuffer()
    41  		testUI = ui.NewTestUI(input, output, NewBuffer())
    42  		fakeConfig = new(commandfakes.FakeConfig)
    43  		fakeSharedActor = new(commandfakes.FakeSharedActor)
    44  		fakeActor = new(v7fakes.FakeActor)
    45  		app = resources.Application{Name: "some-app", GUID: "some-app-guid"}
    46  
    47  		cmd = ScaleCommand{
    48  			BaseCommand: BaseCommand{
    49  				UI:          testUI,
    50  				Config:      fakeConfig,
    51  				SharedActor: fakeSharedActor,
    52  				Actor:       fakeActor,
    53  			},
    54  		}
    55  
    56  		binaryName = "faceman"
    57  		fakeConfig.BinaryNameReturns(binaryName)
    58  
    59  		cmd.RequiredArgs.AppName = app.Name
    60  		cmd.ProcessType = constant.ProcessTypeWeb
    61  	})
    62  
    63  	JustBeforeEach(func() {
    64  		executeErr = cmd.Execute(nil)
    65  	})
    66  
    67  	When("checking target fails", func() {
    68  		BeforeEach(func() {
    69  			fakeSharedActor.CheckTargetReturns(actionerror.NotLoggedInError{BinaryName: binaryName})
    70  		})
    71  
    72  		It("returns an error", func() {
    73  			Expect(executeErr).To(MatchError(actionerror.NotLoggedInError{BinaryName: binaryName}))
    74  
    75  			Expect(fakeSharedActor.CheckTargetCallCount()).To(Equal(1))
    76  			checkTargetedOrg, checkTargetedSpace := fakeSharedActor.CheckTargetArgsForCall(0)
    77  			Expect(checkTargetedOrg).To(BeTrue())
    78  			Expect(checkTargetedSpace).To(BeTrue())
    79  		})
    80  	})
    81  
    82  	When("the user is logged in, and org and space are targeted", func() {
    83  		BeforeEach(func() {
    84  			fakeConfig.HasTargetedOrganizationReturns(true)
    85  			fakeConfig.TargetedOrganizationReturns(configv3.Organization{Name: "some-org"})
    86  			fakeConfig.HasTargetedSpaceReturns(true)
    87  			fakeConfig.TargetedSpaceReturns(configv3.Space{
    88  				GUID: "some-space-guid",
    89  				Name: "some-space"})
    90  			fakeActor.GetCurrentUserReturns(
    91  				configv3.User{Name: "some-user"},
    92  				nil)
    93  		})
    94  
    95  		When("getting the current user returns an error", func() {
    96  			var expectedErr error
    97  
    98  			BeforeEach(func() {
    99  				expectedErr = errors.New("getting current user error")
   100  				fakeActor.GetCurrentUserReturns(
   101  					configv3.User{},
   102  					expectedErr)
   103  			})
   104  
   105  			It("returns the error", func() {
   106  				Expect(executeErr).To(MatchError(expectedErr))
   107  			})
   108  		})
   109  
   110  		When("the application does not exist", func() {
   111  			BeforeEach(func() {
   112  				fakeActor.GetApplicationByNameAndSpaceReturns(
   113  					resources.Application{},
   114  					v7action.Warnings{"get-app-warning"},
   115  					actionerror.ApplicationNotFoundError{Name: app.Name})
   116  			})
   117  
   118  			It("returns an ApplicationNotFoundError and all warnings", func() {
   119  				Expect(executeErr).To(Equal(actionerror.ApplicationNotFoundError{Name: app.Name}))
   120  
   121  				Expect(testUI.Out).ToNot(Say("Showing | Scaling"))
   122  				Expect(testUI.Err).To(Say("get-app-warning"))
   123  
   124  				Expect(fakeActor.GetApplicationByNameAndSpaceCallCount()).To(Equal(1))
   125  				appNameArg, spaceGUIDArg := fakeActor.GetApplicationByNameAndSpaceArgsForCall(0)
   126  				Expect(appNameArg).To(Equal(app.Name))
   127  				Expect(spaceGUIDArg).To(Equal("some-space-guid"))
   128  			})
   129  		})
   130  
   131  		When("an error occurs getting the application", func() {
   132  			var expectedErr error
   133  
   134  			BeforeEach(func() {
   135  				expectedErr = errors.New("get app error")
   136  				fakeActor.GetApplicationByNameAndSpaceReturns(
   137  					resources.Application{},
   138  					v7action.Warnings{"get-app-warning"},
   139  					expectedErr)
   140  			})
   141  
   142  			It("returns the error and displays all warnings", func() {
   143  				Expect(executeErr).To(Equal(expectedErr))
   144  				Expect(testUI.Err).To(Say("get-app-warning"))
   145  			})
   146  		})
   147  
   148  		When("the application exists", func() {
   149  			var appSummary v7action.DetailedApplicationSummary
   150  
   151  			BeforeEach(func() {
   152  				appSummary = v7action.DetailedApplicationSummary{
   153  					ApplicationSummary: v7action.ApplicationSummary{
   154  						ProcessSummaries: v7action.ProcessSummaries{
   155  							{
   156  								Process: resources.Process{
   157  									Type:              constant.ProcessTypeWeb,
   158  									MemoryInMB:        types.NullUint64{Value: 32, IsSet: true},
   159  									DiskInMB:          types.NullUint64{Value: 1024, IsSet: true},
   160  									LogRateLimitInBPS: types.NullInt{Value: 1024 * 10, IsSet: true},
   161  								},
   162  								InstanceDetails: []v7action.ProcessInstance{
   163  									v7action.ProcessInstance{
   164  										Index:        0,
   165  										State:        constant.ProcessInstanceRunning,
   166  										MemoryUsage:  1000000,
   167  										DiskUsage:    1000000,
   168  										LogRate:      1024 * 5,
   169  										MemoryQuota:  33554432,
   170  										DiskQuota:    2000000,
   171  										LogRateLimit: 1024 * 10,
   172  										Uptime:       time.Since(time.Unix(267321600, 0)),
   173  									},
   174  									v7action.ProcessInstance{
   175  										Index:        1,
   176  										State:        constant.ProcessInstanceRunning,
   177  										MemoryUsage:  2000000,
   178  										DiskUsage:    2000000,
   179  										LogRate:      1024 * 3,
   180  										MemoryQuota:  33554432,
   181  										DiskQuota:    4000000,
   182  										LogRateLimit: 1024 * 10,
   183  										Uptime:       time.Since(time.Unix(330480000, 0)),
   184  									},
   185  									v7action.ProcessInstance{
   186  										Index:        2,
   187  										State:        constant.ProcessInstanceRunning,
   188  										MemoryUsage:  3000000,
   189  										DiskUsage:    3000000,
   190  										LogRate:      1024 * 4,
   191  										MemoryQuota:  33554432,
   192  										DiskQuota:    6000000,
   193  										LogRateLimit: 1024 * 10,
   194  										Uptime:       time.Since(time.Unix(1277164800, 0)),
   195  									},
   196  								},
   197  							},
   198  							{
   199  								Process: resources.Process{
   200  									Type:              "console",
   201  									MemoryInMB:        types.NullUint64{Value: 16, IsSet: true},
   202  									DiskInMB:          types.NullUint64{Value: 512, IsSet: true},
   203  									LogRateLimitInBPS: types.NullInt{Value: 1024 * 5, IsSet: true},
   204  								},
   205  								InstanceDetails: []v7action.ProcessInstance{
   206  									v7action.ProcessInstance{
   207  										Index:        0,
   208  										State:        constant.ProcessInstanceRunning,
   209  										MemoryUsage:  1000000,
   210  										DiskUsage:    1000000,
   211  										LogRate:      1024,
   212  										MemoryQuota:  33554432,
   213  										DiskQuota:    8000000,
   214  										LogRateLimit: 1024 * 5,
   215  										Uptime:       time.Since(time.Unix(167572800, 0)),
   216  									},
   217  								},
   218  							},
   219  						},
   220  					},
   221  				}
   222  
   223  				fakeActor.GetApplicationByNameAndSpaceReturns(
   224  					app,
   225  					v7action.Warnings{"get-app-warning"},
   226  					nil)
   227  			})
   228  
   229  			When("no flag options are provided", func() {
   230  				BeforeEach(func() {
   231  					fakeActor.GetDetailedAppSummaryReturns(
   232  						appSummary,
   233  						v7action.Warnings{"get-app-summary-warning"},
   234  						nil)
   235  				})
   236  
   237  				It("displays current scale properties and all warnings", func() {
   238  					Expect(executeErr).ToNot(HaveOccurred())
   239  
   240  					Expect(testUI.Out).To(Say(`Showing current scale of app some-app in org some-org / space some-space as some-user\.\.\.`))
   241  					Expect(testUI.Out).ToNot(Say("Scaling | This will cause the app to restart | Stopping | Starting | Waiting"))
   242  
   243  					firstAppTable := helpers.ParseV3AppProcessTable(output.Contents())
   244  					Expect(len(firstAppTable.Processes)).To(Equal(2))
   245  
   246  					webProcessSummary := firstAppTable.Processes[0]
   247  					Expect(webProcessSummary.Type).To(Equal("web"))
   248  					Expect(webProcessSummary.InstanceCount).To(Equal("3/3"))
   249  					Expect(webProcessSummary.MemUsage).To(Equal("32M"))
   250  
   251  					Expect(webProcessSummary.Instances[0].Memory).To(Equal("976.6K of 32M"))
   252  					Expect(webProcessSummary.Instances[0].Disk).To(Equal("976.6K of 1.9M"))
   253  					Expect(webProcessSummary.Instances[0].CPU).To(Equal("0.0%"))
   254  					Expect(webProcessSummary.Instances[0].LogRate).To(Equal("5K/s of 10K/s"))
   255  
   256  					Expect(webProcessSummary.Instances[1].Memory).To(Equal("1.9M of 32M"))
   257  					Expect(webProcessSummary.Instances[1].Disk).To(Equal("1.9M of 3.8M"))
   258  					Expect(webProcessSummary.Instances[1].CPU).To(Equal("0.0%"))
   259  					Expect(webProcessSummary.Instances[1].LogRate).To(Equal("3K/s of 10K/s"))
   260  
   261  					Expect(webProcessSummary.Instances[2].Memory).To(Equal("2.9M of 32M"))
   262  					Expect(webProcessSummary.Instances[2].Disk).To(Equal("2.9M of 5.7M"))
   263  					Expect(webProcessSummary.Instances[2].CPU).To(Equal("0.0%"))
   264  					Expect(webProcessSummary.Instances[2].LogRate).To(Equal("4K/s of 10K/s"))
   265  
   266  					consoleProcessSummary := firstAppTable.Processes[1]
   267  					Expect(consoleProcessSummary.Type).To(Equal("console"))
   268  					Expect(consoleProcessSummary.InstanceCount).To(Equal("1/1"))
   269  					Expect(consoleProcessSummary.MemUsage).To(Equal("16M"))
   270  
   271  					Expect(consoleProcessSummary.Instances[0].Memory).To(Equal("976.6K of 32M"))
   272  					Expect(consoleProcessSummary.Instances[0].Disk).To(Equal("976.6K of 7.6M"))
   273  					Expect(consoleProcessSummary.Instances[0].CPU).To(Equal("0.0%"))
   274  					Expect(consoleProcessSummary.Instances[0].LogRate).To(Equal("1K/s of 5K/s"))
   275  
   276  					Expect(testUI.Err).To(Say("get-app-warning"))
   277  					Expect(testUI.Err).To(Say("get-app-summary-warning"))
   278  
   279  					Expect(fakeActor.ScaleProcessByApplicationCallCount()).To(Equal(0))
   280  				})
   281  
   282  				When("an error is encountered getting process information", func() {
   283  					var expectedErr error
   284  
   285  					BeforeEach(func() {
   286  						expectedErr = errors.New("get process error")
   287  						fakeActor.GetDetailedAppSummaryReturns(
   288  							v7action.DetailedApplicationSummary{},
   289  							v7action.Warnings{"get-process-warning"},
   290  							expectedErr,
   291  						)
   292  					})
   293  
   294  					It("returns the error and displays all warnings", func() {
   295  						Expect(executeErr).To(Equal(expectedErr))
   296  						Expect(testUI.Err).To(Say("get-process-warning"))
   297  					})
   298  				})
   299  			})
   300  
   301  			When("all flag options are provided", func() {
   302  				BeforeEach(func() {
   303  					cmd.Instances.Value = 2
   304  					cmd.Instances.IsSet = true
   305  					cmd.DiskLimit.Value = 50
   306  					cmd.DiskLimit.IsSet = true
   307  					cmd.MemoryLimit.Value = 100
   308  					cmd.MemoryLimit.IsSet = true
   309  					cmd.LogRateLimit.Value = 1024
   310  					cmd.LogRateLimit.IsSet = true
   311  					fakeActor.ScaleProcessByApplicationReturns(
   312  						v7action.Warnings{"scale-warning"},
   313  						nil)
   314  					fakeActor.GetDetailedAppSummaryReturns(
   315  						appSummary,
   316  						v7action.Warnings{"get-instances-warning"},
   317  						nil)
   318  				})
   319  
   320  				When("force flag is not provided", func() {
   321  					When("the user chooses default", func() {
   322  						BeforeEach(func() {
   323  							_, err := input.Write([]byte("\n"))
   324  							Expect(err).ToNot(HaveOccurred())
   325  						})
   326  
   327  						It("does not scale the app", func() {
   328  							Expect(executeErr).ToNot(HaveOccurred())
   329  
   330  							Expect(testUI.Out).To(Say(`Scaling app some-app in org some-org / space some-space as some-user\.\.\.`))
   331  							Expect(testUI.Out).To(Say(`This will cause the app to restart\. Are you sure you want to scale some-app\? \[yN\]:`))
   332  							Expect(testUI.Out).To(Say("Scaling cancelled"))
   333  							Expect(testUI.Out).ToNot(Say("Showing | Stopping | Starting | Waiting"))
   334  
   335  							Expect(fakeActor.ScaleProcessByApplicationCallCount()).To(Equal(0))
   336  						})
   337  					})
   338  
   339  					When("the user chooses no", func() {
   340  						BeforeEach(func() {
   341  							_, err := input.Write([]byte("n\n"))
   342  							Expect(err).ToNot(HaveOccurred())
   343  						})
   344  
   345  						It("does not scale the app", func() {
   346  							Expect(executeErr).ToNot(HaveOccurred())
   347  
   348  							Expect(testUI.Out).To(Say(`Scaling app some-app in org some-org / space some-space as some-user\.\.\.`))
   349  							Expect(testUI.Out).To(Say(`This will cause the app to restart\. Are you sure you want to scale some-app\? \[yN\]:`))
   350  							Expect(testUI.Out).To(Say("Scaling cancelled"))
   351  							Expect(testUI.Out).ToNot(Say("Showing | Stopping | Starting | Waiting"))
   352  
   353  							Expect(fakeActor.ScaleProcessByApplicationCallCount()).To(Equal(0))
   354  						})
   355  					})
   356  
   357  					When("the user chooses yes", func() {
   358  						BeforeEach(func() {
   359  							_, err := input.Write([]byte("y\n"))
   360  							Expect(err).ToNot(HaveOccurred())
   361  						})
   362  
   363  						When("polling succeeds and the app has running instances", func() {
   364  							BeforeEach(func() {
   365  								fakeActor.PollStartReturns(v7action.Warnings{"some-poll-warning-1", "some-poll-warning-2"}, nil)
   366  							})
   367  
   368  							It("delegates the right appGUID", func() {
   369  								actualApp, _, handleInstanceDetails := fakeActor.PollStartArgsForCall(0)
   370  								Expect(actualApp).To(Equal(app))
   371  								handleInstanceDetails("instance details")
   372  								Expect(testUI.Out).To(Say("instance details"))
   373  							})
   374  
   375  							When("Restarting the app fails to stop the app", func() {
   376  								BeforeEach(func() {
   377  									fakeActor.StopApplicationReturns(v7action.Warnings{"some-restart-warning"}, errors.New("stop-error"))
   378  								})
   379  
   380  								It("Prints warnings and returns an error", func() {
   381  									Expect(executeErr).To(MatchError("stop-error"))
   382  
   383  									Expect(testUI.Err).To(Say("some-restart-warning"))
   384  								})
   385  							})
   386  
   387  							When("Restarting the app fails to start the app", func() {
   388  								BeforeEach(func() {
   389  									fakeActor.StartApplicationReturns(v7action.Warnings{"some-start-warning"}, errors.New("start-error"))
   390  								})
   391  
   392  								It("Delegates the correct appGUID", func() {
   393  									actualAppGUID := fakeActor.StartApplicationArgsForCall(0)
   394  									Expect(actualAppGUID).To(Equal("some-app-guid"))
   395  								})
   396  
   397  								It("Prints warnings and returns an error", func() {
   398  									Expect(executeErr).To(MatchError("start-error"))
   399  
   400  									Expect(testUI.Err).To(Say("some-start-warning"))
   401  								})
   402  							})
   403  
   404  							It("scales, restarts, and displays scale properties", func() {
   405  								Expect(executeErr).ToNot(HaveOccurred())
   406  
   407  								Expect(testUI.Out).To(Say(`Scaling app some-app in org some-org / space some-space as some-user\.\.\.`))
   408  								Expect(testUI.Out).To(Say(`This will cause the app to restart\. Are you sure you want to scale some-app\? \[yN\]:`))
   409  								Expect(testUI.Out).To(Say(`Stopping app some-app in org some-org / space some-space as some-user\.\.\.`))
   410  								Expect(testUI.Out).To(Say(`Starting app some-app in org some-org / space some-space as some-user\.\.\.`))
   411  
   412  								// Note that this does test that the disk quota was scaled to 96M,
   413  								// it is tested below when we check the arguments
   414  								// passed to ScaleProcessByApplication
   415  								firstAppTable := helpers.ParseV3AppProcessTable(output.Contents())
   416  								Expect(len(firstAppTable.Processes)).To(Equal(2))
   417  
   418  								webProcessSummary := firstAppTable.Processes[0]
   419  								Expect(webProcessSummary.Type).To(Equal("web"))
   420  								Expect(webProcessSummary.InstanceCount).To(Equal("3/3"))
   421  								Expect(webProcessSummary.MemUsage).To(Equal("32M"))
   422  
   423  								Expect(webProcessSummary.Instances[0].Memory).To(Equal("976.6K of 32M"))
   424  								Expect(webProcessSummary.Instances[0].Disk).To(Equal("976.6K of 1.9M"))
   425  								Expect(webProcessSummary.Instances[0].CPU).To(Equal("0.0%"))
   426  								Expect(webProcessSummary.Instances[0].LogRate).To(Equal("5K/s of 10K/s"))
   427  
   428  								Expect(webProcessSummary.Instances[1].Memory).To(Equal("1.9M of 32M"))
   429  								Expect(webProcessSummary.Instances[1].Disk).To(Equal("1.9M of 3.8M"))
   430  								Expect(webProcessSummary.Instances[1].CPU).To(Equal("0.0%"))
   431  								Expect(webProcessSummary.Instances[1].LogRate).To(Equal("3K/s of 10K/s"))
   432  
   433  								Expect(webProcessSummary.Instances[2].Memory).To(Equal("2.9M of 32M"))
   434  								Expect(webProcessSummary.Instances[2].Disk).To(Equal("2.9M of 5.7M"))
   435  								Expect(webProcessSummary.Instances[2].CPU).To(Equal("0.0%"))
   436  								Expect(webProcessSummary.Instances[2].LogRate).To(Equal("4K/s of 10K/s"))
   437  
   438  								consoleProcessSummary := firstAppTable.Processes[1]
   439  								Expect(consoleProcessSummary.Type).To(Equal("console"))
   440  								Expect(consoleProcessSummary.InstanceCount).To(Equal("1/1"))
   441  								Expect(consoleProcessSummary.MemUsage).To(Equal("16M"))
   442  
   443  								Expect(consoleProcessSummary.Instances[0].Memory).To(Equal("976.6K of 32M"))
   444  								Expect(consoleProcessSummary.Instances[0].Disk).To(Equal("976.6K of 7.6M"))
   445  								Expect(consoleProcessSummary.Instances[0].CPU).To(Equal("0.0%"))
   446  								Expect(consoleProcessSummary.Instances[0].LogRate).To(Equal("1K/s of 5K/s"))
   447  
   448  								Expect(testUI.Err).To(Say("get-app-warning"))
   449  								Expect(testUI.Err).To(Say("scale-warning"))
   450  								Expect(testUI.Err).To(Say("some-poll-warning-1"))
   451  								Expect(testUI.Err).To(Say("some-poll-warning-2"))
   452  								Expect(testUI.Err).To(Say("get-instances-warning"))
   453  
   454  								Expect(fakeActor.GetApplicationByNameAndSpaceCallCount()).To(Equal(1))
   455  								appNameArg, spaceGUIDArg := fakeActor.GetApplicationByNameAndSpaceArgsForCall(0)
   456  								Expect(appNameArg).To(Equal(app.Name))
   457  								Expect(spaceGUIDArg).To(Equal("some-space-guid"))
   458  
   459  								Expect(fakeActor.ScaleProcessByApplicationCallCount()).To(Equal(1))
   460  								appGUIDArg, scaleProcess := fakeActor.ScaleProcessByApplicationArgsForCall(0)
   461  								Expect(appGUIDArg).To(Equal("some-app-guid"))
   462  								Expect(scaleProcess).To(Equal(resources.Process{
   463  									Type:              constant.ProcessTypeWeb,
   464  									Instances:         types.NullInt{Value: 2, IsSet: true},
   465  									DiskInMB:          types.NullUint64{Value: 50, IsSet: true},
   466  									MemoryInMB:        types.NullUint64{Value: 100, IsSet: true},
   467  									LogRateLimitInBPS: types.NullInt{Value: 1024, IsSet: true},
   468  								}))
   469  
   470  								Expect(fakeActor.StopApplicationCallCount()).To(Equal(1))
   471  								Expect(fakeActor.StopApplicationArgsForCall(0)).To(Equal("some-app-guid"))
   472  
   473  								Expect(fakeActor.StartApplicationCallCount()).To(Equal(1))
   474  								Expect(fakeActor.StartApplicationArgsForCall(0)).To(Equal("some-app-guid"))
   475  							})
   476  						})
   477  
   478  						When("polling succeeds but all the app's instances have crashed", func() {
   479  							BeforeEach(func() {
   480  								fakeActor.PollStartReturns(v7action.Warnings{"some-poll-warning-1", "some-poll-warning-2"}, actionerror.AllInstancesCrashedError{})
   481  							})
   482  
   483  							It("delegates the right appGUID", func() {
   484  								actualApp, _, handleInstanceDetails := fakeActor.PollStartArgsForCall(0)
   485  								Expect(actualApp).To(Equal(app))
   486  								handleInstanceDetails("instance details")
   487  								Expect(testUI.Out).To(Say("instance details"))
   488  							})
   489  
   490  							It("displays the process table", func() {
   491  								Expect(testUI.Out).To(Say("Showing current scale of app " + app.Name))
   492  							})
   493  
   494  							It("displays all warnings and fails", func() {
   495  								Expect(testUI.Err).To(Say("some-poll-warning-1"))
   496  								Expect(testUI.Err).To(Say("some-poll-warning-2"))
   497  
   498  								Expect(executeErr).To(MatchError(translatableerror.ApplicationUnableToStartError{
   499  									AppName:    app.Name,
   500  									BinaryName: binaryName,
   501  								}))
   502  							})
   503  						})
   504  
   505  						When("polling the start fails", func() {
   506  							BeforeEach(func() {
   507  								fakeActor.PollStartReturns(v7action.Warnings{"some-poll-warning-1", "some-poll-warning-2"}, errors.New("some-error"))
   508  							})
   509  
   510  							It("delegates the right appGUID", func() {
   511  								actualApp, _, handleInstanceDetails := fakeActor.PollStartArgsForCall(0)
   512  								Expect(actualApp).To(Equal(app))
   513  								handleInstanceDetails("instance details")
   514  								Expect(testUI.Out).To(Say("instance details"))
   515  							})
   516  
   517  							It("displays all warnings and fails", func() {
   518  								Expect(testUI.Err).To(Say("some-poll-warning-1"))
   519  								Expect(testUI.Err).To(Say("some-poll-warning-2"))
   520  
   521  								Expect(executeErr).To(MatchError("some-error"))
   522  							})
   523  						})
   524  
   525  						When("polling times out", func() {
   526  							BeforeEach(func() {
   527  								fakeActor.PollStartReturns(nil, actionerror.StartupTimeoutError{})
   528  							})
   529  
   530  							It("delegates the right appGUID", func() {
   531  								actualApp, _, handleInstanceDetails := fakeActor.PollStartArgsForCall(0)
   532  								Expect(actualApp).To(Equal(app))
   533  								handleInstanceDetails("instance details")
   534  								Expect(testUI.Out).To(Say("instance details"))
   535  							})
   536  
   537  							It("returns the StartupTimeoutError", func() {
   538  								Expect(executeErr).To(MatchError(translatableerror.StartupTimeoutError{
   539  									AppName:    "some-app",
   540  									BinaryName: binaryName,
   541  								}))
   542  							})
   543  						})
   544  					})
   545  				})
   546  
   547  				When("force flag is provided", func() {
   548  					BeforeEach(func() {
   549  						cmd.Force = true
   550  					})
   551  
   552  					It("does not prompt user to confirm app restart", func() {
   553  						Expect(executeErr).ToNot(HaveOccurred())
   554  
   555  						Expect(testUI.Out).To(Say(`Scaling app some-app in org some-org / space some-space as some-user\.\.\.`))
   556  						Expect(testUI.Out).To(Say(`Stopping app some-app in org some-org / space some-space as some-user\.\.\.`))
   557  						Expect(testUI.Out).To(Say(`Starting app some-app in org some-org / space some-space as some-user\.\.\.`))
   558  						Expect(testUI.Out).NotTo(Say(`This will cause the app to restart\. Are you sure you want to scale some-app\? \[yN\]:`))
   559  
   560  						Expect(fakeActor.GetApplicationByNameAndSpaceCallCount()).To(Equal(1))
   561  						Expect(fakeActor.ScaleProcessByApplicationCallCount()).To(Equal(1))
   562  						Expect(fakeActor.StopApplicationCallCount()).To(Equal(1))
   563  						Expect(fakeActor.StartApplicationCallCount()).To(Equal(1))
   564  						Expect(fakeActor.GetDetailedAppSummaryCallCount()).To(Equal(1))
   565  					})
   566  				})
   567  			})
   568  
   569  			When("only the instances flag option is provided", func() {
   570  				BeforeEach(func() {
   571  					cmd.Instances.Value = 3
   572  					cmd.Instances.IsSet = true
   573  					fakeActor.ScaleProcessByApplicationReturns(
   574  						v7action.Warnings{"scale-warning"},
   575  						nil)
   576  					fakeActor.GetDetailedAppSummaryReturns(
   577  						appSummary,
   578  						v7action.Warnings{"get-instances-warning"},
   579  						nil)
   580  				})
   581  
   582  				It("scales the number of instances, displays scale properties, and does not restart the application", func() {
   583  					Expect(executeErr).ToNot(HaveOccurred())
   584  
   585  					Expect(testUI.Out).To(Say("Scaling"))
   586  					Expect(testUI.Out).NotTo(Say("This will cause the app to restart | Stopping | Starting"))
   587  
   588  					Expect(testUI.Err).To(Say("get-app-warning"))
   589  					Expect(testUI.Err).To(Say("scale-warning"))
   590  					Expect(testUI.Err).To(Say("get-instances-warning"))
   591  
   592  					Expect(fakeActor.GetApplicationByNameAndSpaceCallCount()).To(Equal(1))
   593  					appNameArg, spaceGUIDArg := fakeActor.GetApplicationByNameAndSpaceArgsForCall(0)
   594  					Expect(appNameArg).To(Equal(app.Name))
   595  					Expect(spaceGUIDArg).To(Equal("some-space-guid"))
   596  
   597  					Expect(fakeActor.ScaleProcessByApplicationCallCount()).To(Equal(1))
   598  					appGUIDArg, scaleProcess := fakeActor.ScaleProcessByApplicationArgsForCall(0)
   599  					Expect(appGUIDArg).To(Equal("some-app-guid"))
   600  					Expect(scaleProcess).To(Equal(resources.Process{
   601  						Type:      constant.ProcessTypeWeb,
   602  						Instances: types.NullInt{Value: 3, IsSet: true},
   603  					}))
   604  
   605  					Expect(fakeActor.StopApplicationCallCount()).To(Equal(0))
   606  					Expect(fakeActor.StartApplicationCallCount()).To(Equal(0))
   607  				})
   608  
   609  				When("the app is started", func() {
   610  					BeforeEach(func() {
   611  						app.State = constant.ApplicationStarted
   612  						fakeActor.GetApplicationByNameAndSpaceReturns(
   613  							app, nil, nil)
   614  					})
   615  
   616  					It("polls for the app being started", func() {
   617  						Expect(fakeActor.PollStartCallCount()).To(Equal(1))
   618  					})
   619  				})
   620  
   621  				When("the app is stopped", func() {
   622  					BeforeEach(func() {
   623  						app.State = constant.ApplicationStopped
   624  						fakeActor.GetApplicationByNameAndSpaceReturns(
   625  							app, nil, nil)
   626  					})
   627  					It("does not poll for the app being started", func() {
   628  						Expect(fakeActor.PollStartCallCount()).To(Equal(0))
   629  					})
   630  				})
   631  			})
   632  
   633  			When("only the memory flag option is provided", func() {
   634  				BeforeEach(func() {
   635  					cmd.MemoryLimit.Value = 256
   636  					cmd.MemoryLimit.IsSet = true
   637  					fakeActor.ScaleProcessByApplicationReturns(
   638  						v7action.Warnings{"scale-warning"},
   639  						nil)
   640  					fakeActor.GetDetailedAppSummaryReturns(
   641  						appSummary,
   642  						v7action.Warnings{"get-instances-warning"},
   643  						nil)
   644  
   645  					_, err := input.Write([]byte("y\n"))
   646  					Expect(err).ToNot(HaveOccurred())
   647  				})
   648  
   649  				It("scales, restarts, and displays scale properties", func() {
   650  					Expect(executeErr).ToNot(HaveOccurred())
   651  
   652  					Expect(testUI.Out).To(Say("Scaling"))
   653  					Expect(testUI.Out).To(Say("This will cause the app to restart"))
   654  					Expect(testUI.Out).To(Say("Stopping"))
   655  					Expect(testUI.Out).To(Say("Starting"))
   656  
   657  					Expect(testUI.Err).To(Say("get-app-warning"))
   658  					Expect(testUI.Err).To(Say("scale-warning"))
   659  					Expect(testUI.Err).To(Say("get-instances-warning"))
   660  
   661  					Expect(fakeActor.GetApplicationByNameAndSpaceCallCount()).To(Equal(1))
   662  					appNameArg, spaceGUIDArg := fakeActor.GetApplicationByNameAndSpaceArgsForCall(0)
   663  					Expect(appNameArg).To(Equal(app.Name))
   664  					Expect(spaceGUIDArg).To(Equal("some-space-guid"))
   665  
   666  					Expect(fakeActor.ScaleProcessByApplicationCallCount()).To(Equal(1))
   667  					appGUIDArg, scaleProcess := fakeActor.ScaleProcessByApplicationArgsForCall(0)
   668  					Expect(appGUIDArg).To(Equal("some-app-guid"))
   669  					Expect(scaleProcess).To(Equal(resources.Process{
   670  						Type:       constant.ProcessTypeWeb,
   671  						MemoryInMB: types.NullUint64{Value: 256, IsSet: true},
   672  					}))
   673  
   674  					Expect(fakeActor.StopApplicationCallCount()).To(Equal(1))
   675  					appGUID := fakeActor.StopApplicationArgsForCall(0)
   676  					Expect(appGUID).To(Equal("some-app-guid"))
   677  
   678  					Expect(fakeActor.StartApplicationCallCount()).To(Equal(1))
   679  					appGUID = fakeActor.StartApplicationArgsForCall(0)
   680  					Expect(appGUID).To(Equal("some-app-guid"))
   681  
   682  					Expect(fakeActor.GetDetailedAppSummaryCallCount()).To(Equal(1))
   683  				})
   684  			})
   685  
   686  			When("only the disk flag option is provided", func() {
   687  				BeforeEach(func() {
   688  					cmd.DiskLimit.Value = 1025
   689  					cmd.DiskLimit.IsSet = true
   690  					fakeActor.ScaleProcessByApplicationReturns(
   691  						v7action.Warnings{"scale-warning"},
   692  						nil)
   693  					fakeActor.GetDetailedAppSummaryReturns(
   694  						appSummary,
   695  						v7action.Warnings{"get-instances-warning"},
   696  						nil)
   697  					_, err := input.Write([]byte("y\n"))
   698  					Expect(err).ToNot(HaveOccurred())
   699  				})
   700  
   701  				It("scales the number of instances, displays scale properties, and restarts the application", func() {
   702  					Expect(executeErr).ToNot(HaveOccurred())
   703  
   704  					Expect(testUI.Out).To(Say("Scaling"))
   705  					Expect(testUI.Out).To(Say("This will cause the app to restart"))
   706  					Expect(testUI.Out).To(Say("Stopping"))
   707  					Expect(testUI.Out).To(Say("Starting"))
   708  
   709  					Expect(testUI.Err).To(Say("get-app-warning"))
   710  					Expect(testUI.Err).To(Say("scale-warning"))
   711  					Expect(testUI.Err).To(Say("get-instances-warning"))
   712  
   713  					Expect(fakeActor.GetApplicationByNameAndSpaceCallCount()).To(Equal(1))
   714  					appNameArg, spaceGUIDArg := fakeActor.GetApplicationByNameAndSpaceArgsForCall(0)
   715  					Expect(appNameArg).To(Equal(app.Name))
   716  					Expect(spaceGUIDArg).To(Equal("some-space-guid"))
   717  
   718  					Expect(fakeActor.ScaleProcessByApplicationCallCount()).To(Equal(1))
   719  					appGUIDArg, scaleProcess := fakeActor.ScaleProcessByApplicationArgsForCall(0)
   720  					Expect(appGUIDArg).To(Equal("some-app-guid"))
   721  					Expect(scaleProcess).To(Equal(resources.Process{
   722  						Type:     constant.ProcessTypeWeb,
   723  						DiskInMB: types.NullUint64{Value: 1025, IsSet: true},
   724  					}))
   725  
   726  					Expect(fakeActor.StopApplicationCallCount()).To(Equal(1))
   727  					appGUID := fakeActor.StopApplicationArgsForCall(0)
   728  					Expect(appGUID).To(Equal("some-app-guid"))
   729  
   730  					Expect(fakeActor.StartApplicationCallCount()).To(Equal(1))
   731  					appGUID = fakeActor.StartApplicationArgsForCall(0)
   732  					Expect(appGUID).To(Equal("some-app-guid"))
   733  				})
   734  			})
   735  
   736  			When("process flag is provided", func() {
   737  				BeforeEach(func() {
   738  					cmd.ProcessType = "some-process-type"
   739  					cmd.Instances.Value = 2
   740  					cmd.Instances.IsSet = true
   741  					fakeActor.ScaleProcessByApplicationReturns(
   742  						v7action.Warnings{"scale-warning"},
   743  						nil)
   744  					fakeActor.GetDetailedAppSummaryReturns(
   745  						appSummary,
   746  						v7action.Warnings{"get-instances-warning"},
   747  						nil)
   748  					_, err := input.Write([]byte("y\n"))
   749  					Expect(err).ToNot(HaveOccurred())
   750  				})
   751  
   752  				It("scales the specified process", func() {
   753  					Expect(executeErr).ToNot(HaveOccurred())
   754  
   755  					Expect(testUI.Out).To(Say("Scaling"))
   756  
   757  					Expect(testUI.Err).To(Say("get-app-warning"))
   758  					Expect(testUI.Err).To(Say("scale-warning"))
   759  					Expect(testUI.Err).To(Say("get-instances-warning"))
   760  
   761  					Expect(fakeActor.GetApplicationByNameAndSpaceCallCount()).To(Equal(1))
   762  					appNameArg, spaceGUIDArg := fakeActor.GetApplicationByNameAndSpaceArgsForCall(0)
   763  					Expect(appNameArg).To(Equal(app.Name))
   764  					Expect(spaceGUIDArg).To(Equal("some-space-guid"))
   765  
   766  					Expect(fakeActor.ScaleProcessByApplicationCallCount()).To(Equal(1))
   767  					appGUIDArg, scaleProcess := fakeActor.ScaleProcessByApplicationArgsForCall(0)
   768  					Expect(appGUIDArg).To(Equal("some-app-guid"))
   769  					Expect(scaleProcess).To(Equal(resources.Process{
   770  						Type:      "some-process-type",
   771  						Instances: types.NullInt{Value: 2, IsSet: true},
   772  					}))
   773  				})
   774  			})
   775  
   776  			When("only the log rate limit option is provided", func() {
   777  				BeforeEach(func() {
   778  					cmd.LogRateLimit.Value = 2048
   779  					cmd.LogRateLimit.IsSet = true
   780  					fakeActor.ScaleProcessByApplicationReturns(
   781  						v7action.Warnings{"scale-warning"},
   782  						nil)
   783  					fakeActor.GetDetailedAppSummaryReturns(
   784  						appSummary,
   785  						v7action.Warnings{"get-instances-warning"},
   786  						nil)
   787  
   788  					_, err := input.Write([]byte("y\n"))
   789  					Expect(err).ToNot(HaveOccurred())
   790  				})
   791  
   792  				It("scales, restarts, and displays scale properties", func() {
   793  					Expect(executeErr).ToNot(HaveOccurred())
   794  
   795  					Expect(testUI.Out).To(Say("Scaling"))
   796  					Expect(testUI.Out).To(Say("This will cause the app to restart"))
   797  					Expect(testUI.Out).To(Say("Stopping"))
   798  					Expect(testUI.Out).To(Say("Starting"))
   799  
   800  					Expect(testUI.Err).To(Say("get-app-warning"))
   801  					Expect(testUI.Err).To(Say("scale-warning"))
   802  					Expect(testUI.Err).To(Say("get-instances-warning"))
   803  
   804  					Expect(fakeActor.GetApplicationByNameAndSpaceCallCount()).To(Equal(1))
   805  					appNameArg, spaceGUIDArg := fakeActor.GetApplicationByNameAndSpaceArgsForCall(0)
   806  					Expect(appNameArg).To(Equal(app.Name))
   807  					Expect(spaceGUIDArg).To(Equal("some-space-guid"))
   808  
   809  					Expect(fakeActor.ScaleProcessByApplicationCallCount()).To(Equal(1))
   810  					appGUIDArg, scaleProcess := fakeActor.ScaleProcessByApplicationArgsForCall(0)
   811  					Expect(appGUIDArg).To(Equal("some-app-guid"))
   812  					Expect(scaleProcess).To(Equal(resources.Process{
   813  						Type:              constant.ProcessTypeWeb,
   814  						LogRateLimitInBPS: types.NullInt{Value: 2048, IsSet: true},
   815  					}))
   816  
   817  					Expect(fakeActor.StopApplicationCallCount()).To(Equal(1))
   818  					appGUID := fakeActor.StopApplicationArgsForCall(0)
   819  					Expect(appGUID).To(Equal("some-app-guid"))
   820  
   821  					Expect(fakeActor.StartApplicationCallCount()).To(Equal(1))
   822  					appGUID = fakeActor.StartApplicationArgsForCall(0)
   823  					Expect(appGUID).To(Equal("some-app-guid"))
   824  
   825  					Expect(fakeActor.GetDetailedAppSummaryCallCount()).To(Equal(1))
   826  				})
   827  			})
   828  
   829  			When("an error is encountered scaling the application", func() {
   830  				var expectedErr error
   831  
   832  				BeforeEach(func() {
   833  					cmd.Instances.Value = 3
   834  					cmd.Instances.IsSet = true
   835  					expectedErr = errors.New("scale process error")
   836  					fakeActor.ScaleProcessByApplicationReturns(
   837  						v7action.Warnings{"scale-process-warning"},
   838  						expectedErr,
   839  					)
   840  				})
   841  
   842  				It("returns the error and displays all warnings", func() {
   843  					Expect(executeErr).To(Equal(expectedErr))
   844  					Expect(testUI.Err).To(Say("scale-process-warning"))
   845  				})
   846  			})
   847  		})
   848  	})
   849  })