github.com/jenspinney/cli@v6.42.1-0.20190207184520-7450c600020e+incompatible/command/v6/start_command_test.go (about) 1 package v6_test 2 3 import ( 4 "errors" 5 "time" 6 7 "code.cloudfoundry.org/cli/actor/actionerror" 8 "code.cloudfoundry.org/cli/actor/v2action" 9 "code.cloudfoundry.org/cli/actor/v2v3action" 10 "code.cloudfoundry.org/cli/actor/v3action" 11 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv2/constant" 12 "code.cloudfoundry.org/cli/command/commandfakes" 13 "code.cloudfoundry.org/cli/command/translatableerror" 14 . "code.cloudfoundry.org/cli/command/v6" 15 "code.cloudfoundry.org/cli/command/v6/shared/sharedfakes" 16 "code.cloudfoundry.org/cli/command/v6/v6fakes" 17 "code.cloudfoundry.org/cli/types" 18 "code.cloudfoundry.org/cli/util/configv3" 19 "code.cloudfoundry.org/cli/util/ui" 20 . "github.com/onsi/ginkgo" 21 . "github.com/onsi/gomega" 22 . "github.com/onsi/gomega/gbytes" 23 ) 24 25 var _ = Describe("Start Command", func() { 26 var ( 27 cmd StartCommand 28 testUI *ui.UI 29 fakeConfig *commandfakes.FakeConfig 30 fakeSharedActor *commandfakes.FakeSharedActor 31 fakeActor *v6fakes.FakeStartActor 32 fakeApplicationSummaryActor *sharedfakes.FakeApplicationSummaryActor 33 binaryName string 34 appName string 35 executeErr error 36 ) 37 38 BeforeEach(func() { 39 testUI = ui.NewTestUI(nil, NewBuffer(), NewBuffer()) 40 fakeConfig = new(commandfakes.FakeConfig) 41 fakeSharedActor = new(commandfakes.FakeSharedActor) 42 fakeActor = new(v6fakes.FakeStartActor) 43 fakeApplicationSummaryActor = new(sharedfakes.FakeApplicationSummaryActor) 44 45 cmd = StartCommand{ 46 UI: testUI, 47 Config: fakeConfig, 48 SharedActor: fakeSharedActor, 49 Actor: fakeActor, 50 ApplicationSummaryActor: fakeApplicationSummaryActor, 51 } 52 53 appName = "some-app" 54 cmd.RequiredArgs.AppName = appName 55 56 binaryName = "faceman" 57 fakeConfig.BinaryNameReturns(binaryName) 58 59 var err error 60 testUI.TimezoneLocation, err = time.LoadLocation("America/Los_Angeles") 61 Expect(err).NotTo(HaveOccurred()) 62 63 fakeActor.StartApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) { 64 messages := make(chan *v2action.LogMessage) 65 logErrs := make(chan error) 66 appState := make(chan v2action.ApplicationStateChange) 67 warnings := make(chan string) 68 errs := make(chan error) 69 70 go func() { 71 close(messages) 72 close(logErrs) 73 close(appState) 74 close(warnings) 75 close(errs) 76 }() 77 78 return messages, logErrs, appState, warnings, errs 79 } 80 }) 81 82 JustBeforeEach(func() { 83 executeErr = cmd.Execute(nil) 84 }) 85 86 When("checking target fails", func() { 87 BeforeEach(func() { 88 fakeSharedActor.CheckTargetReturns(actionerror.NotLoggedInError{BinaryName: binaryName}) 89 }) 90 91 It("returns an error if the check fails", func() { 92 Expect(executeErr).To(MatchError(actionerror.NotLoggedInError{BinaryName: "faceman"})) 93 94 Expect(fakeSharedActor.CheckTargetCallCount()).To(Equal(1)) 95 checkTargetedOrg, checkTargetedSpace := fakeSharedActor.CheckTargetArgsForCall(0) 96 Expect(checkTargetedOrg).To(BeTrue()) 97 Expect(checkTargetedSpace).To(BeTrue()) 98 }) 99 }) 100 101 When("the user is logged in, and org and space are targeted", func() { 102 BeforeEach(func() { 103 fakeConfig.HasTargetedOrganizationReturns(true) 104 fakeConfig.TargetedOrganizationReturns(configv3.Organization{Name: "some-org"}) 105 fakeConfig.HasTargetedSpaceReturns(true) 106 fakeConfig.TargetedSpaceReturns(configv3.Space{ 107 GUID: "some-space-guid", 108 Name: "some-space"}) 109 fakeConfig.CurrentUserReturns( 110 configv3.User{Name: "some-user"}, 111 nil) 112 }) 113 114 When("getting the current user returns an error", func() { 115 var expectedErr error 116 117 BeforeEach(func() { 118 expectedErr = errors.New("getting current user error") 119 fakeConfig.CurrentUserReturns( 120 configv3.User{}, 121 expectedErr) 122 }) 123 124 It("returns the error", func() { 125 Expect(executeErr).To(MatchError(expectedErr)) 126 }) 127 }) 128 129 It("displays flavor text", func() { 130 Expect(testUI.Out).To(Say("Starting app %s in org some-org / space some-space as some-user...", appName)) 131 }) 132 133 When("the app exists", func() { 134 When("the app is already started", func() { 135 BeforeEach(func() { 136 fakeActor.GetApplicationByNameAndSpaceReturns( 137 v2action.Application{State: constant.ApplicationStarted}, 138 v2action.Warnings{"warning-1", "warning-2"}, 139 nil, 140 ) 141 }) 142 143 It("short circuits and displays message", func() { 144 Expect(executeErr).ToNot(HaveOccurred()) 145 146 Expect(testUI.Out).To(Say("App %s is already started", appName)) 147 148 Expect(testUI.Err).To(Say("warning-1")) 149 Expect(testUI.Err).To(Say("warning-2")) 150 151 Expect(fakeActor.StartApplicationCallCount()).To(Equal(0)) 152 }) 153 }) 154 155 When("the app is not already started", func() { 156 BeforeEach(func() { 157 fakeActor.GetApplicationByNameAndSpaceReturns( 158 v2action.Application{GUID: "app-guid", State: constant.ApplicationStopped}, 159 v2action.Warnings{"warning-1", "warning-2"}, 160 nil, 161 ) 162 }) 163 164 It("starts the app", func() { 165 Expect(executeErr).ToNot(HaveOccurred()) 166 167 Expect(testUI.Err).To(Say("warning-1")) 168 Expect(testUI.Err).To(Say("warning-2")) 169 170 Expect(fakeActor.StartApplicationCallCount()).To(Equal(1)) 171 app, _ := fakeActor.StartApplicationArgsForCall(0) 172 Expect(app.GUID).To(Equal("app-guid")) 173 }) 174 175 When("passed an ApplicationStateStarting message", func() { 176 BeforeEach(func() { 177 fakeActor.StartApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) { 178 messages := make(chan *v2action.LogMessage) 179 logErrs := make(chan error) 180 appState := make(chan v2action.ApplicationStateChange) 181 warnings := make(chan string) 182 errs := make(chan error) 183 184 go func() { 185 messages <- v2action.NewLogMessage("log message 1", 1, time.Unix(0, 0), "STG", "1") 186 messages <- v2action.NewLogMessage("log message 2", 1, time.Unix(0, 0), "STG", "1") 187 appState <- v2action.ApplicationStateStarting 188 close(messages) 189 close(logErrs) 190 close(appState) 191 close(warnings) 192 close(errs) 193 }() 194 195 return messages, logErrs, appState, warnings, errs 196 } 197 }) 198 199 It("displays the log", func() { 200 Expect(executeErr).ToNot(HaveOccurred()) 201 Expect(testUI.Out).To(Say("log message 1")) 202 Expect(testUI.Out).To(Say("log message 2")) 203 Expect(testUI.Out).To(Say("Waiting for app to start...")) 204 }) 205 }) 206 207 When("passed a log message", func() { 208 BeforeEach(func() { 209 fakeActor.StartApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) { 210 messages := make(chan *v2action.LogMessage) 211 logErrs := make(chan error) 212 appState := make(chan v2action.ApplicationStateChange) 213 warnings := make(chan string) 214 errs := make(chan error) 215 216 go func() { 217 messages <- v2action.NewLogMessage("log message 1", 1, time.Unix(0, 0), "STG", "1") 218 messages <- v2action.NewLogMessage("log message 2", 1, time.Unix(0, 0), "STG", "1") 219 messages <- v2action.NewLogMessage("log message 3", 1, time.Unix(0, 0), "Something else", "1") 220 close(messages) 221 close(logErrs) 222 close(appState) 223 close(warnings) 224 close(errs) 225 }() 226 227 return messages, logErrs, appState, warnings, errs 228 } 229 }) 230 231 It("displays the log", func() { 232 Expect(executeErr).ToNot(HaveOccurred()) 233 Expect(testUI.Out).To(Say("log message 1")) 234 Expect(testUI.Out).To(Say("log message 2")) 235 Expect(testUI.Out).ToNot(Say("log message 3")) 236 }) 237 }) 238 239 When("passed an log err", func() { 240 Context("NOAA connection times out/closes", func() { 241 BeforeEach(func() { 242 fakeActor.StartApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) { 243 messages := make(chan *v2action.LogMessage) 244 logErrs := make(chan error) 245 appState := make(chan v2action.ApplicationStateChange) 246 warnings := make(chan string) 247 errs := make(chan error) 248 249 go func() { 250 messages <- v2action.NewLogMessage("log message 1", 1, time.Unix(0, 0), "STG", "1") 251 messages <- v2action.NewLogMessage("log message 2", 1, time.Unix(0, 0), "STG", "1") 252 messages <- v2action.NewLogMessage("log message 3", 1, time.Unix(0, 0), "STG", "1") 253 logErrs <- actionerror.NOAATimeoutError{} 254 close(messages) 255 close(logErrs) 256 close(appState) 257 close(warnings) 258 close(errs) 259 }() 260 261 return messages, logErrs, appState, warnings, errs 262 } 263 v3ApplicationSummary := v3action.ApplicationSummary{ 264 Application: v3action.Application{ 265 Name: appName, 266 }, 267 ProcessSummaries: v3action.ProcessSummaries{ 268 { 269 Process: v3action.Process{ 270 Type: "aba", 271 Command: *types.NewFilteredString("some-command-1"), 272 MemoryInMB: types.NullUint64{Value: 32, IsSet: true}, 273 DiskInMB: types.NullUint64{Value: 1024, IsSet: true}, 274 }, 275 }, 276 { 277 Process: v3action.Process{ 278 Type: "console", 279 Command: *types.NewFilteredString("some-command-2"), 280 MemoryInMB: types.NullUint64{Value: 16, IsSet: true}, 281 DiskInMB: types.NullUint64{Value: 512, IsSet: true}, 282 }, 283 }, 284 }, 285 } 286 287 applicationSummary := v2v3action.ApplicationSummary{ 288 ApplicationSummary: v3ApplicationSummary, 289 } 290 291 warnings := []string{"app-summary-warning"} 292 293 fakeApplicationSummaryActor.GetApplicationSummaryByNameAndSpaceReturns(applicationSummary, warnings, nil) 294 }) 295 296 It("displays a warning and continues until app has started", func() { 297 Expect(executeErr).To(BeNil()) 298 Expect(testUI.Out).To(Say("message 1")) 299 Expect(testUI.Out).To(Say("message 2")) 300 Expect(testUI.Out).To(Say("message 3")) 301 Expect(testUI.Err).To(Say("timeout connecting to log server, no log will be shown")) 302 Expect(testUI.Out).To(Say(`name:\s+%s`, appName)) 303 }) 304 }) 305 306 Context("an unexpected error occurs", func() { 307 var expectedErr error 308 309 BeforeEach(func() { 310 expectedErr = errors.New("err log message") 311 fakeActor.StartApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) { 312 messages := make(chan *v2action.LogMessage) 313 logErrs := make(chan error) 314 appState := make(chan v2action.ApplicationStateChange) 315 warnings := make(chan string) 316 errs := make(chan error) 317 318 go func() { 319 logErrs <- expectedErr 320 close(messages) 321 close(logErrs) 322 close(appState) 323 close(warnings) 324 close(errs) 325 }() 326 327 return messages, logErrs, appState, warnings, errs 328 } 329 }) 330 331 It("displays the error and continues to poll", func() { 332 Expect(executeErr).NotTo(HaveOccurred()) 333 Expect(testUI.Err).To(Say(expectedErr.Error())) 334 }) 335 }) 336 }) 337 338 When("passed a warning", func() { 339 Context("while NOAA is still logging", func() { 340 BeforeEach(func() { 341 fakeActor.StartApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) { 342 messages := make(chan *v2action.LogMessage) 343 logErrs := make(chan error) 344 appState := make(chan v2action.ApplicationStateChange) 345 warnings := make(chan string) 346 errs := make(chan error) 347 348 go func() { 349 warnings <- "warning 1" 350 warnings <- "warning 2" 351 close(messages) 352 close(logErrs) 353 close(appState) 354 close(warnings) 355 close(errs) 356 }() 357 358 return messages, logErrs, appState, warnings, errs 359 } 360 }) 361 362 It("displays the warnings to STDERR", func() { 363 Expect(executeErr).ToNot(HaveOccurred()) 364 Expect(testUI.Err).To(Say("warning 1")) 365 Expect(testUI.Err).To(Say("warning 2")) 366 }) 367 }) 368 369 Context("while NOAA is no longer logging", func() { 370 BeforeEach(func() { 371 fakeActor.StartApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) { 372 messages := make(chan *v2action.LogMessage) 373 logErrs := make(chan error) 374 appState := make(chan v2action.ApplicationStateChange) 375 warnings := make(chan string) 376 errs := make(chan error) 377 378 go func() { 379 warnings <- "warning 1" 380 warnings <- "warning 2" 381 logErrs <- actionerror.NOAATimeoutError{} 382 close(messages) 383 close(logErrs) 384 warnings <- "warning 3" 385 warnings <- "warning 4" 386 close(appState) 387 close(warnings) 388 close(errs) 389 }() 390 391 return messages, logErrs, appState, warnings, errs 392 } 393 }) 394 395 It("displays the warnings to STDERR", func() { 396 Expect(executeErr).ToNot(HaveOccurred()) 397 Expect(testUI.Err).To(Say("warning 1")) 398 Expect(testUI.Err).To(Say("warning 2")) 399 Expect(testUI.Err).To(Say("timeout connecting to log server, no log will be shown")) 400 Expect(testUI.Err).To(Say("warning 3")) 401 Expect(testUI.Err).To(Say("warning 4")) 402 }) 403 }) 404 }) 405 406 When("passed an API err", func() { 407 var apiErr error 408 409 BeforeEach(func() { 410 fakeActor.StartApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) { 411 messages := make(chan *v2action.LogMessage) 412 logErrs := make(chan error) 413 appState := make(chan v2action.ApplicationStateChange) 414 warnings := make(chan string) 415 errs := make(chan error) 416 417 go func() { 418 errs <- apiErr 419 close(messages) 420 close(logErrs) 421 close(appState) 422 close(warnings) 423 close(errs) 424 }() 425 426 return messages, logErrs, appState, warnings, errs 427 } 428 }) 429 430 Context("an unexpected error", func() { 431 BeforeEach(func() { 432 apiErr = errors.New("err log message") 433 }) 434 435 It("stops logging and returns the error", func() { 436 Expect(executeErr).To(MatchError(apiErr)) 437 }) 438 }) 439 440 Context("staging failed", func() { 441 BeforeEach(func() { 442 apiErr = actionerror.StagingFailedError{Reason: "Something, but not nothing"} 443 }) 444 445 It("stops logging and returns StagingFailedError", func() { 446 Expect(executeErr).To(MatchError(translatableerror.StagingFailedError{Message: "Something, but not nothing"})) 447 }) 448 }) 449 450 Context("staging timed out", func() { 451 BeforeEach(func() { 452 apiErr = actionerror.StagingTimeoutError{AppName: appName, Timeout: time.Nanosecond} 453 }) 454 455 It("stops logging and returns StagingTimeoutError", func() { 456 Expect(executeErr).To(MatchError(translatableerror.StagingTimeoutError{AppName: appName, Timeout: time.Nanosecond})) 457 }) 458 }) 459 460 When("the app instance crashes", func() { 461 BeforeEach(func() { 462 apiErr = actionerror.ApplicationInstanceCrashedError{Name: appName} 463 }) 464 465 It("stops logging and returns UnsuccessfulStartError", func() { 466 Expect(executeErr).To(MatchError(translatableerror.UnsuccessfulStartError{AppName: appName, BinaryName: "faceman"})) 467 }) 468 }) 469 470 When("the app instance flaps", func() { 471 BeforeEach(func() { 472 apiErr = actionerror.ApplicationInstanceFlappingError{Name: appName} 473 }) 474 475 It("stops logging and returns UnsuccessfulStartError", func() { 476 Expect(executeErr).To(MatchError(translatableerror.UnsuccessfulStartError{AppName: appName, BinaryName: "faceman"})) 477 }) 478 }) 479 480 Context("starting timeout", func() { 481 BeforeEach(func() { 482 apiErr = actionerror.StartupTimeoutError{Name: appName} 483 }) 484 485 It("stops logging and returns StartupTimeoutError", func() { 486 Expect(executeErr).To(MatchError(translatableerror.StartupTimeoutError{AppName: appName, BinaryName: "faceman"})) 487 }) 488 }) 489 }) 490 491 When("the app finishes starting", func() { 492 Describe("version-dependent display", func() { 493 When("CC API >= 3.27.0", func() { 494 var ( 495 applicationSummary v2v3action.ApplicationSummary 496 ) 497 498 BeforeEach(func() { 499 fakeApplicationSummaryActor.CloudControllerV3APIVersionReturns("3.50.0") 500 v3ApplicationSummary := v3action.ApplicationSummary{ 501 Application: v3action.Application{ 502 Name: appName, 503 }, 504 ProcessSummaries: v3action.ProcessSummaries{ 505 { 506 Process: v3action.Process{ 507 Type: "aba", 508 Command: *types.NewFilteredString("some-command-1"), 509 MemoryInMB: types.NullUint64{Value: 32, IsSet: true}, 510 DiskInMB: types.NullUint64{Value: 1024, IsSet: true}, 511 }, 512 }, 513 { 514 Process: v3action.Process{ 515 Type: "console", 516 Command: *types.NewFilteredString("some-command-2"), 517 MemoryInMB: types.NullUint64{Value: 16, IsSet: true}, 518 DiskInMB: types.NullUint64{Value: 512, IsSet: true}, 519 }, 520 }, 521 }, 522 } 523 applicationSummary = v2v3action.ApplicationSummary{ 524 ApplicationSummary: v3ApplicationSummary, 525 } 526 527 fakeApplicationSummaryActor.GetApplicationSummaryByNameAndSpaceReturns( 528 applicationSummary, 529 v2v3action.Warnings{"combo-summary-warning"}, 530 nil) 531 532 }) 533 534 It("uses the multiprocess display", func() { 535 Expect(executeErr).ToNot(HaveOccurred()) 536 537 Expect(fakeApplicationSummaryActor.GetApplicationSummaryByNameAndSpaceCallCount()).To(Equal(1)) 538 passedAppName, spaceGUID, withObfuscatedValues := fakeApplicationSummaryActor.GetApplicationSummaryByNameAndSpaceArgsForCall(0) 539 Expect(passedAppName).To(Equal(appName)) 540 Expect(spaceGUID).To(Equal("some-space-guid")) 541 Expect(withObfuscatedValues).To(BeTrue()) 542 543 Expect(testUI.Out).To(Say(`name:\s+%s`, appName)) 544 Expect(testUI.Out).To(Say(`type:\s+aba`)) 545 Expect(testUI.Out).To(Say(`instances:\s+0/0`)) 546 Expect(testUI.Out).To(Say(`memory usage:\s+32M`)) 547 Expect(testUI.Out).To(Say(`start command:\s+some-command-1`)) 548 Expect(testUI.Out).To(Say(`type:\s+console`)) 549 Expect(testUI.Out).To(Say(`instances:\s+0/0`)) 550 Expect(testUI.Out).To(Say(`memory usage:\s+16M`)) 551 Expect(testUI.Out).To(Say(`start command:\s+some-command-2`)) 552 553 Expect(testUI.Err).To(Say("combo-summary-warning")) 554 }) 555 }) 556 }) 557 }) 558 }) 559 }) 560 561 When("the app does *not* exists", func() { 562 BeforeEach(func() { 563 fakeActor.GetApplicationByNameAndSpaceReturns( 564 v2action.Application{}, 565 v2action.Warnings{"warning-1", "warning-2"}, 566 actionerror.ApplicationNotFoundError{Name: appName}, 567 ) 568 }) 569 570 It("returns back an error", func() { 571 Expect(executeErr).To(MatchError(actionerror.ApplicationNotFoundError{Name: "some-app"})) 572 573 Expect(testUI.Err).To(Say("warning-1")) 574 Expect(testUI.Err).To(Say("warning-2")) 575 }) 576 }) 577 }) 578 })