github.com/liamawhite/cli-with-i18n@v6.32.1-0.20171122084555-dede0a5c3448+incompatible/actor/pushaction/apply_test.go (about) 1 package pushaction_test 2 3 import ( 4 "errors" 5 "io/ioutil" 6 7 . "github.com/liamawhite/cli-with-i18n/actor/pushaction" 8 "github.com/liamawhite/cli-with-i18n/actor/pushaction/pushactionfakes" 9 "github.com/liamawhite/cli-with-i18n/actor/v2action" 10 "github.com/liamawhite/cli-with-i18n/api/cloudcontroller/ccerror" 11 12 . "github.com/onsi/ginkgo" 13 . "github.com/onsi/gomega" 14 ) 15 16 func streamsDrainedAndClosed(configStream <-chan ApplicationConfig, eventStream <-chan Event, warningsStream <-chan Warnings, errorStream <-chan error) bool { 17 var configStreamClosed, eventStreamClosed, warningsStreamClosed, errorStreamClosed bool 18 for { 19 select { 20 case _, ok := <-configStream: 21 if !ok { 22 configStreamClosed = true 23 } 24 case _, ok := <-eventStream: 25 if !ok { 26 eventStreamClosed = true 27 } 28 case _, ok := <-warningsStream: 29 if !ok { 30 warningsStreamClosed = true 31 } 32 case _, ok := <-errorStream: 33 if !ok { 34 errorStreamClosed = true 35 } 36 } 37 if configStreamClosed && eventStreamClosed && warningsStreamClosed && errorStreamClosed { 38 break 39 } 40 } 41 return true 42 } 43 44 var _ = Describe("Apply", func() { 45 var ( 46 actor *Actor 47 fakeV2Actor *pushactionfakes.FakeV2Actor 48 fakeSharedActor *pushactionfakes.FakeSharedActor 49 50 config ApplicationConfig 51 fakeProgressBar *pushactionfakes.FakeProgressBar 52 53 eventStream <-chan Event 54 warningsStream <-chan Warnings 55 errorStream <-chan error 56 configStream <-chan ApplicationConfig 57 ) 58 59 BeforeEach(func() { 60 fakeV2Actor = new(pushactionfakes.FakeV2Actor) 61 fakeSharedActor = new(pushactionfakes.FakeSharedActor) 62 actor = NewActor(fakeV2Actor, fakeSharedActor) 63 config = ApplicationConfig{ 64 DesiredApplication: Application{ 65 Application: v2action.Application{ 66 Name: "some-app-name", 67 SpaceGUID: "some-space-guid", 68 }}, 69 DesiredRoutes: []v2action.Route{{Host: "banana"}}, 70 Path: "some-path", 71 } 72 fakeProgressBar = new(pushactionfakes.FakeProgressBar) 73 }) 74 75 JustBeforeEach(func() { 76 configStream, eventStream, warningsStream, errorStream = actor.Apply(config, fakeProgressBar) 77 }) 78 79 AfterEach(func() { 80 Eventually(streamsDrainedAndClosed(configStream, eventStream, warningsStream, errorStream)).Should(BeTrue()) 81 }) 82 83 Context("when creating/updating the application is successful", func() { 84 var createdApp v2action.Application 85 86 BeforeEach(func() { 87 fakeV2Actor.CreateApplicationStub = func(application v2action.Application) (v2action.Application, v2action.Warnings, error) { 88 createdApp = application 89 createdApp.GUID = "some-app-guid" 90 91 return createdApp, v2action.Warnings{"create-application-warnings-1", "create-application-warnings-2"}, nil 92 } 93 }) 94 95 JustBeforeEach(func() { 96 Eventually(eventStream).Should(Receive(Equal(SettingUpApplication))) 97 Eventually(warningsStream).Should(Receive(ConsistOf("create-application-warnings-1", "create-application-warnings-2"))) 98 Eventually(eventStream).Should(Receive(Equal(CreatedApplication))) 99 }) 100 101 Context("when the route creation is successful", func() { 102 var createdRoutes []v2action.Route 103 104 BeforeEach(func() { 105 createdRoutes = []v2action.Route{{Host: "banana", GUID: "some-route-guid"}} 106 fakeV2Actor.CreateRouteReturns(createdRoutes[0], v2action.Warnings{"create-route-warnings-1", "create-route-warnings-2"}, nil) 107 }) 108 109 JustBeforeEach(func() { 110 Eventually(eventStream).Should(Receive(Equal(ConfiguringRoutes))) 111 Eventually(warningsStream).Should(Receive(ConsistOf("create-route-warnings-1", "create-route-warnings-2"))) 112 Eventually(eventStream).Should(Receive(Equal(CreatedRoutes))) 113 }) 114 115 Context("when binding the routes is successful", func() { 116 var desiredServices map[string]v2action.ServiceInstance 117 118 BeforeEach(func() { 119 desiredServices = map[string]v2action.ServiceInstance{ 120 "service_1": {Name: "service_1", GUID: "service_guid"}, 121 } 122 config.DesiredServices = desiredServices 123 fakeV2Actor.BindRouteToApplicationReturns(v2action.Warnings{"bind-route-warnings-1", "bind-route-warnings-2"}, nil) 124 }) 125 126 JustBeforeEach(func() { 127 Eventually(warningsStream).Should(Receive(ConsistOf("bind-route-warnings-1", "bind-route-warnings-2"))) 128 Eventually(eventStream).Should(Receive(Equal(BoundRoutes))) 129 }) 130 131 Context("when service binding is successful", func() { 132 BeforeEach(func() { 133 fakeV2Actor.BindServiceByApplicationAndServiceInstanceReturns(v2action.Warnings{"bind-service-warnings-1", "bind-service-warnings-2"}, nil) 134 }) 135 136 JustBeforeEach(func() { 137 Eventually(eventStream).Should(Receive(Equal(ConfiguringServices))) 138 Eventually(warningsStream).Should(Receive(ConsistOf("bind-service-warnings-1", "bind-service-warnings-2"))) 139 Eventually(eventStream).Should(Receive(Equal(BoundServices))) 140 }) 141 142 Context("when resource matching happens", func() { 143 BeforeEach(func() { 144 fakeV2Actor.ResourceMatchReturns(nil, nil, v2action.Warnings{"resource-warnings-1", "resource-warnings-2"}, nil) 145 }) 146 147 JustBeforeEach(func() { 148 Eventually(eventStream).Should(Receive(Equal(ResourceMatching))) 149 Eventually(warningsStream).Should(Receive(ConsistOf("resource-warnings-1", "resource-warnings-2"))) 150 }) 151 152 Context("when the archive creation is successful", func() { 153 var archivePath string 154 155 BeforeEach(func() { 156 tmpfile, err := ioutil.TempFile("", "fake-archive") 157 Expect(err).ToNot(HaveOccurred()) 158 _, err = tmpfile.Write([]byte("123456")) 159 Expect(err).ToNot(HaveOccurred()) 160 Expect(tmpfile.Close()).ToNot(HaveOccurred()) 161 162 archivePath = tmpfile.Name() 163 fakeSharedActor.ZipDirectoryResourcesReturns(archivePath, nil) 164 }) 165 166 JustBeforeEach(func() { 167 Eventually(eventStream).Should(Receive(Equal(CreatingArchive))) 168 }) 169 170 Context("when the upload is successful", func() { 171 BeforeEach(func() { 172 fakeV2Actor.UploadApplicationPackageReturns(v2action.Job{}, v2action.Warnings{"upload-warnings-1", "upload-warnings-2"}, nil) 173 }) 174 175 JustBeforeEach(func() { 176 Eventually(eventStream).Should(Receive(Equal(UploadingApplication))) 177 Eventually(eventStream).Should(Receive(Equal(UploadComplete))) 178 Eventually(warningsStream).Should(Receive(ConsistOf("upload-warnings-1", "upload-warnings-2"))) 179 }) 180 181 It("sends the updated config and a complete event", func() { 182 Eventually(configStream).Should(Receive(Equal(ApplicationConfig{ 183 CurrentApplication: Application{Application: createdApp}, 184 CurrentRoutes: createdRoutes, 185 CurrentServices: desiredServices, 186 DesiredApplication: Application{Application: createdApp}, 187 DesiredRoutes: createdRoutes, 188 DesiredServices: desiredServices, 189 Path: "some-path", 190 }))) 191 Eventually(eventStream).Should(Receive(Equal(Complete))) 192 193 Expect(fakeV2Actor.UploadApplicationPackageCallCount()).To(Equal(1)) 194 }) 195 }) 196 197 Context("when the upload errors", func() { 198 Context("with a retryable error", func() { 199 BeforeEach(func() { 200 fakeV2Actor.UploadApplicationPackageReturns(v2action.Job{}, v2action.Warnings{"upload-warnings-1", "upload-warnings-2"}, ccerror.PipeSeekError{}) 201 }) 202 203 It("retries the download up to three times", func() { 204 Eventually(eventStream).Should(Receive(Equal(UploadingApplication))) 205 Eventually(fakeProgressBar.NewProgressBarWrapperCallCount).Should(Equal(1)) 206 Eventually(warningsStream).Should(Receive(ConsistOf("upload-warnings-1", "upload-warnings-2"))) 207 Eventually(eventStream).Should(Receive(Equal(RetryUpload))) 208 209 Eventually(eventStream).Should(Receive(Equal(UploadingApplication))) 210 Eventually(fakeProgressBar.NewProgressBarWrapperCallCount).Should(Equal(2)) 211 Eventually(warningsStream).Should(Receive(ConsistOf("upload-warnings-1", "upload-warnings-2"))) 212 Eventually(eventStream).Should(Receive(Equal(RetryUpload))) 213 214 Eventually(eventStream).Should(Receive(Equal(UploadingApplication))) 215 Eventually(fakeProgressBar.NewProgressBarWrapperCallCount).Should(Equal(3)) 216 Eventually(warningsStream).Should(Receive(ConsistOf("upload-warnings-1", "upload-warnings-2"))) 217 Eventually(eventStream).Should(Receive(Equal(RetryUpload))) 218 219 Eventually(errorStream).Should(Receive(Equal(UploadFailedError{}))) 220 }) 221 }) 222 223 Context("with a generic error", func() { 224 var expectedErr error 225 226 BeforeEach(func() { 227 expectedErr = errors.New("dios mio") 228 fakeV2Actor.UploadApplicationPackageReturns(v2action.Job{}, v2action.Warnings{"upload-warnings-1", "upload-warnings-2"}, expectedErr) 229 }) 230 231 It("sends warnings and errors, then stops", func() { 232 Eventually(eventStream).Should(Receive(Equal(UploadingApplication))) 233 Eventually(warningsStream).Should(Receive(ConsistOf("upload-warnings-1", "upload-warnings-2"))) 234 Eventually(errorStream).Should(Receive(MatchError(expectedErr))) 235 Consistently(eventStream).ShouldNot(Receive()) 236 }) 237 }) 238 }) 239 }) 240 241 Context("when the archive creation errors", func() { 242 var expectedErr error 243 244 BeforeEach(func() { 245 expectedErr = errors.New("dios mio") 246 fakeSharedActor.ZipDirectoryResourcesReturns("", expectedErr) 247 }) 248 249 It("sends warnings and errors, then stops", func() { 250 Eventually(errorStream).Should(Receive(MatchError(expectedErr))) 251 Consistently(eventStream).ShouldNot(Receive()) 252 }) 253 }) 254 }) 255 256 Context("when a docker image is provided", func() { 257 BeforeEach(func() { 258 config.DesiredApplication.DockerImage = "some-docker-image-path" 259 }) 260 261 It("skips achiving and uploading", func() { 262 Eventually(configStream).Should(Receive()) 263 Eventually(eventStream).Should(Receive(Equal(Complete))) 264 265 Expect(fakeSharedActor.ZipDirectoryResourcesCallCount()).To(Equal(0)) 266 }) 267 }) 268 }) 269 270 Context("when there are no services to bind", func() { 271 BeforeEach(func() { 272 services := map[string]v2action.ServiceInstance{ 273 "service_1": {Name: "service_1", GUID: "service_guid"}, 274 } 275 config.CurrentServices = services 276 config.DesiredServices = services 277 }) 278 279 It("should not send the BoundServices event", func() { 280 Eventually(eventStream).ShouldNot(Receive(Equal(ConfiguringServices))) 281 Consistently(eventStream).ShouldNot(Receive(Equal(BoundServices))) 282 }) 283 }) 284 285 Context("when binding routes fails", func() { 286 var expectedErr error 287 288 BeforeEach(func() { 289 expectedErr = errors.New("dios mio") 290 fakeV2Actor.BindServiceByApplicationAndServiceInstanceReturns(v2action.Warnings{"bind-service-warnings-1", "bind-service-warnings-2"}, expectedErr) 291 }) 292 293 It("sends warnings and errors, then stops", func() { 294 Eventually(eventStream).Should(Receive(Equal(ConfiguringServices))) 295 Eventually(warningsStream).Should(Receive(ConsistOf("bind-service-warnings-1", "bind-service-warnings-2"))) 296 Eventually(errorStream).Should(Receive(MatchError(expectedErr))) 297 Consistently(eventStream).ShouldNot(Receive()) 298 }) 299 }) 300 }) 301 302 Context("when there are no routes to bind", func() { 303 BeforeEach(func() { 304 config.CurrentRoutes = createdRoutes 305 }) 306 307 It("should not send the RouteCreated event", func() { 308 Eventually(warningsStream).Should(Receive()) 309 Consistently(eventStream).ShouldNot(Receive(Equal(CreatedRoutes))) 310 }) 311 }) 312 313 Context("when binding the routes errors", func() { 314 var expectedErr error 315 316 BeforeEach(func() { 317 expectedErr = errors.New("dios mio") 318 fakeV2Actor.BindRouteToApplicationReturns(v2action.Warnings{"bind-route-warnings-1", "bind-route-warnings-2"}, expectedErr) 319 }) 320 321 It("sends warnings and errors, then stops", func() { 322 Eventually(warningsStream).Should(Receive(ConsistOf("bind-route-warnings-1", "bind-route-warnings-2"))) 323 Eventually(errorStream).Should(Receive(MatchError(expectedErr))) 324 Consistently(eventStream).ShouldNot(Receive()) 325 }) 326 }) 327 }) 328 329 Context("when there are no routes to create", func() { 330 BeforeEach(func() { 331 config.DesiredRoutes[0].GUID = "some-route-guid" 332 }) 333 334 It("should not send the RouteCreated event", func() { 335 Eventually(eventStream).Should(Receive(Equal(ConfiguringRoutes))) 336 Eventually(warningsStream).Should(Receive()) 337 Consistently(eventStream).ShouldNot(Receive()) 338 }) 339 }) 340 341 Context("when the route creation errors", func() { 342 var expectedErr error 343 344 BeforeEach(func() { 345 expectedErr = errors.New("dios mio") 346 fakeV2Actor.CreateRouteReturns(v2action.Route{}, v2action.Warnings{"create-route-warnings-1", "create-route-warnings-2"}, expectedErr) 347 }) 348 349 It("sends warnings and errors, then stops", func() { 350 Eventually(eventStream).Should(Receive(Equal(ConfiguringRoutes))) 351 Eventually(warningsStream).Should(Receive(ConsistOf("create-route-warnings-1", "create-route-warnings-2"))) 352 Eventually(errorStream).Should(Receive(MatchError(expectedErr))) 353 Consistently(eventStream).ShouldNot(Receive()) 354 }) 355 }) 356 }) 357 358 Context("when creating/updating errors", func() { 359 var expectedErr error 360 361 BeforeEach(func() { 362 expectedErr = errors.New("dios mio") 363 fakeV2Actor.CreateApplicationReturns(v2action.Application{}, v2action.Warnings{"create-application-warnings-1", "create-application-warnings-2"}, expectedErr) 364 }) 365 366 It("sends warnings and errors, then stops", func() { 367 Eventually(eventStream).Should(Receive(Equal(SettingUpApplication))) 368 Eventually(warningsStream).Should(Receive(ConsistOf("create-application-warnings-1", "create-application-warnings-2"))) 369 Eventually(errorStream).Should(Receive(MatchError(expectedErr))) 370 Consistently(eventStream).ShouldNot(Receive()) 371 }) 372 }) 373 })