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