github.com/randomtask1155/cli@v6.41.1-0.20181227003417-a98eed78cbde+incompatible/actor/pushaction/actualize_test.go (about) 1 package pushaction_test 2 3 import ( 4 "errors" 5 "time" 6 7 "code.cloudfoundry.org/cli/actor/actionerror" 8 . "code.cloudfoundry.org/cli/actor/pushaction" 9 "code.cloudfoundry.org/cli/actor/pushaction/pushactionfakes" 10 "code.cloudfoundry.org/cli/actor/sharedaction" 11 "code.cloudfoundry.org/cli/actor/v3action" 12 "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" 13 14 . "github.com/onsi/ginkgo" 15 . "github.com/onsi/gomega" 16 . "github.com/onsi/gomega/gstruct" 17 ) 18 19 func actualizedStreamsDrainedAndClosed( 20 configStream <-chan PushState, 21 eventStream <-chan Event, 22 warningsStream <-chan Warnings, 23 errorStream <-chan error, 24 ) bool { 25 var configStreamClosed, eventStreamClosed, warningsStreamClosed, errorStreamClosed bool 26 for { 27 select { 28 case _, ok := <-configStream: 29 if !ok { 30 configStreamClosed = true 31 } 32 case _, ok := <-eventStream: 33 if !ok { 34 eventStreamClosed = true 35 } 36 case _, ok := <-warningsStream: 37 if !ok { 38 warningsStreamClosed = true 39 } 40 case _, ok := <-errorStream: 41 if !ok { 42 errorStreamClosed = true 43 } 44 } 45 if configStreamClosed && eventStreamClosed && warningsStreamClosed && errorStreamClosed { 46 break 47 } 48 } 49 return true 50 } 51 52 // TODO: for refactor: We can use the following style of code to validate that 53 // each event is received in a specific order 54 55 // Expect(nextEvent()).Should(Equal(SettingUpApplication)) 56 // Expect(nextEvent()).Should(Equal(CreatingApplication)) 57 // Expect(nextEvent()).Should(Equal(...)) 58 // Expect(nextEvent()).Should(Equal(...)) 59 // Expect(nextEvent()).Should(Equal(...)) 60 func getNextEvent(c <-chan PushState, e <-chan Event, w <-chan Warnings) func() Event { 61 timeOut := time.Tick(500 * time.Millisecond) 62 63 return func() Event { 64 for { 65 select { 66 case <-c: 67 case event, ok := <-e: 68 if ok { 69 return event 70 } 71 return "" 72 case <-w: 73 case <-timeOut: 74 return "" 75 } 76 } 77 } 78 } 79 80 var _ = Describe("Actualize", func() { 81 var ( 82 actor *Actor 83 fakeV2Actor *pushactionfakes.FakeV2Actor 84 fakeV3Actor *pushactionfakes.FakeV3Actor 85 fakeSharedActor *pushactionfakes.FakeSharedActor 86 87 state PushState 88 fakeProgressBar *pushactionfakes.FakeProgressBar 89 90 stateStream <-chan PushState 91 eventStream <-chan Event 92 warningsStream <-chan Warnings 93 errorStream <-chan error 94 ) 95 96 BeforeEach(func() { 97 fakeV2Actor = new(pushactionfakes.FakeV2Actor) 98 fakeV3Actor = new(pushactionfakes.FakeV3Actor) 99 fakeSharedActor = new(pushactionfakes.FakeSharedActor) 100 fakeSharedActor.ReadArchiveReturns(new(pushactionfakes.FakeReadCloser), 0, nil) 101 actor = NewActor(fakeV2Actor, fakeV3Actor, fakeSharedActor) 102 103 fakeProgressBar = new(pushactionfakes.FakeProgressBar) 104 state = PushState{ 105 Application: v3action.Application{ 106 Name: "some-app", 107 }, 108 SpaceGUID: "some-space-guid", 109 } 110 }) 111 112 AfterEach(func() { 113 Eventually(actualizedStreamsDrainedAndClosed(stateStream, eventStream, warningsStream, errorStream)).Should(BeTrue()) 114 }) 115 116 JustBeforeEach(func() { 117 stateStream, eventStream, warningsStream, errorStream = actor.Actualize(state, fakeProgressBar) 118 }) 119 120 Describe("application creation", func() { 121 When("the application exists", func() { 122 BeforeEach(func() { 123 state.Application.GUID = "some-app-guid" 124 }) 125 126 It("returns a skipped app creation event", func() { 127 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(SkippingApplicationCreation)) 128 129 Eventually(stateStream).Should(Receive(MatchFields(IgnoreExtras, 130 Fields{ 131 "Application": Equal(v3action.Application{ 132 Name: "some-app", 133 GUID: "some-app-guid", 134 }), 135 }))) 136 137 Consistently(fakeV3Actor.CreateApplicationInSpaceCallCount).Should(Equal(0)) 138 }) 139 }) 140 141 When("the application does not exist", func() { 142 When("the creation is successful", func() { 143 var expectedApp v3action.Application 144 145 BeforeEach(func() { 146 expectedApp = v3action.Application{ 147 GUID: "some-app-guid", 148 Name: "some-app", 149 } 150 151 fakeV3Actor.CreateApplicationInSpaceReturns(expectedApp, v3action.Warnings{"some-app-warnings"}, nil) 152 }) 153 154 It("returns an app created event, warnings, and updated state", func() { 155 Eventually(warningsStream).Should(Receive(ConsistOf("some-app-warnings"))) 156 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(CreatedApplication)) 157 Eventually(stateStream).Should(Receive(MatchFields(IgnoreExtras, 158 Fields{ 159 "Application": Equal(expectedApp), 160 }))) 161 }) 162 163 It("creates the application", func() { 164 Eventually(fakeV3Actor.CreateApplicationInSpaceCallCount).Should(Equal(1)) 165 passedApp, passedSpaceGUID := fakeV3Actor.CreateApplicationInSpaceArgsForCall(0) 166 Expect(passedApp).To(Equal(state.Application)) 167 Expect(passedSpaceGUID).To(Equal(state.SpaceGUID)) 168 }) 169 }) 170 171 When("the creation errors", func() { 172 var expectedErr error 173 174 BeforeEach(func() { 175 expectedErr = errors.New("SPICY!!") 176 177 fakeV3Actor.CreateApplicationInSpaceReturns(v3action.Application{}, v3action.Warnings{"some-app-warnings"}, expectedErr) 178 }) 179 180 It("returns warnings and error", func() { 181 Eventually(warningsStream).Should(Receive(ConsistOf("some-app-warnings"))) 182 Eventually(errorStream).Should(Receive(MatchError(expectedErr))) 183 }) 184 }) 185 }) 186 }) 187 188 Describe("package upload", func() { 189 When("app bits are provided", func() { 190 BeforeEach(func() { 191 state = PushState{ 192 Application: v3action.Application{ 193 Name: "some-app", 194 GUID: "some-app-guid", 195 }, 196 BitsPath: "/some-bits-path", 197 AllResources: []sharedaction.Resource{ 198 {Filename: "some-filename", Size: 6}, 199 }, 200 MatchedResources: []sharedaction.Resource{ 201 {Filename: "some-matched-filename", Size: 6}, 202 }, 203 } 204 }) 205 206 It("creates the archive", func() { 207 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(CreatingArchive)) 208 209 Eventually(fakeSharedActor.ZipDirectoryResourcesCallCount).Should(Equal(1)) 210 bitsPath, resources := fakeSharedActor.ZipDirectoryResourcesArgsForCall(0) 211 Expect(bitsPath).To(Equal("/some-bits-path")) 212 Expect(resources).To(ConsistOf(sharedaction.Resource{ 213 Filename: "some-filename", 214 Size: 6, 215 })) 216 }) 217 218 When("the archive creation is successful", func() { 219 BeforeEach(func() { 220 fakeSharedActor.ZipDirectoryResourcesReturns("/some/archive/path", nil) 221 }) 222 223 It("creates the package", func() { 224 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(CreatingPackage)) 225 226 Eventually(fakeV3Actor.CreateBitsPackageByApplicationCallCount).Should(Equal(1)) 227 Expect(fakeV3Actor.CreateBitsPackageByApplicationArgsForCall(0)).To(Equal("some-app-guid")) 228 }) 229 230 When("the package creation is successful", func() { 231 BeforeEach(func() { 232 fakeV3Actor.CreateBitsPackageByApplicationReturns(v3action.Package{GUID: "some-guid"}, v3action.Warnings{"some-create-package-warning"}, nil) 233 }) 234 235 It("reads the archive", func() { 236 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(ReadingArchive)) 237 Eventually(fakeSharedActor.ReadArchiveCallCount).Should(Equal(1)) 238 Expect(fakeSharedActor.ReadArchiveArgsForCall(0)).To(Equal("/some/archive/path")) 239 }) 240 241 When("reading the archive is successful", func() { 242 BeforeEach(func() { 243 fakeReadCloser := new(pushactionfakes.FakeReadCloser) 244 fakeSharedActor.ReadArchiveReturns(fakeReadCloser, 6, nil) 245 }) 246 247 It("uploads the bits package", func() { 248 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(UploadingApplicationWithArchive)) 249 Eventually(fakeV3Actor.UploadBitsPackageCallCount).Should(Equal(1)) 250 pkg, resource, _, size := fakeV3Actor.UploadBitsPackageArgsForCall(0) 251 252 Expect(pkg).To(Equal(v3action.Package{GUID: "some-guid"})) 253 Expect(resource).To(ConsistOf(sharedaction.Resource{ 254 Filename: "some-matched-filename", 255 Size: 6, 256 })) 257 Expect(size).To(BeNumerically("==", 6)) 258 }) 259 260 When("the upload is successful", func() { 261 BeforeEach(func() { 262 fakeV3Actor.UploadBitsPackageReturns(v3action.Package{GUID: "some-guid"}, v3action.Warnings{"some-upload-package-warning"}, nil) 263 }) 264 265 It("returns an upload complete event and warnings", func() { 266 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(UploadingApplicationWithArchive)) 267 Eventually(warningsStream).Should(Receive(ConsistOf("some-upload-package-warning"))) 268 Eventually(eventStream).Should(Receive(Equal(UploadWithArchiveComplete))) 269 }) 270 271 When("the upload errors", func() { 272 When("the upload error is a retryable error", func() { 273 var someErr error 274 275 BeforeEach(func() { 276 someErr = errors.New("I AM A BANANA") 277 fakeV3Actor.UploadBitsPackageReturns(v3action.Package{}, v3action.Warnings{"upload-warnings-1", "upload-warnings-2"}, ccerror.PipeSeekError{Err: someErr}) 278 }) 279 280 It("should send a RetryUpload event and retry uploading", func() { 281 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(UploadingApplicationWithArchive)) 282 Eventually(warningsStream).Should(Receive(ConsistOf("upload-warnings-1", "upload-warnings-2"))) 283 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(RetryUpload)) 284 285 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(UploadingApplicationWithArchive)) 286 Eventually(warningsStream).Should(Receive(ConsistOf("upload-warnings-1", "upload-warnings-2"))) 287 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(RetryUpload)) 288 289 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(UploadingApplicationWithArchive)) 290 Eventually(warningsStream).Should(Receive(ConsistOf("upload-warnings-1", "upload-warnings-2"))) 291 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(RetryUpload)) 292 293 Consistently(getNextEvent(stateStream, eventStream, warningsStream)).ShouldNot(EqualEither(RetryUpload, UploadWithArchiveComplete, Complete)) 294 Eventually(fakeV3Actor.UploadBitsPackageCallCount).Should(Equal(3)) 295 Expect(errorStream).To(Receive(MatchError(actionerror.UploadFailedError{Err: someErr}))) 296 }) 297 298 }) 299 300 When("the upload error is not a retryable error", func() { 301 BeforeEach(func() { 302 fakeV3Actor.UploadBitsPackageReturns(v3action.Package{}, v3action.Warnings{"upload-warnings-1", "upload-warnings-2"}, errors.New("dios mio")) 303 }) 304 305 It("sends warnings and errors, then stops", func() { 306 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(UploadingApplicationWithArchive)) 307 Eventually(warningsStream).Should(Receive(ConsistOf("upload-warnings-1", "upload-warnings-2"))) 308 Consistently(getNextEvent(stateStream, eventStream, warningsStream)).ShouldNot(EqualEither(RetryUpload, UploadWithArchiveComplete, Complete)) 309 Eventually(errorStream).Should(Receive(MatchError("dios mio"))) 310 }) 311 }) 312 }) 313 }) 314 315 When("reading the archive fails", func() { 316 BeforeEach(func() { 317 fakeSharedActor.ReadArchiveReturns(nil, 0, errors.New("the bits!")) 318 }) 319 320 It("returns an error", func() { 321 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(ReadingArchive)) 322 Eventually(errorStream).Should(Receive(MatchError("the bits!"))) 323 }) 324 }) 325 }) 326 327 When("the package creation errors", func() { 328 BeforeEach(func() { 329 fakeV3Actor.CreateBitsPackageByApplicationReturns(v3action.Package{}, v3action.Warnings{"package-creation-warning"}, errors.New("the bits!")) 330 }) 331 332 It("it returns errors and warnings", func() { 333 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(CreatingPackage)) 334 335 Eventually(warningsStream).Should(Receive(ConsistOf("package-creation-warning"))) 336 Eventually(errorStream).Should(Receive(MatchError("the bits!"))) 337 }) 338 }) 339 }) 340 341 When("the archive creation errors", func() { 342 BeforeEach(func() { 343 fakeSharedActor.ZipDirectoryResourcesReturns("", errors.New("oh no")) 344 }) 345 346 It("returns an error and exits", func() { 347 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(CreatingArchive)) 348 349 Eventually(errorStream).Should(Receive(MatchError("oh no"))) 350 }) 351 }) 352 }) 353 }) 354 }) 355 356 Describe("polling package", func() { 357 When("the the polling is succesful", func() { 358 BeforeEach(func() { 359 fakeV3Actor.PollPackageReturns(v3action.Package{}, v3action.Warnings{"some-poll-package-warning"}, nil) 360 }) 361 362 It("returns warnings", func() { 363 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(UploadWithArchiveComplete)) 364 Eventually(warningsStream).Should(Receive(ConsistOf("some-poll-package-warning"))) 365 }) 366 367 }) 368 369 When("the the polling returns an error", func() { 370 var someErr error 371 372 BeforeEach(func() { 373 someErr = errors.New("I AM A BANANA") 374 fakeV3Actor.PollPackageReturns(v3action.Package{}, v3action.Warnings{"some-poll-package-warning"}, someErr) 375 }) 376 377 It("returns errors and warnings", func() { 378 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(UploadWithArchiveComplete)) 379 Eventually(warningsStream).Should(Receive(ConsistOf("some-poll-package-warning"))) 380 Eventually(errorStream).Should(Receive(MatchError(someErr))) 381 }) 382 }) 383 }) 384 385 Describe("staging package", func() { 386 BeforeEach(func() { 387 fakeV3Actor.PollPackageReturns(v3action.Package{GUID: "some-pkg-guid"}, nil, nil) 388 }) 389 390 It("stages the application using the package guid", func() { 391 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(StartingStaging)) 392 Eventually(fakeV3Actor.StageApplicationPackageCallCount).Should(Equal(1)) 393 Expect(fakeV3Actor.StageApplicationPackageArgsForCall(0)).To(Equal("some-pkg-guid")) 394 }) 395 396 When("staging is successful", func() { 397 BeforeEach(func() { 398 fakeV3Actor.StageApplicationPackageReturns(v3action.Build{GUID: "some-build-guid"}, v3action.Warnings{"some-staging-warning"}, nil) 399 }) 400 401 It("returns a polling build event and warnings", func() { 402 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(StartingStaging)) 403 Eventually(warningsStream).Should(Receive(ConsistOf("some-staging-warning"))) 404 Eventually(eventStream).Should(Receive(Equal(PollingBuild))) 405 }) 406 }) 407 408 When("staging errors", func() { 409 BeforeEach(func() { 410 fakeV3Actor.StageApplicationPackageReturns(v3action.Build{}, v3action.Warnings{"some-staging-warning"}, errors.New("ahhh, i failed")) 411 }) 412 413 It("returns errors and warnings", func() { 414 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(StartingStaging)) 415 Eventually(warningsStream).Should(Receive(ConsistOf("some-staging-warning"))) 416 Eventually(errorStream).Should(Receive(MatchError("ahhh, i failed"))) 417 }) 418 }) 419 }) 420 421 Describe("polling build", func() { 422 When("the the polling is succesful", func() { 423 BeforeEach(func() { 424 fakeV3Actor.PollBuildReturns(v3action.Droplet{}, v3action.Warnings{"some-poll-build-warning"}, nil) 425 }) 426 427 It("returns a staging complete event and warnings", func() { 428 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(PollingBuild)) 429 Eventually(warningsStream).Should(Receive(ConsistOf("some-poll-build-warning"))) 430 Eventually(eventStream).Should(Receive(Equal(StagingComplete))) 431 }) 432 }) 433 434 When("the the polling returns an error", func() { 435 var someErr error 436 437 BeforeEach(func() { 438 someErr = errors.New("I AM A BANANA") 439 fakeV3Actor.PollBuildReturns(v3action.Droplet{}, v3action.Warnings{"some-poll-build-warning"}, someErr) 440 }) 441 442 It("returns errors and warnings", func() { 443 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(PollingBuild)) 444 Eventually(warningsStream).Should(Receive(ConsistOf("some-poll-build-warning"))) 445 Eventually(errorStream).Should(Receive(MatchError(someErr))) 446 }) 447 }) 448 }) 449 450 Describe("setting droplet", func() { 451 When("setting the droplet is successful", func() { 452 BeforeEach(func() { 453 fakeV3Actor.SetApplicationDropletReturns(v3action.Warnings{"some-set-droplet-warning"}, nil) 454 }) 455 456 It("returns a SetDropletComplete event and warnings", func() { 457 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(SettingDroplet)) 458 Eventually(warningsStream).Should(Receive(ConsistOf("some-set-droplet-warning"))) 459 Eventually(eventStream).Should(Receive(Equal(SetDropletComplete))) 460 }) 461 }) 462 463 When("setting the droplet errors", func() { 464 BeforeEach(func() { 465 fakeV3Actor.SetApplicationDropletReturns(v3action.Warnings{"some-set-droplet-warning"}, errors.New("the climate is arid")) 466 }) 467 468 It("returns an error and warnings", func() { 469 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(SettingDroplet)) 470 Eventually(warningsStream).Should(Receive(ConsistOf("some-set-droplet-warning"))) 471 Eventually(errorStream).Should(Receive(MatchError("the climate is arid"))) 472 }) 473 }) 474 }) 475 476 When("all operations are finished", func() { 477 It("returns a complete event", func() { 478 Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(Complete)) 479 }) 480 }) 481 })