loading up the forgejo repo on tangled to test page performance
1// Copyright 2017 The Gitea Authors. All rights reserved.
2// SPDX-License-Identifier: MIT
3
4package integration
5
6import (
7 "fmt"
8 "net/http"
9 "net/url"
10 "strconv"
11 "sync"
12 "testing"
13 "time"
14
15 auth_model "forgejo.org/models/auth"
16 "forgejo.org/models/db"
17 issues_model "forgejo.org/models/issues"
18 repo_model "forgejo.org/models/repo"
19 "forgejo.org/models/unittest"
20 user_model "forgejo.org/models/user"
21 "forgejo.org/modules/setting"
22 api "forgejo.org/modules/structs"
23 "forgejo.org/tests"
24
25 "github.com/stretchr/testify/assert"
26 "github.com/stretchr/testify/require"
27)
28
29func TestAPIListIssues(t *testing.T) {
30 defer tests.PrepareTestEnv(t)()
31
32 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
33 owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
34
35 session := loginUser(t, owner.Name)
36 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadIssue)
37 link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/issues", owner.Name, repo.Name))
38
39 link.RawQuery = url.Values{"token": {token}, "state": {"all"}}.Encode()
40 resp := MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
41 var apiIssues []*api.Issue
42 DecodeJSON(t, resp, &apiIssues)
43 assert.Len(t, apiIssues, unittest.GetCount(t, &issues_model.Issue{RepoID: repo.ID}))
44 for _, apiIssue := range apiIssues {
45 unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: apiIssue.ID, RepoID: repo.ID})
46 }
47
48 // test milestone filter
49 link.RawQuery = url.Values{"token": {token}, "state": {"all"}, "type": {"all"}, "milestones": {"ignore,milestone1,3,4"}}.Encode()
50 resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
51 DecodeJSON(t, resp, &apiIssues)
52 if assert.Len(t, apiIssues, 2) {
53 assert.EqualValues(t, 3, apiIssues[0].Milestone.ID)
54 assert.EqualValues(t, 1, apiIssues[1].Milestone.ID)
55 }
56
57 link.RawQuery = url.Values{"token": {token}, "state": {"all"}, "created_by": {"user2"}}.Encode()
58 resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
59 DecodeJSON(t, resp, &apiIssues)
60 if assert.Len(t, apiIssues, 1) {
61 assert.EqualValues(t, 5, apiIssues[0].ID)
62 }
63
64 link.RawQuery = url.Values{"token": {token}, "state": {"all"}, "assigned_by": {"user1"}}.Encode()
65 resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
66 DecodeJSON(t, resp, &apiIssues)
67 if assert.Len(t, apiIssues, 1) {
68 assert.EqualValues(t, 1, apiIssues[0].ID)
69 }
70
71 link.RawQuery = url.Values{"token": {token}, "state": {"all"}, "mentioned_by": {"user4"}}.Encode()
72 resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
73 DecodeJSON(t, resp, &apiIssues)
74 if assert.Len(t, apiIssues, 1) {
75 assert.EqualValues(t, 1, apiIssues[0].ID)
76 }
77
78 t.Run("Sort", func(t *testing.T) {
79 defer tests.PrintCurrentTest(t)()
80
81 link.RawQuery = url.Values{"token": {token}, "sort": {"oldest"}}.Encode()
82 resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
83 DecodeJSON(t, resp, &apiIssues)
84 if assert.Len(t, apiIssues, 4) {
85 assert.EqualValues(t, 1, apiIssues[0].ID)
86 assert.EqualValues(t, 2, apiIssues[1].ID)
87 assert.EqualValues(t, 3, apiIssues[2].ID)
88 assert.EqualValues(t, 11, apiIssues[3].ID)
89 }
90
91 link.RawQuery = url.Values{"token": {token}, "sort": {"newest"}}.Encode()
92 resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
93 DecodeJSON(t, resp, &apiIssues)
94 if assert.Len(t, apiIssues, 4) {
95 assert.EqualValues(t, 11, apiIssues[0].ID)
96 assert.EqualValues(t, 3, apiIssues[1].ID)
97 assert.EqualValues(t, 2, apiIssues[2].ID)
98 assert.EqualValues(t, 1, apiIssues[3].ID)
99 }
100
101 link.RawQuery = url.Values{"token": {token}, "sort": {"recentupdate"}}.Encode()
102 resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
103 DecodeJSON(t, resp, &apiIssues)
104 if assert.Len(t, apiIssues, 4) {
105 assert.EqualValues(t, 11, apiIssues[0].ID)
106 assert.EqualValues(t, 1, apiIssues[1].ID)
107 assert.EqualValues(t, 2, apiIssues[2].ID)
108 assert.EqualValues(t, 3, apiIssues[3].ID)
109 }
110
111 link.RawQuery = url.Values{"token": {token}, "sort": {"leastupdate"}}.Encode()
112 resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
113 DecodeJSON(t, resp, &apiIssues)
114 if assert.Len(t, apiIssues, 4) {
115 assert.EqualValues(t, 3, apiIssues[0].ID)
116 assert.EqualValues(t, 2, apiIssues[1].ID)
117 assert.EqualValues(t, 1, apiIssues[2].ID)
118 assert.EqualValues(t, 11, apiIssues[3].ID)
119 }
120 })
121}
122
123func TestAPIListIssuesPublicOnly(t *testing.T) {
124 defer tests.PrepareTestEnv(t)()
125
126 repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
127 owner1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo1.OwnerID})
128
129 session := loginUser(t, owner1.Name)
130 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadIssue)
131 link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/issues", owner1.Name, repo1.Name))
132 link.RawQuery = url.Values{"state": {"all"}}.Encode()
133 req := NewRequest(t, "GET", link.String()).AddTokenAuth(token)
134 MakeRequest(t, req, http.StatusOK)
135
136 repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
137 owner2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo2.OwnerID})
138
139 session = loginUser(t, owner2.Name)
140 token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadIssue)
141 link, _ = url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/issues", owner2.Name, repo2.Name))
142 link.RawQuery = url.Values{"state": {"all"}}.Encode()
143 req = NewRequest(t, "GET", link.String()).AddTokenAuth(token)
144 MakeRequest(t, req, http.StatusOK)
145
146 publicOnlyToken := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadIssue, auth_model.AccessTokenScopePublicOnly)
147 req = NewRequest(t, "GET", link.String()).AddTokenAuth(publicOnlyToken)
148 MakeRequest(t, req, http.StatusForbidden)
149}
150
151func TestAPICreateIssue(t *testing.T) {
152 defer tests.PrepareTestEnv(t)()
153 const body, title = "apiTestBody", "apiTestTitle"
154
155 repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
156 owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID})
157
158 session := loginUser(t, owner.Name)
159 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
160 urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues?state=all", owner.Name, repoBefore.Name)
161 req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateIssueOption{
162 Body: body,
163 Title: title,
164 Assignee: owner.Name,
165 }).AddTokenAuth(token)
166 resp := MakeRequest(t, req, http.StatusCreated)
167 var apiIssue api.Issue
168 DecodeJSON(t, resp, &apiIssue)
169 assert.Equal(t, body, apiIssue.Body)
170 assert.Equal(t, title, apiIssue.Title)
171
172 unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{
173 RepoID: repoBefore.ID,
174 AssigneeID: owner.ID,
175 Content: body,
176 Title: title,
177 })
178
179 repoAfter := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
180 assert.Equal(t, repoBefore.NumIssues+1, repoAfter.NumIssues)
181 assert.Equal(t, repoBefore.NumClosedIssues, repoAfter.NumClosedIssues)
182}
183
184func TestAPICreateIssueParallel(t *testing.T) {
185 defer tests.PrepareTestEnv(t)()
186 const body, title = "apiTestBody", "apiTestTitle"
187
188 repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
189 owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID})
190
191 session := loginUser(t, owner.Name)
192 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
193 urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues?state=all", owner.Name, repoBefore.Name)
194
195 var wg sync.WaitGroup
196 for i := 0; i < 10; i++ {
197 wg.Add(1)
198 go func(parentT *testing.T, i int) {
199 parentT.Run(fmt.Sprintf("ParallelCreateIssue_%d", i), func(t *testing.T) {
200 newTitle := title + strconv.Itoa(i)
201 newBody := body + strconv.Itoa(i)
202 req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateIssueOption{
203 Body: newBody,
204 Title: newTitle,
205 Assignee: owner.Name,
206 }).AddTokenAuth(token)
207 resp := MakeRequest(t, req, http.StatusCreated)
208 var apiIssue api.Issue
209 DecodeJSON(t, resp, &apiIssue)
210 assert.Equal(t, newBody, apiIssue.Body)
211 assert.Equal(t, newTitle, apiIssue.Title)
212
213 unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{
214 RepoID: repoBefore.ID,
215 AssigneeID: owner.ID,
216 Content: newBody,
217 Title: newTitle,
218 })
219
220 wg.Done()
221 })
222 }(t, i)
223 }
224 wg.Wait()
225}
226
227func TestAPIEditIssue(t *testing.T) {
228 defer tests.PrepareTestEnv(t)()
229
230 issueBefore := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10})
231 repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID})
232 owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID})
233 require.NoError(t, issueBefore.LoadAttributes(db.DefaultContext))
234 assert.Equal(t, int64(1019307200), int64(issueBefore.DeadlineUnix))
235 assert.Equal(t, api.StateOpen, issueBefore.State())
236
237 session := loginUser(t, owner.Name)
238 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
239
240 // update values of issue
241 issueState := "closed"
242 removeDeadline := true
243 milestone := int64(4)
244 body := "new content!"
245 title := "new title from api set"
246
247 urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d", owner.Name, repoBefore.Name, issueBefore.Index)
248 req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
249 State: &issueState,
250 RemoveDeadline: &removeDeadline,
251 Milestone: &milestone,
252 Body: &body,
253 Title: title,
254
255 // ToDo change more
256 }).AddTokenAuth(token)
257 resp := MakeRequest(t, req, http.StatusCreated)
258 var apiIssue api.Issue
259 DecodeJSON(t, resp, &apiIssue)
260
261 issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10})
262 repoAfter := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID})
263
264 // check comment history
265 unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: issueAfter.ID, OldTitle: issueBefore.Title, NewTitle: title})
266 unittest.AssertExistsAndLoadBean(t, &issues_model.ContentHistory{IssueID: issueAfter.ID, ContentText: body, IsFirstCreated: false})
267
268 // check deleted user
269 assert.Equal(t, int64(500), issueAfter.PosterID)
270 require.NoError(t, issueAfter.LoadAttributes(db.DefaultContext))
271 assert.Equal(t, int64(-1), issueAfter.PosterID)
272 assert.Equal(t, int64(-1), issueBefore.PosterID)
273 assert.Equal(t, int64(-1), apiIssue.Poster.ID)
274
275 // check repo change
276 assert.Equal(t, repoBefore.NumClosedIssues+1, repoAfter.NumClosedIssues)
277
278 // API response
279 assert.Equal(t, api.StateClosed, apiIssue.State)
280 assert.Equal(t, milestone, apiIssue.Milestone.ID)
281 assert.Equal(t, body, apiIssue.Body)
282 assert.Nil(t, apiIssue.Deadline)
283 assert.Equal(t, title, apiIssue.Title)
284
285 // in database
286 assert.Equal(t, api.StateClosed, issueAfter.State())
287 assert.Equal(t, milestone, issueAfter.MilestoneID)
288 assert.Equal(t, int64(0), int64(issueAfter.DeadlineUnix))
289 assert.Equal(t, body, issueAfter.Content)
290 assert.Equal(t, title, issueAfter.Title)
291
292 // verify the idempotency of state, milestone, body and title changes
293 req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
294 State: &issueState,
295 Milestone: &milestone,
296 Body: &body,
297 Title: title,
298 }).AddTokenAuth(token)
299 resp = MakeRequest(t, req, http.StatusCreated)
300 var apiIssueIdempotent api.Issue
301 DecodeJSON(t, resp, &apiIssueIdempotent)
302 assert.Equal(t, apiIssue.State, apiIssueIdempotent.State)
303 assert.Equal(t, apiIssue.Milestone.Title, apiIssueIdempotent.Milestone.Title)
304 assert.Equal(t, apiIssue.Body, apiIssueIdempotent.Body)
305 assert.Equal(t, apiIssue.Title, apiIssueIdempotent.Title)
306}
307
308func TestAPIEditIssueAutoDate(t *testing.T) {
309 defer tests.PrepareTestEnv(t)()
310
311 issueBefore := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 13})
312 repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID})
313 owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID})
314 require.NoError(t, issueBefore.LoadAttributes(db.DefaultContext))
315
316 t.Run("WithAutoDate", func(t *testing.T) {
317 defer tests.PrintCurrentTest(t)()
318
319 // User2 is not owner, but can update the 'public' issue with auto date
320 session := loginUser(t, "user2")
321 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
322 urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d", owner.Name, repoBefore.Name, issueBefore.Index)
323
324 body := "new content!"
325 req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
326 Body: &body,
327 }).AddTokenAuth(token)
328 resp := MakeRequest(t, req, http.StatusCreated)
329 var apiIssue api.Issue
330 DecodeJSON(t, resp, &apiIssue)
331
332 // the execution of the API call supposedly lasted less than one minute
333 updatedSince := time.Since(apiIssue.Updated)
334 assert.LessOrEqual(t, updatedSince, time.Minute)
335
336 issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issueBefore.ID})
337 updatedSince = time.Since(issueAfter.UpdatedUnix.AsTime())
338 assert.LessOrEqual(t, updatedSince, time.Minute)
339 })
340
341 t.Run("WithUpdateDate", func(t *testing.T) {
342 defer tests.PrintCurrentTest(t)()
343
344 // User1 is admin, and so can update the issue without auto date
345 session := loginUser(t, "user1")
346 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
347 urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d", owner.Name, repoBefore.Name, issueBefore.Index)
348
349 body := "new content, with updated time"
350 updatedAt := time.Now().Add(-time.Hour).Truncate(time.Second)
351 req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
352 Body: &body,
353 Updated: &updatedAt,
354 }).AddTokenAuth(token)
355 resp := MakeRequest(t, req, http.StatusCreated)
356 var apiIssue api.Issue
357 DecodeJSON(t, resp, &apiIssue)
358
359 // dates are converted into the same tz, in order to compare them
360 utcTZ, _ := time.LoadLocation("UTC")
361 assert.Equal(t, updatedAt.In(utcTZ), apiIssue.Updated.In(utcTZ))
362
363 issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issueBefore.ID})
364 assert.Equal(t, updatedAt.In(utcTZ), issueAfter.UpdatedUnix.AsTime().In(utcTZ))
365 })
366
367 t.Run("WithoutPermission", func(t *testing.T) {
368 defer tests.PrintCurrentTest(t)()
369
370 // User2 is not owner nor admin, and so can't update the issue without auto date
371 session := loginUser(t, "user2")
372 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
373 urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d", owner.Name, repoBefore.Name, issueBefore.Index)
374
375 body := "new content, with updated time"
376 updatedAt := time.Now().Add(-time.Hour).Truncate(time.Second)
377 req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
378 Body: &body,
379 Updated: &updatedAt,
380 }).AddTokenAuth(token)
381 resp := MakeRequest(t, req, http.StatusForbidden)
382 var apiError api.APIError
383 DecodeJSON(t, resp, &apiError)
384
385 assert.Equal(t, "user needs to have admin or owner right", apiError.Message)
386 })
387}
388
389func TestAPIEditIssueMilestoneAutoDate(t *testing.T) {
390 defer tests.PrepareTestEnv(t)()
391
392 issueBefore := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
393 repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID})
394
395 owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID})
396 require.NoError(t, issueBefore.LoadAttributes(db.DefaultContext))
397
398 session := loginUser(t, owner.Name)
399 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
400 urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d", owner.Name, repoBefore.Name, issueBefore.Index)
401
402 t.Run("WithAutoDate", func(t *testing.T) {
403 defer tests.PrintCurrentTest(t)()
404
405 milestone := int64(1)
406 req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
407 Milestone: &milestone,
408 }).AddTokenAuth(token)
409 MakeRequest(t, req, http.StatusCreated)
410
411 // the execution of the API call supposedly lasted less than one minute
412 milestoneAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: milestone})
413 updatedSince := time.Since(milestoneAfter.UpdatedUnix.AsTime())
414 assert.LessOrEqual(t, updatedSince, time.Minute)
415 })
416
417 t.Run("WithPostUpdateDate", func(t *testing.T) {
418 defer tests.PrintCurrentTest(t)()
419
420 // Note: the updated_unix field of the test Milestones is set to NULL
421 // Hence, any date is higher than the Milestone's updated date
422 updatedAt := time.Now().Add(-time.Hour).Truncate(time.Second)
423 milestone := int64(2)
424 req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
425 Milestone: &milestone,
426 Updated: &updatedAt,
427 }).AddTokenAuth(token)
428 MakeRequest(t, req, http.StatusCreated)
429
430 // the milestone date should be set to 'updatedAt'
431 // dates are converted into the same tz, in order to compare them
432 utcTZ, _ := time.LoadLocation("UTC")
433 milestoneAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: milestone})
434 assert.Equal(t, updatedAt.In(utcTZ), milestoneAfter.UpdatedUnix.AsTime().In(utcTZ))
435 })
436
437 t.Run("WithPastUpdateDate", func(t *testing.T) {
438 defer tests.PrintCurrentTest(t)()
439
440 // Note: This Milestone's updated_unix has been set to Now() by the first subtest
441 milestone := int64(1)
442 milestoneBefore := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: milestone})
443
444 updatedAt := time.Now().Add(-time.Hour).Truncate(time.Second)
445 req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
446 Milestone: &milestone,
447 Updated: &updatedAt,
448 }).AddTokenAuth(token)
449 MakeRequest(t, req, http.StatusCreated)
450
451 // the milestone date should not change
452 // dates are converted into the same tz, in order to compare them
453 utcTZ, _ := time.LoadLocation("UTC")
454 milestoneAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: milestone})
455 assert.Equal(t, milestoneAfter.UpdatedUnix.AsTime().In(utcTZ), milestoneBefore.UpdatedUnix.AsTime().In(utcTZ))
456 })
457}
458
459func TestAPISearchIssues(t *testing.T) {
460 defer tests.PrepareTestEnv(t)()
461
462 // as this API was used in the frontend, it uses UI page size
463 expectedIssueCount := 20 // from the fixtures
464 if expectedIssueCount > setting.UI.IssuePagingNum {
465 expectedIssueCount = setting.UI.IssuePagingNum
466 }
467
468 link, _ := url.Parse("/api/v1/repos/issues/search")
469 token := getUserToken(t, "user1", auth_model.AccessTokenScopeReadIssue)
470 query := url.Values{}
471 var apiIssues []*api.Issue
472
473 link.RawQuery = query.Encode()
474 req := NewRequest(t, "GET", link.String()).AddTokenAuth(token)
475 resp := MakeRequest(t, req, http.StatusOK)
476 DecodeJSON(t, resp, &apiIssues)
477 assert.Len(t, apiIssues, expectedIssueCount)
478
479 publicOnlyToken := getUserToken(t, "user1", auth_model.AccessTokenScopeReadIssue, auth_model.AccessTokenScopePublicOnly)
480 req = NewRequest(t, "GET", link.String()).AddTokenAuth(publicOnlyToken)
481 resp = MakeRequest(t, req, http.StatusOK)
482 DecodeJSON(t, resp, &apiIssues)
483 assert.Len(t, apiIssues, 15) // 15 public issues
484
485 since := "2000-01-01T00:50:01+00:00" // 946687801
486 before := time.Unix(999307200, 0).Format(time.RFC3339)
487 query.Add("since", since)
488 query.Add("before", before)
489 link.RawQuery = query.Encode()
490 req = NewRequest(t, "GET", link.String()).AddTokenAuth(token)
491 resp = MakeRequest(t, req, http.StatusOK)
492 DecodeJSON(t, resp, &apiIssues)
493 assert.Len(t, apiIssues, 11)
494 query.Del("since")
495 query.Del("before")
496
497 query.Add("state", "closed")
498 link.RawQuery = query.Encode()
499 req = NewRequest(t, "GET", link.String()).AddTokenAuth(token)
500 resp = MakeRequest(t, req, http.StatusOK)
501 DecodeJSON(t, resp, &apiIssues)
502 assert.Len(t, apiIssues, 2)
503
504 query.Set("state", "all")
505 link.RawQuery = query.Encode()
506 req = NewRequest(t, "GET", link.String()).AddTokenAuth(token)
507 resp = MakeRequest(t, req, http.StatusOK)
508 DecodeJSON(t, resp, &apiIssues)
509 assert.Equal(t, "22", resp.Header().Get("X-Total-Count"))
510 assert.Len(t, apiIssues, 20)
511
512 query.Add("limit", "10")
513 link.RawQuery = query.Encode()
514 req = NewRequest(t, "GET", link.String()).AddTokenAuth(token)
515 resp = MakeRequest(t, req, http.StatusOK)
516 DecodeJSON(t, resp, &apiIssues)
517 assert.Equal(t, "22", resp.Header().Get("X-Total-Count"))
518 assert.Len(t, apiIssues, 10)
519
520 query = url.Values{"assigned": {"true"}, "state": {"all"}}
521 link.RawQuery = query.Encode()
522 req = NewRequest(t, "GET", link.String()).AddTokenAuth(token)
523 resp = MakeRequest(t, req, http.StatusOK)
524 DecodeJSON(t, resp, &apiIssues)
525 assert.Len(t, apiIssues, 2)
526
527 query = url.Values{"milestones": {"milestone1"}, "state": {"all"}}
528 link.RawQuery = query.Encode()
529 req = NewRequest(t, "GET", link.String()).AddTokenAuth(token)
530 resp = MakeRequest(t, req, http.StatusOK)
531 DecodeJSON(t, resp, &apiIssues)
532 assert.Len(t, apiIssues, 1)
533
534 query = url.Values{"milestones": {"milestone1,milestone3"}, "state": {"all"}}
535 link.RawQuery = query.Encode()
536 req = NewRequest(t, "GET", link.String()).AddTokenAuth(token)
537 resp = MakeRequest(t, req, http.StatusOK)
538 DecodeJSON(t, resp, &apiIssues)
539 assert.Len(t, apiIssues, 2)
540
541 query = url.Values{"owner": {"user2"}} // user
542 link.RawQuery = query.Encode()
543 req = NewRequest(t, "GET", link.String()).AddTokenAuth(token)
544 resp = MakeRequest(t, req, http.StatusOK)
545 DecodeJSON(t, resp, &apiIssues)
546 assert.Len(t, apiIssues, 8)
547
548 query = url.Values{"owner": {"org3"}} // organization
549 link.RawQuery = query.Encode()
550 req = NewRequest(t, "GET", link.String()).AddTokenAuth(token)
551 resp = MakeRequest(t, req, http.StatusOK)
552 DecodeJSON(t, resp, &apiIssues)
553 assert.Len(t, apiIssues, 5)
554
555 query = url.Values{"owner": {"org3"}, "team": {"team1"}} // organization + team
556 link.RawQuery = query.Encode()
557 req = NewRequest(t, "GET", link.String()).AddTokenAuth(token)
558 resp = MakeRequest(t, req, http.StatusOK)
559 DecodeJSON(t, resp, &apiIssues)
560 assert.Len(t, apiIssues, 2)
561}
562
563func TestAPISearchIssuesWithLabels(t *testing.T) {
564 defer tests.PrepareTestEnv(t)()
565
566 // as this API was used in the frontend, it uses UI page size
567 expectedIssueCount := 20 // from the fixtures
568 if expectedIssueCount > setting.UI.IssuePagingNum {
569 expectedIssueCount = setting.UI.IssuePagingNum
570 }
571
572 link, _ := url.Parse("/api/v1/repos/issues/search")
573 token := getUserToken(t, "user1", auth_model.AccessTokenScopeReadIssue)
574 query := url.Values{}
575 var apiIssues []*api.Issue
576
577 link.RawQuery = query.Encode()
578 req := NewRequest(t, "GET", link.String()).AddTokenAuth(token)
579 resp := MakeRequest(t, req, http.StatusOK)
580 DecodeJSON(t, resp, &apiIssues)
581 assert.Len(t, apiIssues, expectedIssueCount)
582
583 query.Add("labels", "label1")
584 link.RawQuery = query.Encode()
585 req = NewRequest(t, "GET", link.String()).AddTokenAuth(token)
586 resp = MakeRequest(t, req, http.StatusOK)
587 DecodeJSON(t, resp, &apiIssues)
588 assert.Len(t, apiIssues, 2)
589
590 // multiple labels
591 query.Set("labels", "label1,label2")
592 link.RawQuery = query.Encode()
593 req = NewRequest(t, "GET", link.String()).AddTokenAuth(token)
594 resp = MakeRequest(t, req, http.StatusOK)
595 DecodeJSON(t, resp, &apiIssues)
596 assert.Len(t, apiIssues, 2)
597
598 // an org label
599 query.Set("labels", "orglabel4")
600 link.RawQuery = query.Encode()
601 req = NewRequest(t, "GET", link.String()).AddTokenAuth(token)
602 resp = MakeRequest(t, req, http.StatusOK)
603 DecodeJSON(t, resp, &apiIssues)
604 assert.Len(t, apiIssues, 1)
605
606 // org and repo label
607 query.Set("labels", "label2,orglabel4")
608 query.Add("state", "all")
609 link.RawQuery = query.Encode()
610 req = NewRequest(t, "GET", link.String()).AddTokenAuth(token)
611 resp = MakeRequest(t, req, http.StatusOK)
612 DecodeJSON(t, resp, &apiIssues)
613 assert.Len(t, apiIssues, 2)
614
615 // org and repo label which share the same issue
616 query.Set("labels", "label1,orglabel4")
617 link.RawQuery = query.Encode()
618 req = NewRequest(t, "GET", link.String()).AddTokenAuth(token)
619 resp = MakeRequest(t, req, http.StatusOK)
620 DecodeJSON(t, resp, &apiIssues)
621 assert.Len(t, apiIssues, 2)
622}