code.gitea.io/gitea@v1.21.7/tests/integration/api_issue_test.go (about) 1 // Copyright 2017 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package integration 5 6 import ( 7 "fmt" 8 "net/http" 9 "net/url" 10 "strconv" 11 "sync" 12 "testing" 13 "time" 14 15 auth_model "code.gitea.io/gitea/models/auth" 16 "code.gitea.io/gitea/models/db" 17 issues_model "code.gitea.io/gitea/models/issues" 18 repo_model "code.gitea.io/gitea/models/repo" 19 "code.gitea.io/gitea/models/unittest" 20 user_model "code.gitea.io/gitea/models/user" 21 "code.gitea.io/gitea/modules/setting" 22 api "code.gitea.io/gitea/modules/structs" 23 "code.gitea.io/gitea/tests" 24 25 "github.com/stretchr/testify/assert" 26 ) 27 28 func TestAPIListIssues(t *testing.T) { 29 defer tests.PrepareTestEnv(t)() 30 31 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 32 owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) 33 34 session := loginUser(t, owner.Name) 35 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadIssue) 36 link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/issues", owner.Name, repo.Name)) 37 38 link.RawQuery = url.Values{"token": {token}, "state": {"all"}}.Encode() 39 resp := MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) 40 var apiIssues []*api.Issue 41 DecodeJSON(t, resp, &apiIssues) 42 assert.Len(t, apiIssues, unittest.GetCount(t, &issues_model.Issue{RepoID: repo.ID})) 43 for _, apiIssue := range apiIssues { 44 unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: apiIssue.ID, RepoID: repo.ID}) 45 } 46 47 // test milestone filter 48 link.RawQuery = url.Values{"token": {token}, "state": {"all"}, "type": {"all"}, "milestones": {"ignore,milestone1,3,4"}}.Encode() 49 resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) 50 DecodeJSON(t, resp, &apiIssues) 51 if assert.Len(t, apiIssues, 2) { 52 assert.EqualValues(t, 3, apiIssues[0].Milestone.ID) 53 assert.EqualValues(t, 1, apiIssues[1].Milestone.ID) 54 } 55 56 link.RawQuery = url.Values{"token": {token}, "state": {"all"}, "created_by": {"user2"}}.Encode() 57 resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) 58 DecodeJSON(t, resp, &apiIssues) 59 if assert.Len(t, apiIssues, 1) { 60 assert.EqualValues(t, 5, apiIssues[0].ID) 61 } 62 63 link.RawQuery = url.Values{"token": {token}, "state": {"all"}, "assigned_by": {"user1"}}.Encode() 64 resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) 65 DecodeJSON(t, resp, &apiIssues) 66 if assert.Len(t, apiIssues, 1) { 67 assert.EqualValues(t, 1, apiIssues[0].ID) 68 } 69 70 link.RawQuery = url.Values{"token": {token}, "state": {"all"}, "mentioned_by": {"user4"}}.Encode() 71 resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) 72 DecodeJSON(t, resp, &apiIssues) 73 if assert.Len(t, apiIssues, 1) { 74 assert.EqualValues(t, 1, apiIssues[0].ID) 75 } 76 } 77 78 func TestAPICreateIssue(t *testing.T) { 79 defer tests.PrepareTestEnv(t)() 80 const body, title = "apiTestBody", "apiTestTitle" 81 82 repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 83 owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}) 84 85 session := loginUser(t, owner.Name) 86 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) 87 urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues?state=all&token=%s", owner.Name, repoBefore.Name, token) 88 req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateIssueOption{ 89 Body: body, 90 Title: title, 91 Assignee: owner.Name, 92 }) 93 resp := MakeRequest(t, req, http.StatusCreated) 94 var apiIssue api.Issue 95 DecodeJSON(t, resp, &apiIssue) 96 assert.Equal(t, body, apiIssue.Body) 97 assert.Equal(t, title, apiIssue.Title) 98 99 unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ 100 RepoID: repoBefore.ID, 101 AssigneeID: owner.ID, 102 Content: body, 103 Title: title, 104 }) 105 106 repoAfter := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 107 assert.Equal(t, repoBefore.NumIssues+1, repoAfter.NumIssues) 108 assert.Equal(t, repoBefore.NumClosedIssues, repoAfter.NumClosedIssues) 109 } 110 111 func TestAPICreateIssueParallel(t *testing.T) { 112 defer tests.PrepareTestEnv(t)() 113 const body, title = "apiTestBody", "apiTestTitle" 114 115 repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 116 owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}) 117 118 session := loginUser(t, owner.Name) 119 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) 120 urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues?state=all&token=%s", owner.Name, repoBefore.Name, token) 121 122 var wg sync.WaitGroup 123 for i := 0; i < 10; i++ { 124 wg.Add(1) 125 go func(parentT *testing.T, i int) { 126 parentT.Run(fmt.Sprintf("ParallelCreateIssue_%d", i), func(t *testing.T) { 127 newTitle := title + strconv.Itoa(i) 128 newBody := body + strconv.Itoa(i) 129 req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateIssueOption{ 130 Body: newBody, 131 Title: newTitle, 132 Assignee: owner.Name, 133 }) 134 resp := MakeRequest(t, req, http.StatusCreated) 135 var apiIssue api.Issue 136 DecodeJSON(t, resp, &apiIssue) 137 assert.Equal(t, newBody, apiIssue.Body) 138 assert.Equal(t, newTitle, apiIssue.Title) 139 140 unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ 141 RepoID: repoBefore.ID, 142 AssigneeID: owner.ID, 143 Content: newBody, 144 Title: newTitle, 145 }) 146 147 wg.Done() 148 }) 149 }(t, i) 150 } 151 wg.Wait() 152 } 153 154 func TestAPIEditIssue(t *testing.T) { 155 defer tests.PrepareTestEnv(t)() 156 157 issueBefore := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10}) 158 repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID}) 159 owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}) 160 assert.NoError(t, issueBefore.LoadAttributes(db.DefaultContext)) 161 assert.Equal(t, int64(1019307200), int64(issueBefore.DeadlineUnix)) 162 assert.Equal(t, api.StateOpen, issueBefore.State()) 163 164 session := loginUser(t, owner.Name) 165 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) 166 167 // update values of issue 168 issueState := "closed" 169 removeDeadline := true 170 milestone := int64(4) 171 body := "new content!" 172 title := "new title from api set" 173 174 urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d?token=%s", owner.Name, repoBefore.Name, issueBefore.Index, token) 175 req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{ 176 State: &issueState, 177 RemoveDeadline: &removeDeadline, 178 Milestone: &milestone, 179 Body: &body, 180 Title: title, 181 182 // ToDo change more 183 }) 184 resp := MakeRequest(t, req, http.StatusCreated) 185 var apiIssue api.Issue 186 DecodeJSON(t, resp, &apiIssue) 187 188 issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10}) 189 repoAfter := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID}) 190 191 // check deleted user 192 assert.Equal(t, int64(500), issueAfter.PosterID) 193 assert.NoError(t, issueAfter.LoadAttributes(db.DefaultContext)) 194 assert.Equal(t, int64(-1), issueAfter.PosterID) 195 assert.Equal(t, int64(-1), issueBefore.PosterID) 196 assert.Equal(t, int64(-1), apiIssue.Poster.ID) 197 198 // check repo change 199 assert.Equal(t, repoBefore.NumClosedIssues+1, repoAfter.NumClosedIssues) 200 201 // API response 202 assert.Equal(t, api.StateClosed, apiIssue.State) 203 assert.Equal(t, milestone, apiIssue.Milestone.ID) 204 assert.Equal(t, body, apiIssue.Body) 205 assert.True(t, apiIssue.Deadline == nil) 206 assert.Equal(t, title, apiIssue.Title) 207 208 // in database 209 assert.Equal(t, api.StateClosed, issueAfter.State()) 210 assert.Equal(t, milestone, issueAfter.MilestoneID) 211 assert.Equal(t, int64(0), int64(issueAfter.DeadlineUnix)) 212 assert.Equal(t, body, issueAfter.Content) 213 assert.Equal(t, title, issueAfter.Title) 214 } 215 216 func TestAPISearchIssues(t *testing.T) { 217 defer tests.PrepareTestEnv(t)() 218 219 token := getUserToken(t, "user2", auth_model.AccessTokenScopeReadIssue) 220 221 // as this API was used in the frontend, it uses UI page size 222 expectedIssueCount := 18 // from the fixtures 223 if expectedIssueCount > setting.UI.IssuePagingNum { 224 expectedIssueCount = setting.UI.IssuePagingNum 225 } 226 227 link, _ := url.Parse("/api/v1/repos/issues/search") 228 query := url.Values{"token": {getUserToken(t, "user1", auth_model.AccessTokenScopeReadIssue)}} 229 var apiIssues []*api.Issue 230 231 link.RawQuery = query.Encode() 232 req := NewRequest(t, "GET", link.String()) 233 resp := MakeRequest(t, req, http.StatusOK) 234 DecodeJSON(t, resp, &apiIssues) 235 assert.Len(t, apiIssues, expectedIssueCount) 236 237 since := "2000-01-01T00:50:01+00:00" // 946687801 238 before := time.Unix(999307200, 0).Format(time.RFC3339) 239 query.Add("since", since) 240 query.Add("before", before) 241 query.Add("token", token) 242 link.RawQuery = query.Encode() 243 req = NewRequest(t, "GET", link.String()) 244 resp = MakeRequest(t, req, http.StatusOK) 245 DecodeJSON(t, resp, &apiIssues) 246 assert.Len(t, apiIssues, 11) 247 query.Del("since") 248 query.Del("before") 249 250 query.Add("state", "closed") 251 link.RawQuery = query.Encode() 252 req = NewRequest(t, "GET", link.String()) 253 resp = MakeRequest(t, req, http.StatusOK) 254 DecodeJSON(t, resp, &apiIssues) 255 assert.Len(t, apiIssues, 2) 256 257 query.Set("state", "all") 258 link.RawQuery = query.Encode() 259 req = NewRequest(t, "GET", link.String()) 260 resp = MakeRequest(t, req, http.StatusOK) 261 DecodeJSON(t, resp, &apiIssues) 262 assert.EqualValues(t, "20", resp.Header().Get("X-Total-Count")) 263 assert.Len(t, apiIssues, 20) 264 265 query.Add("limit", "10") 266 link.RawQuery = query.Encode() 267 req = NewRequest(t, "GET", link.String()) 268 resp = MakeRequest(t, req, http.StatusOK) 269 DecodeJSON(t, resp, &apiIssues) 270 assert.EqualValues(t, "20", resp.Header().Get("X-Total-Count")) 271 assert.Len(t, apiIssues, 10) 272 273 query = url.Values{"assigned": {"true"}, "state": {"all"}, "token": {token}} 274 link.RawQuery = query.Encode() 275 req = NewRequest(t, "GET", link.String()) 276 resp = MakeRequest(t, req, http.StatusOK) 277 DecodeJSON(t, resp, &apiIssues) 278 assert.Len(t, apiIssues, 2) 279 280 query = url.Values{"milestones": {"milestone1"}, "state": {"all"}, "token": {token}} 281 link.RawQuery = query.Encode() 282 req = NewRequest(t, "GET", link.String()) 283 resp = MakeRequest(t, req, http.StatusOK) 284 DecodeJSON(t, resp, &apiIssues) 285 assert.Len(t, apiIssues, 1) 286 287 query = url.Values{"milestones": {"milestone1,milestone3"}, "state": {"all"}, "token": {token}} 288 link.RawQuery = query.Encode() 289 req = NewRequest(t, "GET", link.String()) 290 resp = MakeRequest(t, req, http.StatusOK) 291 DecodeJSON(t, resp, &apiIssues) 292 assert.Len(t, apiIssues, 2) 293 294 query = url.Values{"owner": {"user2"}, "token": {token}} // user 295 link.RawQuery = query.Encode() 296 req = NewRequest(t, "GET", link.String()) 297 resp = MakeRequest(t, req, http.StatusOK) 298 DecodeJSON(t, resp, &apiIssues) 299 assert.Len(t, apiIssues, 8) 300 301 query = url.Values{"owner": {"org3"}, "token": {token}} // organization 302 link.RawQuery = query.Encode() 303 req = NewRequest(t, "GET", link.String()) 304 resp = MakeRequest(t, req, http.StatusOK) 305 DecodeJSON(t, resp, &apiIssues) 306 assert.Len(t, apiIssues, 5) 307 308 query = url.Values{"owner": {"org3"}, "team": {"team1"}, "token": {token}} // organization + team 309 link.RawQuery = query.Encode() 310 req = NewRequest(t, "GET", link.String()) 311 resp = MakeRequest(t, req, http.StatusOK) 312 DecodeJSON(t, resp, &apiIssues) 313 assert.Len(t, apiIssues, 2) 314 } 315 316 func TestAPISearchIssuesWithLabels(t *testing.T) { 317 defer tests.PrepareTestEnv(t)() 318 319 // as this API was used in the frontend, it uses UI page size 320 expectedIssueCount := 18 // from the fixtures 321 if expectedIssueCount > setting.UI.IssuePagingNum { 322 expectedIssueCount = setting.UI.IssuePagingNum 323 } 324 325 link, _ := url.Parse("/api/v1/repos/issues/search") 326 query := url.Values{"token": {getUserToken(t, "user1", auth_model.AccessTokenScopeReadIssue)}} 327 var apiIssues []*api.Issue 328 329 link.RawQuery = query.Encode() 330 req := NewRequest(t, "GET", link.String()) 331 resp := MakeRequest(t, req, http.StatusOK) 332 DecodeJSON(t, resp, &apiIssues) 333 assert.Len(t, apiIssues, expectedIssueCount) 334 335 query.Add("labels", "label1") 336 link.RawQuery = query.Encode() 337 req = NewRequest(t, "GET", link.String()) 338 resp = MakeRequest(t, req, http.StatusOK) 339 DecodeJSON(t, resp, &apiIssues) 340 assert.Len(t, apiIssues, 2) 341 342 // multiple labels 343 query.Set("labels", "label1,label2") 344 link.RawQuery = query.Encode() 345 req = NewRequest(t, "GET", link.String()) 346 resp = MakeRequest(t, req, http.StatusOK) 347 DecodeJSON(t, resp, &apiIssues) 348 assert.Len(t, apiIssues, 2) 349 350 // an org label 351 query.Set("labels", "orglabel4") 352 link.RawQuery = query.Encode() 353 req = NewRequest(t, "GET", link.String()) 354 resp = MakeRequest(t, req, http.StatusOK) 355 DecodeJSON(t, resp, &apiIssues) 356 assert.Len(t, apiIssues, 1) 357 358 // org and repo label 359 query.Set("labels", "label2,orglabel4") 360 query.Add("state", "all") 361 link.RawQuery = query.Encode() 362 req = NewRequest(t, "GET", link.String()) 363 resp = MakeRequest(t, req, http.StatusOK) 364 DecodeJSON(t, resp, &apiIssues) 365 assert.Len(t, apiIssues, 2) 366 367 // org and repo label which share the same issue 368 query.Set("labels", "label1,orglabel4") 369 link.RawQuery = query.Encode() 370 req = NewRequest(t, "GET", link.String()) 371 resp = MakeRequest(t, req, http.StatusOK) 372 DecodeJSON(t, resp, &apiIssues) 373 assert.Len(t, apiIssues, 2) 374 }