code.gitea.io/gitea@v1.21.7/tests/integration/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 "context" 8 "fmt" 9 "net/http" 10 "net/url" 11 "path" 12 "strconv" 13 "strings" 14 "testing" 15 "time" 16 17 "code.gitea.io/gitea/models/db" 18 issues_model "code.gitea.io/gitea/models/issues" 19 repo_model "code.gitea.io/gitea/models/repo" 20 "code.gitea.io/gitea/models/unittest" 21 user_model "code.gitea.io/gitea/models/user" 22 "code.gitea.io/gitea/modules/indexer/issues" 23 "code.gitea.io/gitea/modules/references" 24 "code.gitea.io/gitea/modules/setting" 25 api "code.gitea.io/gitea/modules/structs" 26 "code.gitea.io/gitea/modules/test" 27 "code.gitea.io/gitea/tests" 28 29 "github.com/PuerkitoBio/goquery" 30 "github.com/stretchr/testify/assert" 31 ) 32 33 func getIssuesSelection(t testing.TB, htmlDoc *HTMLDoc) *goquery.Selection { 34 issueList := htmlDoc.doc.Find("#issue-list") 35 assert.EqualValues(t, 1, issueList.Length()) 36 return issueList.Find(".flex-item").Find(".issue-title") 37 } 38 39 func getIssue(t *testing.T, repoID int64, issueSelection *goquery.Selection) *issues_model.Issue { 40 href, exists := issueSelection.Attr("href") 41 assert.True(t, exists) 42 indexStr := href[strings.LastIndexByte(href, '/')+1:] 43 index, err := strconv.Atoi(indexStr) 44 assert.NoError(t, err, "Invalid issue href: %s", href) 45 return unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: repoID, Index: int64(index)}) 46 } 47 48 func assertMatch(t testing.TB, issue *issues_model.Issue, keyword string) { 49 matches := strings.Contains(strings.ToLower(issue.Title), keyword) || 50 strings.Contains(strings.ToLower(issue.Content), keyword) 51 for _, comment := range issue.Comments { 52 matches = matches || strings.Contains( 53 strings.ToLower(comment.Content), 54 keyword, 55 ) 56 } 57 assert.True(t, matches) 58 } 59 60 func TestNoLoginViewIssues(t *testing.T) { 61 defer tests.PrepareTestEnv(t)() 62 63 req := NewRequest(t, "GET", "/user2/repo1/issues") 64 MakeRequest(t, req, http.StatusOK) 65 } 66 67 func TestViewIssuesSortByType(t *testing.T) { 68 defer tests.PrepareTestEnv(t)() 69 70 user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) 71 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 72 73 session := loginUser(t, user.Name) 74 req := NewRequest(t, "GET", repo.Link()+"/issues?type=created_by") 75 resp := session.MakeRequest(t, req, http.StatusOK) 76 77 htmlDoc := NewHTMLParser(t, resp.Body) 78 issuesSelection := getIssuesSelection(t, htmlDoc) 79 expectedNumIssues := unittest.GetCount(t, 80 &issues_model.Issue{RepoID: repo.ID, PosterID: user.ID}, 81 unittest.Cond("is_closed=?", false), 82 unittest.Cond("is_pull=?", false), 83 ) 84 if expectedNumIssues > setting.UI.IssuePagingNum { 85 expectedNumIssues = setting.UI.IssuePagingNum 86 } 87 assert.EqualValues(t, expectedNumIssues, issuesSelection.Length()) 88 89 issuesSelection.Each(func(_ int, selection *goquery.Selection) { 90 issue := getIssue(t, repo.ID, selection) 91 assert.EqualValues(t, user.ID, issue.PosterID) 92 }) 93 } 94 95 func TestViewIssuesKeyword(t *testing.T) { 96 defer tests.PrepareTestEnv(t)() 97 98 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 99 issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ 100 RepoID: repo.ID, 101 Index: 1, 102 }) 103 issues.UpdateIssueIndexer(context.Background(), issue.ID) 104 time.Sleep(time.Second * 1) 105 const keyword = "first" 106 req := NewRequestf(t, "GET", "%s/issues?q=%s", repo.Link(), keyword) 107 resp := MakeRequest(t, req, http.StatusOK) 108 109 htmlDoc := NewHTMLParser(t, resp.Body) 110 issuesSelection := getIssuesSelection(t, htmlDoc) 111 assert.EqualValues(t, 1, issuesSelection.Length()) 112 issuesSelection.Each(func(_ int, selection *goquery.Selection) { 113 issue := getIssue(t, repo.ID, selection) 114 assert.False(t, issue.IsClosed) 115 assert.False(t, issue.IsPull) 116 assertMatch(t, issue, keyword) 117 }) 118 } 119 120 func TestNoLoginViewIssue(t *testing.T) { 121 defer tests.PrepareTestEnv(t)() 122 123 req := NewRequest(t, "GET", "/user2/repo1/issues/1") 124 MakeRequest(t, req, http.StatusOK) 125 } 126 127 func testNewIssue(t *testing.T, session *TestSession, user, repo, title, content string) string { 128 req := NewRequest(t, "GET", path.Join(user, repo, "issues", "new")) 129 resp := session.MakeRequest(t, req, http.StatusOK) 130 131 htmlDoc := NewHTMLParser(t, resp.Body) 132 link, exists := htmlDoc.doc.Find("form.ui.form").Attr("action") 133 assert.True(t, exists, "The template has changed") 134 req = NewRequestWithValues(t, "POST", link, map[string]string{ 135 "_csrf": htmlDoc.GetCSRF(), 136 "title": title, 137 "content": content, 138 }) 139 resp = session.MakeRequest(t, req, http.StatusOK) 140 141 issueURL := test.RedirectURL(resp) 142 req = NewRequest(t, "GET", issueURL) 143 resp = session.MakeRequest(t, req, http.StatusOK) 144 145 htmlDoc = NewHTMLParser(t, resp.Body) 146 val := htmlDoc.doc.Find("#issue-title").Text() 147 assert.Contains(t, val, title) 148 val = htmlDoc.doc.Find(".comment .render-content p").First().Text() 149 assert.Equal(t, content, val) 150 151 return issueURL 152 } 153 154 func testIssueAddComment(t *testing.T, session *TestSession, issueURL, content, status string) int64 { 155 req := NewRequest(t, "GET", issueURL) 156 resp := session.MakeRequest(t, req, http.StatusOK) 157 158 htmlDoc := NewHTMLParser(t, resp.Body) 159 link, exists := htmlDoc.doc.Find("#comment-form").Attr("action") 160 assert.True(t, exists, "The template has changed") 161 162 commentCount := htmlDoc.doc.Find(".comment-list .comment .render-content").Length() 163 164 req = NewRequestWithValues(t, "POST", link, map[string]string{ 165 "_csrf": htmlDoc.GetCSRF(), 166 "content": content, 167 "status": status, 168 }) 169 resp = session.MakeRequest(t, req, http.StatusOK) 170 171 req = NewRequest(t, "GET", test.RedirectURL(resp)) 172 resp = session.MakeRequest(t, req, http.StatusOK) 173 174 htmlDoc = NewHTMLParser(t, resp.Body) 175 176 val := htmlDoc.doc.Find(".comment-list .comment .render-content p").Eq(commentCount).Text() 177 assert.Equal(t, content, val) 178 179 idAttr, has := htmlDoc.doc.Find(".comment-list .comment").Eq(commentCount).Attr("id") 180 idStr := idAttr[strings.LastIndexByte(idAttr, '-')+1:] 181 assert.True(t, has) 182 id, err := strconv.Atoi(idStr) 183 assert.NoError(t, err) 184 return int64(id) 185 } 186 187 func TestNewIssue(t *testing.T) { 188 defer tests.PrepareTestEnv(t)() 189 session := loginUser(t, "user2") 190 testNewIssue(t, session, "user2", "repo1", "Title", "Description") 191 } 192 193 func TestIssueCommentClose(t *testing.T) { 194 defer tests.PrepareTestEnv(t)() 195 session := loginUser(t, "user2") 196 issueURL := testNewIssue(t, session, "user2", "repo1", "Title", "Description") 197 testIssueAddComment(t, session, issueURL, "Test comment 1", "") 198 testIssueAddComment(t, session, issueURL, "Test comment 2", "") 199 testIssueAddComment(t, session, issueURL, "Test comment 3", "close") 200 201 // Validate that issue content has not been updated 202 req := NewRequest(t, "GET", issueURL) 203 resp := session.MakeRequest(t, req, http.StatusOK) 204 htmlDoc := NewHTMLParser(t, resp.Body) 205 val := htmlDoc.doc.Find(".comment-list .comment .render-content p").First().Text() 206 assert.Equal(t, "Description", val) 207 } 208 209 func TestIssueCommentDelete(t *testing.T) { 210 defer tests.PrepareTestEnv(t)() 211 session := loginUser(t, "user2") 212 issueURL := testNewIssue(t, session, "user2", "repo1", "Title", "Description") 213 comment1 := "Test comment 1" 214 commentID := testIssueAddComment(t, session, issueURL, comment1, "") 215 comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: commentID}) 216 assert.Equal(t, comment1, comment.Content) 217 218 // Using the ID of a comment that does not belong to the repository must fail 219 req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/comments/%d/delete", "user5", "repo4", commentID), map[string]string{ 220 "_csrf": GetCSRF(t, session, issueURL), 221 }) 222 session.MakeRequest(t, req, http.StatusNotFound) 223 req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/comments/%d/delete", "user2", "repo1", commentID), map[string]string{ 224 "_csrf": GetCSRF(t, session, issueURL), 225 }) 226 session.MakeRequest(t, req, http.StatusOK) 227 unittest.AssertNotExistsBean(t, &issues_model.Comment{ID: commentID}) 228 } 229 230 func TestIssueCommentUpdate(t *testing.T) { 231 defer tests.PrepareTestEnv(t)() 232 session := loginUser(t, "user2") 233 issueURL := testNewIssue(t, session, "user2", "repo1", "Title", "Description") 234 comment1 := "Test comment 1" 235 commentID := testIssueAddComment(t, session, issueURL, comment1, "") 236 237 comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: commentID}) 238 assert.Equal(t, comment1, comment.Content) 239 240 modifiedContent := comment.Content + "MODIFIED" 241 242 // Using the ID of a comment that does not belong to the repository must fail 243 req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/comments/%d", "user5", "repo4", commentID), map[string]string{ 244 "_csrf": GetCSRF(t, session, issueURL), 245 "content": modifiedContent, 246 }) 247 session.MakeRequest(t, req, http.StatusNotFound) 248 249 req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/comments/%d", "user2", "repo1", commentID), map[string]string{ 250 "_csrf": GetCSRF(t, session, issueURL), 251 "content": modifiedContent, 252 }) 253 session.MakeRequest(t, req, http.StatusOK) 254 255 comment = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: commentID}) 256 assert.Equal(t, modifiedContent, comment.Content) 257 } 258 259 func TestIssueReaction(t *testing.T) { 260 defer tests.PrepareTestEnv(t)() 261 session := loginUser(t, "user2") 262 issueURL := testNewIssue(t, session, "user2", "repo1", "Title", "Description") 263 264 req := NewRequest(t, "GET", issueURL) 265 resp := session.MakeRequest(t, req, http.StatusOK) 266 htmlDoc := NewHTMLParser(t, resp.Body) 267 268 req = NewRequestWithValues(t, "POST", path.Join(issueURL, "/reactions/react"), map[string]string{ 269 "_csrf": htmlDoc.GetCSRF(), 270 "content": "8ball", 271 }) 272 session.MakeRequest(t, req, http.StatusInternalServerError) 273 req = NewRequestWithValues(t, "POST", path.Join(issueURL, "/reactions/react"), map[string]string{ 274 "_csrf": htmlDoc.GetCSRF(), 275 "content": "eyes", 276 }) 277 session.MakeRequest(t, req, http.StatusOK) 278 req = NewRequestWithValues(t, "POST", path.Join(issueURL, "/reactions/unreact"), map[string]string{ 279 "_csrf": htmlDoc.GetCSRF(), 280 "content": "eyes", 281 }) 282 session.MakeRequest(t, req, http.StatusOK) 283 } 284 285 func TestIssueCrossReference(t *testing.T) { 286 defer tests.PrepareTestEnv(t)() 287 288 // Issue that will be referenced 289 _, issueBase := testIssueWithBean(t, "user2", 1, "Title", "Description") 290 291 // Ref from issue title 292 issueRefURL, issueRef := testIssueWithBean(t, "user2", 1, fmt.Sprintf("Title ref #%d", issueBase.Index), "Description") 293 unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ 294 IssueID: issueBase.ID, 295 RefRepoID: 1, 296 RefIssueID: issueRef.ID, 297 RefCommentID: 0, 298 RefIsPull: false, 299 RefAction: references.XRefActionNone, 300 }) 301 302 // Edit title, neuter ref 303 testIssueChangeInfo(t, "user2", issueRefURL, "title", "Title no ref") 304 unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ 305 IssueID: issueBase.ID, 306 RefRepoID: 1, 307 RefIssueID: issueRef.ID, 308 RefCommentID: 0, 309 RefIsPull: false, 310 RefAction: references.XRefActionNeutered, 311 }) 312 313 // Ref from issue content 314 issueRefURL, issueRef = testIssueWithBean(t, "user2", 1, "TitleXRef", fmt.Sprintf("Description ref #%d", issueBase.Index)) 315 unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ 316 IssueID: issueBase.ID, 317 RefRepoID: 1, 318 RefIssueID: issueRef.ID, 319 RefCommentID: 0, 320 RefIsPull: false, 321 RefAction: references.XRefActionNone, 322 }) 323 324 // Edit content, neuter ref 325 testIssueChangeInfo(t, "user2", issueRefURL, "content", "Description no ref") 326 unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ 327 IssueID: issueBase.ID, 328 RefRepoID: 1, 329 RefIssueID: issueRef.ID, 330 RefCommentID: 0, 331 RefIsPull: false, 332 RefAction: references.XRefActionNeutered, 333 }) 334 335 // Ref from a comment 336 session := loginUser(t, "user2") 337 commentID := testIssueAddComment(t, session, issueRefURL, fmt.Sprintf("Adding ref from comment #%d", issueBase.Index), "") 338 comment := &issues_model.Comment{ 339 IssueID: issueBase.ID, 340 RefRepoID: 1, 341 RefIssueID: issueRef.ID, 342 RefCommentID: commentID, 343 RefIsPull: false, 344 RefAction: references.XRefActionNone, 345 } 346 unittest.AssertExistsAndLoadBean(t, comment) 347 348 // Ref from a different repository 349 _, issueRef = testIssueWithBean(t, "user12", 10, "TitleXRef", fmt.Sprintf("Description ref user2/repo1#%d", issueBase.Index)) 350 unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ 351 IssueID: issueBase.ID, 352 RefRepoID: 10, 353 RefIssueID: issueRef.ID, 354 RefCommentID: 0, 355 RefIsPull: false, 356 RefAction: references.XRefActionNone, 357 }) 358 } 359 360 func testIssueWithBean(t *testing.T, user string, repoID int64, title, content string) (string, *issues_model.Issue) { 361 session := loginUser(t, user) 362 issueURL := testNewIssue(t, session, user, fmt.Sprintf("repo%d", repoID), title, content) 363 indexStr := issueURL[strings.LastIndexByte(issueURL, '/')+1:] 364 index, err := strconv.Atoi(indexStr) 365 assert.NoError(t, err, "Invalid issue href: %s", issueURL) 366 issue := &issues_model.Issue{RepoID: repoID, Index: int64(index)} 367 unittest.AssertExistsAndLoadBean(t, issue) 368 return issueURL, issue 369 } 370 371 func testIssueChangeInfo(t *testing.T, user, issueURL, info, value string) { 372 session := loginUser(t, user) 373 374 req := NewRequest(t, "GET", issueURL) 375 resp := session.MakeRequest(t, req, http.StatusOK) 376 htmlDoc := NewHTMLParser(t, resp.Body) 377 378 req = NewRequestWithValues(t, "POST", path.Join(issueURL, info), map[string]string{ 379 "_csrf": htmlDoc.GetCSRF(), 380 info: value, 381 }) 382 _ = session.MakeRequest(t, req, http.StatusOK) 383 } 384 385 func TestIssueRedirect(t *testing.T) { 386 defer tests.PrepareTestEnv(t)() 387 session := loginUser(t, "user2") 388 389 // Test external tracker where style not set (shall default numeric) 390 req := NewRequest(t, "GET", path.Join("org26", "repo_external_tracker", "issues", "1")) 391 resp := session.MakeRequest(t, req, http.StatusSeeOther) 392 assert.Equal(t, "https://tracker.com/org26/repo_external_tracker/issues/1", test.RedirectURL(resp)) 393 394 // Test external tracker with numeric style 395 req = NewRequest(t, "GET", path.Join("org26", "repo_external_tracker_numeric", "issues", "1")) 396 resp = session.MakeRequest(t, req, http.StatusSeeOther) 397 assert.Equal(t, "https://tracker.com/org26/repo_external_tracker_numeric/issues/1", test.RedirectURL(resp)) 398 399 // Test external tracker with alphanumeric style (for a pull request) 400 req = NewRequest(t, "GET", path.Join("org26", "repo_external_tracker_alpha", "issues", "1")) 401 resp = session.MakeRequest(t, req, http.StatusSeeOther) 402 assert.Equal(t, "/"+path.Join("org26", "repo_external_tracker_alpha", "pulls", "1"), test.RedirectURL(resp)) 403 } 404 405 func TestSearchIssues(t *testing.T) { 406 defer tests.PrepareTestEnv(t)() 407 408 session := loginUser(t, "user2") 409 410 expectedIssueCount := 18 // from the fixtures 411 if expectedIssueCount > setting.UI.IssuePagingNum { 412 expectedIssueCount = setting.UI.IssuePagingNum 413 } 414 415 link, _ := url.Parse("/issues/search") 416 req := NewRequest(t, "GET", link.String()) 417 resp := session.MakeRequest(t, req, http.StatusOK) 418 var apiIssues []*api.Issue 419 DecodeJSON(t, resp, &apiIssues) 420 assert.Len(t, apiIssues, expectedIssueCount) 421 422 since := "2000-01-01T00:50:01+00:00" // 946687801 423 before := time.Unix(999307200, 0).Format(time.RFC3339) 424 query := url.Values{} 425 query.Add("since", since) 426 query.Add("before", before) 427 link.RawQuery = query.Encode() 428 req = NewRequest(t, "GET", link.String()) 429 resp = session.MakeRequest(t, req, http.StatusOK) 430 DecodeJSON(t, resp, &apiIssues) 431 assert.Len(t, apiIssues, 11) 432 query.Del("since") 433 query.Del("before") 434 435 query.Add("state", "closed") 436 link.RawQuery = query.Encode() 437 req = NewRequest(t, "GET", link.String()) 438 resp = session.MakeRequest(t, req, http.StatusOK) 439 DecodeJSON(t, resp, &apiIssues) 440 assert.Len(t, apiIssues, 2) 441 442 query.Set("state", "all") 443 link.RawQuery = query.Encode() 444 req = NewRequest(t, "GET", link.String()) 445 resp = session.MakeRequest(t, req, http.StatusOK) 446 DecodeJSON(t, resp, &apiIssues) 447 assert.EqualValues(t, "20", resp.Header().Get("X-Total-Count")) 448 assert.Len(t, apiIssues, 20) 449 450 query.Add("limit", "5") 451 link.RawQuery = query.Encode() 452 req = NewRequest(t, "GET", link.String()) 453 resp = session.MakeRequest(t, req, http.StatusOK) 454 DecodeJSON(t, resp, &apiIssues) 455 assert.EqualValues(t, "20", resp.Header().Get("X-Total-Count")) 456 assert.Len(t, apiIssues, 5) 457 458 query = url.Values{"assigned": {"true"}, "state": {"all"}} 459 link.RawQuery = query.Encode() 460 req = NewRequest(t, "GET", link.String()) 461 resp = session.MakeRequest(t, req, http.StatusOK) 462 DecodeJSON(t, resp, &apiIssues) 463 assert.Len(t, apiIssues, 2) 464 465 query = url.Values{"milestones": {"milestone1"}, "state": {"all"}} 466 link.RawQuery = query.Encode() 467 req = NewRequest(t, "GET", link.String()) 468 resp = session.MakeRequest(t, req, http.StatusOK) 469 DecodeJSON(t, resp, &apiIssues) 470 assert.Len(t, apiIssues, 1) 471 472 query = url.Values{"milestones": {"milestone1,milestone3"}, "state": {"all"}} 473 link.RawQuery = query.Encode() 474 req = NewRequest(t, "GET", link.String()) 475 resp = session.MakeRequest(t, req, http.StatusOK) 476 DecodeJSON(t, resp, &apiIssues) 477 assert.Len(t, apiIssues, 2) 478 479 query = url.Values{"owner": {"user2"}} // user 480 link.RawQuery = query.Encode() 481 req = NewRequest(t, "GET", link.String()) 482 resp = session.MakeRequest(t, req, http.StatusOK) 483 DecodeJSON(t, resp, &apiIssues) 484 assert.Len(t, apiIssues, 8) 485 486 query = url.Values{"owner": {"org3"}} // organization 487 link.RawQuery = query.Encode() 488 req = NewRequest(t, "GET", link.String()) 489 resp = session.MakeRequest(t, req, http.StatusOK) 490 DecodeJSON(t, resp, &apiIssues) 491 assert.Len(t, apiIssues, 5) 492 493 query = url.Values{"owner": {"org3"}, "team": {"team1"}} // organization + team 494 link.RawQuery = query.Encode() 495 req = NewRequest(t, "GET", link.String()) 496 resp = session.MakeRequest(t, req, http.StatusOK) 497 DecodeJSON(t, resp, &apiIssues) 498 assert.Len(t, apiIssues, 2) 499 } 500 501 func TestSearchIssuesWithLabels(t *testing.T) { 502 defer tests.PrepareTestEnv(t)() 503 504 expectedIssueCount := 18 // from the fixtures 505 if expectedIssueCount > setting.UI.IssuePagingNum { 506 expectedIssueCount = setting.UI.IssuePagingNum 507 } 508 509 session := loginUser(t, "user1") 510 link, _ := url.Parse("/issues/search") 511 query := url.Values{} 512 var apiIssues []*api.Issue 513 514 link.RawQuery = query.Encode() 515 req := NewRequest(t, "GET", link.String()) 516 resp := session.MakeRequest(t, req, http.StatusOK) 517 DecodeJSON(t, resp, &apiIssues) 518 assert.Len(t, apiIssues, expectedIssueCount) 519 520 query.Add("labels", "label1") 521 link.RawQuery = query.Encode() 522 req = NewRequest(t, "GET", link.String()) 523 resp = session.MakeRequest(t, req, http.StatusOK) 524 DecodeJSON(t, resp, &apiIssues) 525 assert.Len(t, apiIssues, 2) 526 527 // multiple labels 528 query.Set("labels", "label1,label2") 529 link.RawQuery = query.Encode() 530 req = NewRequest(t, "GET", link.String()) 531 resp = session.MakeRequest(t, req, http.StatusOK) 532 DecodeJSON(t, resp, &apiIssues) 533 assert.Len(t, apiIssues, 2) 534 535 // an org label 536 query.Set("labels", "orglabel4") 537 link.RawQuery = query.Encode() 538 req = NewRequest(t, "GET", link.String()) 539 resp = session.MakeRequest(t, req, http.StatusOK) 540 DecodeJSON(t, resp, &apiIssues) 541 assert.Len(t, apiIssues, 1) 542 543 // org and repo label 544 query.Set("labels", "label2,orglabel4") 545 query.Add("state", "all") 546 link.RawQuery = query.Encode() 547 req = NewRequest(t, "GET", link.String()) 548 resp = session.MakeRequest(t, req, http.StatusOK) 549 DecodeJSON(t, resp, &apiIssues) 550 assert.Len(t, apiIssues, 2) 551 552 // org and repo label which share the same issue 553 query.Set("labels", "label1,orglabel4") 554 link.RawQuery = query.Encode() 555 req = NewRequest(t, "GET", link.String()) 556 resp = session.MakeRequest(t, req, http.StatusOK) 557 DecodeJSON(t, resp, &apiIssues) 558 assert.Len(t, apiIssues, 2) 559 } 560 561 func TestGetIssueInfo(t *testing.T) { 562 defer tests.PrepareTestEnv(t)() 563 564 issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10}) 565 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) 566 owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) 567 assert.NoError(t, issue.LoadAttributes(db.DefaultContext)) 568 assert.Equal(t, int64(1019307200), int64(issue.DeadlineUnix)) 569 assert.Equal(t, api.StateOpen, issue.State()) 570 571 session := loginUser(t, owner.Name) 572 573 urlStr := fmt.Sprintf("/%s/%s/issues/%d/info", owner.Name, repo.Name, issue.Index) 574 req := NewRequest(t, "GET", urlStr) 575 resp := session.MakeRequest(t, req, http.StatusOK) 576 var apiIssue api.Issue 577 DecodeJSON(t, resp, &apiIssue) 578 579 assert.EqualValues(t, issue.ID, apiIssue.ID) 580 } 581 582 func TestUpdateIssueDeadline(t *testing.T) { 583 defer tests.PrepareTestEnv(t)() 584 585 issueBefore := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10}) 586 repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID}) 587 owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}) 588 assert.NoError(t, issueBefore.LoadAttributes(db.DefaultContext)) 589 assert.Equal(t, int64(1019307200), int64(issueBefore.DeadlineUnix)) 590 assert.Equal(t, api.StateOpen, issueBefore.State()) 591 592 session := loginUser(t, owner.Name) 593 594 issueURL := fmt.Sprintf("%s/%s/issues/%d", owner.Name, repoBefore.Name, issueBefore.Index) 595 req := NewRequest(t, "GET", issueURL) 596 resp := session.MakeRequest(t, req, http.StatusOK) 597 htmlDoc := NewHTMLParser(t, resp.Body) 598 599 urlStr := issueURL + "/deadline?_csrf=" + htmlDoc.GetCSRF() 600 req = NewRequestWithJSON(t, "POST", urlStr, map[string]string{ 601 "due_date": "2022-04-06T00:00:00.000Z", 602 }) 603 604 resp = session.MakeRequest(t, req, http.StatusCreated) 605 var apiIssue api.IssueDeadline 606 DecodeJSON(t, resp, &apiIssue) 607 608 assert.EqualValues(t, "2022-04-06", apiIssue.Deadline.Format("2006-01-02")) 609 } 610 611 func TestIssueReferenceURL(t *testing.T) { 612 defer tests.PrepareTestEnv(t)() 613 session := loginUser(t, "user2") 614 615 issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}) 616 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) 617 618 req := NewRequest(t, "GET", fmt.Sprintf("%s/issues/%d", repo.FullName(), issue.Index)) 619 resp := session.MakeRequest(t, req, http.StatusOK) 620 htmlDoc := NewHTMLParser(t, resp.Body) 621 622 // the "reference" uses relative URLs, then JS code will convert them to absolute URLs for current origin, in case users are using multiple domains 623 ref, _ := htmlDoc.Find(`.timeline-item.comment.first .reference-issue`).Attr("data-reference") 624 assert.EqualValues(t, "/user2/repo1/issues/1#issue-1", ref) 625 626 ref, _ = htmlDoc.Find(`.timeline-item.comment:not(.first) .reference-issue`).Attr("data-reference") 627 assert.EqualValues(t, "/user2/repo1/issues/1#issuecomment-2", ref) 628 }