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 })