github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/api/pipelines_test.go (about) 1 package api_test 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io/ioutil" 9 "net/http" 10 "time" 11 12 "github.com/pf-qiu/concourse/v6/atc" 13 "github.com/pf-qiu/concourse/v6/atc/db" 14 "github.com/pf-qiu/concourse/v6/atc/db/dbfakes" 15 . "github.com/pf-qiu/concourse/v6/atc/testhelpers" 16 . "github.com/onsi/ginkgo" 17 . "github.com/onsi/gomega" 18 ) 19 20 var _ = Describe("Pipelines API", func() { 21 var ( 22 dbPipeline *dbfakes.FakePipeline 23 fakeTeam *dbfakes.FakeTeam 24 25 publicPipeline *dbfakes.FakePipeline 26 anotherPublicPipeline *dbfakes.FakePipeline 27 privatePipeline *dbfakes.FakePipeline 28 privatePipelineFromAnotherTeam *dbfakes.FakePipeline 29 ) 30 BeforeEach(func() { 31 dbPipeline = new(dbfakes.FakePipeline) 32 fakeTeam = new(dbfakes.FakeTeam) 33 publicPipeline = new(dbfakes.FakePipeline) 34 35 publicPipeline.IDReturns(1) 36 publicPipeline.PausedReturns(true) 37 publicPipeline.PublicReturns(true) 38 publicPipeline.TeamNameReturns("main") 39 publicPipeline.NameReturns("public-pipeline") 40 publicPipeline.GroupsReturns(atc.GroupConfigs{ 41 { 42 Name: "group2", 43 Jobs: []string{"job3", "job4"}, 44 Resources: []string{"resource3", "resource4"}, 45 }, 46 }) 47 publicPipeline.DisplayReturns(&atc.DisplayConfig{ 48 BackgroundImage: "background.jpg", 49 }) 50 publicPipeline.LastUpdatedReturns(time.Unix(1, 0)) 51 52 anotherPublicPipeline = new(dbfakes.FakePipeline) 53 anotherPublicPipeline.IDReturns(2) 54 anotherPublicPipeline.PausedReturns(true) 55 anotherPublicPipeline.PublicReturns(true) 56 anotherPublicPipeline.TeamNameReturns("another") 57 anotherPublicPipeline.NameReturns("another-pipeline") 58 anotherPublicPipeline.LastUpdatedReturns(time.Unix(1, 0)) 59 60 privatePipeline = new(dbfakes.FakePipeline) 61 privatePipeline.IDReturns(3) 62 privatePipeline.PausedReturns(false) 63 privatePipeline.PublicReturns(false) 64 privatePipeline.ArchivedReturns(true) 65 privatePipeline.TeamNameReturns("main") 66 privatePipeline.NameReturns("private-pipeline") 67 privatePipeline.GroupsReturns(atc.GroupConfigs{ 68 { 69 Name: "group1", 70 Jobs: []string{"job1", "job2"}, 71 Resources: []string{"resource1", "resource2"}, 72 }, 73 }) 74 privatePipeline.LastUpdatedReturns(time.Unix(1, 0)) 75 76 privatePipelineFromAnotherTeam = new(dbfakes.FakePipeline) 77 privatePipelineFromAnotherTeam.IDReturns(3) 78 privatePipelineFromAnotherTeam.PausedReturns(false) 79 privatePipelineFromAnotherTeam.PublicReturns(false) 80 privatePipelineFromAnotherTeam.TeamNameReturns("main") 81 privatePipelineFromAnotherTeam.NameReturns("private-pipeline") 82 privatePipelineFromAnotherTeam.LastUpdatedReturns(time.Unix(1, 0)) 83 84 fakeTeam.PipelinesReturns([]db.Pipeline{ 85 privatePipeline, 86 publicPipeline, 87 }, nil) 88 89 fakeTeam.PublicPipelinesReturns([]db.Pipeline{publicPipeline}, nil) 90 91 dbPipelineFactory.VisiblePipelinesReturns([]db.Pipeline{publicPipeline, anotherPublicPipeline}, nil) 92 }) 93 94 Describe("GET /api/v1/pipelines", func() { 95 var response *http.Response 96 97 JustBeforeEach(func() { 98 req, err := http.NewRequest("GET", server.URL+"/api/v1/pipelines", nil) 99 Expect(err).NotTo(HaveOccurred()) 100 101 req.Header.Set("Content-Type", "application/json") 102 103 response, err = client.Do(req) 104 Expect(err).NotTo(HaveOccurred()) 105 }) 106 107 It("returns 200 OK", func() { 108 Expect(response.StatusCode).To(Equal(http.StatusOK)) 109 }) 110 111 It("returns application/json", func() { 112 expectedHeaderEntries := map[string]string{ 113 "Content-Type": "application/json", 114 } 115 Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries)) 116 }) 117 118 It("returns a JSON array of pipeline objects", func() { 119 body, err := ioutil.ReadAll(response.Body) 120 Expect(err).NotTo(HaveOccurred()) 121 Expect(body).To(MatchJSON(`[ 122 { 123 "id": 1, 124 "name": "public-pipeline", 125 "paused": true, 126 "public": true, 127 "archived": false, 128 "team_name": "main", 129 "last_updated": 1, 130 "groups": [ 131 { 132 "name": "group2", 133 "jobs": ["job3", "job4"], 134 "resources": ["resource3", "resource4"] 135 } 136 ], 137 "display": { 138 "background_image": "background.jpg" 139 } 140 }, 141 { 142 "id": 2, 143 "name": "another-pipeline", 144 "paused": true, 145 "public": true, 146 "archived": false, 147 "team_name": "another", 148 "last_updated": 1 149 } 150 ]`)) 151 }) 152 153 Context("when team is set in user context", func() { 154 BeforeEach(func() { 155 fakeAccess.TeamNamesReturns([]string{"some-team"}) 156 }) 157 158 It("constructs pipeline factory with provided team names", func() { 159 Expect(dbPipelineFactory.VisiblePipelinesCallCount()).To(Equal(1)) 160 Expect(dbPipelineFactory.VisiblePipelinesArgsForCall(0)).To(ContainElement("some-team")) 161 }) 162 }) 163 164 Context("when not authenticated", func() { 165 It("returns only public pipelines", func() { 166 body, err := ioutil.ReadAll(response.Body) 167 Expect(err).NotTo(HaveOccurred()) 168 169 var pipelines []map[string]interface{} 170 err = json.Unmarshal(body, &pipelines) 171 Expect(pipelines).To(ConsistOf( 172 HaveKeyWithValue("id", BeNumerically("==", publicPipeline.ID())), 173 HaveKeyWithValue("id", BeNumerically("==", anotherPublicPipeline.ID())), 174 )) 175 }) 176 177 It("populates pipeline factory with no team names", func() { 178 Expect(dbPipelineFactory.VisiblePipelinesCallCount()).To(Equal(1)) 179 Expect(dbPipelineFactory.VisiblePipelinesArgsForCall(0)).To(BeEmpty()) 180 }) 181 }) 182 183 Context("when authenticated", func() { 184 BeforeEach(func() { 185 fakeAccess.TeamNamesReturns([]string{"main"}) 186 dbPipelineFactory.VisiblePipelinesReturns([]db.Pipeline{privatePipeline, publicPipeline, anotherPublicPipeline}, nil) 187 }) 188 189 It("returns all pipelines of the team + all public pipelines", func() { 190 body, err := ioutil.ReadAll(response.Body) 191 Expect(err).NotTo(HaveOccurred()) 192 193 Expect(dbPipelineFactory.VisiblePipelinesCallCount()).To(Equal(1)) 194 195 var pipelines []map[string]interface{} 196 err = json.Unmarshal(body, &pipelines) 197 Expect(pipelines).To(ConsistOf( 198 HaveKeyWithValue("id", BeNumerically("==", publicPipeline.ID())), 199 HaveKeyWithValue("id", BeNumerically("==", privatePipeline.ID())), 200 HaveKeyWithValue("id", BeNumerically("==", anotherPublicPipeline.ID())), 201 )) 202 }) 203 204 Context("user has the Admin privilege", func() { 205 BeforeEach(func() { 206 fakeAccess.IsAdminReturns(true) 207 dbPipelineFactory.AllPipelinesReturns( 208 []db.Pipeline{publicPipeline, privatePipeline, anotherPublicPipeline, privatePipelineFromAnotherTeam}, 209 nil) 210 }) 211 212 It("user can see all private and public pipelines from all teams", func() { 213 Expect(dbPipelineFactory.AllPipelinesCallCount()).To(Equal(1), 214 "Expected AllPipelines() to be called once") 215 216 body, err := ioutil.ReadAll(response.Body) 217 Expect(err).NotTo(HaveOccurred()) 218 219 var pipelinesResponse []atc.Pipeline 220 err = json.Unmarshal(body, &pipelinesResponse) 221 Expect(err).NotTo(HaveOccurred()) 222 Expect(len(pipelinesResponse)).To(Equal(4)) 223 }) 224 }) 225 226 Context("when the call to get active pipelines fails", func() { 227 BeforeEach(func() { 228 dbPipelineFactory.VisiblePipelinesReturns(nil, errors.New("disaster")) 229 }) 230 231 It("returns 500 internal server error", func() { 232 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 233 }) 234 }) 235 }) 236 }) 237 238 Describe("GET /api/v1/teams/:team_name/pipelines", func() { 239 var response *http.Response 240 241 JustBeforeEach(func() { 242 req, err := http.NewRequest("GET", server.URL+"/api/v1/teams/main/pipelines", nil) 243 Expect(err).NotTo(HaveOccurred()) 244 245 req.Header.Set("Content-Type", "application/json") 246 247 response, err = client.Do(req) 248 Expect(err).NotTo(HaveOccurred()) 249 }) 250 251 Context("when authenticated as requested team", func() { 252 BeforeEach(func() { 253 fakeAccess.IsAuthorizedReturns(true) 254 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 255 }) 256 257 It("returns 200 OK", func() { 258 Expect(response.StatusCode).To(Equal(http.StatusOK)) 259 }) 260 261 It("returns application/json", func() { 262 expectedHeaderEntries := map[string]string{ 263 "Content-Type": "application/json", 264 } 265 Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries)) 266 }) 267 268 It("constructs team with provided team name", func() { 269 Expect(dbTeamFactory.FindTeamCallCount()).To(Equal(1)) 270 Expect(dbTeamFactory.FindTeamArgsForCall(0)).To(Equal("main")) 271 }) 272 273 It("returns a JSON array of pipeline objects", func() { 274 body, err := ioutil.ReadAll(response.Body) 275 Expect(err).NotTo(HaveOccurred()) 276 Expect(body).To(MatchJSON(`[ 277 { 278 "id": 3, 279 "name": "private-pipeline", 280 "paused": false, 281 "public": false, 282 "archived": true, 283 "team_name": "main", 284 "last_updated": 1, 285 "groups": [ 286 { 287 "name": "group1", 288 "jobs": ["job1", "job2"], 289 "resources": ["resource1", "resource2"] 290 } 291 ] 292 }, 293 { 294 "id": 1, 295 "name": "public-pipeline", 296 "paused": true, 297 "public": true, 298 "archived": false, 299 "team_name": "main", 300 "last_updated": 1, 301 "groups": [ 302 { 303 "name": "group2", 304 "jobs": ["job3", "job4"], 305 "resources": ["resource3", "resource4"] 306 } 307 ], 308 "display": { 309 "background_image": "background.jpg" 310 } 311 } 312 ]`)) 313 }) 314 315 It("returns all team's pipelines", func() { 316 body, err := ioutil.ReadAll(response.Body) 317 Expect(err).NotTo(HaveOccurred()) 318 var pipelines []map[string]interface{} 319 json.Unmarshal(body, &pipelines) 320 321 Expect(pipelines).To(ConsistOf( 322 HaveKeyWithValue("id", BeNumerically("==", publicPipeline.ID())), 323 HaveKeyWithValue("id", BeNumerically("==", privatePipeline.ID())), 324 )) 325 }) 326 327 Context("when the call to get active pipelines fails", func() { 328 BeforeEach(func() { 329 fakeTeam.PipelinesReturns(nil, errors.New("disaster")) 330 }) 331 332 It("returns 500 internal server error", func() { 333 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 334 }) 335 }) 336 }) 337 338 Context("when authenticated as another team", func() { 339 BeforeEach(func() { 340 fakeAccess.IsAuthenticatedReturns(false) 341 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 342 }) 343 344 It("returns only team's public pipelines", func() { 345 body, err := ioutil.ReadAll(response.Body) 346 Expect(err).NotTo(HaveOccurred()) 347 var pipelines []map[string]interface{} 348 json.Unmarshal(body, &pipelines) 349 350 Expect(pipelines).To(ConsistOf( 351 HaveKeyWithValue("id", BeNumerically("==", publicPipeline.ID())), 352 )) 353 }) 354 }) 355 356 Context("when not authenticated", func() { 357 BeforeEach(func() { 358 fakeAccess.IsAuthenticatedReturns(false) 359 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 360 }) 361 362 It("returns only team's public pipelines", func() { 363 body, err := ioutil.ReadAll(response.Body) 364 Expect(err).NotTo(HaveOccurred()) 365 var pipelines []map[string]interface{} 366 json.Unmarshal(body, &pipelines) 367 368 Expect(pipelines).To(ConsistOf( 369 HaveKeyWithValue("id", BeNumerically("==", publicPipeline.ID())), 370 )) 371 }) 372 }) 373 }) 374 375 Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name", func() { 376 var response *http.Response 377 var fakePipeline *dbfakes.FakePipeline 378 379 BeforeEach(func() { 380 fakePipeline = new(dbfakes.FakePipeline) 381 fakePipeline.IDReturns(4) 382 fakePipeline.NameReturns("some-specific-pipeline") 383 fakePipeline.PausedReturns(false) 384 fakePipeline.PublicReturns(true) 385 fakePipeline.TeamNameReturns("a-team") 386 fakePipeline.GroupsReturns(atc.GroupConfigs{ 387 { 388 Name: "group1", 389 Jobs: []string{"job1", "job2"}, 390 Resources: []string{"resource1", "resource2"}, 391 }, 392 { 393 Name: "group2", 394 Jobs: []string{"job3", "job4"}, 395 Resources: []string{"resource3", "resource4"}, 396 }, 397 }) 398 fakePipeline.DisplayReturns(&atc.DisplayConfig{ 399 BackgroundImage: "background.jpg", 400 }) 401 fakePipeline.LastUpdatedReturns(time.Unix(1, 0)) 402 }) 403 404 JustBeforeEach(func() { 405 req, err := http.NewRequest("GET", server.URL+"/api/v1/teams/a-team/pipelines/some-specific-pipeline", nil) 406 Expect(err).NotTo(HaveOccurred()) 407 408 req.Header.Set("Content-Type", "application/json") 409 410 response, err = client.Do(req) 411 Expect(err).NotTo(HaveOccurred()) 412 }) 413 414 Context("when not authenticated", func() { 415 BeforeEach(func() { 416 fakeAccess.IsAuthenticatedReturns(false) 417 }) 418 419 It("returns 401", func() { 420 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 421 }) 422 }) 423 424 Context("when authenticated as requested team", func() { 425 BeforeEach(func() { 426 fakeAccess.IsAuthenticatedReturns(true) 427 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 428 fakeTeam.PipelineReturns(fakePipeline, true, nil) 429 }) 430 431 It("returns 200 ok", func() { 432 Expect(response.StatusCode).To(Equal(http.StatusOK)) 433 }) 434 435 It("returns application/json", func() { 436 expectedHeaderEntries := map[string]string{ 437 "Content-Type": "application/json", 438 } 439 Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries)) 440 }) 441 442 It("returns a pipeline JSON", func() { 443 body, err := ioutil.ReadAll(response.Body) 444 Expect(err).NotTo(HaveOccurred()) 445 446 Expect(body).To(MatchJSON(` 447 { 448 "id": 4, 449 "name": "some-specific-pipeline", 450 "paused": false, 451 "public": true, 452 "archived": false, 453 "team_name": "a-team", 454 "last_updated": 1, 455 "groups": [ 456 { 457 "name": "group1", 458 "jobs": ["job1", "job2"], 459 "resources": ["resource1", "resource2"] 460 }, 461 { 462 "name": "group2", 463 "jobs": ["job3", "job4"], 464 "resources": ["resource3", "resource4"] 465 } 466 ], 467 "display": { 468 "background_image": "background.jpg" 469 } 470 }`)) 471 }) 472 }) 473 474 Context("when authenticated as another team", func() { 475 BeforeEach(func() { 476 fakeAccess.IsAuthenticatedReturns(true) 477 fakeAccess.IsAuthorizedReturns(false) 478 479 fakeTeam.PipelineReturns(fakePipeline, true, nil) 480 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 481 }) 482 483 Context("and the pipeline is private", func() { 484 BeforeEach(func() { 485 fakePipeline.PublicReturns(false) 486 }) 487 488 It("returns 403", func() { 489 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 490 }) 491 }) 492 493 Context("and the pipeline is public", func() { 494 BeforeEach(func() { 495 fakeTeam.PipelineReturns(fakePipeline, true, nil) 496 fakePipeline.PublicReturns(true) 497 }) 498 499 It("returns 200 OK", func() { 500 Expect(response.StatusCode).To(Equal(http.StatusOK)) 501 }) 502 }) 503 }) 504 505 Context("when not authenticated at all", func() { 506 BeforeEach(func() { 507 fakeAccess.IsAuthenticatedReturns(false) 508 dbTeam.PipelineReturns(fakePipeline, true, nil) 509 }) 510 511 Context("and the pipeline is private", func() { 512 BeforeEach(func() { 513 fakePipeline.PublicReturns(false) 514 }) 515 516 It("returns 401", func() { 517 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 518 }) 519 }) 520 521 Context("and the pipeline is public", func() { 522 BeforeEach(func() { 523 fakePipeline.PublicReturns(true) 524 }) 525 526 It("returns 200 OK", func() { 527 Expect(response.StatusCode).To(Equal(http.StatusOK)) 528 }) 529 }) 530 }) 531 }) 532 533 Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name/badge", func() { 534 var response *http.Response 535 var jobWithNoBuilds, jobWithSucceededBuild, jobWithAbortedBuild, jobWithErroredBuild, jobWithFailedBuild *dbfakes.FakeJob 536 537 BeforeEach(func() { 538 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 539 dbPipeline.NameReturns("some-pipeline") 540 fakeTeam.PipelineReturns(dbPipeline, true, nil) 541 542 jobWithNoBuilds = new(dbfakes.FakeJob) 543 jobWithSucceededBuild = new(dbfakes.FakeJob) 544 jobWithAbortedBuild = new(dbfakes.FakeJob) 545 jobWithErroredBuild = new(dbfakes.FakeJob) 546 jobWithFailedBuild = new(dbfakes.FakeJob) 547 548 succeededBuild := new(dbfakes.FakeBuild) 549 succeededBuild.StatusReturns(db.BuildStatusSucceeded) 550 jobWithSucceededBuild.FinishedAndNextBuildReturns(succeededBuild, nil, nil) 551 552 abortedBuild := new(dbfakes.FakeBuild) 553 abortedBuild.StatusReturns(db.BuildStatusAborted) 554 jobWithAbortedBuild.FinishedAndNextBuildReturns(abortedBuild, nil, nil) 555 556 erroredBuild := new(dbfakes.FakeBuild) 557 erroredBuild.StatusReturns(db.BuildStatusErrored) 558 jobWithErroredBuild.FinishedAndNextBuildReturns(erroredBuild, nil, nil) 559 560 failedBuild := new(dbfakes.FakeBuild) 561 failedBuild.StatusReturns(db.BuildStatusFailed) 562 jobWithFailedBuild.FinishedAndNextBuildReturns(failedBuild, nil, nil) 563 }) 564 565 JustBeforeEach(func() { 566 var err error 567 568 response, err = client.Get(server.URL + "/api/v1/teams/some-team/pipelines/some-pipeline/badge") 569 Expect(err).NotTo(HaveOccurred()) 570 }) 571 572 Context("when not authorized", func() { 573 BeforeEach(func() { 574 fakeAccess.IsAuthorizedReturns(false) 575 }) 576 577 Context("and the pipeline is private", func() { 578 BeforeEach(func() { 579 dbPipeline.PublicReturns(false) 580 }) 581 582 Context("when user is authenticated", func() { 583 BeforeEach(func() { 584 fakeAccess.IsAuthenticatedReturns(true) 585 }) 586 It("returns 403", func() { 587 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 588 }) 589 }) 590 591 Context("when user is not authenticated", func() { 592 BeforeEach(func() { 593 fakeAccess.IsAuthenticatedReturns(false) 594 }) 595 596 It("returns 401", func() { 597 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 598 }) 599 }) 600 }) 601 602 Context("and the pipeline is public", func() { 603 BeforeEach(func() { 604 dbPipeline.PublicReturns(true) 605 }) 606 607 It("returns 200 OK", func() { 608 Expect(response.StatusCode).To(Equal(http.StatusOK)) 609 }) 610 }) 611 }) 612 613 Context("when authorized", func() { 614 BeforeEach(func() { 615 fakeAccess.IsAuthenticatedReturns(true) 616 fakeAccess.IsAuthorizedReturns(true) 617 }) 618 619 It("returns 200 OK", func() { 620 Expect(response.StatusCode).To(Equal(http.StatusOK)) 621 }) 622 623 It("returns Content-Type as image/svg+xml and disables caching", func() { 624 expectedHeaderEntries := map[string]string{ 625 "Content-Type": "image/svg+xml", 626 "Cache-Control": "no-cache, no-store, must-revalidate", 627 "Expires": "0", 628 } 629 Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries)) 630 }) 631 632 Context("when the pipeline has no finished builds", func() { 633 BeforeEach(func() { 634 dbPipeline.JobsReturns([]db.Job{jobWithNoBuilds}, nil) 635 }) 636 637 It("returns an unknown badge", func() { 638 body, err := ioutil.ReadAll(response.Body) 639 Expect(err).NotTo(HaveOccurred()) 640 641 Expect(string(body)).To(Equal(`<?xml version="1.0" encoding="UTF-8"?> 642 <svg xmlns="http://www.w3.org/2000/svg" width="98" height="20"> 643 <linearGradient id="b" x2="0" y2="100%"> 644 <stop offset="0" stop-color="#bbb" stop-opacity=".1" /> 645 <stop offset="1" stop-opacity=".1" /> 646 </linearGradient> 647 <mask id="a"> 648 <rect width="98" height="20" rx="3" fill="#fff" /> 649 </mask> 650 <g mask="url(#a)"> 651 <path fill="#555" d="M0 0h37v20H0z" /> 652 <path fill="#9f9f9f" d="M37 0h61v20H37z" /> 653 <path fill="url(#b)" d="M0 0h98v20H0z" /> 654 </g> 655 <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> 656 <text x="18.5" y="15" fill="#010101" fill-opacity=".3">build</text> 657 <text x="18.5" y="14">build</text> 658 <text x="66.5" y="15" fill="#010101" fill-opacity=".3">unknown</text> 659 <text x="66.5" y="14">unknown</text> 660 </g> 661 </svg>`)) 662 }) 663 }) 664 665 Context("when the pipeline has a successful build", func() { 666 BeforeEach(func() { 667 dbPipeline.JobsReturns([]db.Job{jobWithNoBuilds, jobWithSucceededBuild}, nil) 668 }) 669 670 It("returns a successful badge", func() { 671 body, err := ioutil.ReadAll(response.Body) 672 Expect(err).NotTo(HaveOccurred()) 673 674 Expect(string(body)).To(Equal(`<?xml version="1.0" encoding="UTF-8"?> 675 <svg xmlns="http://www.w3.org/2000/svg" width="88" height="20"> 676 <linearGradient id="b" x2="0" y2="100%"> 677 <stop offset="0" stop-color="#bbb" stop-opacity=".1" /> 678 <stop offset="1" stop-opacity=".1" /> 679 </linearGradient> 680 <mask id="a"> 681 <rect width="88" height="20" rx="3" fill="#fff" /> 682 </mask> 683 <g mask="url(#a)"> 684 <path fill="#555" d="M0 0h37v20H0z" /> 685 <path fill="#44cc11" d="M37 0h51v20H37z" /> 686 <path fill="url(#b)" d="M0 0h88v20H0z" /> 687 </g> 688 <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> 689 <text x="18.5" y="15" fill="#010101" fill-opacity=".3">build</text> 690 <text x="18.5" y="14">build</text> 691 <text x="61.5" y="15" fill="#010101" fill-opacity=".3">passing</text> 692 <text x="61.5" y="14">passing</text> 693 </g> 694 </svg>`)) 695 }) 696 }) 697 698 Context("when the pipeline has an aborted build", func() { 699 BeforeEach(func() { 700 dbPipeline.JobsReturns([]db.Job{jobWithNoBuilds, jobWithSucceededBuild, jobWithAbortedBuild}, nil) 701 }) 702 703 It("returns an aborted badge", func() { 704 body, err := ioutil.ReadAll(response.Body) 705 Expect(err).NotTo(HaveOccurred()) 706 707 Expect(string(body)).To(Equal(`<?xml version="1.0" encoding="UTF-8"?> 708 <svg xmlns="http://www.w3.org/2000/svg" width="90" height="20"> 709 <linearGradient id="b" x2="0" y2="100%"> 710 <stop offset="0" stop-color="#bbb" stop-opacity=".1" /> 711 <stop offset="1" stop-opacity=".1" /> 712 </linearGradient> 713 <mask id="a"> 714 <rect width="90" height="20" rx="3" fill="#fff" /> 715 </mask> 716 <g mask="url(#a)"> 717 <path fill="#555" d="M0 0h37v20H0z" /> 718 <path fill="#8f4b2d" d="M37 0h53v20H37z" /> 719 <path fill="url(#b)" d="M0 0h90v20H0z" /> 720 </g> 721 <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> 722 <text x="18.5" y="15" fill="#010101" fill-opacity=".3">build</text> 723 <text x="18.5" y="14">build</text> 724 <text x="62.5" y="15" fill="#010101" fill-opacity=".3">aborted</text> 725 <text x="62.5" y="14">aborted</text> 726 </g> 727 </svg>`)) 728 }) 729 }) 730 731 Context("when the pipeline has an errored build", func() { 732 BeforeEach(func() { 733 dbPipeline.JobsReturns([]db.Job{jobWithNoBuilds, jobWithSucceededBuild, jobWithAbortedBuild, jobWithErroredBuild}, nil) 734 }) 735 736 It("returns an errored badge", func() { 737 body, err := ioutil.ReadAll(response.Body) 738 Expect(err).NotTo(HaveOccurred()) 739 740 Expect(string(body)).To(Equal(`<?xml version="1.0" encoding="UTF-8"?> 741 <svg xmlns="http://www.w3.org/2000/svg" width="88" height="20"> 742 <linearGradient id="b" x2="0" y2="100%"> 743 <stop offset="0" stop-color="#bbb" stop-opacity=".1" /> 744 <stop offset="1" stop-opacity=".1" /> 745 </linearGradient> 746 <mask id="a"> 747 <rect width="88" height="20" rx="3" fill="#fff" /> 748 </mask> 749 <g mask="url(#a)"> 750 <path fill="#555" d="M0 0h37v20H0z" /> 751 <path fill="#fe7d37" d="M37 0h51v20H37z" /> 752 <path fill="url(#b)" d="M0 0h88v20H0z" /> 753 </g> 754 <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> 755 <text x="18.5" y="15" fill="#010101" fill-opacity=".3">build</text> 756 <text x="18.5" y="14">build</text> 757 <text x="61.5" y="15" fill="#010101" fill-opacity=".3">errored</text> 758 <text x="61.5" y="14">errored</text> 759 </g> 760 </svg>`)) 761 }) 762 }) 763 764 Context("when the pipeline has a failed build", func() { 765 BeforeEach(func() { 766 dbPipeline.JobsReturns([]db.Job{jobWithNoBuilds, jobWithSucceededBuild, jobWithAbortedBuild, jobWithErroredBuild, jobWithFailedBuild}, nil) 767 }) 768 769 It("returns a failed badge", func() { 770 body, err := ioutil.ReadAll(response.Body) 771 Expect(err).NotTo(HaveOccurred()) 772 773 Expect(string(body)).To(Equal(`<?xml version="1.0" encoding="UTF-8"?> 774 <svg xmlns="http://www.w3.org/2000/svg" width="80" height="20"> 775 <linearGradient id="b" x2="0" y2="100%"> 776 <stop offset="0" stop-color="#bbb" stop-opacity=".1" /> 777 <stop offset="1" stop-opacity=".1" /> 778 </linearGradient> 779 <mask id="a"> 780 <rect width="80" height="20" rx="3" fill="#fff" /> 781 </mask> 782 <g mask="url(#a)"> 783 <path fill="#555" d="M0 0h37v20H0z" /> 784 <path fill="#e05d44" d="M37 0h43v20H37z" /> 785 <path fill="url(#b)" d="M0 0h80v20H0z" /> 786 </g> 787 <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> 788 <text x="18.5" y="15" fill="#010101" fill-opacity=".3">build</text> 789 <text x="18.5" y="14">build</text> 790 <text x="57.5" y="15" fill="#010101" fill-opacity=".3">failing</text> 791 <text x="57.5" y="14">failing</text> 792 </g> 793 </svg>`)) 794 }) 795 }) 796 }) 797 }) 798 799 Describe("DELETE /api/v1/teams/:team_name/pipelines/:pipeline_name", func() { 800 var response *http.Response 801 802 JustBeforeEach(func() { 803 pipelineName := "a-pipeline-name" 804 req, err := http.NewRequest("DELETE", server.URL+"/api/v1/teams/a-team/pipelines/"+pipelineName, nil) 805 Expect(err).NotTo(HaveOccurred()) 806 807 req.Header.Set("Content-Type", "application/json") 808 809 response, err = client.Do(req) 810 Expect(err).NotTo(HaveOccurred()) 811 }) 812 813 Context("when authenticated", func() { 814 BeforeEach(func() { 815 fakeAccess.IsAuthenticatedReturns(true) 816 }) 817 818 Context("when requester belongs to the team", func() { 819 BeforeEach(func() { 820 fakeAccess.IsAuthorizedReturns(true) 821 822 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 823 dbPipeline.NameReturns("a-pipeline-name") 824 fakeTeam.PipelineReturns(dbPipeline, true, nil) 825 }) 826 827 It("returns 204 No Content", func() { 828 Expect(response.StatusCode).To(Equal(http.StatusNoContent)) 829 }) 830 831 It("constructs team with provided team name", func() { 832 Expect(dbTeamFactory.FindTeamCallCount()).To(Equal(1)) 833 Expect(dbTeamFactory.FindTeamArgsForCall(0)).To(Equal("a-team")) 834 }) 835 836 It("injects the proper pipelineDB", func() { 837 pipelineRef := fakeTeam.PipelineArgsForCall(0) 838 Expect(pipelineRef).To(Equal(atc.PipelineRef{Name: "a-pipeline-name"})) 839 }) 840 841 It("deletes the named pipeline from the database", func() { 842 Expect(dbPipeline.DestroyCallCount()).To(Equal(1)) 843 }) 844 845 Context("when an error occurs destroying the pipeline", func() { 846 BeforeEach(func() { 847 fakeTeam.PipelineReturns(dbPipeline, true, nil) 848 err := errors.New("disaster!") 849 dbPipeline.DestroyReturns(err) 850 }) 851 852 It("returns a 500 Internal Server Error", func() { 853 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 854 }) 855 }) 856 }) 857 858 Context("when requester does not belong to the team", func() { 859 BeforeEach(func() { 860 fakeAccess.IsAuthorizedReturns(false) 861 }) 862 863 It("returns 403", func() { 864 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 865 }) 866 }) 867 }) 868 869 Context("when the user is not logged in", func() { 870 BeforeEach(func() { 871 fakeAccess.IsAuthenticatedReturns(false) 872 }) 873 874 It("returns 401 Unauthorized", func() { 875 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 876 }) 877 }) 878 }) 879 880 Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/pause", func() { 881 var response *http.Response 882 883 JustBeforeEach(func() { 884 var err error 885 886 request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/pause", nil) 887 Expect(err).NotTo(HaveOccurred()) 888 889 response, err = client.Do(request) 890 Expect(err).NotTo(HaveOccurred()) 891 }) 892 893 Context("when authenticated", func() { 894 BeforeEach(func() { 895 fakeAccess.IsAuthenticatedReturns(true) 896 }) 897 898 Context("when requester belongs to the team", func() { 899 BeforeEach(func() { 900 fakeAccess.IsAuthorizedReturns(true) 901 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 902 }) 903 904 It("constructs team with provided team name", func() { 905 Expect(dbTeamFactory.FindTeamCallCount()).To(Equal(1)) 906 Expect(dbTeamFactory.FindTeamArgsForCall(0)).To(Equal("a-team")) 907 }) 908 909 It("injects the proper pipelineDB", func() { 910 pipelineRef := fakeTeam.PipelineArgsForCall(0) 911 Expect(pipelineRef).To(Equal(atc.PipelineRef{Name: "a-pipeline"})) 912 }) 913 914 Context("when pausing the pipeline succeeds", func() { 915 BeforeEach(func() { 916 fakeTeam.PipelineReturns(dbPipeline, true, nil) 917 dbPipeline.PauseReturns(nil) 918 }) 919 920 It("returns 200", func() { 921 Expect(response.StatusCode).To(Equal(http.StatusOK)) 922 }) 923 }) 924 925 Context("when pausing the pipeline fails", func() { 926 BeforeEach(func() { 927 fakeTeam.PipelineReturns(dbPipeline, true, nil) 928 dbPipeline.PauseReturns(errors.New("welp")) 929 }) 930 931 It("returns 500", func() { 932 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 933 }) 934 }) 935 }) 936 937 Context("when requester does not belong to the team", func() { 938 BeforeEach(func() { 939 fakeAccess.IsAuthorizedReturns(false) 940 }) 941 942 It("returns 403", func() { 943 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 944 }) 945 }) 946 }) 947 948 Context("when not authenticated", func() { 949 BeforeEach(func() { 950 fakeAccess.IsAuthenticatedReturns(false) 951 }) 952 953 It("returns 401", func() { 954 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 955 }) 956 }) 957 }) 958 959 Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/archive", func() { 960 var response *http.Response 961 962 BeforeEach(func() { 963 fakeAccess.IsAuthenticatedReturns(true) 964 fakeAccess.IsAuthorizedReturns(true) 965 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 966 fakeTeam.PipelineReturns(dbPipeline, true, nil) 967 }) 968 969 JustBeforeEach(func() { 970 request, _ := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/archive", nil) 971 var err error 972 response, err = client.Do(request) 973 Expect(err).NotTo(HaveOccurred()) 974 }) 975 976 It("returns 200", func() { 977 Expect(response.StatusCode).To(Equal(http.StatusOK)) 978 }) 979 980 It("archives the pipeline", func() { 981 Expect(dbPipeline.ArchiveCallCount()).To(Equal(1), "Archive() called the wrong number of times") 982 }) 983 984 Context("when archiving the pipeline fails due to the DB", func() { 985 BeforeEach(func() { 986 dbPipeline.ArchiveReturns(errors.New("pq: a db error")) 987 }) 988 989 It("gives a server error", func() { 990 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 991 }) 992 }) 993 994 Context("when not authenticated", func() { 995 BeforeEach(func() { 996 fakeAccess.IsAuthenticatedReturns(false) 997 }) 998 999 It("returns 401", func() { 1000 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 1001 }) 1002 }) 1003 }) 1004 1005 Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/unpause", func() { 1006 var response *http.Response 1007 1008 JustBeforeEach(func() { 1009 var err error 1010 1011 request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/unpause", nil) 1012 Expect(err).NotTo(HaveOccurred()) 1013 1014 response, err = client.Do(request) 1015 Expect(err).NotTo(HaveOccurred()) 1016 }) 1017 1018 Context("when authenticated", func() { 1019 BeforeEach(func() { 1020 fakeAccess.IsAuthenticatedReturns(true) 1021 }) 1022 Context("when requester belongs to the team", func() { 1023 BeforeEach(func() { 1024 fakeAccess.IsAuthorizedReturns(true) 1025 1026 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 1027 fakeTeam.PipelineReturns(dbPipeline, true, nil) 1028 }) 1029 1030 It("constructs team with provided team name", func() { 1031 Expect(dbTeamFactory.FindTeamCallCount()).To(Equal(1)) 1032 Expect(dbTeamFactory.FindTeamArgsForCall(0)).To(Equal("a-team")) 1033 }) 1034 1035 It("injects the proper pipelineDB", func() { 1036 pipelineRef := fakeTeam.PipelineArgsForCall(0) 1037 Expect(pipelineRef).To(Equal(atc.PipelineRef{Name: "a-pipeline"})) 1038 }) 1039 1040 Context("when unpausing the pipeline succeeds", func() { 1041 BeforeEach(func() { 1042 fakeTeam.PipelineReturns(dbPipeline, true, nil) 1043 dbPipeline.UnpauseReturns(nil) 1044 }) 1045 1046 It("returns 200", func() { 1047 Expect(response.StatusCode).To(Equal(http.StatusOK)) 1048 }) 1049 1050 It("notifies the resource scanner", func() { 1051 Expect(dbTeamFactory.NotifyResourceScannerCallCount()).To(Equal(1)) 1052 }) 1053 }) 1054 1055 Context("when unpausing the pipeline fails for an unknown reason", func() { 1056 BeforeEach(func() { 1057 fakeTeam.PipelineReturns(dbPipeline, true, nil) 1058 dbPipeline.UnpauseReturns(errors.New("welp")) 1059 }) 1060 1061 It("returns 500", func() { 1062 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 1063 }) 1064 }) 1065 }) 1066 1067 Context("when requester does not belong to the team", func() { 1068 BeforeEach(func() { 1069 fakeAccess.IsAuthorizedReturns(false) 1070 }) 1071 1072 It("returns 403", func() { 1073 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 1074 }) 1075 }) 1076 }) 1077 1078 Context("when not authenticated", func() { 1079 BeforeEach(func() { 1080 fakeAccess.IsAuthenticatedReturns(false) 1081 }) 1082 1083 It("returns 401 Unauthorized", func() { 1084 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 1085 }) 1086 }) 1087 }) 1088 1089 Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/expose", func() { 1090 var response *http.Response 1091 1092 JustBeforeEach(func() { 1093 var err error 1094 1095 request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/expose", nil) 1096 Expect(err).NotTo(HaveOccurred()) 1097 1098 response, err = client.Do(request) 1099 Expect(err).NotTo(HaveOccurred()) 1100 }) 1101 1102 Context("when authenticated", func() { 1103 BeforeEach(func() { 1104 fakeAccess.IsAuthenticatedReturns(true) 1105 }) 1106 1107 Context("when requester belongs to the team", func() { 1108 BeforeEach(func() { 1109 fakeAccess.IsAuthorizedReturns(true) 1110 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 1111 }) 1112 1113 It("constructs team with provided team name", func() { 1114 Expect(dbTeamFactory.FindTeamCallCount()).To(Equal(1)) 1115 Expect(dbTeamFactory.FindTeamArgsForCall(0)).To(Equal("a-team")) 1116 }) 1117 1118 It("injects the proper pipelineDB", func() { 1119 Expect(fakeTeam.PipelineCallCount()).To(Equal(1)) 1120 pipelineRef := fakeTeam.PipelineArgsForCall(0) 1121 Expect(pipelineRef).To(Equal(atc.PipelineRef{Name: "a-pipeline"})) 1122 }) 1123 1124 Context("when exposing the pipeline succeeds", func() { 1125 BeforeEach(func() { 1126 fakeTeam.PipelineReturns(dbPipeline, true, nil) 1127 dbPipeline.ExposeReturns(nil) 1128 }) 1129 1130 It("returns 200", func() { 1131 Expect(response.StatusCode).To(Equal(http.StatusOK)) 1132 }) 1133 }) 1134 1135 Context("when exposing the pipeline fails", func() { 1136 BeforeEach(func() { 1137 fakeTeam.PipelineReturns(dbPipeline, true, nil) 1138 dbPipeline.ExposeReturns(errors.New("welp")) 1139 }) 1140 1141 It("returns 500", func() { 1142 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 1143 }) 1144 }) 1145 }) 1146 1147 Context("when requester does not belong to the team", func() { 1148 BeforeEach(func() { 1149 fakeAccess.IsAuthorizedReturns(false) 1150 }) 1151 1152 It("returns 403", func() { 1153 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 1154 }) 1155 }) 1156 }) 1157 1158 Context("when not authenticated", func() { 1159 BeforeEach(func() { 1160 fakeAccess.IsAuthenticatedReturns(false) 1161 }) 1162 1163 It("returns 401 Unauthorized", func() { 1164 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 1165 }) 1166 }) 1167 }) 1168 1169 Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/hide", func() { 1170 var response *http.Response 1171 1172 JustBeforeEach(func() { 1173 var err error 1174 1175 request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/hide", nil) 1176 Expect(err).NotTo(HaveOccurred()) 1177 1178 response, err = client.Do(request) 1179 Expect(err).NotTo(HaveOccurred()) 1180 }) 1181 1182 Context("when authenticated", func() { 1183 BeforeEach(func() { 1184 fakeAccess.IsAuthenticatedReturns(true) 1185 }) 1186 Context("when requester belongs to the team", func() { 1187 BeforeEach(func() { 1188 fakeAccess.IsAuthorizedReturns(true) 1189 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 1190 }) 1191 1192 It("constructs team with provided team name", func() { 1193 Expect(dbTeamFactory.FindTeamCallCount()).To(Equal(1)) 1194 Expect(dbTeamFactory.FindTeamArgsForCall(0)).To(Equal("a-team")) 1195 }) 1196 1197 It("injects the proper pipeline", func() { 1198 pipelineRef := fakeTeam.PipelineArgsForCall(0) 1199 Expect(pipelineRef).To(Equal(atc.PipelineRef{Name: "a-pipeline"})) 1200 }) 1201 1202 Context("when hiding the pipeline succeeds", func() { 1203 BeforeEach(func() { 1204 fakeTeam.PipelineReturns(dbPipeline, true, nil) 1205 dbPipeline.HideReturns(nil) 1206 }) 1207 1208 It("returns 200", func() { 1209 Expect(response.StatusCode).To(Equal(http.StatusOK)) 1210 }) 1211 }) 1212 1213 Context("when hiding the pipeline fails", func() { 1214 BeforeEach(func() { 1215 fakeTeam.PipelineReturns(dbPipeline, true, nil) 1216 dbPipeline.HideReturns(errors.New("welp")) 1217 }) 1218 1219 It("returns 500", func() { 1220 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 1221 }) 1222 }) 1223 }) 1224 1225 Context("when requester does not belong to the team", func() { 1226 BeforeEach(func() { 1227 fakeAccess.IsAuthorizedReturns(false) 1228 }) 1229 1230 It("returns 403 Forbidden", func() { 1231 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 1232 }) 1233 }) 1234 }) 1235 1236 Context("when not authorized", func() { 1237 BeforeEach(func() { 1238 fakeAccess.IsAuthenticatedReturns(false) 1239 }) 1240 1241 It("returns 401 Unauthorized", func() { 1242 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 1243 }) 1244 }) 1245 }) 1246 1247 Describe("PUT /api/v1/teams/:team_name/pipelines/ordering", func() { 1248 var response *http.Response 1249 var requestBody atc.OrderPipelinesRequest 1250 1251 BeforeEach(func() { 1252 requestBody = atc.OrderPipelinesRequest{ 1253 {Name: "a-pipeline"}, 1254 {Name: "another-pipeline"}, 1255 {Name: "yet-another-pipeline"}, 1256 {Name: "one-final-pipeline"}, 1257 {Name: "just-kidding"}, 1258 } 1259 }) 1260 1261 JustBeforeEach(func() { 1262 requestPayload, err := json.Marshal(requestBody) 1263 Expect(err).NotTo(HaveOccurred()) 1264 1265 request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/ordering", bytes.NewBuffer(requestPayload)) 1266 Expect(err).NotTo(HaveOccurred()) 1267 1268 response, err = client.Do(request) 1269 Expect(err).NotTo(HaveOccurred()) 1270 }) 1271 1272 Context("when authenticated", func() { 1273 BeforeEach(func() { 1274 fakeAccess.IsAuthenticatedReturns(true) 1275 }) 1276 1277 Context("when requester belongs to the team", func() { 1278 BeforeEach(func() { 1279 fakeAccess.IsAuthorizedReturns(true) 1280 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 1281 }) 1282 1283 It("constructs team with provided team name", func() { 1284 Expect(dbTeamFactory.FindTeamCallCount()).To(Equal(1)) 1285 Expect(dbTeamFactory.FindTeamArgsForCall(0)).To(Equal("a-team")) 1286 }) 1287 1288 Context("when ordering the pipelines succeeds", func() { 1289 BeforeEach(func() { 1290 fakeTeam.OrderPipelinesReturns(nil) 1291 }) 1292 1293 It("orders the pipelines", func() { 1294 Expect(fakeTeam.OrderPipelinesCallCount()).To(Equal(1)) 1295 pipelineRefs := fakeTeam.OrderPipelinesArgsForCall(0) 1296 Expect(pipelineRefs).To(Equal([]atc.PipelineRef{ 1297 {Name: "a-pipeline"}, 1298 {Name: "another-pipeline"}, 1299 {Name: "yet-another-pipeline"}, 1300 {Name: "one-final-pipeline"}, 1301 {Name: "just-kidding"}, 1302 })) 1303 }) 1304 1305 It("returns 200", func() { 1306 Expect(response.StatusCode).To(Equal(http.StatusOK)) 1307 }) 1308 }) 1309 1310 Context("when ordering the pipelines fails", func() { 1311 BeforeEach(func() { 1312 fakeTeam.OrderPipelinesReturns(errors.New("welp")) 1313 }) 1314 1315 It("returns 500", func() { 1316 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 1317 }) 1318 }) 1319 1320 Context("when ordering pipeline instances", func() { 1321 BeforeEach(func() { 1322 requestBody = atc.OrderPipelinesRequest{ 1323 {Name: "some-pipeline", InstanceVars: atc.InstanceVars{"branch": "master"}}, 1324 {Name: "some-pipeline", InstanceVars: atc.InstanceVars{"branch": "feature/bar"}}, 1325 {Name: "some-pipeline", InstanceVars: atc.InstanceVars{"branch": "feature/foo"}}, 1326 {Name: "some-pipeline", InstanceVars: atc.InstanceVars{"tag": "0.0.0"}}, 1327 } 1328 }) 1329 1330 It("orders the pipelines", func() { 1331 Expect(fakeTeam.OrderPipelinesCallCount()).To(Equal(1)) 1332 pipelineRefs := fakeTeam.OrderPipelinesArgsForCall(0) 1333 Expect(pipelineRefs).To(Equal([]atc.PipelineRef{ 1334 {Name: "some-pipeline", InstanceVars: atc.InstanceVars{"branch": "master"}}, 1335 {Name: "some-pipeline", InstanceVars: atc.InstanceVars{"branch": "feature/bar"}}, 1336 {Name: "some-pipeline", InstanceVars: atc.InstanceVars{"branch": "feature/foo"}}, 1337 {Name: "some-pipeline", InstanceVars: atc.InstanceVars{"tag": "0.0.0"}}, 1338 })) 1339 }) 1340 1341 It("returns 200", func() { 1342 Expect(response.StatusCode).To(Equal(http.StatusOK)) 1343 }) 1344 }) 1345 1346 }) 1347 1348 Context("when requester does not belong to the team", func() { 1349 BeforeEach(func() { 1350 fakeAccess.IsAuthorizedReturns(false) 1351 }) 1352 1353 It("returns 403", func() { 1354 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 1355 }) 1356 }) 1357 }) 1358 1359 Context("when not authenticated", func() { 1360 BeforeEach(func() { 1361 fakeAccess.IsAuthenticatedReturns(false) 1362 }) 1363 1364 It("returns 401 Unauthorized", func() { 1365 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 1366 }) 1367 }) 1368 }) 1369 1370 Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name/versions-db", func() { 1371 var response *http.Response 1372 1373 JustBeforeEach(func() { 1374 var err error 1375 1376 request, err := http.NewRequest("GET", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/versions-db", nil) 1377 Expect(err).NotTo(HaveOccurred()) 1378 1379 response, err = client.Do(request) 1380 Expect(err).NotTo(HaveOccurred()) 1381 }) 1382 1383 Context("when authenticated", func() { 1384 BeforeEach(func() { 1385 fakeAccess.IsAuthenticatedReturns(true) 1386 fakeAccess.IsAuthorizedReturns(true) 1387 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 1388 fakeTeam.PipelineReturns(dbPipeline, true, nil) 1389 }) 1390 1391 Context("when getting the debug versions db works", func() { 1392 BeforeEach(func() { 1393 scopeID := 789 1394 1395 dbPipeline.LoadDebugVersionsDBReturns( 1396 &atc.DebugVersionsDB{ 1397 ResourceVersions: []atc.DebugResourceVersion{ 1398 { 1399 VersionID: 73, 1400 ResourceID: 127, 1401 CheckOrder: 123, 1402 ScopeID: 111, 1403 }, 1404 }, 1405 BuildOutputs: []atc.DebugBuildOutput{ 1406 { 1407 DebugResourceVersion: atc.DebugResourceVersion{ 1408 VersionID: 73, 1409 ResourceID: 127, 1410 CheckOrder: 123, 1411 ScopeID: 111, 1412 }, 1413 BuildID: 66, 1414 JobID: 13, 1415 }, 1416 }, 1417 BuildInputs: []atc.DebugBuildInput{ 1418 { 1419 DebugResourceVersion: atc.DebugResourceVersion{ 1420 VersionID: 66, 1421 ResourceID: 77, 1422 CheckOrder: 88, 1423 ScopeID: 222, 1424 }, 1425 BuildID: 66, 1426 JobID: 13, 1427 InputName: "some-input-name", 1428 }, 1429 }, 1430 BuildReruns: []atc.DebugBuildRerun{ 1431 { 1432 JobID: 13, 1433 BuildID: 111, 1434 RerunOf: 222, 1435 }, 1436 }, 1437 Jobs: []atc.DebugJob{ 1438 { 1439 ID: 13, 1440 Name: "bad-luck-job", 1441 }, 1442 }, 1443 Resources: []atc.DebugResource{ 1444 { 1445 ID: 127, 1446 Name: "resource-127", 1447 ScopeID: nil, 1448 }, 1449 { 1450 ID: 128, 1451 Name: "resource-128", 1452 ScopeID: &scopeID, 1453 }, 1454 }, 1455 }, 1456 nil, 1457 ) 1458 }) 1459 1460 It("constructs teamDB with provided team name", func() { 1461 Expect(dbTeamFactory.FindTeamCallCount()).To(Equal(1)) 1462 Expect(dbTeamFactory.FindTeamArgsForCall(0)).To(Equal("a-team")) 1463 }) 1464 1465 It("returns 200", func() { 1466 Expect(response.StatusCode).To(Equal(http.StatusOK)) 1467 }) 1468 1469 It("returns application/json", func() { 1470 expectedHeaderEntries := map[string]string{ 1471 "Content-Type": "application/json", 1472 } 1473 Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries)) 1474 }) 1475 1476 It("returns a json representation of all the versions in the pipeline", func() { 1477 body, err := ioutil.ReadAll(response.Body) 1478 Expect(err).NotTo(HaveOccurred()) 1479 1480 Expect(body).To(MatchJSON(`{ 1481 "ResourceVersions": [ 1482 { 1483 "VersionID": 73, 1484 "ResourceID": 127, 1485 "CheckOrder": 123, 1486 "ScopeID": 111 1487 } 1488 ], 1489 "BuildOutputs": [ 1490 { 1491 "VersionID": 73, 1492 "ResourceID": 127, 1493 "BuildID": 66, 1494 "JobID": 13, 1495 "CheckOrder": 123, 1496 "ScopeID": 111 1497 } 1498 ], 1499 "BuildInputs": [ 1500 { 1501 "VersionID": 66, 1502 "ResourceID": 77, 1503 "BuildID": 66, 1504 "JobID": 13, 1505 "CheckOrder": 88, 1506 "ScopeID": 222, 1507 "InputName": "some-input-name" 1508 } 1509 ], 1510 "BuildReruns": [ 1511 { 1512 "JobID": 13, 1513 "BuildID": 111, 1514 "RerunOf": 222 1515 } 1516 ], 1517 "Jobs": [ 1518 { 1519 "ID": 13, 1520 "Name": "bad-luck-job" 1521 } 1522 ], 1523 "Resources": [ 1524 { 1525 "ID": 127, 1526 "Name": "resource-127", 1527 "ScopeID": null 1528 }, 1529 { 1530 "ID": 128, 1531 "Name": "resource-128", 1532 "ScopeID": 789 1533 } 1534 ] 1535 }`)) 1536 }) 1537 }) 1538 1539 Context("when getting the debug versions db fails", func() { 1540 BeforeEach(func() { 1541 dbPipeline.LoadDebugVersionsDBReturns(nil, errors.New("nope")) 1542 }) 1543 1544 It("constructs teamDB with provided team name", func() { 1545 Expect(dbTeamFactory.FindTeamCallCount()).To(Equal(1)) 1546 Expect(dbTeamFactory.FindTeamArgsForCall(0)).To(Equal("a-team")) 1547 }) 1548 1549 It("returns 500", func() { 1550 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 1551 }) 1552 1553 It("does not return application/json", func() { 1554 expectedHeaderEntries := map[string]string{ 1555 "Content-Type": "", 1556 } 1557 Expect(response).ShouldNot(IncludeHeaderEntries(expectedHeaderEntries)) 1558 }) 1559 }) 1560 }) 1561 1562 Context("when not authenticated", func() { 1563 BeforeEach(func() { 1564 fakeAccess.IsAuthenticatedReturns(false) 1565 }) 1566 1567 It("returns 401 Unauthorized", func() { 1568 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 1569 }) 1570 }) 1571 }) 1572 1573 Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/rename", func() { 1574 var response *http.Response 1575 var requestBody string 1576 1577 BeforeEach(func() { 1578 requestBody = `{"name":"some-new-name"}` 1579 }) 1580 1581 JustBeforeEach(func() { 1582 var err error 1583 1584 request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/rename", bytes.NewBufferString(requestBody)) 1585 Expect(err).NotTo(HaveOccurred()) 1586 1587 response, err = client.Do(request) 1588 Expect(err).NotTo(HaveOccurred()) 1589 }) 1590 1591 Context("when authenticated", func() { 1592 BeforeEach(func() { 1593 fakeAccess.IsAuthenticatedReturns(true) 1594 }) 1595 Context("when requester belongs to the team", func() { 1596 BeforeEach(func() { 1597 fakeAccess.IsAuthorizedReturns(true) 1598 1599 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 1600 fakeTeam.PipelineReturns(dbPipeline, true, nil) 1601 }) 1602 1603 It("constructs teamDB with provided team name", func() { 1604 Expect(dbTeamFactory.FindTeamCallCount()).To(Equal(1)) 1605 Expect(dbTeamFactory.FindTeamArgsForCall(0)).To(Equal("a-team")) 1606 }) 1607 1608 It("injects the proper pipeline", func() { 1609 pipelineRef := fakeTeam.PipelineArgsForCall(0) 1610 Expect(pipelineRef).To(Equal(atc.PipelineRef{Name: "a-pipeline"})) 1611 }) 1612 1613 It("returns 200", func() { 1614 Expect(response.StatusCode).To(Equal(http.StatusOK)) 1615 }) 1616 1617 It("renames the pipeline to the name provided", func() { 1618 Expect(dbPipeline.RenameCallCount()).To(Equal(1)) 1619 Expect(dbPipeline.RenameArgsForCall(0)).To(Equal("some-new-name")) 1620 }) 1621 1622 Context("when a warning occurs", func() { 1623 1624 BeforeEach(func() { 1625 requestBody = `{"name":"_some-new-name"}` 1626 }) 1627 1628 It("returns a warning in the response body", func() { 1629 Expect(ioutil.ReadAll(response.Body)).To(MatchJSON(` 1630 { 1631 "warnings": [ 1632 { 1633 "type": "invalid_identifier", 1634 "message": "pipeline: '_some-new-name' is not a valid identifier: must start with a lowercase letter" 1635 } 1636 ] 1637 }`)) 1638 }) 1639 }) 1640 1641 Context("when an error occurs on update", func() { 1642 BeforeEach(func() { 1643 fakeTeam.PipelineReturns(dbPipeline, true, nil) 1644 dbPipeline.RenameReturns(errors.New("whoops")) 1645 }) 1646 1647 It("returns a 500 internal server error", func() { 1648 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 1649 }) 1650 }) 1651 }) 1652 1653 Context("when requester does not belong to the team", func() { 1654 BeforeEach(func() { 1655 fakeAccess.IsAuthorizedReturns(false) 1656 }) 1657 1658 It("returns 403 Forbidden", func() { 1659 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 1660 }) 1661 }) 1662 }) 1663 1664 Context("when not authenticated", func() { 1665 BeforeEach(func() { 1666 fakeAccess.IsAuthenticatedReturns(false) 1667 }) 1668 1669 It("returns 401 Unauthorized", func() { 1670 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 1671 }) 1672 }) 1673 }) 1674 1675 Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name/builds", func() { 1676 var response *http.Response 1677 var queryParams string 1678 1679 JustBeforeEach(func() { 1680 var err error 1681 1682 fakePipeline.NameReturns("some-pipeline") 1683 response, err = client.Get(server.URL + "/api/v1/teams/some-team/pipelines/some-pipeline/builds" + queryParams) 1684 Expect(err).NotTo(HaveOccurred()) 1685 }) 1686 1687 Context("when not authenticated", func() { 1688 BeforeEach(func() { 1689 fakeAccess.IsAuthenticatedReturns(false) 1690 }) 1691 1692 Context("and the pipeline is private", func() { 1693 BeforeEach(func() { 1694 fakePipeline.PublicReturns(false) 1695 }) 1696 1697 It("returns 401", func() { 1698 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 1699 }) 1700 }) 1701 1702 Context("and the pipeline is public", func() { 1703 BeforeEach(func() { 1704 fakePipeline.PublicReturns(true) 1705 }) 1706 1707 It("returns 200 OK", func() { 1708 Expect(response.StatusCode).To(Equal(http.StatusOK)) 1709 }) 1710 }) 1711 }) 1712 1713 Context("when authorized", func() { 1714 BeforeEach(func() { 1715 fakeAccess.IsAuthenticatedReturns(true) 1716 fakeAccess.IsAuthorizedReturns(true) 1717 }) 1718 1719 Context("when no params are passed", func() { 1720 It("does not set defaults for since and until", func() { 1721 Expect(fakePipeline.BuildsCallCount()).To(Equal(1)) 1722 1723 page := fakePipeline.BuildsArgsForCall(0) 1724 Expect(page).To(Equal(db.Page{ 1725 Limit: 100, 1726 })) 1727 }) 1728 }) 1729 1730 Context("when all the params are passed", func() { 1731 BeforeEach(func() { 1732 queryParams = "?from=2&to=3&limit=8" 1733 }) 1734 1735 It("passes them through", func() { 1736 Expect(fakePipeline.BuildsCallCount()).To(Equal(1)) 1737 1738 page := fakePipeline.BuildsArgsForCall(0) 1739 Expect(page).To(Equal(db.Page{ 1740 From: db.NewIntPtr(2), 1741 To: db.NewIntPtr(3), 1742 Limit: 8, 1743 })) 1744 }) 1745 }) 1746 1747 Context("when getting the builds succeeds", func() { 1748 var returnedBuilds []db.Build 1749 1750 BeforeEach(func() { 1751 queryParams = "?since=5&limit=2" 1752 1753 build1 := new(dbfakes.FakeBuild) 1754 build1.IDReturns(4) 1755 build1.NameReturns("2") 1756 build1.JobNameReturns("some-job") 1757 build1.PipelineNameReturns("some-pipeline") 1758 build1.TeamNameReturns("some-team") 1759 build1.StatusReturns(db.BuildStatusStarted) 1760 build1.StartTimeReturns(time.Unix(1, 0)) 1761 build1.EndTimeReturns(time.Unix(100, 0)) 1762 1763 build2 := new(dbfakes.FakeBuild) 1764 build2.IDReturns(2) 1765 build2.NameReturns("1") 1766 build2.JobNameReturns("some-job") 1767 build2.PipelineNameReturns("some-pipeline") 1768 build2.TeamNameReturns("some-team") 1769 build2.StatusReturns(db.BuildStatusSucceeded) 1770 build2.StartTimeReturns(time.Unix(101, 0)) 1771 build2.EndTimeReturns(time.Unix(200, 0)) 1772 1773 returnedBuilds = []db.Build{build1, build2} 1774 fakePipeline.BuildsReturns(returnedBuilds, db.Pagination{}, nil) 1775 }) 1776 1777 It("returns 200 OK", func() { 1778 Expect(response.StatusCode).To(Equal(http.StatusOK)) 1779 }) 1780 1781 It("returns Content-Type 'application/json'", func() { 1782 expectedHeaderEntries := map[string]string{ 1783 "Content-Type": "application/json", 1784 } 1785 Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries)) 1786 }) 1787 1788 It("returns the builds", func() { 1789 body, err := ioutil.ReadAll(response.Body) 1790 Expect(err).NotTo(HaveOccurred()) 1791 1792 Expect(body).To(MatchJSON(`[ 1793 { 1794 "id": 4, 1795 "name": "2", 1796 "job_name": "some-job", 1797 "status": "started", 1798 "api_url": "/api/v1/builds/4", 1799 "pipeline_name":"some-pipeline", 1800 "team_name": "some-team", 1801 "start_time": 1, 1802 "end_time": 100 1803 }, 1804 { 1805 "id": 2, 1806 "name": "1", 1807 "job_name": "some-job", 1808 "status": "succeeded", 1809 "api_url": "/api/v1/builds/2", 1810 "pipeline_name": "some-pipeline", 1811 "team_name": "some-team", 1812 "start_time": 101, 1813 "end_time": 200 1814 } 1815 ]`)) 1816 }) 1817 1818 Context("when next/previous pages are available", func() { 1819 BeforeEach(func() { 1820 fakePipeline.BuildsReturns(returnedBuilds, db.Pagination{ 1821 Newer: &db.Page{From: db.NewIntPtr(4), Limit: 2}, 1822 Older: &db.Page{To: db.NewIntPtr(2), Limit: 2}, 1823 }, nil) 1824 }) 1825 1826 It("returns Link headers per rfc5988", func() { 1827 Expect(response.Header["Link"]).To(ConsistOf([]string{ 1828 fmt.Sprintf(`<%s/api/v1/teams/some-team/pipelines/some-pipeline/builds?from=4&limit=2>; rel="previous"`, externalURL), 1829 fmt.Sprintf(`<%s/api/v1/teams/some-team/pipelines/some-pipeline/builds?to=2&limit=2>; rel="next"`, externalURL), 1830 })) 1831 }) 1832 1833 Context("and pipeline is instanced", func() { 1834 BeforeEach(func() { 1835 fakePipeline.InstanceVarsReturns(atc.InstanceVars{"branch": "master"}) 1836 }) 1837 1838 It("returns Link headers per rfc5988", func() { 1839 link := fmt.Sprintf(`<%s/api/v1/teams/some-team/pipelines/some-pipeline/builds?`, externalURL) 1840 Expect(response.Header["Link"]).To(ConsistOf([]string{ 1841 link + `to=2&limit=2&instance_vars=%7B%22branch%22%3A%22master%22%7D>; rel="next"`, 1842 link + `from=4&limit=2&instance_vars=%7B%22branch%22%3A%22master%22%7D>; rel="previous"`, 1843 })) 1844 }) 1845 }) 1846 }) 1847 }) 1848 1849 Context("when getting the build fails", func() { 1850 BeforeEach(func() { 1851 fakePipeline.BuildsReturns(nil, db.Pagination{}, errors.New("oh no!")) 1852 }) 1853 1854 It("returns 404 Not Found", func() { 1855 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 1856 }) 1857 }) 1858 }) 1859 }) 1860 1861 Describe("POST /api/v1/teams/:team_name/pipelines/:pipeline_name/builds", func() { 1862 var plan atc.Plan 1863 var response *http.Response 1864 1865 BeforeEach(func() { 1866 plan = atc.Plan{ 1867 Task: &atc.TaskPlan{ 1868 Config: &atc.TaskConfig{ 1869 Run: atc.TaskRunConfig{ 1870 Path: "ls", 1871 }, 1872 }, 1873 }, 1874 } 1875 }) 1876 1877 JustBeforeEach(func() { 1878 reqPayload, err := json.Marshal(plan) 1879 Expect(err).NotTo(HaveOccurred()) 1880 1881 req, err := http.NewRequest("POST", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/builds", bytes.NewBuffer(reqPayload)) 1882 Expect(err).NotTo(HaveOccurred()) 1883 1884 req.Header.Set("Content-Type", "application/json") 1885 1886 response, err = client.Do(req) 1887 Expect(err).NotTo(HaveOccurred()) 1888 }) 1889 1890 Context("when not authenticated", func() { 1891 BeforeEach(func() { 1892 fakeAccess.IsAuthenticatedReturns(false) 1893 }) 1894 1895 It("returns 401", func() { 1896 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 1897 }) 1898 1899 It("does not trigger a build", func() { 1900 Expect(dbPipeline.CreateOneOffBuildCallCount()).To(BeZero()) 1901 }) 1902 }) 1903 1904 Context("when authenticated", func() { 1905 BeforeEach(func() { 1906 fakeAccess.IsAuthenticatedReturns(true) 1907 }) 1908 1909 Context("when not authorized", func() { 1910 BeforeEach(func() { 1911 fakeAccess.IsAuthorizedReturns(false) 1912 }) 1913 1914 It("returns 403", func() { 1915 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 1916 }) 1917 }) 1918 1919 Context("when authorized", func() { 1920 BeforeEach(func() { 1921 fakeAccess.IsAuthorizedReturns(true) 1922 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 1923 fakeTeam.PipelineReturns(dbPipeline, true, nil) 1924 }) 1925 1926 Context("when creating a started build fails", func() { 1927 BeforeEach(func() { 1928 dbPipeline.CreateStartedBuildReturns(nil, errors.New("oh no!")) 1929 }) 1930 1931 It("returns 500 Internal Server Error", func() { 1932 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 1933 }) 1934 }) 1935 1936 Context("when creating a started build succeeds", func() { 1937 var fakeBuild *dbfakes.FakeBuild 1938 1939 BeforeEach(func() { 1940 fakeBuild = new(dbfakes.FakeBuild) 1941 fakeBuild.IDReturns(42) 1942 fakeBuild.NameReturns("1") 1943 fakeBuild.TeamNameReturns("some-team") 1944 fakeBuild.StatusReturns("started") 1945 fakeBuild.StartTimeReturns(time.Unix(1, 0)) 1946 fakeBuild.EndTimeReturns(time.Unix(100, 0)) 1947 fakeBuild.ReapTimeReturns(time.Unix(200, 0)) 1948 1949 dbPipeline.CreateStartedBuildReturns(fakeBuild, nil) 1950 }) 1951 1952 It("returns 201 Created", func() { 1953 Expect(response.StatusCode).To(Equal(http.StatusCreated)) 1954 }) 1955 1956 It("returns Content-Type 'application/json'", func() { 1957 expectedHeaderEntries := map[string]string{ 1958 "Content-Type": "application/json", 1959 } 1960 Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries)) 1961 }) 1962 1963 It("creates a started build", func() { 1964 Expect(dbPipeline.CreateStartedBuildCallCount()).To(Equal(1)) 1965 Expect(dbPipeline.CreateStartedBuildArgsForCall(0)).To(Equal(plan)) 1966 }) 1967 1968 It("returns the created build", func() { 1969 body, err := ioutil.ReadAll(response.Body) 1970 Expect(err).NotTo(HaveOccurred()) 1971 1972 Expect(body).To(MatchJSON(`{ 1973 "id": 42, 1974 "name": "1", 1975 "team_name": "some-team", 1976 "status": "started", 1977 "api_url": "/api/v1/builds/42", 1978 "start_time": 1, 1979 "end_time": 100, 1980 "reap_time": 200 1981 }`)) 1982 }) 1983 }) 1984 }) 1985 }) 1986 }) 1987 })