-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Close/reopen issues by keywords in titles and comments (#8866)
* Add close/reopen from comment functionality * Fix comment * Rewrite closing/reopening template * Check xref permissions, move action to services/pull * Fix RefIsPull field * Add xref tests * Fix xref unique filter * Only highlight keywords for actionable xrefs * Fix xref neuter filter * Fix check return status * Restart CI
- Loading branch information
Showing
7 changed files
with
300 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
// Copyright 2019 The Gitea Authors. All rights reserved. | ||
// Use of this source code is governed by a MIT-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package models | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"code.gitea.io/gitea/modules/references" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestXRef_AddCrossReferences(t *testing.T) { | ||
assert.NoError(t, PrepareTestDatabase()) | ||
|
||
// Issue #1 to test against | ||
itarget := testCreateIssue(t, 1, 2, "title1", "content1", false) | ||
|
||
// PR to close issue #1 | ||
content := fmt.Sprintf("content2, closes #%d", itarget.Index) | ||
pr := testCreateIssue(t, 1, 2, "title2", content, true) | ||
ref := AssertExistsAndLoadBean(t, &Comment{IssueID: itarget.ID, RefIssueID: pr.ID, RefCommentID: 0}).(*Comment) | ||
assert.Equal(t, CommentTypePullRef, ref.Type) | ||
assert.Equal(t, pr.RepoID, ref.RefRepoID) | ||
assert.Equal(t, true, ref.RefIsPull) | ||
assert.Equal(t, references.XRefActionCloses, ref.RefAction) | ||
|
||
// Comment on PR to reopen issue #1 | ||
content = fmt.Sprintf("content2, reopens #%d", itarget.Index) | ||
c := testCreateComment(t, 1, 2, pr.ID, content) | ||
ref = AssertExistsAndLoadBean(t, &Comment{IssueID: itarget.ID, RefIssueID: pr.ID, RefCommentID: c.ID}).(*Comment) | ||
assert.Equal(t, CommentTypeCommentRef, ref.Type) | ||
assert.Equal(t, pr.RepoID, ref.RefRepoID) | ||
assert.Equal(t, true, ref.RefIsPull) | ||
assert.Equal(t, references.XRefActionReopens, ref.RefAction) | ||
|
||
// Issue mentioning issue #1 | ||
content = fmt.Sprintf("content3, mentions #%d", itarget.Index) | ||
i := testCreateIssue(t, 1, 2, "title3", content, false) | ||
ref = AssertExistsAndLoadBean(t, &Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*Comment) | ||
assert.Equal(t, CommentTypeIssueRef, ref.Type) | ||
assert.Equal(t, pr.RepoID, ref.RefRepoID) | ||
assert.Equal(t, false, ref.RefIsPull) | ||
assert.Equal(t, references.XRefActionNone, ref.RefAction) | ||
|
||
// Issue #4 to test against | ||
itarget = testCreateIssue(t, 3, 3, "title4", "content4", false) | ||
|
||
// Cross-reference to issue #4 by admin | ||
content = fmt.Sprintf("content5, mentions user3/repo3#%d", itarget.Index) | ||
i = testCreateIssue(t, 2, 1, "title5", content, false) | ||
ref = AssertExistsAndLoadBean(t, &Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*Comment) | ||
assert.Equal(t, CommentTypeIssueRef, ref.Type) | ||
assert.Equal(t, i.RepoID, ref.RefRepoID) | ||
assert.Equal(t, false, ref.RefIsPull) | ||
assert.Equal(t, references.XRefActionNone, ref.RefAction) | ||
|
||
// Cross-reference to issue #4 with no permission | ||
content = fmt.Sprintf("content6, mentions user3/repo3#%d", itarget.Index) | ||
i = testCreateIssue(t, 4, 5, "title6", content, false) | ||
AssertNotExistsBean(t, &Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}) | ||
} | ||
|
||
func TestXRef_NeuterCrossReferences(t *testing.T) { | ||
assert.NoError(t, PrepareTestDatabase()) | ||
|
||
// Issue #1 to test against | ||
itarget := testCreateIssue(t, 1, 2, "title1", "content1", false) | ||
|
||
// Issue mentioning issue #1 | ||
title := fmt.Sprintf("title2, mentions #%d", itarget.Index) | ||
i := testCreateIssue(t, 1, 2, title, "content2", false) | ||
ref := AssertExistsAndLoadBean(t, &Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*Comment) | ||
assert.Equal(t, CommentTypeIssueRef, ref.Type) | ||
assert.Equal(t, references.XRefActionNone, ref.RefAction) | ||
|
||
d := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) | ||
i.Title = "title2, no mentions" | ||
assert.NoError(t, i.ChangeTitle(d, title)) | ||
|
||
ref = AssertExistsAndLoadBean(t, &Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*Comment) | ||
assert.Equal(t, CommentTypeIssueRef, ref.Type) | ||
assert.Equal(t, references.XRefActionNeutered, ref.RefAction) | ||
} | ||
|
||
func TestXRef_ResolveCrossReferences(t *testing.T) { | ||
assert.NoError(t, PrepareTestDatabase()) | ||
|
||
d := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) | ||
|
||
i1 := testCreateIssue(t, 1, 2, "title1", "content1", false) | ||
i2 := testCreateIssue(t, 1, 2, "title2", "content2", false) | ||
i3 := testCreateIssue(t, 1, 2, "title3", "content3", false) | ||
assert.NoError(t, i3.ChangeStatus(d, true)) | ||
|
||
pr := testCreatePR(t, 1, 2, "titlepr", fmt.Sprintf("closes #%d", i1.Index)) | ||
rp := AssertExistsAndLoadBean(t, &Comment{IssueID: i1.ID, RefIssueID: pr.Issue.ID, RefCommentID: 0}).(*Comment) | ||
|
||
c1 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("closes #%d", i2.Index)) | ||
r1 := AssertExistsAndLoadBean(t, &Comment{IssueID: i2.ID, RefIssueID: pr.Issue.ID, RefCommentID: c1.ID}).(*Comment) | ||
|
||
// Must be ignored | ||
c2 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("mentions #%d", i2.Index)) | ||
AssertExistsAndLoadBean(t, &Comment{IssueID: i2.ID, RefIssueID: pr.Issue.ID, RefCommentID: c2.ID}) | ||
|
||
// Must be superseded by c4/r4 | ||
c3 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("reopens #%d", i3.Index)) | ||
AssertExistsAndLoadBean(t, &Comment{IssueID: i3.ID, RefIssueID: pr.Issue.ID, RefCommentID: c3.ID}) | ||
|
||
c4 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("closes #%d", i3.Index)) | ||
r4 := AssertExistsAndLoadBean(t, &Comment{IssueID: i3.ID, RefIssueID: pr.Issue.ID, RefCommentID: c4.ID}).(*Comment) | ||
|
||
refs, err := pr.ResolveCrossReferences() | ||
assert.NoError(t, err) | ||
assert.Len(t, refs, 3) | ||
assert.Equal(t, rp.ID, refs[0].ID, "bad ref rp: %+v", refs[0]) | ||
assert.Equal(t, r1.ID, refs[1].ID, "bad ref r1: %+v", refs[1]) | ||
assert.Equal(t, r4.ID, refs[2].ID, "bad ref r4: %+v", refs[2]) | ||
} | ||
|
||
func testCreateIssue(t *testing.T, repo, doer int64, title, content string, ispull bool) *Issue { | ||
r := AssertExistsAndLoadBean(t, &Repository{ID: repo}).(*Repository) | ||
d := AssertExistsAndLoadBean(t, &User{ID: doer}).(*User) | ||
i := &Issue{RepoID: r.ID, PosterID: d.ID, Poster: d, Title: title, Content: content, IsPull: ispull} | ||
|
||
sess := x.NewSession() | ||
defer sess.Close() | ||
assert.NoError(t, sess.Begin()) | ||
_, err := sess.SetExpr("`index`", "coalesce(MAX(`index`),0)+1").Where("repo_id=?", repo).Insert(i) | ||
assert.NoError(t, err) | ||
i, err = getIssueByID(sess, i.ID) | ||
assert.NoError(t, err) | ||
assert.NoError(t, i.addCrossReferences(sess, d)) | ||
assert.NoError(t, sess.Commit()) | ||
return i | ||
} | ||
|
||
func testCreatePR(t *testing.T, repo, doer int64, title, content string) *PullRequest { | ||
r := AssertExistsAndLoadBean(t, &Repository{ID: repo}).(*Repository) | ||
d := AssertExistsAndLoadBean(t, &User{ID: doer}).(*User) | ||
i := &Issue{RepoID: r.ID, PosterID: d.ID, Poster: d, Title: title, Content: content, IsPull: true} | ||
pr := &PullRequest{HeadRepoID: repo, BaseRepoID: repo, HeadBranch: "head", BaseBranch: "base"} | ||
assert.NoError(t, NewPullRequest(r, i, nil, nil, pr, nil)) | ||
pr.Issue = i | ||
return pr | ||
} | ||
|
||
func testCreateComment(t *testing.T, repo, doer, issue int64, content string) *Comment { | ||
d := AssertExistsAndLoadBean(t, &User{ID: doer}).(*User) | ||
i := AssertExistsAndLoadBean(t, &Issue{ID: issue}).(*Issue) | ||
c := &Comment{Type: CommentTypeComment, PosterID: doer, Poster: d, IssueID: issue, Issue: i, Content: content} | ||
|
||
sess := x.NewSession() | ||
defer sess.Close() | ||
assert.NoError(t, sess.Begin()) | ||
_, err := sess.Insert(c) | ||
assert.NoError(t, err) | ||
assert.NoError(t, c.addCrossReferences(sess, d)) | ||
assert.NoError(t, sess.Commit()) | ||
return c | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.