github.com/randomtask1155/cli@v6.41.1-0.20181227003417-a98eed78cbde+incompatible/api/cloudcontroller/ccv3/job_test.go (about) 1 package ccv3_test 2 3 import ( 4 "fmt" 5 "net/http" 6 "time" 7 8 "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" 9 . "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" 10 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/ccv3fakes" 11 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" 12 . "github.com/onsi/ginkgo" 13 . "github.com/onsi/ginkgo/extensions/table" 14 . "github.com/onsi/gomega" 15 . "github.com/onsi/gomega/ghttp" 16 ) 17 18 var _ = Describe("Job", func() { 19 var client *Client 20 21 Describe("Job", func() { 22 DescribeTable("IsComplete", 23 func(status constant.JobState, expected bool) { 24 job := Job{State: status} 25 Expect(job.IsComplete()).To(Equal(expected)) 26 }, 27 28 Entry("when failed, it returns false", constant.JobFailed, false), 29 Entry("when complete, it returns true", constant.JobComplete, true), 30 Entry("when processing, it returns false", constant.JobProcessing, false), 31 ) 32 33 DescribeTable("HasFailed", 34 func(status constant.JobState, expected bool) { 35 job := Job{State: status} 36 Expect(job.HasFailed()).To(Equal(expected)) 37 }, 38 39 Entry("when failed, it returns true", constant.JobFailed, true), 40 Entry("when complete, it returns false", constant.JobComplete, false), 41 Entry("when processing, it returns false", constant.JobProcessing, false), 42 ) 43 }) 44 45 Describe("GetJob", func() { 46 var ( 47 jobLocation JobURL 48 49 job Job 50 warnings Warnings 51 executeErr error 52 ) 53 54 BeforeEach(func() { 55 client, _ = NewTestClient() 56 jobLocation = JobURL(fmt.Sprintf("%s/some-job-location", server.URL())) 57 }) 58 59 JustBeforeEach(func() { 60 job, warnings, executeErr = client.GetJob(jobLocation) 61 }) 62 63 When("no errors are encountered", func() { 64 BeforeEach(func() { 65 jsonResponse := `{ 66 "guid": "job-guid", 67 "created_at": "2016-06-08T16:41:27Z", 68 "updated_at": "2016-06-08T16:41:27Z", 69 "operation": "app.delete", 70 "state": "PROCESSING", 71 "links": { 72 "self": { 73 "href": "/v3/jobs/job-guid" 74 } 75 } 76 } 77 }` 78 79 server.AppendHandlers( 80 CombineHandlers( 81 VerifyRequest(http.MethodGet, "/some-job-location"), 82 RespondWith(http.StatusOK, jsonResponse, http.Header{"X-Cf-Warnings": {"warning-1, warning-2"}}), 83 )) 84 }) 85 86 It("returns job with all warnings", func() { 87 Expect(executeErr).NotTo(HaveOccurred()) 88 Expect(warnings).To(ConsistOf(Warnings{"warning-1", "warning-2"})) 89 Expect(job.GUID).To(Equal("job-guid")) 90 Expect(job.State).To(Equal(constant.JobProcessing)) 91 }) 92 }) 93 94 When("the job fails", func() { 95 BeforeEach(func() { 96 jsonResponse := `{ 97 "guid": "job-guid", 98 "created_at": "2016-06-08T16:41:27Z", 99 "updated_at": "2016-06-08T16:41:27Z", 100 "operation": "delete", 101 "state": "FAILED", 102 "errors": [ 103 { 104 "detail": "blah blah", 105 "title": "CF-JobFail", 106 "code": 1234 107 } 108 ], 109 "links": { 110 "self": { 111 "href": "/v3/jobs/job-guid" 112 } 113 } 114 } 115 }` 116 117 server.AppendHandlers( 118 CombineHandlers( 119 VerifyRequest(http.MethodGet, "/some-job-location"), 120 RespondWith(http.StatusOK, jsonResponse, http.Header{"X-Cf-Warnings": {"warning-1, warning-2"}}), 121 )) 122 }) 123 124 It("returns job with all warnings", func() { 125 Expect(executeErr).NotTo(HaveOccurred()) 126 Expect(warnings).To(ConsistOf(Warnings{"warning-1", "warning-2"})) 127 Expect(job.GUID).To(Equal("job-guid")) 128 Expect(job.State).To(Equal(constant.JobFailed)) 129 Expect(job.Errors[0].Detail).To(Equal("blah blah")) 130 Expect(job.Errors[0].Title).To(Equal("CF-JobFail")) 131 Expect(job.Errors[0].Code).To(Equal(1234)) 132 }) 133 }) 134 }) 135 136 Describe("PollJob", func() { 137 var ( 138 jobLocation JobURL 139 140 warnings Warnings 141 executeErr error 142 143 startTime time.Time 144 ) 145 146 BeforeEach(func() { 147 client, _ = NewTestClient(Config{JobPollingTimeout: time.Minute}) 148 jobLocation = JobURL(fmt.Sprintf("%s/some-job-location", server.URL())) 149 }) 150 151 JustBeforeEach(func() { 152 startTime = time.Now() 153 warnings, executeErr = client.PollJob(jobLocation) 154 }) 155 156 When("the job starts queued and then finishes successfully", func() { 157 BeforeEach(func() { 158 server.AppendHandlers( 159 CombineHandlers( 160 VerifyRequest(http.MethodGet, "/some-job-location"), 161 RespondWith(http.StatusAccepted, `{ 162 "guid": "job-guid", 163 "created_at": "2016-06-08T16:41:27Z", 164 "updated_at": "2016-06-08T16:41:27Z", 165 "operation": "app.delete", 166 "state": "PROCESSING", 167 "links": { 168 "self": { 169 "href": "/v3/jobs/job-guid" 170 } 171 } 172 }`, http.Header{"X-Cf-Warnings": {"warning-1"}}), 173 )) 174 175 server.AppendHandlers( 176 CombineHandlers( 177 VerifyRequest(http.MethodGet, "/some-job-location"), 178 RespondWith(http.StatusAccepted, `{ 179 "guid": "job-guid", 180 "created_at": "2016-06-08T16:41:27Z", 181 "updated_at": "2016-06-08T16:41:27Z", 182 "operation": "app.delete", 183 "state": "PROCESSING", 184 "links": { 185 "self": { 186 "href": "/v3/jobs/job-guid" 187 } 188 } 189 }`, http.Header{"X-Cf-Warnings": {"warning-2"}}), 190 )) 191 192 server.AppendHandlers( 193 CombineHandlers( 194 VerifyRequest(http.MethodGet, "/some-job-location"), 195 RespondWith(http.StatusAccepted, `{ 196 "guid": "job-guid", 197 "created_at": "2016-06-08T16:41:27Z", 198 "updated_at": "2016-06-08T16:41:27Z", 199 "operation": "app.delete", 200 "state": "COMPLETE", 201 "links": { 202 "self": { 203 "href": "/v3/jobs/job-guid" 204 } 205 } 206 }`, http.Header{"X-Cf-Warnings": {"warning-3, warning-4"}}), 207 )) 208 }) 209 210 It("should poll until completion", func() { 211 Expect(executeErr).ToNot(HaveOccurred()) 212 Expect(warnings).To(ConsistOf("warning-1", "warning-2", "warning-3", "warning-4")) 213 }) 214 }) 215 216 When("the job starts queued and then fails", func() { 217 var jobFailureMessage string 218 219 BeforeEach(func() { 220 jobFailureMessage = "I am a banana!!!" 221 222 server.AppendHandlers( 223 CombineHandlers( 224 VerifyRequest(http.MethodGet, "/some-job-location"), 225 RespondWith(http.StatusAccepted, `{ 226 "guid": "job-guid", 227 "created_at": "2016-06-08T16:41:27Z", 228 "updated_at": "2016-06-08T16:41:27Z", 229 "operation": "app.delete", 230 "state": "PROCESSING", 231 "links": { 232 "self": { 233 "href": "/v3/jobs/job-guid" 234 } 235 } 236 }`, http.Header{"X-Cf-Warnings": {"warning-1"}}), 237 )) 238 239 server.AppendHandlers( 240 CombineHandlers( 241 VerifyRequest(http.MethodGet, "/some-job-location"), 242 RespondWith(http.StatusAccepted, `{ 243 "guid": "job-guid", 244 "created_at": "2016-06-08T16:41:27Z", 245 "updated_at": "2016-06-08T16:41:27Z", 246 "operation": "app.delete", 247 "state": "PROCESSING", 248 "links": { 249 "self": { 250 "href": "/v3/jobs/job-guid" 251 } 252 } 253 }`, http.Header{"X-Cf-Warnings": {"warning-2, warning-3"}}), 254 )) 255 256 server.AppendHandlers( 257 CombineHandlers( 258 VerifyRequest(http.MethodGet, "/some-job-location"), 259 RespondWith(http.StatusOK, fmt.Sprintf(`{ 260 "guid": "job-guid", 261 "created_at": "2016-06-08T16:41:27Z", 262 "updated_at": "2016-06-08T16:41:27Z", 263 "operation": "app.delete", 264 "state": "FAILED", 265 "errors": [ { 266 "detail": "%s", 267 "title": "CF-AppBitsUploadInvalid", 268 "code": 160001 269 } ], 270 "links": { 271 "self": { 272 "href": "/v3/jobs/job-guid" 273 } 274 } 275 }`, jobFailureMessage), http.Header{"X-Cf-Warnings": {"warning-4"}}), 276 )) 277 }) 278 279 It("returns a JobFailedError", func() { 280 Expect(executeErr).To(MatchError(ccerror.JobFailedError{ 281 JobGUID: "job-guid", 282 Message: jobFailureMessage, 283 })) 284 Expect(warnings).To(ConsistOf("warning-1", "warning-2", "warning-3", "warning-4")) 285 }) 286 }) 287 288 Context("polling timeouts", func() { 289 When("the job runs longer than the OverallPollingTimeout", func() { 290 var ( 291 jobPollingTimeout time.Duration 292 fakeClock *ccv3fakes.FakeClock 293 ) 294 295 BeforeEach(func() { 296 jobPollingTimeout = 100 * time.Millisecond 297 client, fakeClock = NewTestClient(Config{ 298 JobPollingTimeout: jobPollingTimeout, 299 }) 300 301 clockTime := time.Now() 302 fakeClock.NowReturnsOnCall(0, clockTime) 303 fakeClock.NowReturnsOnCall(1, clockTime) 304 fakeClock.NowReturnsOnCall(2, clockTime.Add(60*time.Millisecond)) 305 fakeClock.NowReturnsOnCall(3, clockTime.Add(60*time.Millisecond*2)) 306 307 server.AppendHandlers( 308 CombineHandlers( 309 VerifyRequest(http.MethodGet, "/some-job-location"), 310 RespondWith(http.StatusAccepted, `{ 311 "guid": "job-guid", 312 "created_at": "2016-06-08T16:41:27Z", 313 "updated_at": "2016-06-08T16:41:27Z", 314 "operation": "app.delete", 315 "state": "PROCESSING", 316 "links": { 317 "self": { 318 "href": "/v3/jobs/job-guid" 319 } 320 } 321 }`, http.Header{"X-Cf-Warnings": {"warning-1"}}), 322 )) 323 324 server.AppendHandlers( 325 CombineHandlers( 326 VerifyRequest(http.MethodGet, "/some-job-location"), 327 RespondWith(http.StatusAccepted, `{ 328 "guid": "job-guid", 329 "created_at": "2016-06-08T16:41:27Z", 330 "updated_at": "2016-06-08T16:41:27Z", 331 "operation": "app.delete", 332 "state": "PROCESSING", 333 "links": { 334 "self": { 335 "href": "/v3/jobs/job-guid" 336 } 337 } 338 }`, http.Header{"X-Cf-Warnings": {"warning-2, warning-3"}}), 339 )) 340 341 server.AppendHandlers( 342 CombineHandlers( 343 VerifyRequest(http.MethodGet, "/some-job-location"), 344 RespondWith(http.StatusAccepted, `{ 345 "guid": "job-guid", 346 "created_at": "2016-06-08T16:41:27Z", 347 "updated_at": "2016-06-08T16:41:27Z", 348 "operation": "app.delete", 349 "state": "FINISHED", 350 "links": { 351 "self": { 352 "href": "/v3/jobs/job-guid" 353 } 354 } 355 }`, http.Header{"X-Cf-Warnings": {"warning-4"}}), 356 )) 357 }) 358 359 It("raises a JobTimeoutError", func() { 360 Expect(executeErr).To(MatchError(ccerror.JobTimeoutError{ 361 Timeout: jobPollingTimeout, 362 JobGUID: "job-guid", 363 })) 364 Expect(warnings).To(ConsistOf("warning-1", "warning-2", "warning-3")) 365 }) 366 367 // Fuzzy test to ensure that the overall function time isn't [far] 368 // greater than the OverallPollingTimeout. Since this is partially 369 // dependent on the speed of the system, the expectation is that the 370 // function *should* never exceed three times the timeout. 371 It("does not run [too much] longer than the timeout", func() { 372 endTime := time.Now() 373 Expect(executeErr).To(HaveOccurred()) 374 375 // If the jobPollingTimeout is less than the PollingInterval, 376 // then the margin may be too small, we should not allow the 377 // jobPollingTimeout to be set to less than the PollingInterval 378 Expect(endTime).To(BeTemporally("~", startTime, 3*jobPollingTimeout)) 379 }) 380 }) 381 }) 382 }) 383 })