github.com/loggregator/cli@v6.33.1-0.20180224010324-82334f081791+incompatible/cf/commands/quota/update_quota_test.go (about)

     1  package quota_test
     2  
     3  import (
     4  	"code.cloudfoundry.org/cli/cf/api/quotas/quotasfakes"
     5  	"code.cloudfoundry.org/cli/cf/api/resources"
     6  	"code.cloudfoundry.org/cli/cf/commandregistry"
     7  	cmdsQuota "code.cloudfoundry.org/cli/cf/commands/quota"
     8  	"code.cloudfoundry.org/cli/cf/configuration/coreconfig"
     9  	"code.cloudfoundry.org/cli/cf/errors"
    10  	testcmd "code.cloudfoundry.org/cli/util/testhelpers/commands"
    11  	testconfig "code.cloudfoundry.org/cli/util/testhelpers/configuration"
    12  	. "code.cloudfoundry.org/cli/util/testhelpers/matchers"
    13  	testterm "code.cloudfoundry.org/cli/util/testhelpers/terminal"
    14  
    15  	"encoding/json"
    16  
    17  	"code.cloudfoundry.org/cli/cf/flags"
    18  	"code.cloudfoundry.org/cli/cf/models"
    19  	"code.cloudfoundry.org/cli/cf/requirements"
    20  	"code.cloudfoundry.org/cli/cf/requirements/requirementsfakes"
    21  	"github.com/blang/semver"
    22  	. "github.com/onsi/ginkgo"
    23  	. "github.com/onsi/gomega"
    24  )
    25  
    26  var _ = Describe("app Command", func() {
    27  	var (
    28  		ui                  *testterm.FakeUI
    29  		requirementsFactory *requirementsfakes.FakeFactory
    30  		quotaRepo           *quotasfakes.FakeQuotaRepository
    31  		quota               models.QuotaFields
    32  		configRepo          coreconfig.Repository
    33  		deps                commandregistry.Dependency
    34  	)
    35  
    36  	updateCommandDependency := func(pluginCall bool) {
    37  		deps.UI = ui
    38  		deps.Config = configRepo
    39  		deps.RepoLocator = deps.RepoLocator.SetQuotaRepository(quotaRepo)
    40  		commandregistry.Commands.SetCommand(commandregistry.Commands.FindCommand("update-quota").SetDependency(deps, pluginCall))
    41  	}
    42  
    43  	BeforeEach(func() {
    44  		ui = &testterm.FakeUI{}
    45  		configRepo = testconfig.NewRepositoryWithDefaults()
    46  		requirementsFactory = new(requirementsfakes.FakeFactory)
    47  		requirementsFactory.NewLoginRequirementReturns(requirements.Passing{})
    48  		requirementsFactory.NewMinAPIVersionRequirementReturns(requirements.Passing{})
    49  		quotaRepo = new(quotasfakes.FakeQuotaRepository)
    50  	})
    51  
    52  	runCommand := func(args ...string) bool {
    53  		return testcmd.RunCLICommand("update-quota", args, requirementsFactory, updateCommandDependency, false, ui)
    54  	}
    55  
    56  	Describe("Help text", func() {
    57  		var usage string
    58  
    59  		BeforeEach(func() {
    60  			uq := &cmdsQuota.UpdateQuota{}
    61  			up := commandregistry.CLICommandUsagePresenter(uq)
    62  			usage = up.Usage()
    63  		})
    64  
    65  		It("has an instance memory flag", func() {
    66  			Expect(usage).To(MatchRegexp(`-i\s+Maximum amount of memory an application instance can have \(e.g. 1024M, 1G, 10G\)`))
    67  
    68  			Expect(usage).To(MatchRegexp(`cf update-quota.*\[-i INSTANCE_MEMORY\]`))
    69  		})
    70  
    71  		It("has a total memory flag", func() {
    72  			Expect(usage).To(MatchRegexp(`-m\s+Total amount of memory \(e.g. 1024M, 1G, 10G\)`))
    73  
    74  			Expect(usage).To(MatchRegexp(`cf update-quota.*\[-m TOTAL_MEMORY\]`))
    75  		})
    76  
    77  		It("has a new name flag", func() {
    78  			Expect(usage).To(MatchRegexp(`-n\s+New name`))
    79  
    80  			Expect(usage).To(MatchRegexp(`cf update-quota.*\[-n NEW_NAME\]`))
    81  		})
    82  
    83  		It("has a routes flag", func() {
    84  			Expect(usage).To(MatchRegexp(`-r\s+Total number of routes`))
    85  
    86  			Expect(usage).To(MatchRegexp(`cf update-quota.*\[-r ROUTES\]`))
    87  		})
    88  
    89  		It("has a service instances flag", func() {
    90  			Expect(usage).To(MatchRegexp(`-s\s+Total number of service instances`))
    91  
    92  			Expect(usage).To(MatchRegexp(`cf update-quota.*\[-s SERVICE_INSTANCES\]`))
    93  		})
    94  
    95  		It("has an app instances flag", func() {
    96  			Expect(usage).To(MatchRegexp(`-a\s+Total number of application instances. -1 represents an unlimited amount.`))
    97  
    98  			Expect(usage).To(MatchRegexp(`cf update-quota.*\[-a APP_INSTANCES\]`))
    99  		})
   100  
   101  		It("has an allow-paid-service-plans flag", func() {
   102  			Expect(usage).To(MatchRegexp(`--allow-paid-service-plans\s+Can provision instances of paid service plans`))
   103  
   104  			Expect(usage).To(MatchRegexp(`cf update-quota.*\[--allow-paid-service-plans`))
   105  		})
   106  
   107  		It("has a disallow-paid-service-plans flag", func() {
   108  			Expect(usage).To(MatchRegexp(`--disallow-paid-service-plans\s+Cannot provision instances of paid service plans`))
   109  
   110  			Expect(usage).To(MatchRegexp(`cf update-quota.*\--disallow-paid-service-plans\]`))
   111  		})
   112  
   113  		It("has a --reserved-route-ports flag", func() {
   114  			Expect(usage).To(MatchRegexp(`--reserved-route-ports\s+Maximum number of routes that may be created with reserved ports`))
   115  
   116  			Expect(usage).To(MatchRegexp(`cf update-quota.*\--reserved-route-ports RESERVED_ROUTE_PORTS\]`))
   117  		})
   118  	})
   119  
   120  	Context("when the user is not logged in", func() {
   121  		BeforeEach(func() {
   122  			requirementsFactory.NewLoginRequirementReturns(requirements.Failing{Message: "not logged in"})
   123  		})
   124  
   125  		It("fails requirements", func() {
   126  			Expect(runCommand("my-quota", "-m", "50G")).To(BeFalse())
   127  		})
   128  	})
   129  
   130  	Context("when the user is logged in", func() {
   131  		BeforeEach(func() {
   132  			quota = models.QuotaFields{
   133  				GUID:             "quota-guid",
   134  				Name:             "quota-name",
   135  				MemoryLimit:      1024,
   136  				RoutesLimit:      111,
   137  				ServicesLimit:    222,
   138  				AppInstanceLimit: 333,
   139  			}
   140  		})
   141  
   142  		JustBeforeEach(func() {
   143  			quotaRepo.FindByNameReturns(quota, nil)
   144  		})
   145  
   146  		Context("when the -i flag is provided", func() {
   147  			It("updates the instance memory limit", func() {
   148  				runCommand("-i", "15G", "quota-name")
   149  				Expect(quotaRepo.UpdateArgsForCall(0).Name).To(Equal("quota-name"))
   150  				Expect(quotaRepo.UpdateArgsForCall(0).InstanceMemoryLimit).To(Equal(int64(15360)))
   151  			})
   152  
   153  			It("totally accepts -1 as a value because it means unlimited", func() {
   154  				runCommand("-i", "-1", "quota-name")
   155  				Expect(quotaRepo.UpdateArgsForCall(0).Name).To(Equal("quota-name"))
   156  				Expect(quotaRepo.UpdateArgsForCall(0).InstanceMemoryLimit).To(Equal(int64(-1)))
   157  			})
   158  
   159  			It("fails with usage when the value cannot be parsed", func() {
   160  				runCommand("-m", "blasé", "le-tired")
   161  				Expect(ui.Outputs()).To(ContainSubstrings(
   162  					[]string{"Incorrect Usage"},
   163  				))
   164  			})
   165  		})
   166  
   167  		Context("when the -a flag is provided", func() {
   168  			It("updated the total number of application instances limit", func() {
   169  				runCommand("-a", "2", "quota-name")
   170  				Expect(quotaRepo.UpdateCallCount()).To(Equal(1))
   171  				Expect(quotaRepo.UpdateArgsForCall(0).AppInstanceLimit).To(Equal(2))
   172  			})
   173  
   174  			It("totally accepts -1 as a value because it means unlimited", func() {
   175  				runCommand("-a", "-1", "quota-name")
   176  				Expect(quotaRepo.UpdateCallCount()).To(Equal(1))
   177  				Expect(quotaRepo.UpdateArgsForCall(0).AppInstanceLimit).To(Equal(resources.UnlimitedAppInstances))
   178  			})
   179  
   180  			It("does not override the value if a different field is updated", func() {
   181  				runCommand("-s", "5", "quota-name")
   182  				Expect(quotaRepo.UpdateCallCount()).To(Equal(1))
   183  				Expect(quotaRepo.UpdateArgsForCall(0).AppInstanceLimit).To(Equal(333))
   184  			})
   185  		})
   186  
   187  		Context("when the -m flag is provided", func() {
   188  			It("updates the memory limit", func() {
   189  				runCommand("-m", "15G", "quota-name")
   190  				Expect(quotaRepo.UpdateArgsForCall(0).Name).To(Equal("quota-name"))
   191  				Expect(quotaRepo.UpdateArgsForCall(0).MemoryLimit).To(Equal(int64(15360)))
   192  			})
   193  
   194  			It("fails with usage when the value cannot be parsed", func() {
   195  				runCommand("-m", "blasé", "le-tired")
   196  				Expect(ui.Outputs()).To(ContainSubstrings(
   197  					[]string{"Incorrect Usage"},
   198  				))
   199  			})
   200  		})
   201  
   202  		Context("when the -n flag is provided", func() {
   203  			It("updates the quota name", func() {
   204  				runCommand("-n", "quota-new-name", "quota-name")
   205  
   206  				Expect(quotaRepo.UpdateArgsForCall(0).Name).To(Equal("quota-new-name"))
   207  
   208  				Expect(ui.Outputs()).To(ContainSubstrings(
   209  					[]string{"Updating quota", "quota-name", "as", "my-user"},
   210  					[]string{"OK"},
   211  				))
   212  			})
   213  		})
   214  
   215  		Context("when the --reserved-route-ports flag is provided", func() {
   216  			It("updates the route port limit", func() {
   217  				runCommand("--reserved-route-ports", "5", "quota-name")
   218  
   219  				Expect(quotaRepo.UpdateCallCount()).To(Equal(1))
   220  				Expect(quotaRepo.UpdateArgsForCall(0).ReservedRoutePorts).To(Equal(json.Number("5")))
   221  			})
   222  
   223  			It("can update the route port limit to be -1, infinity", func() {
   224  				runCommand("--reserved-route-ports", "-1", "quota-name")
   225  
   226  				Expect(quotaRepo.UpdateCallCount()).To(Equal(1))
   227  				Expect(quotaRepo.UpdateArgsForCall(0).ReservedRoutePorts).To(Equal(json.Number("-1")))
   228  			})
   229  		})
   230  
   231  		It("updates the total allowed services", func() {
   232  			runCommand("-s", "9000", "quota-name")
   233  			Expect(quotaRepo.UpdateArgsForCall(0).ServicesLimit).To(Equal(9000))
   234  		})
   235  
   236  		It("updates the total allowed routes", func() {
   237  			runCommand("-r", "9001", "quota-name")
   238  			Expect(quotaRepo.UpdateArgsForCall(0).RoutesLimit).To(Equal(9001))
   239  		})
   240  
   241  		Context("update paid service plans", func() {
   242  			BeforeEach(func() {
   243  				quota.NonBasicServicesAllowed = false
   244  			})
   245  
   246  			It("changes to paid service plan when --allow flag is provided", func() {
   247  				runCommand("--allow-paid-service-plans", "quota-name")
   248  				Expect(quotaRepo.UpdateArgsForCall(0).NonBasicServicesAllowed).To(BeTrue())
   249  			})
   250  
   251  			It("shows an error when both --allow and --disallow flags are provided", func() {
   252  				runCommand("--allow-paid-service-plans", "--disallow-paid-service-plans", "quota-name")
   253  
   254  				Expect(ui.Outputs()).To(ContainSubstrings(
   255  					[]string{"FAILED"},
   256  					[]string{"Both flags are not permitted"},
   257  				))
   258  			})
   259  
   260  			Context("when paid services are allowed", func() {
   261  				BeforeEach(func() {
   262  					quota.NonBasicServicesAllowed = true
   263  				})
   264  				It("changes to non-paid service plan when --disallow flag is provided", func() {
   265  					quotaRepo.FindByNameReturns(quota, nil) // updating an existing quota
   266  
   267  					runCommand("--disallow-paid-service-plans", "quota-name")
   268  					Expect(quotaRepo.UpdateArgsForCall(0).NonBasicServicesAllowed).To(BeFalse())
   269  				})
   270  			})
   271  		})
   272  
   273  		It("shows an error when updating fails", func() {
   274  			quotaRepo.UpdateReturns(errors.New("I accidentally a quota"))
   275  			runCommand("-m", "1M", "dead-serious")
   276  			Expect(ui.Outputs()).To(ContainSubstrings([]string{"FAILED"}))
   277  		})
   278  
   279  		It("shows a message explaining the update", func() {
   280  			quota.Name = "i-love-ui"
   281  			quotaRepo.FindByNameReturns(quota, nil)
   282  
   283  			runCommand("-m", "50G", "i-love-ui")
   284  			Expect(ui.Outputs()).To(ContainSubstrings(
   285  				[]string{"Updating quota", "i-love-ui", "as", "my-user"},
   286  				[]string{"OK"},
   287  			))
   288  		})
   289  
   290  		It("shows the user an error when finding the quota fails", func() {
   291  			quotaRepo.FindByNameReturns(models.QuotaFields{}, errors.New("i can't believe it's not quotas!"))
   292  
   293  			runCommand("-m", "50Somethings", "what-could-possibly-go-wrong?")
   294  			Expect(ui.Outputs()).To(ContainSubstrings([]string{"FAILED"}))
   295  		})
   296  	})
   297  
   298  	Describe("Requirements", func() {
   299  		var (
   300  			requirementsFactory *requirementsfakes.FakeFactory
   301  
   302  			ui   *testterm.FakeUI
   303  			cmd  commandregistry.Command
   304  			deps commandregistry.Dependency
   305  
   306  			quotaRepo   *quotasfakes.FakeQuotaRepository
   307  			flagContext flags.FlagContext
   308  
   309  			loginRequirement         requirements.Requirement
   310  			minAPIVersionRequirement requirements.Requirement
   311  		)
   312  
   313  		BeforeEach(func() {
   314  			ui = &testterm.FakeUI{}
   315  
   316  			configRepo = testconfig.NewRepositoryWithDefaults()
   317  			quotaRepo = new(quotasfakes.FakeQuotaRepository)
   318  			repoLocator := deps.RepoLocator.SetQuotaRepository(quotaRepo)
   319  
   320  			deps = commandregistry.Dependency{
   321  				UI:          ui,
   322  				Config:      configRepo,
   323  				RepoLocator: repoLocator,
   324  			}
   325  
   326  			requirementsFactory = new(requirementsfakes.FakeFactory)
   327  
   328  			cmd = &cmdsQuota.UpdateQuota{}
   329  			cmd.SetDependency(deps, false)
   330  
   331  			flagContext = flags.NewFlagContext(cmd.MetaData().Flags)
   332  
   333  			loginRequirement = &passingRequirement{Name: "login-requirement"}
   334  			requirementsFactory.NewLoginRequirementReturns(loginRequirement)
   335  
   336  			minAPIVersionRequirement = &passingRequirement{Name: "min-api-version-requirement"}
   337  			requirementsFactory.NewMinAPIVersionRequirementReturns(minAPIVersionRequirement)
   338  		})
   339  
   340  		Context("when not provided exactly one arg", func() {
   341  			BeforeEach(func() {
   342  				flagContext.Parse("quota", "extra-arg")
   343  			})
   344  
   345  			It("fails with usage", func() {
   346  				_, err := cmd.Requirements(requirementsFactory, flagContext)
   347  				Expect(err).To(HaveOccurred())
   348  				Expect(ui.Outputs()).To(ContainSubstrings(
   349  					[]string{"FAILED"},
   350  					[]string{"Incorrect Usage. Requires an argument"},
   351  				))
   352  			})
   353  		})
   354  
   355  		Context("when provided exactly one arg", func() {
   356  			BeforeEach(func() {
   357  				flagContext.Parse("quota")
   358  			})
   359  
   360  			It("returns a LoginRequirement", func() {
   361  				actualRequirements, err := cmd.Requirements(requirementsFactory, flagContext)
   362  				Expect(err).NotTo(HaveOccurred())
   363  				Expect(requirementsFactory.NewLoginRequirementCallCount()).To(Equal(1))
   364  				Expect(actualRequirements).To(ContainElement(loginRequirement))
   365  			})
   366  
   367  			It("does not return a MinAPIVersionRequirement", func() {
   368  				actualRequirements, err := cmd.Requirements(requirementsFactory, flagContext)
   369  				Expect(err).NotTo(HaveOccurred())
   370  				Expect(requirementsFactory.NewMinAPIVersionRequirementCallCount()).To(Equal(0))
   371  				Expect(actualRequirements).NotTo(ContainElement(minAPIVersionRequirement))
   372  			})
   373  
   374  			Context("when an app instance limit is passed", func() {
   375  				BeforeEach(func() {
   376  					flagContext = flags.NewFlagContext(cmd.MetaData().Flags)
   377  					flagContext.Parse("domain-name", "-a", "2")
   378  				})
   379  
   380  				It("returns a MinAPIVersionRequirement as the second requirement", func() {
   381  					actualRequirements, err := cmd.Requirements(requirementsFactory, flagContext)
   382  					Expect(err).NotTo(HaveOccurred())
   383  
   384  					expectedVersion, err := semver.Make("2.33.0")
   385  					Expect(err).NotTo(HaveOccurred())
   386  
   387  					Expect(requirementsFactory.NewMinAPIVersionRequirementCallCount()).To(Equal(1))
   388  					feature, requiredVersion := requirementsFactory.NewMinAPIVersionRequirementArgsForCall(0)
   389  					Expect(feature).To(Equal("Option '-a'"))
   390  					Expect(requiredVersion).To(Equal(expectedVersion))
   391  					Expect(actualRequirements[1]).To(Equal(minAPIVersionRequirement))
   392  				})
   393  			})
   394  
   395  			Context("when reserved route ports limit is passed", func() {
   396  				BeforeEach(func() {
   397  					flagContext = flags.NewFlagContext(cmd.MetaData().Flags)
   398  					flagContext.Parse("domain-name", "--reserved-route-ports", "3")
   399  				})
   400  
   401  				It("returns a MinAPIVersionRequirement as the second requirement", func() {
   402  					actualRequirements, err := cmd.Requirements(requirementsFactory, flagContext)
   403  					Expect(err).NotTo(HaveOccurred())
   404  
   405  					expectedVersion, err := semver.Make("2.55.0")
   406  					Expect(err).NotTo(HaveOccurred())
   407  
   408  					Expect(requirementsFactory.NewMinAPIVersionRequirementCallCount()).To(Equal(1))
   409  					feature, requiredVersion := requirementsFactory.NewMinAPIVersionRequirementArgsForCall(0)
   410  					Expect(feature).To(Equal("Option '--reserved-route-ports'"))
   411  					Expect(requiredVersion).To(Equal(expectedVersion))
   412  					Expect(actualRequirements[1]).To(Equal(minAPIVersionRequirement))
   413  				})
   414  			})
   415  		})
   416  	})
   417  })