1package api_test
2
3import (
4	"errors"
5	"io/ioutil"
6	"net/http"
7	"time"
8
9	"github.com/concourse/concourse/atc"
10	"github.com/concourse/concourse/atc/db"
11	"github.com/concourse/concourse/atc/db/dbfakes"
12	. "github.com/concourse/concourse/atc/testhelpers"
13	"github.com/tedsuo/rata"
14
15	. "github.com/onsi/ginkgo"
16	. "github.com/onsi/gomega"
17)
18
19var _ = Describe("cc.xml", func() {
20	var (
21		requestGenerator *rata.RequestGenerator
22	)
23
24	BeforeEach(func() {
25		requestGenerator = rata.NewRequestGenerator(server.URL, atc.Routes)
26	})
27
28	Describe("GET /api/v1/teams/:team_name/cc.xml", func() {
29		var response *http.Response
30
31		JustBeforeEach(func() {
32			req, err := requestGenerator.CreateRequest(atc.GetCC, rata.Params{
33				"team_name": "a-team",
34			}, nil)
35			Expect(err).NotTo(HaveOccurred())
36
37			response, err = client.Do(req)
38			Expect(err).NotTo(HaveOccurred())
39		})
40
41		Context("when authorized", func() {
42			BeforeEach(func() {
43				fakeAccess.IsAuthenticatedReturns(true)
44				fakeAccess.IsAuthorizedReturns(true)
45			})
46
47			Context("when the team is found", func() {
48				var fakeTeam *dbfakes.FakeTeam
49				BeforeEach(func() {
50					fakeTeam = new(dbfakes.FakeTeam)
51					fakeTeam.NameReturns("a-team")
52					dbTeamFactory.FindTeamReturns(fakeTeam, true, nil)
53				})
54
55				Context("when a pipeline is found", func() {
56					var fakePipeline *dbfakes.FakePipeline
57					BeforeEach(func() {
58						fakePipeline = new(dbfakes.FakePipeline)
59						fakeTeam.PipelinesReturns([]db.Pipeline{
60							fakePipeline,
61						}, nil)
62					})
63
64					Context("when a job is found", func() {
65						var endTime time.Time
66						BeforeEach(func() {
67							fakePipeline.DashboardReturns(atc.Dashboard{
68								{
69									Name:         "some-job",
70									PipelineName: "something-else",
71									TeamName:     "a-team",
72								},
73							}, nil)
74
75							endTime, _ = time.Parse(time.RFC3339, "2018-11-04T21:26:38Z")
76						})
77
78						Context("when the last build is successful", func() {
79							BeforeEach(func() {
80								fakePipeline.DashboardReturns(atc.Dashboard{
81									{
82										Name:         "some-job",
83										PipelineName: "something-else",
84										TeamName:     "a-team",
85										FinishedBuild: &atc.DashboardBuild{
86											Name:    "42",
87											Status:  "succeeded",
88											EndTime: endTime,
89										},
90									},
91								}, nil)
92							})
93
94							It("returns 200", func() {
95								Expect(response.StatusCode).To(Equal(http.StatusOK))
96							})
97
98							It("returns Content-Type 'application/xml'", func() {
99								expectedHeaderEntries := map[string]string{
100									"Content-Type": "application/xml",
101								}
102								Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
103							})
104
105							It("returns the CC.xml", func() {
106								body, err := ioutil.ReadAll(response.Body)
107								Expect(err).NotTo(HaveOccurred())
108
109								Expect(body).To(MatchXML(`
110<Projects>
111  <Project activity="Sleeping" lastBuildLabel="42" lastBuildStatus="Success" lastBuildTime="2018-11-04T21:26:38Z" name="something-else/some-job" webUrl="https://example.com/teams/a-team/pipelines/something-else/jobs/some-job"/>
112</Projects>
113`))
114							})
115						})
116
117						Context("when the last build is aborted", func() {
118							BeforeEach(func() {
119								fakePipeline.DashboardReturns(atc.Dashboard{
120									{
121										Name:         "some-job",
122										PipelineName: "something-else",
123										TeamName:     "a-team",
124										FinishedBuild: &atc.DashboardBuild{
125											Name:    "42",
126											Status:  "aborted",
127											EndTime: endTime,
128										},
129									},
130								}, nil)
131							})
132
133							It("returns the CC.xml", func() {
134								body, err := ioutil.ReadAll(response.Body)
135								Expect(err).NotTo(HaveOccurred())
136
137								Expect(body).To(MatchXML(`
138<Projects>
139  <Project activity="Sleeping" lastBuildLabel="42" lastBuildStatus="Exception" lastBuildTime="2018-11-04T21:26:38Z" name="something-else/some-job" webUrl="https://example.com/teams/a-team/pipelines/something-else/jobs/some-job"/>
140</Projects>
141`))
142							})
143						})
144
145						Context("when the last build is errored", func() {
146							BeforeEach(func() {
147								fakePipeline.DashboardReturns(atc.Dashboard{
148									{
149										Name:         "some-job",
150										PipelineName: "something-else",
151										TeamName:     "a-team",
152										FinishedBuild: &atc.DashboardBuild{
153											Name:    "42",
154											Status:  "errored",
155											EndTime: endTime,
156										},
157									},
158								}, nil)
159							})
160
161							It("returns the CC.xml", func() {
162								body, err := ioutil.ReadAll(response.Body)
163								Expect(err).NotTo(HaveOccurred())
164
165								Expect(body).To(MatchXML(`
166<Projects>
167  <Project activity="Sleeping" lastBuildLabel="42" lastBuildStatus="Exception" lastBuildTime="2018-11-04T21:26:38Z" name="something-else/some-job" webUrl="https://example.com/teams/a-team/pipelines/something-else/jobs/some-job"/>
168</Projects>
169`))
170							})
171						})
172
173						Context("when the last build is failed", func() {
174							BeforeEach(func() {
175								fakePipeline.DashboardReturns(atc.Dashboard{
176									{
177										Name:         "some-job",
178										PipelineName: "something-else",
179										TeamName:     "a-team",
180										FinishedBuild: &atc.DashboardBuild{
181											Name:    "42",
182											Status:  "failed",
183											EndTime: endTime,
184										},
185									},
186								}, nil)
187							})
188
189							It("returns the CC.xml", func() {
190								body, err := ioutil.ReadAll(response.Body)
191								Expect(err).NotTo(HaveOccurred())
192
193								Expect(body).To(MatchXML(`
194<Projects>
195  <Project activity="Sleeping" lastBuildLabel="42" lastBuildStatus="Failure" lastBuildTime="2018-11-04T21:26:38Z" name="something-else/some-job" webUrl="https://example.com/teams/a-team/pipelines/something-else/jobs/some-job"/>
196</Projects>
197`))
198							})
199						})
200
201						Context("when a next build exists", func() {
202							BeforeEach(func() {
203								fakePipeline.DashboardReturns(atc.Dashboard{
204									{
205										Name:         "some-job",
206										PipelineName: "something-else",
207										TeamName:     "a-team",
208										FinishedBuild: &atc.DashboardBuild{
209											Name:    "42",
210											Status:  "succeeded",
211											EndTime: endTime,
212										},
213										NextBuild: &atc.DashboardBuild{},
214									},
215								}, nil)
216							})
217
218							It("returns the CC.xml", func() {
219								body, err := ioutil.ReadAll(response.Body)
220								Expect(err).NotTo(HaveOccurred())
221
222								Expect(body).To(MatchXML(`
223<Projects>
224  <Project activity="Building" lastBuildLabel="42" lastBuildStatus="Success" lastBuildTime="2018-11-04T21:26:38Z" name="something-else/some-job" webUrl="https://example.com/teams/a-team/pipelines/something-else/jobs/some-job"/>
225</Projects>
226`))
227							})
228						})
229
230						Context("when no last build exists", func() {
231							It("returns the CC.xml without the job", func() {
232								body, err := ioutil.ReadAll(response.Body)
233								Expect(err).NotTo(HaveOccurred())
234
235								Expect(body).To(MatchXML("<Projects></Projects>"))
236							})
237						})
238					})
239
240					Context("when no job is found", func() {
241						BeforeEach(func() {
242							fakePipeline.DashboardReturns(atc.Dashboard{}, nil)
243						})
244
245						It("returns 200", func() {
246							Expect(response.StatusCode).To(Equal(http.StatusOK))
247						})
248
249						It("returns the CC.xml", func() {
250							body, err := ioutil.ReadAll(response.Body)
251							Expect(err).NotTo(HaveOccurred())
252
253							Expect(body).To(MatchXML("<Projects></Projects>"))
254						})
255					})
256
257					Context("when finding the jobs fails", func() {
258						BeforeEach(func() {
259							fakePipeline.DashboardReturns(nil, errors.New("failed"))
260						})
261
262						It("returns 500", func() {
263							Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
264						})
265					})
266				})
267
268				Context("when no pipeline is found", func() {
269					BeforeEach(func() {
270						fakeTeam.PipelinesReturns([]db.Pipeline{}, nil)
271					})
272
273					It("returns 200", func() {
274						Expect(response.StatusCode).To(Equal(http.StatusOK))
275					})
276
277					It("returns the CC.xml", func() {
278						body, err := ioutil.ReadAll(response.Body)
279						Expect(err).NotTo(HaveOccurred())
280
281						Expect(body).To(MatchXML("<Projects></Projects>"))
282					})
283				})
284
285				Context("when getting the pipelines fails", func() {
286					BeforeEach(func() {
287						fakeTeam.PipelinesReturns(nil, errors.New("failed"))
288					})
289
290					It("returns 500", func() {
291						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
292					})
293				})
294			})
295
296			Context("when the team is not found", func() {
297				BeforeEach(func() {
298					dbTeamFactory.FindTeamReturns(nil, false, nil)
299				})
300
301				It("returns 404", func() {
302					Expect(response.StatusCode).To(Equal(http.StatusNotFound))
303				})
304			})
305
306			Context("when finding the team fails", func() {
307				BeforeEach(func() {
308					dbTeamFactory.FindTeamReturns(nil, false, errors.New("failed"))
309				})
310
311				It("returns 500", func() {
312					Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
313				})
314			})
315		})
316
317		Context("when not authenticated", func() {
318			BeforeEach(func() {
319				fakeAccess.IsAuthenticatedReturns(false)
320			})
321
322			It("returns 401", func() {
323				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
324			})
325		})
326	})
327})
328