Merge remote-tracking branch 'upstream/main' into feature-activitypub

This commit is contained in:
Anthony Wang 2022-06-13 16:49:36 -05:00
commit 718f35aab6
Signed by: a
GPG key ID: BC96B00AEC5F2D76
218 changed files with 3835 additions and 3774 deletions

View file

@ -45,6 +45,5 @@ Steven Kriegler <sk.bunsenbrenner@gmail.com> (@justusbunsi)
Jimmy Praet <jimmy.praet@telenet.be> (@jpraet)
Leon Hofmeister <dev.lh@web.de> (@delvh)
Gusted <williamzijl7@hotmail.com) (@Gusted)
singuliere <singuliere@autistici.org> (@singuliere)
silentcode <silentcode@senga.org> (@silentcodeg)
Wim <wim@42.be> (@42wim)

View file

@ -68,7 +68,7 @@ Ensure you are running in the correct environment or set the correct configurati
If this is the intended configuration file complete the [database] section.`, setting.CustomConf)
}
if err := db.InitEngine(ctx); err != nil {
return fmt.Errorf("unable to initialise the database using the configuration in %q. Error: %v", setting.CustomConf, err)
return fmt.Errorf("unable to initialize the database using the configuration in %q. Error: %v", setting.CustomConf, err)
}
return nil
}

View file

@ -349,7 +349,7 @@ recommended that pausing only done for a very short period of time.
It is possible to add and remove logging whilst Gitea is running using the `gitea manager logging add` and `remove` subcommands.
This functionality can only adjust running log systems and cannot be used to start the access or router loggers if they
were not already initialised. If you wish to start these systems you are advised to adjust the app.ini and (gracefully) restart
were not already initialized. If you wish to start these systems you are advised to adjust the app.ini and (gracefully) restart
the Gitea service.
The main intention of these commands is to easily add a temporary logger to investigate problems on running systems where a restart

View file

@ -10,7 +10,7 @@ import (
"net/url"
"testing"
"code.gitea.io/gitea/models"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
@ -23,9 +23,9 @@ import (
func TestAPIListRepoComments(t *testing.T) {
defer prepareTestEnv(t)()
comment := unittest.AssertExistsAndLoadBean(t, &models.Comment{},
unittest.Cond("type = ?", models.CommentTypeComment)).(*models.Comment)
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: comment.IssueID}).(*models.Issue)
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{},
unittest.Cond("type = ?", issues_model.CommentTypeComment)).(*issues_model.Comment)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}).(*issues_model.Issue)
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository)
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
@ -38,10 +38,10 @@ func TestAPIListRepoComments(t *testing.T) {
DecodeJSON(t, resp, &apiComments)
assert.Len(t, apiComments, 2)
for _, apiComment := range apiComments {
c := &models.Comment{ID: apiComment.ID}
c := &issues_model.Comment{ID: apiComment.ID}
unittest.AssertExistsAndLoadBean(t, c,
unittest.Cond("type = ?", models.CommentTypeComment))
unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: c.IssueID, RepoID: repo.ID})
unittest.Cond("type = ?", issues_model.CommentTypeComment))
unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: c.IssueID, RepoID: repo.ID})
}
// test before and since filters
@ -69,9 +69,9 @@ func TestAPIListRepoComments(t *testing.T) {
func TestAPIListIssueComments(t *testing.T) {
defer prepareTestEnv(t)()
comment := unittest.AssertExistsAndLoadBean(t, &models.Comment{},
unittest.Cond("type = ?", models.CommentTypeComment)).(*models.Comment)
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: comment.IssueID}).(*models.Issue)
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{},
unittest.Cond("type = ?", issues_model.CommentTypeComment)).(*issues_model.Comment)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}).(*issues_model.Issue)
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository)
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
@ -82,8 +82,8 @@ func TestAPIListIssueComments(t *testing.T) {
var comments []*api.Comment
DecodeJSON(t, resp, &comments)
expectedCount := unittest.GetCount(t, &models.Comment{IssueID: issue.ID},
unittest.Cond("type = ?", models.CommentTypeComment))
expectedCount := unittest.GetCount(t, &issues_model.Comment{IssueID: issue.ID},
unittest.Cond("type = ?", issues_model.CommentTypeComment))
assert.EqualValues(t, expectedCount, len(comments))
}
@ -91,7 +91,7 @@ func TestAPICreateComment(t *testing.T) {
defer prepareTestEnv(t)()
const commentBody = "Comment body"
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{}).(*models.Issue)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{}).(*issues_model.Issue)
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository)
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
@ -107,13 +107,13 @@ func TestAPICreateComment(t *testing.T) {
var updatedComment api.Comment
DecodeJSON(t, resp, &updatedComment)
assert.EqualValues(t, commentBody, updatedComment.Body)
unittest.AssertExistsAndLoadBean(t, &models.Comment{ID: updatedComment.ID, IssueID: issue.ID, Content: commentBody})
unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: updatedComment.ID, IssueID: issue.ID, Content: commentBody})
}
func TestAPIGetComment(t *testing.T) {
defer prepareTestEnv(t)()
comment := unittest.AssertExistsAndLoadBean(t, &models.Comment{ID: 2}).(*models.Comment)
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 2}).(*issues_model.Comment)
assert.NoError(t, comment.LoadIssue())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: comment.Issue.RepoID}).(*repo_model.Repository)
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
@ -141,9 +141,9 @@ func TestAPIEditComment(t *testing.T) {
defer prepareTestEnv(t)()
const newCommentBody = "This is the new comment body"
comment := unittest.AssertExistsAndLoadBean(t, &models.Comment{},
unittest.Cond("type = ?", models.CommentTypeComment)).(*models.Comment)
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: comment.IssueID}).(*models.Issue)
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{},
unittest.Cond("type = ?", issues_model.CommentTypeComment)).(*issues_model.Comment)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}).(*issues_model.Issue)
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository)
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
@ -160,15 +160,15 @@ func TestAPIEditComment(t *testing.T) {
DecodeJSON(t, resp, &updatedComment)
assert.EqualValues(t, comment.ID, updatedComment.ID)
assert.EqualValues(t, newCommentBody, updatedComment.Body)
unittest.AssertExistsAndLoadBean(t, &models.Comment{ID: comment.ID, IssueID: issue.ID, Content: newCommentBody})
unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: comment.ID, IssueID: issue.ID, Content: newCommentBody})
}
func TestAPIDeleteComment(t *testing.T) {
defer prepareTestEnv(t)()
comment := unittest.AssertExistsAndLoadBean(t, &models.Comment{},
unittest.Cond("type = ?", models.CommentTypeComment)).(*models.Comment)
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: comment.IssueID}).(*models.Issue)
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{},
unittest.Cond("type = ?", issues_model.CommentTypeComment)).(*issues_model.Comment)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}).(*issues_model.Issue)
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository)
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
@ -178,14 +178,14 @@ func TestAPIDeleteComment(t *testing.T) {
repoOwner.Name, repo.Name, comment.ID, token)
session.MakeRequest(t, req, http.StatusNoContent)
unittest.AssertNotExistsBean(t, &models.Comment{ID: comment.ID})
unittest.AssertNotExistsBean(t, &issues_model.Comment{ID: comment.ID})
}
func TestAPIListIssueTimeline(t *testing.T) {
defer prepareTestEnv(t)()
// load comment
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 1}).(*models.Issue)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue)
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository)
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
@ -199,6 +199,6 @@ func TestAPIListIssueTimeline(t *testing.T) {
// lists extracted directly from DB are the same
var comments []*api.TimelineComment
DecodeJSON(t, resp, &comments)
expectedCount := unittest.GetCount(t, &models.Comment{IssueID: issue.ID})
expectedCount := unittest.GetCount(t, &issues_model.Comment{IssueID: issue.ID})
assert.EqualValues(t, expectedCount, len(comments))
}

View file

@ -10,7 +10,7 @@ import (
"strings"
"testing"
"code.gitea.io/gitea/models"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
@ -37,7 +37,7 @@ func TestAPIModifyLabels(t *testing.T) {
resp := session.MakeRequest(t, req, http.StatusCreated)
apiLabel := new(api.Label)
DecodeJSON(t, resp, &apiLabel)
dbLabel := unittest.AssertExistsAndLoadBean(t, &models.Label{ID: apiLabel.ID, RepoID: repo.ID}).(*models.Label)
dbLabel := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: apiLabel.ID, RepoID: repo.ID}).(*issues_model.Label)
assert.EqualValues(t, dbLabel.Name, apiLabel.Name)
assert.EqualValues(t, strings.TrimLeft(dbLabel.Color, "#"), apiLabel.Color)
@ -92,8 +92,8 @@ func TestAPIAddIssueLabels(t *testing.T) {
assert.NoError(t, unittest.LoadFixtures())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{RepoID: repo.ID}).(*models.Issue)
_ = unittest.AssertExistsAndLoadBean(t, &models.Label{RepoID: repo.ID, ID: 2}).(*models.Label)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: repo.ID}).(*issues_model.Issue)
_ = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{RepoID: repo.ID, ID: 2}).(*issues_model.Label)
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
session := loginUser(t, owner.Name)
@ -106,17 +106,17 @@ func TestAPIAddIssueLabels(t *testing.T) {
resp := session.MakeRequest(t, req, http.StatusOK)
var apiLabels []*api.Label
DecodeJSON(t, resp, &apiLabels)
assert.Len(t, apiLabels, unittest.GetCount(t, &models.IssueLabel{IssueID: issue.ID}))
assert.Len(t, apiLabels, unittest.GetCount(t, &issues_model.IssueLabel{IssueID: issue.ID}))
unittest.AssertExistsAndLoadBean(t, &models.IssueLabel{IssueID: issue.ID, LabelID: 2})
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: 2})
}
func TestAPIReplaceIssueLabels(t *testing.T) {
assert.NoError(t, unittest.LoadFixtures())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{RepoID: repo.ID}).(*models.Issue)
label := unittest.AssertExistsAndLoadBean(t, &models.Label{RepoID: repo.ID}).(*models.Label)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: repo.ID}).(*issues_model.Issue)
label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{RepoID: repo.ID}).(*issues_model.Label)
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
session := loginUser(t, owner.Name)
@ -133,8 +133,8 @@ func TestAPIReplaceIssueLabels(t *testing.T) {
assert.EqualValues(t, label.ID, apiLabels[0].ID)
}
unittest.AssertCount(t, &models.IssueLabel{IssueID: issue.ID}, 1)
unittest.AssertExistsAndLoadBean(t, &models.IssueLabel{IssueID: issue.ID, LabelID: label.ID})
unittest.AssertCount(t, &issues_model.IssueLabel{IssueID: issue.ID}, 1)
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label.ID})
}
func TestAPIModifyOrgLabels(t *testing.T) {
@ -156,7 +156,7 @@ func TestAPIModifyOrgLabels(t *testing.T) {
resp := session.MakeRequest(t, req, http.StatusCreated)
apiLabel := new(api.Label)
DecodeJSON(t, resp, &apiLabel)
dbLabel := unittest.AssertExistsAndLoadBean(t, &models.Label{ID: apiLabel.ID, OrgID: owner.ID}).(*models.Label)
dbLabel := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: apiLabel.ID, OrgID: owner.ID}).(*issues_model.Label)
assert.EqualValues(t, dbLabel.Name, apiLabel.Name)
assert.EqualValues(t, strings.TrimLeft(dbLabel.Color, "#"), apiLabel.Color)

View file

@ -10,8 +10,8 @@ import (
"testing"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/convert"
@ -23,7 +23,7 @@ import (
func TestAPIIssuesReactions(t *testing.T) {
defer prepareTestEnv(t)()
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 1}).(*models.Issue)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue)
_ = issue.LoadRepo(db.DefaultContext)
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}).(*user_model.User)
@ -80,7 +80,7 @@ func TestAPIIssuesReactions(t *testing.T) {
func TestAPICommentReactions(t *testing.T) {
defer prepareTestEnv(t)()
comment := unittest.AssertExistsAndLoadBean(t, &models.Comment{ID: 2}).(*models.Comment)
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 2}).(*issues_model.Comment)
_ = comment.LoadIssue()
issue := comment.Issue
_ = issue.LoadRepo(db.DefaultContext)

View file

@ -8,8 +8,8 @@ import (
"net/http"
"testing"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
@ -30,8 +30,8 @@ func TestAPIListStopWatches(t *testing.T) {
resp := session.MakeRequest(t, req, http.StatusOK)
var apiWatches []*api.StopWatch
DecodeJSON(t, resp, &apiWatches)
stopwatch := unittest.AssertExistsAndLoadBean(t, &models.Stopwatch{UserID: owner.ID}).(*models.Stopwatch)
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: stopwatch.IssueID}).(*models.Issue)
stopwatch := unittest.AssertExistsAndLoadBean(t, &issues_model.Stopwatch{UserID: owner.ID}).(*issues_model.Stopwatch)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: stopwatch.IssueID}).(*issues_model.Issue)
if assert.Len(t, apiWatches, 1) {
assert.EqualValues(t, stopwatch.CreatedUnix.AsTime().Unix(), apiWatches[0].Created.Unix())
assert.EqualValues(t, issue.Index, apiWatches[0].IssueIndex)
@ -45,7 +45,7 @@ func TestAPIListStopWatches(t *testing.T) {
func TestAPIStopStopWatches(t *testing.T) {
defer prepareTestEnv(t)()
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 2}).(*models.Issue)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}).(*issues_model.Issue)
_ = issue.LoadRepo(db.DefaultContext)
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}).(*user_model.User)
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
@ -61,7 +61,7 @@ func TestAPIStopStopWatches(t *testing.T) {
func TestAPICancelStopWatches(t *testing.T) {
defer prepareTestEnv(t)()
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 1}).(*models.Issue)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue)
_ = issue.LoadRepo(db.DefaultContext)
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}).(*user_model.User)
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
@ -77,7 +77,7 @@ func TestAPICancelStopWatches(t *testing.T) {
func TestAPIStartStopWatches(t *testing.T) {
defer prepareTestEnv(t)()
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 3}).(*models.Issue)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3}).(*issues_model.Issue)
_ = issue.LoadRepo(db.DefaultContext)
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}).(*user_model.User)
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)

View file

@ -9,7 +9,7 @@ import (
"net/http"
"testing"
"code.gitea.io/gitea/models"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
@ -21,18 +21,18 @@ import (
func TestAPIIssueSubscriptions(t *testing.T) {
defer prepareTestEnv(t)()
issue1 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 1}).(*models.Issue)
issue2 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 2}).(*models.Issue)
issue3 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 3}).(*models.Issue)
issue4 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 4}).(*models.Issue)
issue5 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 8}).(*models.Issue)
issue1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue)
issue2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}).(*issues_model.Issue)
issue3 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3}).(*issues_model.Issue)
issue4 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4}).(*issues_model.Issue)
issue5 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 8}).(*issues_model.Issue)
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue1.PosterID}).(*user_model.User)
session := loginUser(t, owner.Name)
token := getTokenForLoggedInUser(t, session)
testSubscription := func(issue *models.Issue, isWatching bool) {
testSubscription := func(issue *issues_model.Issue, isWatching bool) {
issueRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository)
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/subscriptions/check?token=%s", issueRepo.OwnerName, issueRepo.Name, issue.Index, token)

View file

@ -11,7 +11,8 @@ import (
"testing"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
@ -34,9 +35,9 @@ func TestAPIListIssues(t *testing.T) {
resp := session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
var apiIssues []*api.Issue
DecodeJSON(t, resp, &apiIssues)
assert.Len(t, apiIssues, unittest.GetCount(t, &models.Issue{RepoID: repo.ID}))
assert.Len(t, apiIssues, unittest.GetCount(t, &issues_model.Issue{RepoID: repo.ID}))
for _, apiIssue := range apiIssues {
unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: apiIssue.ID, RepoID: repo.ID})
unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: apiIssue.ID, RepoID: repo.ID})
}
// test milestone filter
@ -91,7 +92,7 @@ func TestAPICreateIssue(t *testing.T) {
assert.Equal(t, body, apiIssue.Body)
assert.Equal(t, title, apiIssue.Title)
unittest.AssertExistsAndLoadBean(t, &models.Issue{
unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{
RepoID: repoBefore.ID,
AssigneeID: owner.ID,
Content: body,
@ -106,10 +107,10 @@ func TestAPICreateIssue(t *testing.T) {
func TestAPIEditIssue(t *testing.T) {
defer prepareTestEnv(t)()
issueBefore := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 10}).(*models.Issue)
issueBefore := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10}).(*issues_model.Issue)
repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID}).(*repo_model.Repository)
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}).(*user_model.User)
assert.NoError(t, issueBefore.LoadAttributes())
assert.NoError(t, issueBefore.LoadAttributes(db.DefaultContext))
assert.Equal(t, int64(1019307200), int64(issueBefore.DeadlineUnix))
assert.Equal(t, api.StateOpen, issueBefore.State())
@ -137,12 +138,12 @@ func TestAPIEditIssue(t *testing.T) {
var apiIssue api.Issue
DecodeJSON(t, resp, &apiIssue)
issueAfter := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 10}).(*models.Issue)
issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10}).(*issues_model.Issue)
repoAfter := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID}).(*repo_model.Repository)
// check deleted user
assert.Equal(t, int64(500), issueAfter.PosterID)
assert.NoError(t, issueAfter.LoadAttributes())
assert.NoError(t, issueAfter.LoadAttributes(db.DefaultContext))
assert.Equal(t, int64(-1), issueAfter.PosterID)
assert.Equal(t, int64(-1), issueBefore.PosterID)
assert.Equal(t, int64(-1), apiIssue.Poster.ID)

View file

@ -10,8 +10,8 @@ import (
"testing"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
api "code.gitea.io/gitea/modules/structs"
@ -23,7 +23,7 @@ func TestAPIGetTrackedTimes(t *testing.T) {
defer prepareTestEnv(t)()
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
issue2 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 2}).(*models.Issue)
issue2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}).(*issues_model.Issue)
assert.NoError(t, issue2.LoadRepo(db.DefaultContext))
session := loginUser(t, user2.Name)
@ -33,7 +33,7 @@ func TestAPIGetTrackedTimes(t *testing.T) {
resp := session.MakeRequest(t, req, http.StatusOK)
var apiTimes api.TrackedTimeList
DecodeJSON(t, resp, &apiTimes)
expect, err := models.GetTrackedTimes(db.DefaultContext, &models.FindTrackedTimesOptions{IssueID: issue2.ID})
expect, err := issues_model.GetTrackedTimes(db.DefaultContext, &issues_model.FindTrackedTimesOptions{IssueID: issue2.ID})
assert.NoError(t, err)
assert.Len(t, apiTimes, 3)
@ -64,8 +64,8 @@ func TestAPIGetTrackedTimes(t *testing.T) {
func TestAPIDeleteTrackedTime(t *testing.T) {
defer prepareTestEnv(t)()
time6 := unittest.AssertExistsAndLoadBean(t, &models.TrackedTime{ID: 6}).(*models.TrackedTime)
issue2 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 2}).(*models.Issue)
time6 := unittest.AssertExistsAndLoadBean(t, &issues_model.TrackedTime{ID: 6}).(*issues_model.TrackedTime)
issue2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}).(*issues_model.Issue)
assert.NoError(t, issue2.LoadRepo(db.DefaultContext))
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
@ -76,14 +76,14 @@ func TestAPIDeleteTrackedTime(t *testing.T) {
req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/issues/%d/times/%d?token=%s", user2.Name, issue2.Repo.Name, issue2.Index, time6.ID, token)
session.MakeRequest(t, req, http.StatusForbidden)
time3 := unittest.AssertExistsAndLoadBean(t, &models.TrackedTime{ID: 3}).(*models.TrackedTime)
time3 := unittest.AssertExistsAndLoadBean(t, &issues_model.TrackedTime{ID: 3}).(*issues_model.TrackedTime)
req = NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/issues/%d/times/%d?token=%s", user2.Name, issue2.Repo.Name, issue2.Index, time3.ID, token)
session.MakeRequest(t, req, http.StatusNoContent)
// Delete non existing time
session.MakeRequest(t, req, http.StatusNotFound)
// Reset time of user 2 on issue 2
trackedSeconds, err := models.GetTrackedSeconds(db.DefaultContext, models.FindTrackedTimesOptions{IssueID: 2, UserID: 2})
trackedSeconds, err := issues_model.GetTrackedSeconds(db.DefaultContext, issues_model.FindTrackedTimesOptions{IssueID: 2, UserID: 2})
assert.NoError(t, err)
assert.Equal(t, int64(3661), trackedSeconds)
@ -91,7 +91,7 @@ func TestAPIDeleteTrackedTime(t *testing.T) {
session.MakeRequest(t, req, http.StatusNoContent)
session.MakeRequest(t, req, http.StatusNotFound)
trackedSeconds, err = models.GetTrackedSeconds(db.DefaultContext, models.FindTrackedTimesOptions{IssueID: 2, UserID: 2})
trackedSeconds, err = issues_model.GetTrackedSeconds(db.DefaultContext, issues_model.FindTrackedTimesOptions{IssueID: 2, UserID: 2})
assert.NoError(t, err)
assert.Equal(t, int64(0), trackedSeconds)
}
@ -99,7 +99,7 @@ func TestAPIDeleteTrackedTime(t *testing.T) {
func TestAPIAddTrackedTimes(t *testing.T) {
defer prepareTestEnv(t)()
issue2 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 2}).(*models.Issue)
issue2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}).(*issues_model.Issue)
assert.NoError(t, issue2.LoadRepo(db.DefaultContext))
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)

View file

@ -8,7 +8,7 @@ import (
"net/http"
"testing"
"code.gitea.io/gitea/models"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
api "code.gitea.io/gitea/modules/structs"
@ -18,7 +18,7 @@ import (
func TestAPIPullCommits(t *testing.T) {
defer prepareTestEnv(t)()
pullIssue := unittest.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 2}).(*models.PullRequest)
pullIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}).(*issues_model.PullRequest)
assert.NoError(t, pullIssue.LoadIssue())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue.HeadRepoID}).(*repo_model.Repository)

View file

@ -9,7 +9,8 @@ import (
"net/http"
"testing"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/json"
@ -20,8 +21,8 @@ import (
func TestAPIPullReview(t *testing.T) {
defer prepareTestEnv(t)()
pullIssue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 3}).(*models.Issue)
assert.NoError(t, pullIssue.LoadAttributes())
pullIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3}).(*issues_model.Issue)
assert.NoError(t, pullIssue.LoadAttributes(db.DefaultContext))
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue.RepoID}).(*repo_model.Repository)
// test ListPullReviews
@ -64,7 +65,7 @@ func TestAPIPullReview(t *testing.T) {
assert.EqualValues(t, *reviews[5], review)
// test GetPullReviewComments
comment := unittest.AssertExistsAndLoadBean(t, &models.Comment{ID: 7}).(*models.Comment)
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 7}).(*issues_model.Comment)
req = NewRequestf(t, http.MethodGet, "/api/v1/repos/%s/%s/pulls/%d/reviews/%d/comments?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, 10, token)
resp = session.MakeRequest(t, req, http.StatusOK)
var reviewComments []*api.PullReviewComment
@ -199,8 +200,8 @@ func TestAPIPullReview(t *testing.T) {
// test get review requests
// to make it simple, use same api with get review
pullIssue12 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 12}).(*models.Issue)
assert.NoError(t, pullIssue12.LoadAttributes())
pullIssue12 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 12}).(*issues_model.Issue)
assert.NoError(t, pullIssue12.LoadAttributes(db.DefaultContext))
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue12.RepoID}).(*repo_model.Repository)
req = NewRequestf(t, http.MethodGet, "/api/v1/repos/%s/%s/pulls/%d/reviews?token=%s", repo3.OwnerName, repo3.Name, pullIssue12.Index, token)
@ -223,8 +224,8 @@ func TestAPIPullReview(t *testing.T) {
func TestAPIPullReviewRequest(t *testing.T) {
defer prepareTestEnv(t)()
pullIssue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 3}).(*models.Issue)
assert.NoError(t, pullIssue.LoadAttributes())
pullIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3}).(*issues_model.Issue)
assert.NoError(t, pullIssue.LoadAttributes(db.DefaultContext))
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue.RepoID}).(*repo_model.Repository)
// Test add Review Request
@ -268,8 +269,8 @@ func TestAPIPullReviewRequest(t *testing.T) {
session.MakeRequest(t, req, http.StatusNoContent)
// Test team review request
pullIssue12 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 12}).(*models.Issue)
assert.NoError(t, pullIssue12.LoadAttributes())
pullIssue12 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 12}).(*issues_model.Issue)
assert.NoError(t, pullIssue12.LoadAttributes(db.DefaultContext))
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue12.RepoID}).(*repo_model.Repository)
// Test add Team Review Request

View file

@ -9,7 +9,7 @@ import (
"net/http"
"testing"
"code.gitea.io/gitea/models"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
@ -33,7 +33,7 @@ func TestAPIViewPulls(t *testing.T) {
var pulls []*api.PullRequest
DecodeJSON(t, resp, &pulls)
expectedLen := unittest.GetCount(t, &models.Issue{RepoID: repo.ID}, unittest.Cond("is_pull = ?", true))
expectedLen := unittest.GetCount(t, &issues_model.Issue{RepoID: repo.ID}, unittest.Cond("is_pull = ?", true))
assert.Len(t, pulls, expectedLen)
}
@ -42,7 +42,7 @@ func TestAPIMergePullWIP(t *testing.T) {
defer prepareTestEnv(t)()
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
pr := unittest.AssertExistsAndLoadBean(t, &models.PullRequest{Status: models.PullRequestStatusMergeable}, unittest.Cond("has_merged = ?", false)).(*models.PullRequest)
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{Status: issues_model.PullRequestStatusMergeable}, unittest.Cond("has_merged = ?", false)).(*issues_model.PullRequest)
pr.LoadIssue()
issue_service.ChangeTitle(pr.Issue, owner, setting.Repository.PullRequest.WorkInProgressPrefixes[0]+" "+pr.Issue.Title)

View file

@ -9,7 +9,7 @@ import (
"net/http"
"testing"
"code.gitea.io/gitea/models"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
@ -24,7 +24,7 @@ func assertUserDeleted(t *testing.T, userID int64) {
unittest.AssertNotExistsBean(t, &repo_model.Repository{OwnerID: userID})
unittest.AssertNotExistsBean(t, &access_model.Access{UserID: userID})
unittest.AssertNotExistsBean(t, &organization.OrgUser{UID: userID})
unittest.AssertNotExistsBean(t, &models.IssueUser{UID: userID})
unittest.AssertNotExistsBean(t, &issues_model.IssueUser{UID: userID})
unittest.AssertNotExistsBean(t, &organization.TeamUser{UID: userID})
unittest.AssertNotExistsBean(t, &repo_model.Star{UID: userID})
}

View file

@ -17,8 +17,8 @@ import (
"testing"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
@ -715,7 +715,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headB
defer gitRepo.Close()
var (
pr1, pr2 *models.PullRequest
pr1, pr2 *issues_model.PullRequest
commit string
)
repo, err := repo_model.GetRepositoryByOwnerAndName(ctx.Username, ctx.Reponame)
@ -723,7 +723,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headB
return
}
pullNum := unittest.GetCount(t, &models.PullRequest{})
pullNum := unittest.GetCount(t, &issues_model.PullRequest{})
t.Run("CreateHeadBranch", doGitCreateBranch(dstPath, headBranch))
@ -759,11 +759,11 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headB
if !assert.NoError(t, err) {
return
}
unittest.AssertCount(t, &models.PullRequest{}, pullNum+1)
pr1 = unittest.AssertExistsAndLoadBean(t, &models.PullRequest{
unittest.AssertCount(t, &issues_model.PullRequest{}, pullNum+1)
pr1 = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{
HeadRepoID: repo.ID,
Flow: models.PullRequestFlowAGit,
}).(*models.PullRequest)
Flow: issues_model.PullRequestFlowAGit,
}).(*issues_model.PullRequest)
if !assert.NotEmpty(t, pr1) {
return
}
@ -780,12 +780,12 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headB
if !assert.NoError(t, err) {
return
}
unittest.AssertCount(t, &models.PullRequest{}, pullNum+2)
pr2 = unittest.AssertExistsAndLoadBean(t, &models.PullRequest{
unittest.AssertCount(t, &issues_model.PullRequest{}, pullNum+2)
pr2 = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{
HeadRepoID: repo.ID,
Index: pr1.Index + 1,
Flow: models.PullRequestFlowAGit,
}).(*models.PullRequest)
Flow: issues_model.PullRequestFlowAGit,
}).(*issues_model.PullRequest)
if !assert.NotEmpty(t, pr2) {
return
}
@ -833,7 +833,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headB
if !assert.NoError(t, err) {
return
}
unittest.AssertCount(t, &models.PullRequest{}, pullNum+2)
unittest.AssertCount(t, &issues_model.PullRequest{}, pullNum+2)
prMsg, err := doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr1.Index)(t)
if !assert.NoError(t, err) {
return
@ -845,7 +845,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headB
if !assert.NoError(t, err) {
return
}
unittest.AssertCount(t, &models.PullRequest{}, pullNum+2)
unittest.AssertCount(t, &issues_model.PullRequest{}, pullNum+2)
prMsg, err = doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr2.Index)(t)
if !assert.NoError(t, err) {
return

View file

@ -14,7 +14,8 @@ import (
"testing"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
@ -34,16 +35,16 @@ func getIssuesSelection(t testing.TB, htmlDoc *HTMLDoc) *goquery.Selection {
return issueList.Find("li").Find(".title")
}
func getIssue(t *testing.T, repoID int64, issueSelection *goquery.Selection) *models.Issue {
func getIssue(t *testing.T, repoID int64, issueSelection *goquery.Selection) *issues_model.Issue {
href, exists := issueSelection.Attr("href")
assert.True(t, exists)
indexStr := href[strings.LastIndexByte(href, '/')+1:]
index, err := strconv.Atoi(indexStr)
assert.NoError(t, err, "Invalid issue href: %s", href)
return unittest.AssertExistsAndLoadBean(t, &models.Issue{RepoID: repoID, Index: int64(index)}).(*models.Issue)
return unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: repoID, Index: int64(index)}).(*issues_model.Issue)
}
func assertMatch(t testing.TB, issue *models.Issue, keyword string) {
func assertMatch(t testing.TB, issue *issues_model.Issue, keyword string) {
matches := strings.Contains(strings.ToLower(issue.Title), keyword) ||
strings.Contains(strings.ToLower(issue.Content), keyword)
for _, comment := range issue.Comments {
@ -75,7 +76,7 @@ func TestViewIssuesSortByType(t *testing.T) {
htmlDoc := NewHTMLParser(t, resp.Body)
issuesSelection := getIssuesSelection(t, htmlDoc)
expectedNumIssues := unittest.GetCount(t,
&models.Issue{RepoID: repo.ID, PosterID: user.ID},
&issues_model.Issue{RepoID: repo.ID, PosterID: user.ID},
unittest.Cond("is_closed=?", false),
unittest.Cond("is_pull=?", false),
)
@ -94,10 +95,10 @@ func TestViewIssuesKeyword(t *testing.T) {
defer prepareTestEnv(t)()
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{
RepoID: repo.ID,
Index: 1,
}).(*models.Issue)
}).(*issues_model.Issue)
issues.UpdateIssueIndexer(issue)
time.Sleep(time.Second * 1)
const keyword = "first"
@ -238,7 +239,7 @@ func TestIssueCrossReference(t *testing.T) {
// Ref from issue title
issueRefURL, issueRef := testIssueWithBean(t, "user2", 1, fmt.Sprintf("Title ref #%d", issueBase.Index), "Description")
unittest.AssertExistsAndLoadBean(t, &models.Comment{
unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{
IssueID: issueBase.ID,
RefRepoID: 1,
RefIssueID: issueRef.ID,
@ -249,7 +250,7 @@ func TestIssueCrossReference(t *testing.T) {
// Edit title, neuter ref
testIssueChangeInfo(t, "user2", issueRefURL, "title", "Title no ref")
unittest.AssertExistsAndLoadBean(t, &models.Comment{
unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{
IssueID: issueBase.ID,
RefRepoID: 1,
RefIssueID: issueRef.ID,
@ -260,7 +261,7 @@ func TestIssueCrossReference(t *testing.T) {
// Ref from issue content
issueRefURL, issueRef = testIssueWithBean(t, "user2", 1, "TitleXRef", fmt.Sprintf("Description ref #%d", issueBase.Index))
unittest.AssertExistsAndLoadBean(t, &models.Comment{
unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{
IssueID: issueBase.ID,
RefRepoID: 1,
RefIssueID: issueRef.ID,
@ -271,7 +272,7 @@ func TestIssueCrossReference(t *testing.T) {
// Edit content, neuter ref
testIssueChangeInfo(t, "user2", issueRefURL, "content", "Description no ref")
unittest.AssertExistsAndLoadBean(t, &models.Comment{
unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{
IssueID: issueBase.ID,
RefRepoID: 1,
RefIssueID: issueRef.ID,
@ -283,7 +284,7 @@ func TestIssueCrossReference(t *testing.T) {
// Ref from a comment
session := loginUser(t, "user2")
commentID := testIssueAddComment(t, session, issueRefURL, fmt.Sprintf("Adding ref from comment #%d", issueBase.Index), "")
comment := &models.Comment{
comment := &issues_model.Comment{
IssueID: issueBase.ID,
RefRepoID: 1,
RefIssueID: issueRef.ID,
@ -295,7 +296,7 @@ func TestIssueCrossReference(t *testing.T) {
// Ref from a different repository
_, issueRef = testIssueWithBean(t, "user12", 10, "TitleXRef", fmt.Sprintf("Description ref user2/repo1#%d", issueBase.Index))
unittest.AssertExistsAndLoadBean(t, &models.Comment{
unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{
IssueID: issueBase.ID,
RefRepoID: 10,
RefIssueID: issueRef.ID,
@ -305,13 +306,13 @@ func TestIssueCrossReference(t *testing.T) {
})
}
func testIssueWithBean(t *testing.T, user string, repoID int64, title, content string) (string, *models.Issue) {
func testIssueWithBean(t *testing.T, user string, repoID int64, title, content string) (string, *issues_model.Issue) {
session := loginUser(t, user)
issueURL := testNewIssue(t, session, user, fmt.Sprintf("repo%d", repoID), title, content)
indexStr := issueURL[strings.LastIndexByte(issueURL, '/')+1:]
index, err := strconv.Atoi(indexStr)
assert.NoError(t, err, "Invalid issue href: %s", issueURL)
issue := &models.Issue{RepoID: repoID, Index: int64(index)}
issue := &issues_model.Issue{RepoID: repoID, Index: int64(index)}
unittest.AssertExistsAndLoadBean(t, issue)
return issueURL, issue
}
@ -511,10 +512,10 @@ func TestSearchIssuesWithLabels(t *testing.T) {
func TestGetIssueInfo(t *testing.T) {
defer prepareTestEnv(t)()
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 10}).(*models.Issue)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10}).(*issues_model.Issue)
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository)
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
assert.NoError(t, issue.LoadAttributes())
assert.NoError(t, issue.LoadAttributes(db.DefaultContext))
assert.Equal(t, int64(1019307200), int64(issue.DeadlineUnix))
assert.Equal(t, api.StateOpen, issue.State())
@ -532,10 +533,10 @@ func TestGetIssueInfo(t *testing.T) {
func TestUpdateIssueDeadline(t *testing.T) {
defer prepareTestEnv(t)()
issueBefore := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 10}).(*models.Issue)
issueBefore := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10}).(*issues_model.Issue)
repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID}).(*repo_model.Repository)
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}).(*user_model.User)
assert.NoError(t, issueBefore.LoadAttributes())
assert.NoError(t, issueBefore.LoadAttributes(db.DefaultContext))
assert.Equal(t, int64(1019307200), int64(issueBefore.DeadlineUnix))
assert.Equal(t, api.StateOpen, issueBefore.State())

View file

@ -19,6 +19,7 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
@ -233,12 +234,12 @@ func TestCantMergeConflict(t *testing.T) {
Name: "repo1",
}).(*repo_model.Repository)
pr := unittest.AssertExistsAndLoadBean(t, &models.PullRequest{
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{
HeadRepoID: repo1.ID,
BaseRepoID: repo1.ID,
HeadBranch: "conflict",
BaseBranch: "base",
}).(*models.PullRequest)
}).(*issues_model.PullRequest)
gitRepo, err := git.OpenRepository(git.DefaultContext, repo_model.RepoPath(user1.Name, repo1.Name))
assert.NoError(t, err)
@ -335,12 +336,12 @@ func TestCantMergeUnrelated(t *testing.T) {
// Now this PR could be marked conflict - or at least a race may occur - so drop down to pure code at this point...
gitRepo, err := git.OpenRepository(git.DefaultContext, path)
assert.NoError(t, err)
pr := unittest.AssertExistsAndLoadBean(t, &models.PullRequest{
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{
HeadRepoID: repo1.ID,
BaseRepoID: repo1.ID,
HeadBranch: "unrelated",
BaseBranch: "base",
}).(*models.PullRequest)
}).(*issues_model.PullRequest)
err = pull.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleMerge, "", "UNRELATED")
assert.Error(t, err, "Merge should return an error due to unrelated")
@ -387,7 +388,7 @@ func TestConflictChecking(t *testing.T) {
assert.NoError(t, err)
// create Pull to merge the important-secrets branch into main branch.
pullIssue := &models.Issue{
pullIssue := &issues_model.Issue{
RepoID: baseRepo.ID,
Title: "PR with conflict!",
PosterID: user.ID,
@ -395,26 +396,26 @@ func TestConflictChecking(t *testing.T) {
IsPull: true,
}
pullRequest := &models.PullRequest{
pullRequest := &issues_model.PullRequest{
HeadRepoID: baseRepo.ID,
BaseRepoID: baseRepo.ID,
HeadBranch: "important-secrets",
BaseBranch: "main",
HeadRepo: baseRepo,
BaseRepo: baseRepo,
Type: models.PullRequestGitea,
Type: issues_model.PullRequestGitea,
}
err = pull.NewPullRequest(git.DefaultContext, baseRepo, pullIssue, nil, nil, pullRequest, nil)
assert.NoError(t, err)
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{Title: "PR with conflict!"}).(*models.Issue)
conflictingPR, err := models.GetPullRequestByIssueID(db.DefaultContext, issue.ID)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{Title: "PR with conflict!"}).(*issues_model.Issue)
conflictingPR, err := issues_model.GetPullRequestByIssueID(db.DefaultContext, issue.ID)
assert.NoError(t, err)
// Ensure conflictedFiles is populated.
assert.Equal(t, 1, len(conflictingPR.ConflictedFiles))
// Check if status is correct.
assert.Equal(t, models.PullRequestStatusConflict, conflictingPR.Status)
assert.Equal(t, issues_model.PullRequestStatusConflict, conflictingPR.Status)
// Ensure that mergeable returns false
assert.False(t, conflictingPR.Mergeable())
})

View file

@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
@ -78,7 +79,7 @@ func TestAPIPullUpdateByRebase(t *testing.T) {
})
}
func createOutdatedPR(t *testing.T, actor, forkOrg *user_model.User) *models.PullRequest {
func createOutdatedPR(t *testing.T, actor, forkOrg *user_model.User) *issues_model.PullRequest {
baseRepo, err := repo_service.CreateRepository(actor, actor, models.CreateRepoOptions{
Name: "repo-pr-update",
Description: "repo-tmp-pr-update description",
@ -146,27 +147,27 @@ func createOutdatedPR(t *testing.T, actor, forkOrg *user_model.User) *models.Pul
assert.NoError(t, err)
// create Pull
pullIssue := &models.Issue{
pullIssue := &issues_model.Issue{
RepoID: baseRepo.ID,
Title: "Test Pull -to-update-",
PosterID: actor.ID,
Poster: actor,
IsPull: true,
}
pullRequest := &models.PullRequest{
pullRequest := &issues_model.PullRequest{
HeadRepoID: headRepo.ID,
BaseRepoID: baseRepo.ID,
HeadBranch: "newBranch",
BaseBranch: "master",
HeadRepo: headRepo,
BaseRepo: baseRepo,
Type: models.PullRequestGitea,
Type: issues_model.PullRequestGitea,
}
err = pull_service.NewPullRequest(git.DefaultContext, baseRepo, pullIssue, nil, nil, pullRequest, nil)
assert.NoError(t, err)
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{Title: "Test Pull -to-update-"}).(*models.Issue)
pr, err := models.GetPullRequestByIssueID(db.DefaultContext, issue.ID)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{Title: "Test Pull -to-update-"}).(*issues_model.Issue)
pr, err := issues_model.GetPullRequestByIssueID(db.DefaultContext, issue.ID)
assert.NoError(t, err)
return pr

View file

@ -8,7 +8,7 @@ import (
"net/http"
"testing"
"code.gitea.io/gitea/models"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
@ -237,8 +237,8 @@ func TestListStopWatches(t *testing.T) {
resp := session.MakeRequest(t, req, http.StatusOK)
var apiWatches []*api.StopWatch
DecodeJSON(t, resp, &apiWatches)
stopwatch := unittest.AssertExistsAndLoadBean(t, &models.Stopwatch{UserID: owner.ID}).(*models.Stopwatch)
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: stopwatch.IssueID}).(*models.Issue)
stopwatch := unittest.AssertExistsAndLoadBean(t, &issues_model.Stopwatch{UserID: owner.ID}).(*issues_model.Stopwatch)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: stopwatch.IssueID}).(*issues_model.Issue)
if assert.Len(t, apiWatches, 1) {
assert.EqualValues(t, stopwatch.CreatedUnix.AsTime().Unix(), apiWatches[0].Created.Unix())
assert.EqualValues(t, issue.Index, apiWatches[0].IssueIndex)

View file

@ -15,6 +15,7 @@ import (
"time"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
@ -76,7 +77,7 @@ type Action struct {
RepoID int64 `xorm:"INDEX"`
Repo *repo_model.Repository `xorm:"-"`
CommentID int64 `xorm:"INDEX"`
Comment *Comment `xorm:"-"`
Comment *issues_model.Comment `xorm:"-"`
IsDeleted bool `xorm:"INDEX NOT NULL DEFAULT false"`
RefName string
IsPrivate bool `xorm:"INDEX NOT NULL DEFAULT false"`
@ -223,7 +224,7 @@ func (a *Action) getCommentLink(ctx context.Context) string {
return "#"
}
if a.Comment == nil && a.CommentID != 0 {
a.Comment, _ = GetCommentByID(ctx, a.CommentID)
a.Comment, _ = issues_model.GetCommentByID(ctx, a.CommentID)
}
if a.Comment != nil {
return a.Comment.HTMLURL()
@ -238,7 +239,7 @@ func (a *Action) getCommentLink(ctx context.Context) string {
return "#"
}
issue, err := getIssueByID(ctx, issueID)
issue, err := issues_model.GetIssueByID(ctx, issueID)
if err != nil {
return "#"
}
@ -295,7 +296,7 @@ func (a *Action) GetIssueInfos() []string {
// with the action.
func (a *Action) GetIssueTitle() string {
index, _ := strconv.ParseInt(a.GetIssueInfos()[0], 10, 64)
issue, err := GetIssueByIndex(a.RepoID, index)
issue, err := issues_model.GetIssueByIndex(a.RepoID, index)
if err != nil {
log.Error("GetIssueByIndex: %v", err)
return "500 when get issue"
@ -307,7 +308,7 @@ func (a *Action) GetIssueTitle() string {
// this action.
func (a *Action) GetIssueContent() string {
index, _ := strconv.ParseInt(a.GetIssueInfos()[0], 10, 64)
issue, err := GetIssueByIndex(a.RepoID, index)
issue, err := issues_model.GetIssueByIndex(a.RepoID, index)
if err != nil {
log.Error("GetIssueByIndex: %v", err)
return "500 when get issue"
@ -572,3 +573,20 @@ func NotifyWatchersActions(acts []*Action) error {
}
return committer.Commit()
}
// DeleteIssueActions delete all actions related with issueID
func DeleteIssueActions(ctx context.Context, repoID, issueID int64) error {
// delete actions assigned to this issue
subQuery := builder.Select("`id`").
From("`comment`").
Where(builder.Eq{"`issue_id`": issueID})
if _, err := db.GetEngine(ctx).In("comment_id", subQuery).Delete(&Action{}); err != nil {
return err
}
_, err := db.GetEngine(ctx).Table("action").Where("repo_id = ?", repoID).
In("op_type", ActionCreateIssue, ActionCreatePullRequest).
Where("content LIKE ?", strconv.FormatInt(issueID, 10)+"|%").
Delete(&Action{})
return err
}

View file

@ -228,3 +228,46 @@ func TestGetFeedsCorrupted(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, actions, 0)
}
func TestConsistencyUpdateAction(t *testing.T) {
if !setting.Database.UseSQLite3 {
t.Skip("Test is only for SQLite database.")
}
assert.NoError(t, unittest.PrepareTestDatabase())
id := 8
unittest.AssertExistsAndLoadBean(t, &Action{
ID: int64(id),
})
_, err := db.GetEngine(db.DefaultContext).Exec(`UPDATE action SET created_unix = "" WHERE id = ?`, id)
assert.NoError(t, err)
actions := make([]*Action, 0, 1)
//
// XORM returns an error when created_unix is a string
//
err = db.GetEngine(db.DefaultContext).Where("id = ?", id).Find(&actions)
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "type string to a int64: invalid syntax")
}
//
// Get rid of incorrectly set created_unix
//
count, err := CountActionCreatedUnixString()
assert.NoError(t, err)
assert.EqualValues(t, 1, count)
count, err = FixActionCreatedUnixString()
assert.NoError(t, err)
assert.EqualValues(t, 1, count)
count, err = CountActionCreatedUnixString()
assert.NoError(t, err)
assert.EqualValues(t, 0, count)
count, err = FixActionCreatedUnixString()
assert.NoError(t, err)
assert.EqualValues(t, 0, count)
//
// XORM must be happy now
//
assert.NoError(t, db.GetEngine(db.DefaultContext).Where("id = ?", id).Find(&actions))
unittest.CheckConsistencyFor(t, &Action{})
}

View file

@ -1,80 +0,0 @@
// Copyright 2022 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 (
"context"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/modules/log"
)
// HasEnoughApprovals returns true if pr has enough granted approvals.
func HasEnoughApprovals(ctx context.Context, protectBranch *git_model.ProtectedBranch, pr *PullRequest) bool {
if protectBranch.RequiredApprovals == 0 {
return true
}
return GetGrantedApprovalsCount(ctx, protectBranch, pr) >= protectBranch.RequiredApprovals
}
// GetGrantedApprovalsCount returns the number of granted approvals for pr. A granted approval must be authored by a user in an approval whitelist.
func GetGrantedApprovalsCount(ctx context.Context, protectBranch *git_model.ProtectedBranch, pr *PullRequest) int64 {
sess := db.GetEngine(ctx).Where("issue_id = ?", pr.IssueID).
And("type = ?", ReviewTypeApprove).
And("official = ?", true).
And("dismissed = ?", false)
if protectBranch.DismissStaleApprovals {
sess = sess.And("stale = ?", false)
}
approvals, err := sess.Count(new(Review))
if err != nil {
log.Error("GetGrantedApprovalsCount: %v", err)
return 0
}
return approvals
}
// MergeBlockedByRejectedReview returns true if merge is blocked by rejected reviews
func MergeBlockedByRejectedReview(ctx context.Context, protectBranch *git_model.ProtectedBranch, pr *PullRequest) bool {
if !protectBranch.BlockOnRejectedReviews {
return false
}
rejectExist, err := db.GetEngine(ctx).Where("issue_id = ?", pr.IssueID).
And("type = ?", ReviewTypeReject).
And("official = ?", true).
And("dismissed = ?", false).
Exist(new(Review))
if err != nil {
log.Error("MergeBlockedByRejectedReview: %v", err)
return true
}
return rejectExist
}
// MergeBlockedByOfficialReviewRequests block merge because of some review request to official reviewer
// of from official review
func MergeBlockedByOfficialReviewRequests(ctx context.Context, protectBranch *git_model.ProtectedBranch, pr *PullRequest) bool {
if !protectBranch.BlockOnOfficialReviewRequests {
return false
}
has, err := db.GetEngine(ctx).Where("issue_id = ?", pr.IssueID).
And("type = ?", ReviewTypeRequest).
And("official = ?", true).
Exist(new(Review))
if err != nil {
log.Error("MergeBlockedByOfficialReviewRequests: %v", err)
return true
}
return has
}
// MergeBlockedByOutdatedBranch returns true if merge is blocked by an outdated head branch
func MergeBlockedByOutdatedBranch(protectBranch *git_model.ProtectedBranch, pr *PullRequest) bool {
return protectBranch.BlockOnOutdatedBranch && pr.CommitsBehind > 0
}

View file

@ -5,7 +5,6 @@
package models
import (
admin_model "code.gitea.io/gitea/models/admin"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
@ -14,151 +13,6 @@ import (
"xorm.io/builder"
)
// CountOrphanedLabels return count of labels witch are broken and not accessible via ui anymore
func CountOrphanedLabels() (int64, error) {
noref, err := db.GetEngine(db.DefaultContext).Table("label").Where("repo_id=? AND org_id=?", 0, 0).Count("label.id")
if err != nil {
return 0, err
}
norepo, err := db.GetEngine(db.DefaultContext).Table("label").
Where(builder.And(
builder.Gt{"repo_id": 0},
builder.NotIn("repo_id", builder.Select("id").From("repository")),
)).
Count()
if err != nil {
return 0, err
}
noorg, err := db.GetEngine(db.DefaultContext).Table("label").
Where(builder.And(
builder.Gt{"org_id": 0},
builder.NotIn("org_id", builder.Select("id").From("user")),
)).
Count()
if err != nil {
return 0, err
}
return noref + norepo + noorg, nil
}
// DeleteOrphanedLabels delete labels witch are broken and not accessible via ui anymore
func DeleteOrphanedLabels() error {
// delete labels with no reference
if _, err := db.GetEngine(db.DefaultContext).Table("label").Where("repo_id=? AND org_id=?", 0, 0).Delete(new(Label)); err != nil {
return err
}
// delete labels with none existing repos
if _, err := db.GetEngine(db.DefaultContext).
Where(builder.And(
builder.Gt{"repo_id": 0},
builder.NotIn("repo_id", builder.Select("id").From("repository")),
)).
Delete(Label{}); err != nil {
return err
}
// delete labels with none existing orgs
if _, err := db.GetEngine(db.DefaultContext).
Where(builder.And(
builder.Gt{"org_id": 0},
builder.NotIn("org_id", builder.Select("id").From("user")),
)).
Delete(Label{}); err != nil {
return err
}
return nil
}
// CountOrphanedIssueLabels return count of IssueLabels witch have no label behind anymore
func CountOrphanedIssueLabels() (int64, error) {
return db.GetEngine(db.DefaultContext).Table("issue_label").
NotIn("label_id", builder.Select("id").From("label")).
Count()
}
// DeleteOrphanedIssueLabels delete IssueLabels witch have no label behind anymore
func DeleteOrphanedIssueLabels() error {
_, err := db.GetEngine(db.DefaultContext).
NotIn("label_id", builder.Select("id").From("label")).
Delete(IssueLabel{})
return err
}
// CountOrphanedIssues count issues without a repo
func CountOrphanedIssues() (int64, error) {
return db.GetEngine(db.DefaultContext).Table("issue").
Join("LEFT", "repository", "issue.repo_id=repository.id").
Where(builder.IsNull{"repository.id"}).
Select("COUNT(`issue`.`id`)").
Count()
}
// DeleteOrphanedIssues delete issues without a repo
func DeleteOrphanedIssues() error {
ctx, committer, err := db.TxContext()
if err != nil {
return err
}
defer committer.Close()
var ids []int64
if err := db.GetEngine(ctx).Table("issue").Distinct("issue.repo_id").
Join("LEFT", "repository", "issue.repo_id=repository.id").
Where(builder.IsNull{"repository.id"}).GroupBy("issue.repo_id").
Find(&ids); err != nil {
return err
}
var attachmentPaths []string
for i := range ids {
paths, err := deleteIssuesByRepoID(ctx, ids[i])
if err != nil {
return err
}
attachmentPaths = append(attachmentPaths, paths...)
}
if err := committer.Commit(); err != nil {
return err
}
committer.Close()
// Remove issue attachment files.
for i := range attachmentPaths {
admin_model.RemoveAllWithNotice(db.DefaultContext, "Delete issue attachment", attachmentPaths[i])
}
return nil
}
// CountOrphanedObjects count subjects with have no existing refobject anymore
func CountOrphanedObjects(subject, refobject, joinCond string) (int64, error) {
return db.GetEngine(db.DefaultContext).Table("`"+subject+"`").
Join("LEFT", "`"+refobject+"`", joinCond).
Where(builder.IsNull{"`" + refobject + "`.id"}).
Select("COUNT(`" + subject + "`.`id`)").
Count()
}
// DeleteOrphanedObjects delete subjects with have no existing refobject anymore
func DeleteOrphanedObjects(subject, refobject, joinCond string) error {
subQuery := builder.Select("`"+subject+"`.id").
From("`"+subject+"`").
Join("LEFT", "`"+refobject+"`", joinCond).
Where(builder.IsNull{"`" + refobject + "`.id"})
sql, args, err := builder.Delete(builder.In("id", subQuery)).From("`" + subject + "`").ToSQL()
if err != nil {
return err
}
_, err = db.GetEngine(db.DefaultContext).Exec(append([]interface{}{sql}, args...)...)
return err
}
// CountNullArchivedRepository counts the number of repositories with is_archived is null
func CountNullArchivedRepository() (int64, error) {
return db.GetEngine(db.DefaultContext).Where(builder.IsNull{"is_archived"}).Count(new(repo_model.Repository))
@ -181,74 +35,6 @@ func FixWrongUserType() (int64, error) {
return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": 0}.And(builder.Neq{"num_teams": 0})).Cols("type").NoAutoTime().Update(&user_model.User{Type: 1})
}
// CountCommentTypeLabelWithEmptyLabel count label comments with empty label
func CountCommentTypeLabelWithEmptyLabel() (int64, error) {
return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": CommentTypeLabel, "label_id": 0}).Count(new(Comment))
}
// FixCommentTypeLabelWithEmptyLabel count label comments with empty label
func FixCommentTypeLabelWithEmptyLabel() (int64, error) {
return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": CommentTypeLabel, "label_id": 0}).Delete(new(Comment))
}
// CountCommentTypeLabelWithOutsideLabels count label comments with outside label
func CountCommentTypeLabelWithOutsideLabels() (int64, error) {
return db.GetEngine(db.DefaultContext).Where("comment.type = ? AND ((label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id))", CommentTypeLabel).
Table("comment").
Join("inner", "label", "label.id = comment.label_id").
Join("inner", "issue", "issue.id = comment.issue_id ").
Join("inner", "repository", "issue.repo_id = repository.id").
Count(new(Comment))
}
// FixCommentTypeLabelWithOutsideLabels count label comments with outside label
func FixCommentTypeLabelWithOutsideLabels() (int64, error) {
res, err := db.GetEngine(db.DefaultContext).Exec(`DELETE FROM comment WHERE comment.id IN (
SELECT il_too.id FROM (
SELECT com.id
FROM comment AS com
INNER JOIN label ON com.label_id = label.id
INNER JOIN issue on issue.id = com.issue_id
INNER JOIN repository ON issue.repo_id = repository.id
WHERE
com.type = ? AND ((label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id))
) AS il_too)`, CommentTypeLabel)
if err != nil {
return 0, err
}
return res.RowsAffected()
}
// CountIssueLabelWithOutsideLabels count label comments with outside label
func CountIssueLabelWithOutsideLabels() (int64, error) {
return db.GetEngine(db.DefaultContext).Where(builder.Expr("(label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id)")).
Table("issue_label").
Join("inner", "label", "issue_label.label_id = label.id ").
Join("inner", "issue", "issue.id = issue_label.issue_id ").
Join("inner", "repository", "issue.repo_id = repository.id").
Count(new(IssueLabel))
}
// FixIssueLabelWithOutsideLabels fix label comments with outside label
func FixIssueLabelWithOutsideLabels() (int64, error) {
res, err := db.GetEngine(db.DefaultContext).Exec(`DELETE FROM issue_label WHERE issue_label.id IN (
SELECT il_too.id FROM (
SELECT il_too_too.id
FROM issue_label AS il_too_too
INNER JOIN label ON il_too_too.label_id = label.id
INNER JOIN issue on issue.id = il_too_too.issue_id
INNER JOIN repository on repository.id = issue.repo_id
WHERE
(label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id)
) AS il_too )`)
if err != nil {
return 0, err
}
return res.RowsAffected()
}
// CountActionCreatedUnixString count actions where created_unix is an empty string
func CountActionCreatedUnixString() (int64, error) {
if setting.Database.UseSQLite3 {

View file

@ -1,149 +0,0 @@
// Copyright 2021 Gitea. 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 (
"testing"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"github.com/stretchr/testify/assert"
)
func TestDeleteOrphanedObjects(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
countBefore, err := db.GetEngine(db.DefaultContext).Count(&PullRequest{})
assert.NoError(t, err)
_, err = db.GetEngine(db.DefaultContext).Insert(&PullRequest{IssueID: 1000}, &PullRequest{IssueID: 1001}, &PullRequest{IssueID: 1003})
assert.NoError(t, err)
orphaned, err := CountOrphanedObjects("pull_request", "issue", "pull_request.issue_id=issue.id")
assert.NoError(t, err)
assert.EqualValues(t, 3, orphaned)
err = DeleteOrphanedObjects("pull_request", "issue", "pull_request.issue_id=issue.id")
assert.NoError(t, err)
countAfter, err := db.GetEngine(db.DefaultContext).Count(&PullRequest{})
assert.NoError(t, err)
assert.EqualValues(t, countBefore, countAfter)
}
func TestNewMilestone(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
milestone := &issues_model.Milestone{
RepoID: 1,
Name: "milestoneName",
Content: "milestoneContent",
}
assert.NoError(t, issues_model.NewMilestone(milestone))
unittest.AssertExistsAndLoadBean(t, milestone)
unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: milestone.RepoID}, &issues_model.Milestone{})
}
func TestChangeMilestoneStatus(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}).(*issues_model.Milestone)
assert.NoError(t, issues_model.ChangeMilestoneStatus(milestone, true))
unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}, "is_closed=1")
unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: milestone.RepoID}, &issues_model.Milestone{})
assert.NoError(t, issues_model.ChangeMilestoneStatus(milestone, false))
unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}, "is_closed=0")
unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: milestone.RepoID}, &issues_model.Milestone{})
}
func TestDeleteMilestoneByRepoID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
assert.NoError(t, issues_model.DeleteMilestoneByRepoID(1, 1))
unittest.AssertNotExistsBean(t, &issues_model.Milestone{ID: 1})
unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: 1})
assert.NoError(t, issues_model.DeleteMilestoneByRepoID(unittest.NonexistentID, unittest.NonexistentID))
}
func TestUpdateMilestone(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}).(*issues_model.Milestone)
milestone.Name = " newMilestoneName "
milestone.Content = "newMilestoneContent"
assert.NoError(t, issues_model.UpdateMilestone(milestone, milestone.IsClosed))
milestone = unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}).(*issues_model.Milestone)
assert.EqualValues(t, "newMilestoneName", milestone.Name)
unittest.CheckConsistencyFor(t, &issues_model.Milestone{})
}
func TestUpdateMilestoneCounters(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
issue := unittest.AssertExistsAndLoadBean(t, &Issue{MilestoneID: 1},
"is_closed=0").(*Issue)
issue.IsClosed = true
issue.ClosedUnix = timeutil.TimeStampNow()
_, err := db.GetEngine(db.DefaultContext).ID(issue.ID).Cols("is_closed", "closed_unix").Update(issue)
assert.NoError(t, err)
assert.NoError(t, issues_model.UpdateMilestoneCounters(db.DefaultContext, issue.MilestoneID))
unittest.CheckConsistencyFor(t, &issues_model.Milestone{})
issue.IsClosed = false
issue.ClosedUnix = 0
_, err = db.GetEngine(db.DefaultContext).ID(issue.ID).Cols("is_closed", "closed_unix").Update(issue)
assert.NoError(t, err)
assert.NoError(t, issues_model.UpdateMilestoneCounters(db.DefaultContext, issue.MilestoneID))
unittest.CheckConsistencyFor(t, &issues_model.Milestone{})
}
func TestConsistencyUpdateAction(t *testing.T) {
if !setting.Database.UseSQLite3 {
t.Skip("Test is only for SQLite database.")
}
assert.NoError(t, unittest.PrepareTestDatabase())
id := 8
unittest.AssertExistsAndLoadBean(t, &Action{
ID: int64(id),
})
_, err := db.GetEngine(db.DefaultContext).Exec(`UPDATE action SET created_unix = "" WHERE id = ?`, id)
assert.NoError(t, err)
actions := make([]*Action, 0, 1)
//
// XORM returns an error when created_unix is a string
//
err = db.GetEngine(db.DefaultContext).Where("id = ?", id).Find(&actions)
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "type string to a int64: invalid syntax")
}
//
// Get rid of incorrectly set created_unix
//
count, err := CountActionCreatedUnixString()
assert.NoError(t, err)
assert.EqualValues(t, 1, count)
count, err = FixActionCreatedUnixString()
assert.NoError(t, err)
assert.EqualValues(t, 1, count)
count, err = CountActionCreatedUnixString()
assert.NoError(t, err)
assert.EqualValues(t, 0, count)
count, err = FixActionCreatedUnixString()
assert.NoError(t, err)
assert.EqualValues(t, 0, count)
//
// XORM must be happy now
//
assert.NoError(t, db.GetEngine(db.DefaultContext).Where("id = ?", id).Find(&actions))
unittest.CheckConsistencyFor(t, &Action{})
}

27
models/db/consistency.go Normal file
View file

@ -0,0 +1,27 @@
// Copyright 2022 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 db
import "xorm.io/builder"
// CountOrphanedObjects count subjects with have no existing refobject anymore
func CountOrphanedObjects(subject, refobject, joinCond string) (int64, error) {
return GetEngine(DefaultContext).Table("`"+subject+"`").
Join("LEFT", "`"+refobject+"`", joinCond).
Where(builder.IsNull{"`" + refobject + "`.id"}).
Select("COUNT(`" + subject + "`.`id`)").
Count()
}
// DeleteOrphanedObjects delete subjects with have no existing refobject anymore
func DeleteOrphanedObjects(subject, refobject, joinCond string) error {
subQuery := builder.Select("`"+subject+"`.id").
From("`"+subject+"`").
Join("LEFT", "`"+refobject+"`", joinCond).
Where(builder.IsNull{"`" + refobject + "`.id"})
b := builder.Delete(builder.In("id", subQuery)).From("`" + subject + "`")
_, err := GetEngine(DefaultContext).Exec(b)
return err
}

View file

@ -20,21 +20,21 @@ type ResourceIndex struct {
}
// UpsertResourceIndex the function will not return until it acquires the lock or receives an error.
func UpsertResourceIndex(e Engine, tableName string, groupID int64) (err error) {
func UpsertResourceIndex(ctx context.Context, tableName string, groupID int64) (err error) {
// An atomic UPSERT operation (INSERT/UPDATE) is the only operation
// that ensures that the key is actually locked.
switch {
case setting.Database.UseSQLite3 || setting.Database.UsePostgreSQL:
_, err = e.Exec(fmt.Sprintf("INSERT INTO %s (group_id, max_index) "+
_, err = Exec(ctx, fmt.Sprintf("INSERT INTO %s (group_id, max_index) "+
"VALUES (?,1) ON CONFLICT (group_id) DO UPDATE SET max_index = %s.max_index+1",
tableName, tableName), groupID)
case setting.Database.UseMySQL:
_, err = e.Exec(fmt.Sprintf("INSERT INTO %s (group_id, max_index) "+
_, err = Exec(ctx, fmt.Sprintf("INSERT INTO %s (group_id, max_index) "+
"VALUES (?,1) ON DUPLICATE KEY UPDATE max_index = max_index+1", tableName),
groupID)
case setting.Database.UseMSSQL:
// https://weblogs.sqlteam.com/dang/2009/01/31/upsert-race-condition-with-merge/
_, err = e.Exec(fmt.Sprintf("MERGE %s WITH (HOLDLOCK) as target "+
_, err = Exec(ctx, fmt.Sprintf("MERGE %s WITH (HOLDLOCK) as target "+
"USING (SELECT ? AS group_id) AS src "+
"ON src.group_id = target.group_id "+
"WHEN MATCHED THEN UPDATE SET target.max_index = target.max_index+1 "+
@ -82,30 +82,29 @@ func DeleteResouceIndex(ctx context.Context, tableName string, groupID int64) er
// getNextResourceIndex return the next index
func getNextResourceIndex(tableName string, groupID int64) (int64, error) {
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return 0, err
}
var preIdx int64
_, err := sess.SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id = ?", tableName), groupID).Get(&preIdx)
ctx, commiter, err := TxContext()
if err != nil {
return 0, err
}
defer commiter.Close()
var preIdx int64
if _, err := GetEngine(ctx).SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id = ?", tableName), groupID).Get(&preIdx); err != nil {
return 0, err
}
if err := UpsertResourceIndex(sess, tableName, groupID); err != nil {
if err := UpsertResourceIndex(ctx, tableName, groupID); err != nil {
return 0, err
}
var curIdx int64
has, err := sess.SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id = ? AND max_index=?", tableName), groupID, preIdx+1).Get(&curIdx)
has, err := GetEngine(ctx).SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id = ? AND max_index=?", tableName), groupID, preIdx+1).Get(&curIdx)
if err != nil {
return 0, err
}
if !has {
return 0, ErrResouceOutdated
}
if err := sess.Commit(); err != nil {
if err := commiter.Commit(); err != nil {
return 0, err
}
return curIdx, nil

View file

@ -10,6 +10,11 @@ import (
"xorm.io/xorm"
)
const (
// DefaultMaxInSize represents default variables number on IN () in SQL
DefaultMaxInSize = 50
)
// Paginator is the base for different ListOptions types
type Paginator interface {
GetSkipTake() (skip, take int)

View file

@ -405,22 +405,6 @@ func (err ErrFilePathProtected) Error() string {
return fmt.Sprintf("path is protected and can not be changed [path: %s]", err.Path)
}
// ErrUserDoesNotHaveAccessToRepo represets an error where the user doesn't has access to a given repo.
type ErrUserDoesNotHaveAccessToRepo struct {
UserID int64
RepoName string
}
// IsErrUserDoesNotHaveAccessToRepo checks if an error is a ErrRepoFileAlreadyExists.
func IsErrUserDoesNotHaveAccessToRepo(err error) bool {
_, ok := err.(ErrUserDoesNotHaveAccessToRepo)
return ok
}
func (err ErrUserDoesNotHaveAccessToRepo) Error() string {
return fmt.Sprintf("user doesn't have access to repo [user_id: %d, repo_name: %s]", err.UserID, err.RepoName)
}
// __________ .__
// \______ \____________ ____ ____ | |__
// | | _/\_ __ \__ \ / \_/ ___\| | \
@ -580,162 +564,6 @@ func (err ErrSHAOrCommitIDNotProvided) Error() string {
return "a SHA or commit ID must be proved when updating a file"
}
// .___
// | | ______ ________ __ ____
// | |/ ___// ___/ | \_/ __ \
// | |\___ \ \___ \| | /\ ___/
// |___/____ >____ >____/ \___ >
// \/ \/ \/
// ErrIssueNotExist represents a "IssueNotExist" kind of error.
type ErrIssueNotExist struct {
ID int64
RepoID int64
Index int64
}
// IsErrIssueNotExist checks if an error is a ErrIssueNotExist.
func IsErrIssueNotExist(err error) bool {
_, ok := err.(ErrIssueNotExist)
return ok
}
func (err ErrIssueNotExist) Error() string {
return fmt.Sprintf("issue does not exist [id: %d, repo_id: %d, index: %d]", err.ID, err.RepoID, err.Index)
}
// ErrIssueIsClosed represents a "IssueIsClosed" kind of error.
type ErrIssueIsClosed struct {
ID int64
RepoID int64
Index int64
}
// IsErrIssueIsClosed checks if an error is a ErrIssueNotExist.
func IsErrIssueIsClosed(err error) bool {
_, ok := err.(ErrIssueIsClosed)
return ok
}
func (err ErrIssueIsClosed) Error() string {
return fmt.Sprintf("issue is closed [id: %d, repo_id: %d, index: %d]", err.ID, err.RepoID, err.Index)
}
// ErrNewIssueInsert is used when the INSERT statement in newIssue fails
type ErrNewIssueInsert struct {
OriginalError error
}
// IsErrNewIssueInsert checks if an error is a ErrNewIssueInsert.
func IsErrNewIssueInsert(err error) bool {
_, ok := err.(ErrNewIssueInsert)
return ok
}
func (err ErrNewIssueInsert) Error() string {
return err.OriginalError.Error()
}
// ErrIssueWasClosed is used when close a closed issue
type ErrIssueWasClosed struct {
ID int64
Index int64
}
// IsErrIssueWasClosed checks if an error is a ErrIssueWasClosed.
func IsErrIssueWasClosed(err error) bool {
_, ok := err.(ErrIssueWasClosed)
return ok
}
func (err ErrIssueWasClosed) Error() string {
return fmt.Sprintf("Issue [%d] %d was already closed", err.ID, err.Index)
}
// ErrPullWasClosed is used close a closed pull request
type ErrPullWasClosed struct {
ID int64
Index int64
}
// IsErrPullWasClosed checks if an error is a ErrErrPullWasClosed.
func IsErrPullWasClosed(err error) bool {
_, ok := err.(ErrPullWasClosed)
return ok
}
func (err ErrPullWasClosed) Error() string {
return fmt.Sprintf("Pull request [%d] %d was already closed", err.ID, err.Index)
}
// __________ .__ .__ __________ __
// \______ \__ __| | | |\______ \ ____ ________ __ ____ _______/ |_
// | ___/ | \ | | | | _// __ \/ ____/ | \_/ __ \ / ___/\ __\
// | | | | / |_| |_| | \ ___< <_| | | /\ ___/ \___ \ | |
// |____| |____/|____/____/____|_ /\___ >__ |____/ \___ >____ > |__|
// \/ \/ |__| \/ \/
// ErrPullRequestNotExist represents a "PullRequestNotExist" kind of error.
type ErrPullRequestNotExist struct {
ID int64
IssueID int64
HeadRepoID int64
BaseRepoID int64
HeadBranch string
BaseBranch string
}
// IsErrPullRequestNotExist checks if an error is a ErrPullRequestNotExist.
func IsErrPullRequestNotExist(err error) bool {
_, ok := err.(ErrPullRequestNotExist)
return ok
}
func (err ErrPullRequestNotExist) Error() string {
return fmt.Sprintf("pull request does not exist [id: %d, issue_id: %d, head_repo_id: %d, base_repo_id: %d, head_branch: %s, base_branch: %s]",
err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch)
}
// ErrPullRequestAlreadyExists represents a "PullRequestAlreadyExists"-error
type ErrPullRequestAlreadyExists struct {
ID int64
IssueID int64
HeadRepoID int64
BaseRepoID int64
HeadBranch string
BaseBranch string
}
// IsErrPullRequestAlreadyExists checks if an error is a ErrPullRequestAlreadyExists.
func IsErrPullRequestAlreadyExists(err error) bool {
_, ok := err.(ErrPullRequestAlreadyExists)
return ok
}
// Error does pretty-printing :D
func (err ErrPullRequestAlreadyExists) Error() string {
return fmt.Sprintf("pull request already exists for these targets [id: %d, issue_id: %d, head_repo_id: %d, base_repo_id: %d, head_branch: %s, base_branch: %s]",
err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch)
}
// ErrPullRequestHeadRepoMissing represents a "ErrPullRequestHeadRepoMissing" error
type ErrPullRequestHeadRepoMissing struct {
ID int64
HeadRepoID int64
}
// IsErrErrPullRequestHeadRepoMissing checks if an error is a ErrPullRequestHeadRepoMissing.
func IsErrErrPullRequestHeadRepoMissing(err error) bool {
_, ok := err.(ErrPullRequestHeadRepoMissing)
return ok
}
// Error does pretty-printing :D
func (err ErrPullRequestHeadRepoMissing) Error() string {
return fmt.Sprintf("pull request head repo missing [id: %d, head_repo_id: %d]",
err.ID, err.HeadRepoID)
}
// ErrInvalidMergeStyle represents an error if merging with disabled merge strategy
type ErrInvalidMergeStyle struct {
ID int64
@ -830,29 +658,6 @@ func (err ErrPullRequestHasMerged) Error() string {
err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch)
}
// _________ __
// \_ ___ \ ____ _____ _____ ____ _____/ |_
// / \ \/ / _ \ / \ / \_/ __ \ / \ __\
// \ \___( <_> ) Y Y \ Y Y \ ___/| | \ |
// \______ /\____/|__|_| /__|_| /\___ >___| /__|
// \/ \/ \/ \/ \/
// ErrCommentNotExist represents a "CommentNotExist" kind of error.
type ErrCommentNotExist struct {
ID int64
IssueID int64
}
// IsErrCommentNotExist checks if an error is a ErrCommentNotExist.
func IsErrCommentNotExist(err error) bool {
_, ok := err.(ErrCommentNotExist)
return ok
}
func (err ErrCommentNotExist) Error() string {
return fmt.Sprintf("comment does not exist [id: %d, issue_id: %d]", err.ID, err.IssueID)
}
// _________ __ __ .__
// / _____// |_ ____ ________ _ _______ _/ |_ ____ | |__
// \_____ \\ __\/ _ \\____ \ \/ \/ /\__ \\ __\/ ___\| | \
@ -897,60 +702,6 @@ func (err ErrTrackedTimeNotExist) Error() string {
return fmt.Sprintf("tracked time does not exist [id: %d]", err.ID)
}
// .____ ___. .__
// | | _____ \_ |__ ____ | |
// | | \__ \ | __ \_/ __ \| |
// | |___ / __ \| \_\ \ ___/| |__
// |_______ (____ /___ /\___ >____/
// \/ \/ \/ \/
// ErrRepoLabelNotExist represents a "RepoLabelNotExist" kind of error.
type ErrRepoLabelNotExist struct {
LabelID int64
RepoID int64
}
// IsErrRepoLabelNotExist checks if an error is a RepoErrLabelNotExist.
func IsErrRepoLabelNotExist(err error) bool {
_, ok := err.(ErrRepoLabelNotExist)
return ok
}
func (err ErrRepoLabelNotExist) Error() string {
return fmt.Sprintf("label does not exist [label_id: %d, repo_id: %d]", err.LabelID, err.RepoID)
}
// ErrOrgLabelNotExist represents a "OrgLabelNotExist" kind of error.
type ErrOrgLabelNotExist struct {
LabelID int64
OrgID int64
}
// IsErrOrgLabelNotExist checks if an error is a OrgErrLabelNotExist.
func IsErrOrgLabelNotExist(err error) bool {
_, ok := err.(ErrOrgLabelNotExist)
return ok
}
func (err ErrOrgLabelNotExist) Error() string {
return fmt.Sprintf("label does not exist [label_id: %d, org_id: %d]", err.LabelID, err.OrgID)
}
// ErrLabelNotExist represents a "LabelNotExist" kind of error.
type ErrLabelNotExist struct {
LabelID int64
}
// IsErrLabelNotExist checks if an error is a ErrLabelNotExist.
func IsErrLabelNotExist(err error) bool {
_, ok := err.(ErrLabelNotExist)
return ok
}
func (err ErrLabelNotExist) Error() string {
return fmt.Sprintf("label does not exist [label_id: %d]", err.LabelID)
}
// ____ ___ .__ .___
// | | \______ | | _________ __| _/
// | | /\____ \| | / _ \__ \ / __ |
@ -974,130 +725,3 @@ func IsErrUploadNotExist(err error) bool {
func (err ErrUploadNotExist) Error() string {
return fmt.Sprintf("attachment does not exist [id: %d, uuid: %s]", err.ID, err.UUID)
}
// .___ ________ .___ .__
// | | ______ ________ __ ____ \______ \ ____ ______ ____ ____ __| _/____ ____ ____ |__| ____ ______
// | |/ ___// ___/ | \_/ __ \ | | \_/ __ \\____ \_/ __ \ / \ / __ |/ __ \ / \_/ ___\| |/ __ \ / ___/
// | |\___ \ \___ \| | /\ ___/ | ` \ ___/| |_> > ___/| | \/ /_/ \ ___/| | \ \___| \ ___/ \___ \
// |___/____ >____ >____/ \___ >_______ /\___ > __/ \___ >___| /\____ |\___ >___| /\___ >__|\___ >____ >
// \/ \/ \/ \/ \/|__| \/ \/ \/ \/ \/ \/ \/ \/
// ErrDependencyExists represents a "DependencyAlreadyExists" kind of error.
type ErrDependencyExists struct {
IssueID int64
DependencyID int64
}
// IsErrDependencyExists checks if an error is a ErrDependencyExists.
func IsErrDependencyExists(err error) bool {
_, ok := err.(ErrDependencyExists)
return ok
}
func (err ErrDependencyExists) Error() string {
return fmt.Sprintf("issue dependency does already exist [issue id: %d, dependency id: %d]", err.IssueID, err.DependencyID)
}
// ErrDependencyNotExists represents a "DependencyAlreadyExists" kind of error.
type ErrDependencyNotExists struct {
IssueID int64
DependencyID int64
}
// IsErrDependencyNotExists checks if an error is a ErrDependencyExists.
func IsErrDependencyNotExists(err error) bool {
_, ok := err.(ErrDependencyNotExists)
return ok
}
func (err ErrDependencyNotExists) Error() string {
return fmt.Sprintf("issue dependency does not exist [issue id: %d, dependency id: %d]", err.IssueID, err.DependencyID)
}
// ErrCircularDependency represents a "DependencyCircular" kind of error.
type ErrCircularDependency struct {
IssueID int64
DependencyID int64
}
// IsErrCircularDependency checks if an error is a ErrCircularDependency.
func IsErrCircularDependency(err error) bool {
_, ok := err.(ErrCircularDependency)
return ok
}
func (err ErrCircularDependency) Error() string {
return fmt.Sprintf("circular dependencies exists (two issues blocking each other) [issue id: %d, dependency id: %d]", err.IssueID, err.DependencyID)
}
// ErrDependenciesLeft represents an error where the issue you're trying to close still has dependencies left.
type ErrDependenciesLeft struct {
IssueID int64
}
// IsErrDependenciesLeft checks if an error is a ErrDependenciesLeft.
func IsErrDependenciesLeft(err error) bool {
_, ok := err.(ErrDependenciesLeft)
return ok
}
func (err ErrDependenciesLeft) Error() string {
return fmt.Sprintf("issue has open dependencies [issue id: %d]", err.IssueID)
}
// ErrUnknownDependencyType represents an error where an unknown dependency type was passed
type ErrUnknownDependencyType struct {
Type DependencyType
}
// IsErrUnknownDependencyType checks if an error is ErrUnknownDependencyType
func IsErrUnknownDependencyType(err error) bool {
_, ok := err.(ErrUnknownDependencyType)
return ok
}
func (err ErrUnknownDependencyType) Error() string {
return fmt.Sprintf("unknown dependency type [type: %d]", err.Type)
}
// __________ .__
// \______ \ _______ _|__| ______ _ __
// | _// __ \ \/ / |/ __ \ \/ \/ /
// | | \ ___/\ /| \ ___/\ /
// |____|_ /\___ >\_/ |__|\___ >\/\_/
// \/ \/ \/
// ErrReviewNotExist represents a "ReviewNotExist" kind of error.
type ErrReviewNotExist struct {
ID int64
}
// IsErrReviewNotExist checks if an error is a ErrReviewNotExist.
func IsErrReviewNotExist(err error) bool {
_, ok := err.(ErrReviewNotExist)
return ok
}
func (err ErrReviewNotExist) Error() string {
return fmt.Sprintf("review does not exist [id: %d]", err.ID)
}
// ErrNotValidReviewRequest an not allowed review request modify
type ErrNotValidReviewRequest struct {
Reason string
UserID int64
RepoID int64
}
// IsErrNotValidReviewRequest checks if an error is a ErrNotValidReviewRequest.
func IsErrNotValidReviewRequest(err error) bool {
_, ok := err.(ErrNotValidReviewRequest)
return ok
}
func (err ErrNotValidReviewRequest) Error() string {
return fmt.Sprintf("%s [user_id: %d, repo_id: %d]",
err.Reason,
err.UserID,
err.RepoID)
}

View file

@ -7,9 +7,9 @@ package git_test
import (
"testing"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
@ -120,10 +120,10 @@ func TestRenameBranch(t *testing.T) {
repo1 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
assert.Equal(t, "main", repo1.DefaultBranch)
pull := unittest.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 1}).(*models.PullRequest) // merged
pull := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest) // merged
assert.Equal(t, "master", pull.BaseBranch)
pull = unittest.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 2}).(*models.PullRequest) // open
pull = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}).(*issues_model.PullRequest) // open
assert.Equal(t, "main", pull.BaseBranch)
renamedBranch := unittest.AssertExistsAndLoadBean(t, &git_model.RenamedBranch{ID: 2}).(*git_model.RenamedBranch)

View file

@ -8,6 +8,7 @@ import (
"path/filepath"
"testing"
_ "code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/unittest"
)

View file

@ -1,394 +0,0 @@
// Copyright 2017 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 (
"html/template"
"testing"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"github.com/stretchr/testify/assert"
)
// TODO TestGetLabelTemplateFile
func TestLabel_CalOpenIssues(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label := unittest.AssertExistsAndLoadBean(t, &Label{ID: 1}).(*Label)
label.CalOpenIssues()
assert.EqualValues(t, 2, label.NumOpenIssues)
}
func TestLabel_ForegroundColor(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label := unittest.AssertExistsAndLoadBean(t, &Label{ID: 1}).(*Label)
assert.Equal(t, template.CSS("#000"), label.ForegroundColor())
label = unittest.AssertExistsAndLoadBean(t, &Label{ID: 2}).(*Label)
assert.Equal(t, template.CSS("#fff"), label.ForegroundColor())
}
func TestNewLabels(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
labels := []*Label{
{RepoID: 2, Name: "labelName2", Color: "#123456"},
{RepoID: 3, Name: "labelName3", Color: "#123"},
{RepoID: 4, Name: "labelName4", Color: "ABCDEF"},
{RepoID: 5, Name: "labelName5", Color: "DEF"},
}
assert.Error(t, NewLabel(db.DefaultContext, &Label{RepoID: 3, Name: "invalid Color", Color: ""}))
assert.Error(t, NewLabel(db.DefaultContext, &Label{RepoID: 3, Name: "invalid Color", Color: "#45G"}))
assert.Error(t, NewLabel(db.DefaultContext, &Label{RepoID: 3, Name: "invalid Color", Color: "#12345G"}))
assert.Error(t, NewLabel(db.DefaultContext, &Label{RepoID: 3, Name: "invalid Color", Color: "45G"}))
assert.Error(t, NewLabel(db.DefaultContext, &Label{RepoID: 3, Name: "invalid Color", Color: "12345G"}))
for _, label := range labels {
unittest.AssertNotExistsBean(t, label)
}
assert.NoError(t, NewLabels(labels...))
for _, label := range labels {
unittest.AssertExistsAndLoadBean(t, label, unittest.Cond("id = ?", label.ID))
}
unittest.CheckConsistencyFor(t, &Label{}, &repo_model.Repository{})
}
func TestGetLabelByID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label, err := GetLabelByID(db.DefaultContext, 1)
assert.NoError(t, err)
assert.EqualValues(t, 1, label.ID)
_, err = GetLabelByID(db.DefaultContext, unittest.NonexistentID)
assert.True(t, IsErrLabelNotExist(err))
}
func TestGetLabelInRepoByName(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label, err := GetLabelInRepoByName(db.DefaultContext, 1, "label1")
assert.NoError(t, err)
assert.EqualValues(t, 1, label.ID)
assert.Equal(t, "label1", label.Name)
_, err = GetLabelInRepoByName(db.DefaultContext, 1, "")
assert.True(t, IsErrRepoLabelNotExist(err))
_, err = GetLabelInRepoByName(db.DefaultContext, unittest.NonexistentID, "nonexistent")
assert.True(t, IsErrRepoLabelNotExist(err))
}
func TestGetLabelInRepoByNames(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
labelIDs, err := GetLabelIDsInRepoByNames(1, []string{"label1", "label2"})
assert.NoError(t, err)
assert.Len(t, labelIDs, 2)
assert.Equal(t, int64(1), labelIDs[0])
assert.Equal(t, int64(2), labelIDs[1])
}
func TestGetLabelInRepoByNamesDiscardsNonExistentLabels(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
// label3 doesn't exists.. See labels.yml
labelIDs, err := GetLabelIDsInRepoByNames(1, []string{"label1", "label2", "label3"})
assert.NoError(t, err)
assert.Len(t, labelIDs, 2)
assert.Equal(t, int64(1), labelIDs[0])
assert.Equal(t, int64(2), labelIDs[1])
assert.NoError(t, err)
}
func TestGetLabelInRepoByID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label, err := GetLabelInRepoByID(db.DefaultContext, 1, 1)
assert.NoError(t, err)
assert.EqualValues(t, 1, label.ID)
_, err = GetLabelInRepoByID(db.DefaultContext, 1, -1)
assert.True(t, IsErrRepoLabelNotExist(err))
_, err = GetLabelInRepoByID(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID)
assert.True(t, IsErrRepoLabelNotExist(err))
}
func TestGetLabelsInRepoByIDs(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
labels, err := GetLabelsInRepoByIDs(1, []int64{1, 2, unittest.NonexistentID})
assert.NoError(t, err)
if assert.Len(t, labels, 2) {
assert.EqualValues(t, 1, labels[0].ID)
assert.EqualValues(t, 2, labels[1].ID)
}
}
func TestGetLabelsByRepoID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
testSuccess := func(repoID int64, sortType string, expectedIssueIDs []int64) {
labels, err := GetLabelsByRepoID(db.DefaultContext, repoID, sortType, db.ListOptions{})
assert.NoError(t, err)
assert.Len(t, labels, len(expectedIssueIDs))
for i, label := range labels {
assert.EqualValues(t, expectedIssueIDs[i], label.ID)
}
}
testSuccess(1, "leastissues", []int64{2, 1})
testSuccess(1, "mostissues", []int64{1, 2})
testSuccess(1, "reversealphabetically", []int64{2, 1})
testSuccess(1, "default", []int64{1, 2})
}
// Org versions
func TestGetLabelInOrgByName(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label, err := GetLabelInOrgByName(db.DefaultContext, 3, "orglabel3")
assert.NoError(t, err)
assert.EqualValues(t, 3, label.ID)
assert.Equal(t, "orglabel3", label.Name)
_, err = GetLabelInOrgByName(db.DefaultContext, 3, "")
assert.True(t, IsErrOrgLabelNotExist(err))
_, err = GetLabelInOrgByName(db.DefaultContext, 0, "orglabel3")
assert.True(t, IsErrOrgLabelNotExist(err))
_, err = GetLabelInOrgByName(db.DefaultContext, -1, "orglabel3")
assert.True(t, IsErrOrgLabelNotExist(err))
_, err = GetLabelInOrgByName(db.DefaultContext, unittest.NonexistentID, "nonexistent")
assert.True(t, IsErrOrgLabelNotExist(err))
}
func TestGetLabelInOrgByNames(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
labelIDs, err := GetLabelIDsInOrgByNames(3, []string{"orglabel3", "orglabel4"})
assert.NoError(t, err)
assert.Len(t, labelIDs, 2)
assert.Equal(t, int64(3), labelIDs[0])
assert.Equal(t, int64(4), labelIDs[1])
}
func TestGetLabelInOrgByNamesDiscardsNonExistentLabels(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
// orglabel99 doesn't exists.. See labels.yml
labelIDs, err := GetLabelIDsInOrgByNames(3, []string{"orglabel3", "orglabel4", "orglabel99"})
assert.NoError(t, err)
assert.Len(t, labelIDs, 2)
assert.Equal(t, int64(3), labelIDs[0])
assert.Equal(t, int64(4), labelIDs[1])
assert.NoError(t, err)
}
func TestGetLabelInOrgByID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label, err := GetLabelInOrgByID(db.DefaultContext, 3, 3)
assert.NoError(t, err)
assert.EqualValues(t, 3, label.ID)
_, err = GetLabelInOrgByID(db.DefaultContext, 3, -1)
assert.True(t, IsErrOrgLabelNotExist(err))
_, err = GetLabelInOrgByID(db.DefaultContext, 0, 3)
assert.True(t, IsErrOrgLabelNotExist(err))
_, err = GetLabelInOrgByID(db.DefaultContext, -1, 3)
assert.True(t, IsErrOrgLabelNotExist(err))
_, err = GetLabelInOrgByID(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID)
assert.True(t, IsErrOrgLabelNotExist(err))
}
func TestGetLabelsInOrgByIDs(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
labels, err := GetLabelsInOrgByIDs(3, []int64{3, 4, unittest.NonexistentID})
assert.NoError(t, err)
if assert.Len(t, labels, 2) {
assert.EqualValues(t, 3, labels[0].ID)
assert.EqualValues(t, 4, labels[1].ID)
}
}
func TestGetLabelsByOrgID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
testSuccess := func(orgID int64, sortType string, expectedIssueIDs []int64) {
labels, err := GetLabelsByOrgID(db.DefaultContext, orgID, sortType, db.ListOptions{})
assert.NoError(t, err)
assert.Len(t, labels, len(expectedIssueIDs))
for i, label := range labels {
assert.EqualValues(t, expectedIssueIDs[i], label.ID)
}
}
testSuccess(3, "leastissues", []int64{3, 4})
testSuccess(3, "mostissues", []int64{4, 3})
testSuccess(3, "reversealphabetically", []int64{4, 3})
testSuccess(3, "default", []int64{3, 4})
var err error
_, err = GetLabelsByOrgID(db.DefaultContext, 0, "leastissues", db.ListOptions{})
assert.True(t, IsErrOrgLabelNotExist(err))
_, err = GetLabelsByOrgID(db.DefaultContext, -1, "leastissues", db.ListOptions{})
assert.True(t, IsErrOrgLabelNotExist(err))
}
//
func TestGetLabelsByIssueID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
labels, err := GetLabelsByIssueID(db.DefaultContext, 1)
assert.NoError(t, err)
if assert.Len(t, labels, 1) {
assert.EqualValues(t, 1, labels[0].ID)
}
labels, err = GetLabelsByIssueID(db.DefaultContext, unittest.NonexistentID)
assert.NoError(t, err)
assert.Len(t, labels, 0)
}
func TestUpdateLabel(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label := unittest.AssertExistsAndLoadBean(t, &Label{ID: 1}).(*Label)
// make sure update wont overwrite it
update := &Label{
ID: label.ID,
Color: "#ffff00",
Name: "newLabelName",
Description: label.Description,
}
label.Color = update.Color
label.Name = update.Name
assert.NoError(t, UpdateLabel(update))
newLabel := unittest.AssertExistsAndLoadBean(t, &Label{ID: 1}).(*Label)
assert.EqualValues(t, label.ID, newLabel.ID)
assert.EqualValues(t, label.Color, newLabel.Color)
assert.EqualValues(t, label.Name, newLabel.Name)
assert.EqualValues(t, label.Description, newLabel.Description)
unittest.CheckConsistencyFor(t, &Label{}, &repo_model.Repository{})
}
func TestDeleteLabel(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label := unittest.AssertExistsAndLoadBean(t, &Label{ID: 1}).(*Label)
assert.NoError(t, DeleteLabel(label.RepoID, label.ID))
unittest.AssertNotExistsBean(t, &Label{ID: label.ID, RepoID: label.RepoID})
assert.NoError(t, DeleteLabel(label.RepoID, label.ID))
unittest.AssertNotExistsBean(t, &Label{ID: label.ID})
assert.NoError(t, DeleteLabel(unittest.NonexistentID, unittest.NonexistentID))
unittest.CheckConsistencyFor(t, &Label{}, &repo_model.Repository{})
}
func TestHasIssueLabel(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
assert.True(t, HasIssueLabel(db.DefaultContext, 1, 1))
assert.False(t, HasIssueLabel(db.DefaultContext, 1, 2))
assert.False(t, HasIssueLabel(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID))
}
func TestNewIssueLabel(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label := unittest.AssertExistsAndLoadBean(t, &Label{ID: 2}).(*Label)
issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue)
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
// add new IssueLabel
prevNumIssues := label.NumIssues
assert.NoError(t, NewIssueLabel(issue, label, doer))
unittest.AssertExistsAndLoadBean(t, &IssueLabel{IssueID: issue.ID, LabelID: label.ID})
unittest.AssertExistsAndLoadBean(t, &Comment{
Type: CommentTypeLabel,
PosterID: doer.ID,
IssueID: issue.ID,
LabelID: label.ID,
Content: "1",
})
label = unittest.AssertExistsAndLoadBean(t, &Label{ID: 2}).(*Label)
assert.EqualValues(t, prevNumIssues+1, label.NumIssues)
// re-add existing IssueLabel
assert.NoError(t, NewIssueLabel(issue, label, doer))
unittest.CheckConsistencyFor(t, &Issue{}, &Label{})
}
func TestNewIssueLabels(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label1 := unittest.AssertExistsAndLoadBean(t, &Label{ID: 1}).(*Label)
label2 := unittest.AssertExistsAndLoadBean(t, &Label{ID: 2}).(*Label)
issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 5}).(*Issue)
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
assert.NoError(t, NewIssueLabels(issue, []*Label{label1, label2}, doer))
unittest.AssertExistsAndLoadBean(t, &IssueLabel{IssueID: issue.ID, LabelID: label1.ID})
unittest.AssertExistsAndLoadBean(t, &Comment{
Type: CommentTypeLabel,
PosterID: doer.ID,
IssueID: issue.ID,
LabelID: label1.ID,
Content: "1",
})
unittest.AssertExistsAndLoadBean(t, &IssueLabel{IssueID: issue.ID, LabelID: label1.ID})
label1 = unittest.AssertExistsAndLoadBean(t, &Label{ID: 1}).(*Label)
assert.EqualValues(t, 3, label1.NumIssues)
assert.EqualValues(t, 1, label1.NumClosedIssues)
label2 = unittest.AssertExistsAndLoadBean(t, &Label{ID: 2}).(*Label)
assert.EqualValues(t, 1, label2.NumIssues)
assert.EqualValues(t, 1, label2.NumClosedIssues)
// corner case: test empty slice
assert.NoError(t, NewIssueLabels(issue, []*Label{}, doer))
unittest.CheckConsistencyFor(t, &Issue{}, &Label{})
}
func TestDeleteIssueLabel(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
testSuccess := func(labelID, issueID, doerID int64) {
label := unittest.AssertExistsAndLoadBean(t, &Label{ID: labelID}).(*Label)
issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: issueID}).(*Issue)
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doerID}).(*user_model.User)
expectedNumIssues := label.NumIssues
expectedNumClosedIssues := label.NumClosedIssues
if unittest.BeanExists(t, &IssueLabel{IssueID: issueID, LabelID: labelID}) {
expectedNumIssues--
if issue.IsClosed {
expectedNumClosedIssues--
}
}
ctx, committer, err := db.TxContext()
defer committer.Close()
assert.NoError(t, err)
assert.NoError(t, DeleteIssueLabel(ctx, issue, label, doer))
assert.NoError(t, committer.Commit())
unittest.AssertNotExistsBean(t, &IssueLabel{IssueID: issueID, LabelID: labelID})
unittest.AssertExistsAndLoadBean(t, &Comment{
Type: CommentTypeLabel,
PosterID: doerID,
IssueID: issueID,
LabelID: labelID,
}, `content=""`)
label = unittest.AssertExistsAndLoadBean(t, &Label{ID: labelID}).(*Label)
assert.EqualValues(t, expectedNumIssues, label.NumIssues)
assert.EqualValues(t, expectedNumClosedIssues, label.NumClosedIssues)
}
testSuccess(1, 1, 2)
testSuccess(2, 5, 2)
testSuccess(1, 1, 2) // delete non-existent IssueLabel
unittest.CheckConsistencyFor(t, &Issue{}, &Label{})
}

View file

@ -1,78 +0,0 @@
// Copyright 2020 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 (
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/timeutil"
"github.com/stretchr/testify/assert"
)
func TestCancelStopwatch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
user1, err := user_model.GetUserByID(1)
assert.NoError(t, err)
issue1, err := GetIssueByID(1)
assert.NoError(t, err)
issue2, err := GetIssueByID(2)
assert.NoError(t, err)
err = CancelStopwatch(user1, issue1)
assert.NoError(t, err)
unittest.AssertNotExistsBean(t, &Stopwatch{UserID: user1.ID, IssueID: issue1.ID})
_ = unittest.AssertExistsAndLoadBean(t, &Comment{Type: CommentTypeCancelTracking, PosterID: user1.ID, IssueID: issue1.ID})
assert.Nil(t, CancelStopwatch(user1, issue2))
}
func TestStopwatchExists(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
assert.True(t, StopwatchExists(1, 1))
assert.False(t, StopwatchExists(1, 2))
}
func TestHasUserStopwatch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
exists, sw, err := HasUserStopwatch(db.DefaultContext, 1)
assert.NoError(t, err)
assert.True(t, exists)
assert.Equal(t, int64(1), sw.ID)
exists, _, err = HasUserStopwatch(db.DefaultContext, 3)
assert.NoError(t, err)
assert.False(t, exists)
}
func TestCreateOrStopIssueStopwatch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
user2, err := user_model.GetUserByID(2)
assert.NoError(t, err)
user3, err := user_model.GetUserByID(3)
assert.NoError(t, err)
issue1, err := GetIssueByID(1)
assert.NoError(t, err)
issue2, err := GetIssueByID(2)
assert.NoError(t, err)
assert.NoError(t, CreateOrStopIssueStopwatch(user3, issue1))
sw := unittest.AssertExistsAndLoadBean(t, &Stopwatch{UserID: 3, IssueID: 1}).(*Stopwatch)
assert.LessOrEqual(t, sw.CreatedUnix, timeutil.TimeStampNow())
assert.NoError(t, CreateOrStopIssueStopwatch(user2, issue2))
unittest.AssertNotExistsBean(t, &Stopwatch{UserID: 2, IssueID: 2})
unittest.AssertExistsAndLoadBean(t, &TrackedTime{UserID: 2, IssueID: 2})
}

View file

@ -1,61 +0,0 @@
// Copyright 2017 The Gogs 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 (
"testing"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
)
func Test_newIssueUsers(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
newIssue := &Issue{
RepoID: repo.ID,
PosterID: 4,
Index: 6,
Title: "newTestIssueTitle",
Content: "newTestIssueContent",
}
// artificially insert new issue
unittest.AssertSuccessfulInsert(t, newIssue)
assert.NoError(t, newIssueUsers(db.DefaultContext, repo, newIssue))
// issue_user table should now have entries for new issue
unittest.AssertExistsAndLoadBean(t, &IssueUser{IssueID: newIssue.ID, UID: newIssue.PosterID})
unittest.AssertExistsAndLoadBean(t, &IssueUser{IssueID: newIssue.ID, UID: repo.OwnerID})
}
func TestUpdateIssueUserByRead(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue)
assert.NoError(t, UpdateIssueUserByRead(4, issue.ID))
unittest.AssertExistsAndLoadBean(t, &IssueUser{IssueID: issue.ID, UID: 4}, "is_read=1")
assert.NoError(t, UpdateIssueUserByRead(4, issue.ID))
unittest.AssertExistsAndLoadBean(t, &IssueUser{IssueID: issue.ID, UID: 4}, "is_read=1")
assert.NoError(t, UpdateIssueUserByRead(unittest.NonexistentID, unittest.NonexistentID))
}
func TestUpdateIssueUsersByMentions(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue)
uids := []int64{2, 5}
assert.NoError(t, UpdateIssueUsersByMentions(db.DefaultContext, issue.ID, uids))
for _, uid := range uids {
unittest.AssertExistsAndLoadBean(t, &IssueUser{IssueID: issue.ID, UID: uid}, "is_mentioned=1")
}
}

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues
import (
"context"

View file

@ -2,12 +2,13 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues_test
import (
"testing"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
@ -18,27 +19,27 @@ func TestUpdateAssignee(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
// Fake issue with assignees
issue, err := GetIssueWithAttrsByID(1)
issue, err := issues_model.GetIssueWithAttrsByID(1)
assert.NoError(t, err)
// Assign multiple users
user2, err := user_model.GetUserByID(2)
assert.NoError(t, err)
_, _, err = ToggleIssueAssignee(issue, &user_model.User{ID: 1}, user2.ID)
_, _, err = issues_model.ToggleIssueAssignee(issue, &user_model.User{ID: 1}, user2.ID)
assert.NoError(t, err)
user3, err := user_model.GetUserByID(3)
assert.NoError(t, err)
_, _, err = ToggleIssueAssignee(issue, &user_model.User{ID: 1}, user3.ID)
_, _, err = issues_model.ToggleIssueAssignee(issue, &user_model.User{ID: 1}, user3.ID)
assert.NoError(t, err)
user1, err := user_model.GetUserByID(1) // This user is already assigned (see the definition in fixtures), so running UpdateAssignee should unassign him
assert.NoError(t, err)
_, _, err = ToggleIssueAssignee(issue, &user_model.User{ID: 1}, user1.ID)
_, _, err = issues_model.ToggleIssueAssignee(issue, &user_model.User{ID: 1}, user1.ID)
assert.NoError(t, err)
// Check if he got removed
isAssigned, err := IsUserAssignedToIssue(db.DefaultContext, issue, user1)
isAssigned, err := issues_model.IsUserAssignedToIssue(db.DefaultContext, issue, user1)
assert.NoError(t, err)
assert.False(t, isAssigned)
@ -54,12 +55,12 @@ func TestUpdateAssignee(t *testing.T) {
}
// Check if the user is assigned
isAssigned, err = IsUserAssignedToIssue(db.DefaultContext, issue, user2)
isAssigned, err = issues_model.IsUserAssignedToIssue(db.DefaultContext, issue, user2)
assert.NoError(t, err)
assert.True(t, isAssigned)
// This user should not be assigned
isAssigned, err = IsUserAssignedToIssue(db.DefaultContext, issue, &user_model.User{ID: 4})
isAssigned, err = issues_model.IsUserAssignedToIssue(db.DefaultContext, issue, &user_model.User{ID: 4})
assert.NoError(t, err)
assert.False(t, isAssigned)
}
@ -70,22 +71,22 @@ func TestMakeIDsFromAPIAssigneesToAdd(t *testing.T) {
_ = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
_ = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
IDs, err := MakeIDsFromAPIAssigneesToAdd("", []string{""})
IDs, err := issues_model.MakeIDsFromAPIAssigneesToAdd("", []string{""})
assert.NoError(t, err)
assert.Equal(t, []int64{}, IDs)
_, err = MakeIDsFromAPIAssigneesToAdd("", []string{"none_existing_user"})
_, err = issues_model.MakeIDsFromAPIAssigneesToAdd("", []string{"none_existing_user"})
assert.Error(t, err)
IDs, err = MakeIDsFromAPIAssigneesToAdd("user1", []string{"user1"})
IDs, err = issues_model.MakeIDsFromAPIAssigneesToAdd("user1", []string{"user1"})
assert.NoError(t, err)
assert.Equal(t, []int64{1}, IDs)
IDs, err = MakeIDsFromAPIAssigneesToAdd("user2", []string{""})
IDs, err = issues_model.MakeIDsFromAPIAssigneesToAdd("user2", []string{""})
assert.NoError(t, err)
assert.Equal(t, []int64{2}, IDs)
IDs, err = MakeIDsFromAPIAssigneesToAdd("", []string{"user1", "user2"})
IDs, err = issues_model.MakeIDsFromAPIAssigneesToAdd("", []string{"user1", "user2"})
assert.NoError(t, err)
assert.Equal(t, []int64{1, 2}, IDs)
}

View file

@ -4,7 +4,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues
import (
"context"
@ -16,7 +16,6 @@ import (
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
project_model "code.gitea.io/gitea/models/project"
repo_model "code.gitea.io/gitea/models/repo"
@ -34,6 +33,22 @@ import (
"xorm.io/xorm"
)
// ErrCommentNotExist represents a "CommentNotExist" kind of error.
type ErrCommentNotExist struct {
ID int64
IssueID int64
}
// IsErrCommentNotExist checks if an error is a ErrCommentNotExist.
func IsErrCommentNotExist(err error) bool {
_, ok := err.(ErrCommentNotExist)
return ok
}
func (err ErrCommentNotExist) Error() string {
return fmt.Sprintf("comment does not exist [id: %d, issue_id: %d]", err.ID, err.IssueID)
}
// CommentType defines whether a comment is just a simple comment, an action (like close) or a reference.
type CommentType int
@ -216,8 +231,8 @@ type Comment struct {
Project *project_model.Project `xorm:"-"`
OldMilestoneID int64
MilestoneID int64
OldMilestone *issues_model.Milestone `xorm:"-"`
Milestone *issues_model.Milestone `xorm:"-"`
OldMilestone *Milestone `xorm:"-"`
Milestone *Milestone `xorm:"-"`
TimeID int64
Time *TrackedTime `xorm:"-"`
AssigneeID int64
@ -250,8 +265,8 @@ type Comment struct {
// Reference issue in commit message
CommitSHA string `xorm:"VARCHAR(40)"`
Attachments []*repo_model.Attachment `xorm:"-"`
Reactions issues_model.ReactionList `xorm:"-"`
Attachments []*repo_model.Attachment `xorm:"-"`
Reactions ReactionList `xorm:"-"`
// For view issue page.
ShowRole RoleDescriptor `xorm:"-"`
@ -299,7 +314,7 @@ func (c *Comment) LoadIssueCtx(ctx context.Context) (err error) {
if c.Issue != nil {
return nil
}
c.Issue, err = getIssueByID(ctx, c.IssueID)
c.Issue, err = GetIssueByID(ctx, c.IssueID)
return
}
@ -503,7 +518,7 @@ func (c *Comment) LoadProject() error {
// LoadMilestone if comment.Type is CommentTypeMilestone, then load milestone
func (c *Comment) LoadMilestone() error {
if c.OldMilestoneID > 0 {
var oldMilestone issues_model.Milestone
var oldMilestone Milestone
has, err := db.GetEngine(db.DefaultContext).ID(c.OldMilestoneID).Get(&oldMilestone)
if err != nil {
return err
@ -513,7 +528,7 @@ func (c *Comment) LoadMilestone() error {
}
if c.MilestoneID > 0 {
var milestone issues_model.Milestone
var milestone Milestone
has, err := db.GetEngine(db.DefaultContext).ID(c.MilestoneID).Get(&milestone)
if err != nil {
return err
@ -625,7 +640,7 @@ func (c *Comment) LoadDepIssueDetails() (err error) {
if c.DependentIssueID <= 0 || c.DependentIssue != nil {
return nil
}
c.DependentIssue, err = getIssueByID(db.DefaultContext, c.DependentIssueID)
c.DependentIssue, err = GetIssueByID(db.DefaultContext, c.DependentIssueID)
return err
}
@ -643,7 +658,7 @@ func (c *Comment) loadReactions(ctx context.Context, repo *repo_model.Repository
if c.Reactions != nil {
return nil
}
c.Reactions, _, err = issues_model.FindReactions(ctx, issues_model.FindReactionsOptions{
c.Reactions, _, err = FindReactions(ctx, FindReactionsOptions{
IssueID: c.IssueID,
CommentID: c.ID,
})
@ -823,7 +838,7 @@ func CreateCommentCtx(ctx context.Context, opts *CreateCommentOptions) (_ *Comme
return nil, err
}
if err = comment.addCrossReferences(ctx, opts.Doer, false); err != nil {
if err = comment.AddCrossReferences(ctx, opts.Doer, false); err != nil {
return nil, err
}
@ -1128,7 +1143,7 @@ func UpdateComment(c *Comment, doer *user_model.User) error {
if err := c.LoadIssueCtx(ctx); err != nil {
return err
}
if err := c.addCrossReferences(ctx, doer, true); err != nil {
if err := c.AddCrossReferences(ctx, doer, true); err != nil {
return err
}
if err := committer.Commit(); err != nil {
@ -1139,27 +1154,13 @@ func UpdateComment(c *Comment, doer *user_model.User) error {
}
// DeleteComment deletes the comment
func DeleteComment(comment *Comment) error {
ctx, committer, err := db.TxContext()
if err != nil {
return err
}
defer committer.Close()
if err := deleteComment(ctx, comment); err != nil {
return err
}
return committer.Commit()
}
func deleteComment(ctx context.Context, comment *Comment) error {
func DeleteComment(ctx context.Context, comment *Comment) error {
e := db.GetEngine(ctx)
if _, err := e.ID(comment.ID).NoAutoCondition().Delete(comment); err != nil {
return err
}
if _, err := db.DeleteByBean(ctx, &issues_model.ContentHistory{
if _, err := db.DeleteByBean(ctx, &ContentHistory{
CommentID: comment.ID,
}); err != nil {
return err
@ -1170,7 +1171,11 @@ func deleteComment(ctx context.Context, comment *Comment) error {
return err
}
}
if _, err := e.Where("comment_id = ?", comment.ID).Cols("is_deleted").Update(&Action{IsDeleted: true}); err != nil {
if _, err := e.Table("action").
Where("comment_id = ?", comment.ID).
Update(map[string]interface{}{
"is_deleted": true,
}); err != nil {
return err
}
@ -1178,7 +1183,7 @@ func deleteComment(ctx context.Context, comment *Comment) error {
return err
}
return issues_model.DeleteReaction(ctx, &issues_model.ReactionOptions{CommentID: comment.ID})
return DeleteReaction(ctx, &ReactionOptions{CommentID: comment.ID})
}
// CodeComments represents comments on code by using this structure: FILENAME -> LINE (+ == proposed; - == previous) -> COMMENTS
@ -1500,3 +1505,42 @@ func (c *Comment) GetExternalName() string { return c.OriginalAuthor }
// GetExternalID ExternalUserRemappable interface
func (c *Comment) GetExternalID() int64 { return c.OriginalAuthorID }
// CountCommentTypeLabelWithEmptyLabel count label comments with empty label
func CountCommentTypeLabelWithEmptyLabel() (int64, error) {
return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": CommentTypeLabel, "label_id": 0}).Count(new(Comment))
}
// FixCommentTypeLabelWithEmptyLabel count label comments with empty label
func FixCommentTypeLabelWithEmptyLabel() (int64, error) {
return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": CommentTypeLabel, "label_id": 0}).Delete(new(Comment))
}
// CountCommentTypeLabelWithOutsideLabels count label comments with outside label
func CountCommentTypeLabelWithOutsideLabels() (int64, error) {
return db.GetEngine(db.DefaultContext).Where("comment.type = ? AND ((label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id))", CommentTypeLabel).
Table("comment").
Join("inner", "label", "label.id = comment.label_id").
Join("inner", "issue", "issue.id = comment.issue_id ").
Join("inner", "repository", "issue.repo_id = repository.id").
Count()
}
// FixCommentTypeLabelWithOutsideLabels count label comments with outside label
func FixCommentTypeLabelWithOutsideLabels() (int64, error) {
res, err := db.GetEngine(db.DefaultContext).Exec(`DELETE FROM comment WHERE comment.id IN (
SELECT il_too.id FROM (
SELECT com.id
FROM comment AS com
INNER JOIN label ON com.label_id = label.id
INNER JOIN issue on issue.id = com.issue_id
INNER JOIN repository ON issue.repo_id = repository.id
WHERE
com.type = ? AND ((label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id))
) AS il_too)`, CommentTypeLabel)
if err != nil {
return 0, err
}
return res.RowsAffected()
}

View file

@ -2,13 +2,12 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues
import (
"context"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container"
@ -36,7 +35,7 @@ func (comments CommentList) loadPosters(ctx context.Context) error {
posterMaps := make(map[int64]*user_model.User, len(posterIDs))
left := len(posterIDs)
for left > 0 {
limit := defaultMaxInSize
limit := db.DefaultMaxInSize
if left < limit {
limit = left
}
@ -80,7 +79,7 @@ func (comments CommentList) getLabelIDs() []int64 {
return container.KeysInt64(ids)
}
func (comments CommentList) loadLabels(ctx context.Context) error {
func (comments CommentList) loadLabels(ctx context.Context) error { //nolint
if len(comments) == 0 {
return nil
}
@ -89,7 +88,7 @@ func (comments CommentList) loadLabels(ctx context.Context) error {
commentLabels := make(map[int64]*Label, len(labelIDs))
left := len(labelIDs)
for left > 0 {
limit := defaultMaxInSize
limit := db.DefaultMaxInSize
if left < limit {
limit = left
}
@ -140,10 +139,10 @@ func (comments CommentList) loadMilestones(ctx context.Context) error {
return nil
}
milestoneMaps := make(map[int64]*issues_model.Milestone, len(milestoneIDs))
milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
left := len(milestoneIDs)
for left > 0 {
limit := defaultMaxInSize
limit := db.DefaultMaxInSize
if left < limit {
limit = left
}
@ -183,10 +182,10 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error {
return nil
}
milestoneMaps := make(map[int64]*issues_model.Milestone, len(milestoneIDs))
milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
left := len(milestoneIDs)
for left > 0 {
limit := defaultMaxInSize
limit := db.DefaultMaxInSize
if left < limit {
limit = left
}
@ -225,7 +224,7 @@ func (comments CommentList) loadAssignees(ctx context.Context) error {
assignees := make(map[int64]*user_model.User, len(assigneeIDs))
left := len(assigneeIDs)
for left > 0 {
limit := defaultMaxInSize
limit := db.DefaultMaxInSize
if left < limit {
limit = left
}
@ -299,7 +298,7 @@ func (comments CommentList) loadIssues(ctx context.Context) error {
issues := make(map[int64]*Issue, len(issueIDs))
left := len(issueIDs)
for left > 0 {
limit := defaultMaxInSize
limit := db.DefaultMaxInSize
if left < limit {
limit = left
}
@ -357,7 +356,7 @@ func (comments CommentList) loadDependentIssues(ctx context.Context) error {
issues := make(map[int64]*Issue, len(issueIDs))
left := len(issueIDs)
for left > 0 {
limit := defaultMaxInSize
limit := db.DefaultMaxInSize
if left < limit {
limit = left
}
@ -406,7 +405,7 @@ func (comments CommentList) loadAttachments(ctx context.Context) (err error) {
commentsIDs := comments.getCommentIDs()
left := len(commentsIDs)
for left > 0 {
limit := defaultMaxInSize
limit := db.DefaultMaxInSize
if left < limit {
limit = left
}
@ -449,7 +448,7 @@ func (comments CommentList) getReviewIDs() []int64 {
return container.KeysInt64(ids)
}
func (comments CommentList) loadReviews(ctx context.Context) error {
func (comments CommentList) loadReviews(ctx context.Context) error { //nolint
if len(comments) == 0 {
return nil
}
@ -458,7 +457,7 @@ func (comments CommentList) loadReviews(ctx context.Context) error {
reviews := make(map[int64]*Review, len(reviewIDs))
left := len(reviewIDs)
for left > 0 {
limit := defaultMaxInSize
limit := db.DefaultMaxInSize
if left < limit {
limit = left
}

View file

@ -2,13 +2,14 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues_test
import (
"testing"
"time"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
@ -19,13 +20,13 @@ import (
func TestCreateComment(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
issue := unittest.AssertExistsAndLoadBean(t, &Issue{}).(*Issue)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{}).(*issues_model.Issue)
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository)
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
now := time.Now().Unix()
comment, err := CreateComment(&CreateCommentOptions{
Type: CommentTypeComment,
comment, err := issues_model.CreateComment(&issues_model.CreateCommentOptions{
Type: issues_model.CommentTypeComment,
Doer: doer,
Repo: repo,
Issue: issue,
@ -34,23 +35,23 @@ func TestCreateComment(t *testing.T) {
assert.NoError(t, err)
then := time.Now().Unix()
assert.EqualValues(t, CommentTypeComment, comment.Type)
assert.EqualValues(t, issues_model.CommentTypeComment, comment.Type)
assert.EqualValues(t, "Hello", comment.Content)
assert.EqualValues(t, issue.ID, comment.IssueID)
assert.EqualValues(t, doer.ID, comment.PosterID)
unittest.AssertInt64InRange(t, now, then, int64(comment.CreatedUnix))
unittest.AssertExistsAndLoadBean(t, comment) // assert actually added to DB
updatedIssue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: issue.ID}).(*Issue)
updatedIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue.ID}).(*issues_model.Issue)
unittest.AssertInt64InRange(t, now, then, int64(updatedIssue.UpdatedUnix))
}
func TestFetchCodeComments(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 2}).(*Issue)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}).(*issues_model.Issue)
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
res, err := FetchCodeComments(db.DefaultContext, issue, user)
res, err := issues_model.FetchCodeComments(db.DefaultContext, issue, user)
assert.NoError(t, err)
assert.Contains(t, res, "README.md")
assert.Contains(t, res["README.md"], int64(4))
@ -58,7 +59,7 @@ func TestFetchCodeComments(t *testing.T) {
assert.Equal(t, int64(4), res["README.md"][4][0].ID)
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
res, err = FetchCodeComments(db.DefaultContext, issue, user2)
res, err = issues_model.FetchCodeComments(db.DefaultContext, issue, user2)
assert.NoError(t, err)
assert.Len(t, res, 1)
}

View file

@ -53,13 +53,13 @@ func SaveIssueContentHistory(ctx context.Context, posterID, issueID, commentID i
}
// We only keep at most 20 history revisions now. It is enough in most cases.
// If there is a special requirement to keep more, we can consider introducing a new setting option then, but not now.
keepLimitedContentHistory(ctx, issueID, commentID, 20)
KeepLimitedContentHistory(ctx, issueID, commentID, 20)
return nil
}
// keepLimitedContentHistory keeps at most `limit` history revisions, it will hard delete out-dated revisions, sorting by revision interval
// KeepLimitedContentHistory keeps at most `limit` history revisions, it will hard delete out-dated revisions, sorting by revision interval
// we can ignore all errors in this function, so we just log them
func keepLimitedContentHistory(ctx context.Context, issueID, commentID int64, limit int) {
func KeepLimitedContentHistory(ctx context.Context, issueID, commentID int64, limit int) {
type IDEditTime struct {
ID int64
EditedUnix timeutil.TimeStamp

View file

@ -2,12 +2,13 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package issues
package issues_test
import (
"testing"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/timeutil"
@ -20,20 +21,20 @@ func TestContentHistory(t *testing.T) {
dbCtx := db.DefaultContext
timeStampNow := timeutil.TimeStampNow()
_ = SaveIssueContentHistory(dbCtx, 1, 10, 0, timeStampNow, "i-a", true)
_ = SaveIssueContentHistory(dbCtx, 1, 10, 0, timeStampNow.Add(2), "i-b", false)
_ = SaveIssueContentHistory(dbCtx, 1, 10, 0, timeStampNow.Add(7), "i-c", false)
_ = issues_model.SaveIssueContentHistory(dbCtx, 1, 10, 0, timeStampNow, "i-a", true)
_ = issues_model.SaveIssueContentHistory(dbCtx, 1, 10, 0, timeStampNow.Add(2), "i-b", false)
_ = issues_model.SaveIssueContentHistory(dbCtx, 1, 10, 0, timeStampNow.Add(7), "i-c", false)
_ = SaveIssueContentHistory(dbCtx, 1, 10, 100, timeStampNow, "c-a", true)
_ = SaveIssueContentHistory(dbCtx, 1, 10, 100, timeStampNow.Add(5), "c-b", false)
_ = SaveIssueContentHistory(dbCtx, 1, 10, 100, timeStampNow.Add(20), "c-c", false)
_ = SaveIssueContentHistory(dbCtx, 1, 10, 100, timeStampNow.Add(50), "c-d", false)
_ = SaveIssueContentHistory(dbCtx, 1, 10, 100, timeStampNow.Add(51), "c-e", false)
_ = issues_model.SaveIssueContentHistory(dbCtx, 1, 10, 100, timeStampNow, "c-a", true)
_ = issues_model.SaveIssueContentHistory(dbCtx, 1, 10, 100, timeStampNow.Add(5), "c-b", false)
_ = issues_model.SaveIssueContentHistory(dbCtx, 1, 10, 100, timeStampNow.Add(20), "c-c", false)
_ = issues_model.SaveIssueContentHistory(dbCtx, 1, 10, 100, timeStampNow.Add(50), "c-d", false)
_ = issues_model.SaveIssueContentHistory(dbCtx, 1, 10, 100, timeStampNow.Add(51), "c-e", false)
h1, _ := GetIssueContentHistoryByID(dbCtx, 1)
h1, _ := issues_model.GetIssueContentHistoryByID(dbCtx, 1)
assert.EqualValues(t, 1, h1.ID)
m, _ := QueryIssueContentHistoryEditedCountMap(dbCtx, 10)
m, _ := issues_model.QueryIssueContentHistoryEditedCountMap(dbCtx, 10)
assert.Equal(t, 3, m[0])
assert.Equal(t, 5, m[100])
@ -48,31 +49,31 @@ func TestContentHistory(t *testing.T) {
}
_ = db.GetEngine(dbCtx).Sync2(&User{})
list1, _ := FetchIssueContentHistoryList(dbCtx, 10, 0)
list1, _ := issues_model.FetchIssueContentHistoryList(dbCtx, 10, 0)
assert.Len(t, list1, 3)
list2, _ := FetchIssueContentHistoryList(dbCtx, 10, 100)
list2, _ := issues_model.FetchIssueContentHistoryList(dbCtx, 10, 100)
assert.Len(t, list2, 5)
hasHistory1, _ := HasIssueContentHistory(dbCtx, 10, 0)
hasHistory1, _ := issues_model.HasIssueContentHistory(dbCtx, 10, 0)
assert.True(t, hasHistory1)
hasHistory2, _ := HasIssueContentHistory(dbCtx, 10, 1)
hasHistory2, _ := issues_model.HasIssueContentHistory(dbCtx, 10, 1)
assert.False(t, hasHistory2)
h6, h6Prev, _ := GetIssueContentHistoryAndPrev(dbCtx, 6)
h6, h6Prev, _ := issues_model.GetIssueContentHistoryAndPrev(dbCtx, 6)
assert.EqualValues(t, 6, h6.ID)
assert.EqualValues(t, 5, h6Prev.ID)
// soft-delete
_ = SoftDeleteIssueContentHistory(dbCtx, 5)
h6, h6Prev, _ = GetIssueContentHistoryAndPrev(dbCtx, 6)
_ = issues_model.SoftDeleteIssueContentHistory(dbCtx, 5)
h6, h6Prev, _ = issues_model.GetIssueContentHistoryAndPrev(dbCtx, 6)
assert.EqualValues(t, 6, h6.ID)
assert.EqualValues(t, 4, h6Prev.ID)
// only keep 3 history revisions for comment_id=100, the first and the last should never be deleted
keepLimitedContentHistory(dbCtx, 10, 100, 3)
list1, _ = FetchIssueContentHistoryList(dbCtx, 10, 0)
issues_model.KeepLimitedContentHistory(dbCtx, 10, 100, 3)
list1, _ = issues_model.FetchIssueContentHistoryList(dbCtx, 10, 0)
assert.Len(t, list1, 3)
list2, _ = FetchIssueContentHistoryList(dbCtx, 10, 100)
list2, _ = issues_model.FetchIssueContentHistoryList(dbCtx, 10, 100)
assert.Len(t, list2, 3)
assert.EqualValues(t, 8, list2[0].HistoryID)
assert.EqualValues(t, 7, list2[1].HistoryID)

View file

@ -2,16 +2,95 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues
import (
"context"
"fmt"
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/timeutil"
)
// ErrDependencyExists represents a "DependencyAlreadyExists" kind of error.
type ErrDependencyExists struct {
IssueID int64
DependencyID int64
}
// IsErrDependencyExists checks if an error is a ErrDependencyExists.
func IsErrDependencyExists(err error) bool {
_, ok := err.(ErrDependencyExists)
return ok
}
func (err ErrDependencyExists) Error() string {
return fmt.Sprintf("issue dependency does already exist [issue id: %d, dependency id: %d]", err.IssueID, err.DependencyID)
}
// ErrDependencyNotExists represents a "DependencyAlreadyExists" kind of error.
type ErrDependencyNotExists struct {
IssueID int64
DependencyID int64
}
// IsErrDependencyNotExists checks if an error is a ErrDependencyExists.
func IsErrDependencyNotExists(err error) bool {
_, ok := err.(ErrDependencyNotExists)
return ok
}
func (err ErrDependencyNotExists) Error() string {
return fmt.Sprintf("issue dependency does not exist [issue id: %d, dependency id: %d]", err.IssueID, err.DependencyID)
}
// ErrCircularDependency represents a "DependencyCircular" kind of error.
type ErrCircularDependency struct {
IssueID int64
DependencyID int64
}
// IsErrCircularDependency checks if an error is a ErrCircularDependency.
func IsErrCircularDependency(err error) bool {
_, ok := err.(ErrCircularDependency)
return ok
}
func (err ErrCircularDependency) Error() string {
return fmt.Sprintf("circular dependencies exists (two issues blocking each other) [issue id: %d, dependency id: %d]", err.IssueID, err.DependencyID)
}
// ErrDependenciesLeft represents an error where the issue you're trying to close still has dependencies left.
type ErrDependenciesLeft struct {
IssueID int64
}
// IsErrDependenciesLeft checks if an error is a ErrDependenciesLeft.
func IsErrDependenciesLeft(err error) bool {
_, ok := err.(ErrDependenciesLeft)
return ok
}
func (err ErrDependenciesLeft) Error() string {
return fmt.Sprintf("issue has open dependencies [issue id: %d]", err.IssueID)
}
// ErrUnknownDependencyType represents an error where an unknown dependency type was passed
type ErrUnknownDependencyType struct {
Type DependencyType
}
// IsErrUnknownDependencyType checks if an error is ErrUnknownDependencyType
func IsErrUnknownDependencyType(err error) bool {
_, ok := err.(ErrUnknownDependencyType)
return ok
}
func (err ErrUnknownDependencyType) Error() string {
return fmt.Sprintf("unknown dependency type [type: %d]", err.Type)
}
// IssueDependency represents an issue dependency
type IssueDependency struct {
ID int64 `xorm:"pk autoincr"`

View file

@ -2,12 +2,13 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues_test
import (
"testing"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
@ -21,42 +22,42 @@ func TestCreateIssueDependency(t *testing.T) {
user1, err := user_model.GetUserByID(1)
assert.NoError(t, err)
issue1, err := GetIssueByID(1)
issue1, err := issues_model.GetIssueByID(db.DefaultContext, 1)
assert.NoError(t, err)
issue2, err := GetIssueByID(2)
issue2, err := issues_model.GetIssueByID(db.DefaultContext, 2)
assert.NoError(t, err)
// Create a dependency and check if it was successful
err = CreateIssueDependency(user1, issue1, issue2)
err = issues_model.CreateIssueDependency(user1, issue1, issue2)
assert.NoError(t, err)
// Do it again to see if it will check if the dependency already exists
err = CreateIssueDependency(user1, issue1, issue2)
err = issues_model.CreateIssueDependency(user1, issue1, issue2)
assert.Error(t, err)
assert.True(t, IsErrDependencyExists(err))
assert.True(t, issues_model.IsErrDependencyExists(err))
// Check for circular dependencies
err = CreateIssueDependency(user1, issue2, issue1)
err = issues_model.CreateIssueDependency(user1, issue2, issue1)
assert.Error(t, err)
assert.True(t, IsErrCircularDependency(err))
assert.True(t, issues_model.IsErrCircularDependency(err))
_ = unittest.AssertExistsAndLoadBean(t, &Comment{Type: CommentTypeAddDependency, PosterID: user1.ID, IssueID: issue1.ID})
_ = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{Type: issues_model.CommentTypeAddDependency, PosterID: user1.ID, IssueID: issue1.ID})
// Check if dependencies left is correct
left, err := IssueNoDependenciesLeft(db.DefaultContext, issue1)
left, err := issues_model.IssueNoDependenciesLeft(db.DefaultContext, issue1)
assert.NoError(t, err)
assert.False(t, left)
// Close #2 and check again
_, err = ChangeIssueStatus(db.DefaultContext, issue2, user1, true)
_, err = issues_model.ChangeIssueStatus(db.DefaultContext, issue2, user1, true)
assert.NoError(t, err)
left, err = IssueNoDependenciesLeft(db.DefaultContext, issue1)
left, err = issues_model.IssueNoDependenciesLeft(db.DefaultContext, issue1)
assert.NoError(t, err)
assert.True(t, left)
// Test removing the dependency
err = RemoveIssueDependency(user1, issue1, issue2, DependencyTypeBlockedBy)
err = issues_model.RemoveIssueDependency(user1, issue1, issue2, issues_model.DependencyTypeBlockedBy)
assert.NoError(t, err)
}

View file

@ -3,7 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues
import (
"context"
@ -16,7 +16,6 @@ import (
admin_model "code.gitea.io/gitea/models/admin"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/foreignreference"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
@ -29,7 +28,6 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/references"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
@ -38,6 +36,71 @@ import (
"xorm.io/xorm"
)
// ErrIssueNotExist represents a "IssueNotExist" kind of error.
type ErrIssueNotExist struct {
ID int64
RepoID int64
Index int64
}
// IsErrIssueNotExist checks if an error is a ErrIssueNotExist.
func IsErrIssueNotExist(err error) bool {
_, ok := err.(ErrIssueNotExist)
return ok
}
func (err ErrIssueNotExist) Error() string {
return fmt.Sprintf("issue does not exist [id: %d, repo_id: %d, index: %d]", err.ID, err.RepoID, err.Index)
}
// ErrIssueIsClosed represents a "IssueIsClosed" kind of error.
type ErrIssueIsClosed struct {
ID int64
RepoID int64
Index int64
}
// IsErrIssueIsClosed checks if an error is a ErrIssueNotExist.
func IsErrIssueIsClosed(err error) bool {
_, ok := err.(ErrIssueIsClosed)
return ok
}
func (err ErrIssueIsClosed) Error() string {
return fmt.Sprintf("issue is closed [id: %d, repo_id: %d, index: %d]", err.ID, err.RepoID, err.Index)
}
// ErrNewIssueInsert is used when the INSERT statement in newIssue fails
type ErrNewIssueInsert struct {
OriginalError error
}
// IsErrNewIssueInsert checks if an error is a ErrNewIssueInsert.
func IsErrNewIssueInsert(err error) bool {
_, ok := err.(ErrNewIssueInsert)
return ok
}
func (err ErrNewIssueInsert) Error() string {
return err.OriginalError.Error()
}
// ErrIssueWasClosed is used when close a closed issue
type ErrIssueWasClosed struct {
ID int64
Index int64
}
// IsErrIssueWasClosed checks if an error is a ErrIssueWasClosed.
func IsErrIssueWasClosed(err error) bool {
_, ok := err.(ErrIssueWasClosed)
return ok
}
func (err ErrIssueWasClosed) Error() string {
return fmt.Sprintf("Issue [%d] %d was already closed", err.ID, err.Index)
}
// Issue represents an issue or pull request of repository.
type Issue struct {
ID int64 `xorm:"pk autoincr"`
@ -47,14 +110,14 @@ type Issue struct {
PosterID int64 `xorm:"INDEX"`
Poster *user_model.User `xorm:"-"`
OriginalAuthor string
OriginalAuthorID int64 `xorm:"index"`
Title string `xorm:"name"`
Content string `xorm:"LONGTEXT"`
RenderedContent string `xorm:"-"`
Labels []*Label `xorm:"-"`
MilestoneID int64 `xorm:"INDEX"`
Milestone *issues_model.Milestone `xorm:"-"`
Project *project_model.Project `xorm:"-"`
OriginalAuthorID int64 `xorm:"index"`
Title string `xorm:"name"`
Content string `xorm:"LONGTEXT"`
RenderedContent string `xorm:"-"`
Labels []*Label `xorm:"-"`
MilestoneID int64 `xorm:"INDEX"`
Milestone *Milestone `xorm:"-"`
Project *project_model.Project `xorm:"-"`
Priority int
AssigneeID int64 `xorm:"-"`
Assignee *user_model.User `xorm:"-"`
@ -73,7 +136,7 @@ type Issue struct {
Attachments []*repo_model.Attachment `xorm:"-"`
Comments []*Comment `xorm:"-"`
Reactions issues_model.ReactionList `xorm:"-"`
Reactions ReactionList `xorm:"-"`
TotalTrackedTime int64 `xorm:"-"`
Assignees []*user_model.User `xorm:"-"`
ForeignReference *foreignreference.ForeignReference `xorm:"-"`
@ -107,7 +170,8 @@ func init() {
db.RegisterModel(new(IssueIndex))
}
func (issue *Issue) loadTotalTimes(ctx context.Context) (err error) {
// LoadTotalTimes load total tracked time
func (issue *Issue) LoadTotalTimes(ctx context.Context) (err error) {
opts := FindTrackedTimesOptions{IssueID: issue.ID}
issue.TotalTrackedTime, err = opts.toSession(db.GetEngine(ctx)).SumInt(&TrackedTime{}, "time")
if err != nil {
@ -237,7 +301,7 @@ func (issue *Issue) loadReactions(ctx context.Context) (err error) {
if issue.Reactions != nil {
return nil
}
reactions, _, err := issues_model.FindReactions(ctx, issues_model.FindReactionsOptions{
reactions, _, err := FindReactions(ctx, FindReactionsOptions{
IssueID: issue.ID,
})
if err != nil {
@ -247,7 +311,7 @@ func (issue *Issue) loadReactions(ctx context.Context) (err error) {
return err
}
// Load reaction user data
if _, err := issues_model.ReactionList(reactions).LoadUsers(ctx, issue.Repo); err != nil {
if _, err := ReactionList(reactions).LoadUsers(ctx, issue.Repo); err != nil {
return err
}
@ -292,15 +356,16 @@ func (issue *Issue) loadForeignReference(ctx context.Context) (err error) {
func (issue *Issue) loadMilestone(ctx context.Context) (err error) {
if (issue.Milestone == nil || issue.Milestone.ID != issue.MilestoneID) && issue.MilestoneID > 0 {
issue.Milestone, err = issues_model.GetMilestoneByRepoID(ctx, issue.RepoID, issue.MilestoneID)
if err != nil && !issues_model.IsErrMilestoneNotExist(err) {
issue.Milestone, err = GetMilestoneByRepoID(ctx, issue.RepoID, issue.MilestoneID)
if err != nil && !IsErrMilestoneNotExist(err) {
return fmt.Errorf("getMilestoneByRepoID [repo_id: %d, milestone_id: %d]: %v", issue.RepoID, issue.MilestoneID, err)
}
}
return nil
}
func (issue *Issue) loadAttributes(ctx context.Context) (err error) {
// LoadAttributes loads the attribute of this issue.
func (issue *Issue) LoadAttributes(ctx context.Context) (err error) {
if err = issue.LoadRepo(ctx); err != nil {
return
}
@ -345,7 +410,7 @@ func (issue *Issue) loadAttributes(ctx context.Context) (err error) {
return err
}
if issue.isTimetrackerEnabled(ctx) {
if err = issue.loadTotalTimes(ctx); err != nil {
if err = issue.LoadTotalTimes(ctx); err != nil {
return err
}
}
@ -357,11 +422,6 @@ func (issue *Issue) loadAttributes(ctx context.Context) (err error) {
return issue.loadReactions(ctx)
}
// LoadAttributes loads the attribute of this issue.
func (issue *Issue) LoadAttributes() error {
return issue.loadAttributes(db.DefaultContext)
}
// LoadMilestone load milestone of this issue.
func (issue *Issue) LoadMilestone() error {
return issue.loadMilestone(db.DefaultContext)
@ -590,15 +650,6 @@ func ReplaceIssueLabels(issue *Issue, labels []*Label, doer *user_model.User) (e
return committer.Commit()
}
// ReadBy sets issue to be read by given user.
func (issue *Issue) ReadBy(ctx context.Context, userID int64) error {
if err := UpdateIssueUserByRead(userID, issue.ID); err != nil {
return err
}
return setIssueNotificationStatusReadIfUnread(ctx, userID, issue.ID)
}
// UpdateIssueCols updates cols of issue
func UpdateIssueCols(ctx context.Context, issue *Issue, cols ...string) error {
if _, err := db.GetEngine(ctx).ID(issue.ID).Cols(cols...).Update(issue); err != nil {
@ -609,7 +660,7 @@ func UpdateIssueCols(ctx context.Context, issue *Issue, cols ...string) error {
func changeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isClosed, isMergePull bool) (*Comment, error) {
// Reload the issue
currentIssue, err := getIssueByID(ctx, issue.ID)
currentIssue, err := GetIssueByID(ctx, issue.ID)
if err != nil {
return nil, err
}
@ -666,7 +717,7 @@ func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.Use
// Update issue count of milestone
if issue.MilestoneID > 0 {
if err := issues_model.UpdateMilestoneCounters(ctx, issue.MilestoneID); err != nil {
if err := UpdateMilestoneCounters(ctx, issue.MilestoneID); err != nil {
return nil, err
}
}
@ -730,7 +781,7 @@ func ChangeIssueTitle(issue *Issue, doer *user_model.User, oldTitle string) (err
if _, err = CreateCommentCtx(ctx, opts); err != nil {
return fmt.Errorf("createComment: %v", err)
}
if err = issue.addCrossReferences(ctx, doer, true); err != nil {
if err = issue.AddCrossReferences(ctx, doer, true); err != nil {
return err
}
@ -772,7 +823,7 @@ func ChangeIssueRef(issue *Issue, doer *user_model.User, oldRef string) (err err
// AddDeletePRBranchComment adds delete branch comment for pull request issue
func AddDeletePRBranchComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, issueID int64, branchName string) error {
issue, err := getIssueByID(ctx, issueID)
issue, err := GetIssueByID(ctx, issueID)
if err != nil {
return err
}
@ -815,12 +866,12 @@ func ChangeIssueContent(issue *Issue, doer *user_model.User, content string) (er
}
defer committer.Close()
hasContentHistory, err := issues_model.HasIssueContentHistory(ctx, issue.ID, 0)
hasContentHistory, err := HasIssueContentHistory(ctx, issue.ID, 0)
if err != nil {
return fmt.Errorf("HasIssueContentHistory: %v", err)
}
if !hasContentHistory {
if err = issues_model.SaveIssueContentHistory(ctx, issue.PosterID, issue.ID, 0,
if err = SaveIssueContentHistory(ctx, issue.PosterID, issue.ID, 0,
issue.CreatedUnix, issue.Content, true); err != nil {
return fmt.Errorf("SaveIssueContentHistory: %v", err)
}
@ -832,12 +883,12 @@ func ChangeIssueContent(issue *Issue, doer *user_model.User, content string) (er
return fmt.Errorf("UpdateIssueCols: %v", err)
}
if err = issues_model.SaveIssueContentHistory(ctx, doer.ID, issue.ID, 0,
if err = SaveIssueContentHistory(ctx, doer.ID, issue.ID, 0,
timeutil.TimeStampNow(), issue.Content, false); err != nil {
return fmt.Errorf("SaveIssueContentHistory: %v", err)
}
if err = issue.addCrossReferences(ctx, doer, true); err != nil {
if err = issue.AddCrossReferences(ctx, doer, true); err != nil {
return fmt.Errorf("addCrossReferences: %v", err)
}
@ -907,13 +958,14 @@ type NewIssueOptions struct {
IsPull bool
}
func newIssue(ctx context.Context, doer *user_model.User, opts NewIssueOptions) (err error) {
// NewIssueWithIndex creates issue with given index
func NewIssueWithIndex(ctx context.Context, doer *user_model.User, opts NewIssueOptions) (err error) {
e := db.GetEngine(ctx)
opts.Issue.Title = strings.TrimSpace(opts.Issue.Title)
if opts.Issue.MilestoneID > 0 {
milestone, err := issues_model.GetMilestoneByRepoID(ctx, opts.Issue.RepoID, opts.Issue.MilestoneID)
if err != nil && !issues_model.IsErrMilestoneNotExist(err) {
milestone, err := GetMilestoneByRepoID(ctx, opts.Issue.RepoID, opts.Issue.MilestoneID)
if err != nil && !IsErrMilestoneNotExist(err) {
return fmt.Errorf("getMilestoneByID: %v", err)
}
@ -937,7 +989,7 @@ func newIssue(ctx context.Context, doer *user_model.User, opts NewIssueOptions)
}
if opts.Issue.MilestoneID > 0 {
if err := issues_model.UpdateMilestoneCounters(ctx, opts.Issue.MilestoneID); err != nil {
if err := UpdateMilestoneCounters(ctx, opts.Issue.MilestoneID); err != nil {
return err
}
@ -987,7 +1039,7 @@ func newIssue(ctx context.Context, doer *user_model.User, opts NewIssueOptions)
}
}
if err = newIssueUsers(ctx, opts.Repo, opts.Issue); err != nil {
if err = NewIssueUsers(ctx, opts.Repo, opts.Issue); err != nil {
return err
}
@ -1004,36 +1056,11 @@ func newIssue(ctx context.Context, doer *user_model.User, opts NewIssueOptions)
}
}
}
if err = opts.Issue.loadAttributes(ctx); err != nil {
if err = opts.Issue.LoadAttributes(ctx); err != nil {
return err
}
return opts.Issue.addCrossReferences(ctx, doer, false)
}
// RecalculateIssueIndexForRepo create issue_index for repo if not exist and
// update it based on highest index of existing issues assigned to a repo
func RecalculateIssueIndexForRepo(repoID int64) error {
ctx, committer, err := db.TxContext()
if err != nil {
return err
}
defer committer.Close()
if err := db.UpsertResourceIndex(db.GetEngine(ctx), "issue_index", repoID); err != nil {
return err
}
var max int64
if _, err := db.GetEngine(ctx).Select(" MAX(`index`)").Table("issue").Where("repo_id=?", repoID).Get(&max); err != nil {
return err
}
if _, err := db.GetEngine(ctx).Exec("UPDATE `issue_index` SET max_index=? WHERE group_id=?", max, repoID); err != nil {
return err
}
return committer.Commit()
return opts.Issue.AddCrossReferences(ctx, doer, false)
}
// NewIssue creates new issue with labels for repository.
@ -1051,13 +1078,13 @@ func NewIssue(repo *repo_model.Repository, issue *Issue, labelIDs []int64, uuids
}
defer committer.Close()
if err = newIssue(ctx, issue.Poster, NewIssueOptions{
if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{
Repo: repo,
Issue: issue,
LabelIDs: labelIDs,
Attachments: uuids,
}); err != nil {
if IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) {
if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) {
return err
}
return fmt.Errorf("newIssue: %v", err)
@ -1114,10 +1141,11 @@ func GetIssueWithAttrsByIndex(repoID, index int64) (*Issue, error) {
if err != nil {
return nil, err
}
return issue, issue.LoadAttributes()
return issue, issue.LoadAttributes(db.DefaultContext)
}
func getIssueByID(ctx context.Context, id int64) (*Issue, error) {
// GetIssueByID returns an issue by given ID.
func GetIssueByID(ctx context.Context, id int64) (*Issue, error) {
issue := new(Issue)
has, err := db.GetEngine(ctx).ID(id).Get(issue)
if err != nil {
@ -1130,16 +1158,11 @@ func getIssueByID(ctx context.Context, id int64) (*Issue, error) {
// GetIssueWithAttrsByID returns an issue with attributes by given ID.
func GetIssueWithAttrsByID(id int64) (*Issue, error) {
issue, err := getIssueByID(db.DefaultContext, id)
issue, err := GetIssueByID(db.DefaultContext, id)
if err != nil {
return nil, err
}
return issue, issue.loadAttributes(db.DefaultContext)
}
// GetIssueByID returns an issue by given ID.
func GetIssueByID(id int64) (*Issue, error) {
return getIssueByID(db.DefaultContext, id)
return issue, issue.LoadAttributes(db.DefaultContext)
}
// GetIssuesByIDs return issues with the given IDs.
@ -1156,7 +1179,7 @@ func GetIssueIDsByRepoID(ctx context.Context, repoID int64) ([]int64, error) {
}
// IssuesOptions represents options of an issue.
type IssuesOptions struct {
type IssuesOptions struct { //nolint
db.ListOptions
RepoID int64 // overwrites RepoCond if not 0
RepoCond builder.Cond
@ -1534,7 +1557,7 @@ func GetParticipantsIDsByIssueID(issueID int64) ([]int64, error) {
// IsUserParticipantsOfIssue return true if user is participants of an issue
func IsUserParticipantsOfIssue(user *user_model.User, issue *Issue) bool {
userIDs, err := issue.getParticipantIDsByIssue(db.DefaultContext)
userIDs, err := issue.GetParticipantIDsByIssue(db.DefaultContext)
if err != nil {
log.Error(err.Error())
return false
@ -1577,17 +1600,6 @@ const (
FilterModeYourRepositories
)
func parseCountResult(results []map[string][]byte) int64 {
if len(results) == 0 {
return 0
}
for _, result := range results[0] {
c, _ := strconv.ParseInt(string(result), 10, 64)
return c
}
return 0
}
// IssueStatsOptions contains parameters accepted by GetIssueStats.
type IssueStatsOptions struct {
RepoID int64
@ -1602,14 +1614,15 @@ type IssueStatsOptions struct {
}
const (
// MaxQueryParameters represents the max query parameters
// When queries are broken down in parts because of the number
// of parameters, attempt to break by this amount
maxQueryParameters = 300
MaxQueryParameters = 300
)
// GetIssueStats returns issue statistic information by given conditions.
func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) {
if len(opts.IssueIDs) <= maxQueryParameters {
if len(opts.IssueIDs) <= MaxQueryParameters {
return getIssueStatsChunk(opts, opts.IssueIDs)
}
@ -1619,7 +1632,7 @@ func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) {
// ids in a temporary table and join from them.
accum := &IssueStats{}
for i := 0; i < len(opts.IssueIDs); {
chunk := i + maxQueryParameters
chunk := i + MaxQueryParameters
if chunk > len(opts.IssueIDs) {
chunk = len(opts.IssueIDs)
}
@ -1950,7 +1963,7 @@ func UpdateIssueByAPI(issue *Issue, doer *user_model.User) (statusChangeComment
}
// Reload the issue
currentIssue, err := getIssueByID(ctx, issue.ID)
currentIssue, err := GetIssueByID(ctx, issue.ID)
if err != nil {
return nil, false, err
}
@ -1985,7 +1998,7 @@ func UpdateIssueByAPI(issue *Issue, doer *user_model.User) (statusChangeComment
}
}
if err := issue.addCrossReferences(ctx, doer, true); err != nil {
if err := issue.AddCrossReferences(ctx, doer, true); err != nil {
return nil, false, err
}
return statusChangeComment, titleChanged, committer.Commit()
@ -2016,22 +2029,8 @@ func UpdateIssueDeadline(issue *Issue, deadlineUnix timeutil.TimeStamp, doer *us
return committer.Commit()
}
// DeleteIssue deletes the issue
func DeleteIssue(issue *Issue) error {
ctx, committer, err := db.TxContext()
if err != nil {
return err
}
defer committer.Close()
if err := deleteIssue(ctx, issue); err != nil {
return err
}
return committer.Commit()
}
func deleteInIssue(ctx context.Context, issueID int64, beans ...interface{}) error {
// DeleteInIssue delete records in beans with external key issue_id = ?
func DeleteInIssue(ctx context.Context, issueID int64, beans ...interface{}) error {
e := db.GetEngine(ctx)
for _, bean := range beans {
if _, err := e.In("issue_id", issueID).Delete(bean); err != nil {
@ -2041,103 +2040,14 @@ func deleteInIssue(ctx context.Context, issueID int64, beans ...interface{}) err
return nil
}
func deleteIssue(ctx context.Context, issue *Issue) error {
e := db.GetEngine(ctx)
if _, err := e.ID(issue.ID).NoAutoCondition().Delete(issue); err != nil {
return err
}
if issue.IsPull {
if _, err := e.ID(issue.RepoID).Decr("num_pulls").Update(new(repo_model.Repository)); err != nil {
return err
}
if issue.IsClosed {
if _, err := e.ID(issue.RepoID).Decr("num_closed_pulls").Update(new(repo_model.Repository)); err != nil {
return err
}
}
} else {
if _, err := e.ID(issue.RepoID).Decr("num_issues").Update(new(repo_model.Repository)); err != nil {
return err
}
if issue.IsClosed {
if _, err := e.ID(issue.RepoID).Decr("num_closed_issues").Update(new(repo_model.Repository)); err != nil {
return err
}
}
}
// delete actions assigned to this issue
subQuery := builder.Select("`id`").
From("`comment`").
Where(builder.Eq{"`issue_id`": issue.ID})
if _, err := e.In("comment_id", subQuery).Delete(&Action{}); err != nil {
return err
}
if _, err := e.Table("action").Where("repo_id = ?", issue.RepoID).
In("op_type", ActionCreateIssue, ActionCreatePullRequest).
Where("content LIKE ?", strconv.FormatInt(issue.ID, 10)+"|%").
Delete(&Action{}); err != nil {
return err
}
// find attachments related to this issue and remove them
var attachments []*repo_model.Attachment
if err := e.In("issue_id", issue.ID).Find(&attachments); err != nil {
return err
}
for i := range attachments {
admin_model.RemoveStorageWithNotice(ctx, storage.Attachments, "Delete issue attachment", attachments[i].RelativePath())
}
// delete all database data still assigned to this issue
if err := deleteInIssue(ctx, issue.ID,
&issues_model.ContentHistory{},
&Comment{},
&IssueLabel{},
&IssueDependency{},
&IssueAssignees{},
&IssueUser{},
&Notification{},
&issues_model.Reaction{},
&IssueWatch{},
&Stopwatch{},
&TrackedTime{},
&project_model.ProjectIssue{},
&repo_model.Attachment{},
&PullRequest{},
); err != nil {
return err
}
// References to this issue in other issues
if _, err := e.In("ref_issue_id", issue.ID).Delete(&Comment{}); err != nil {
return err
}
// Delete dependencies for issues in other repositories
if _, err := e.In("dependency_id", issue.ID).Delete(&IssueDependency{}); err != nil {
return err
}
// delete from dependent issues
if _, err := e.In("dependent_issue_id", issue.ID).Delete(&Comment{}); err != nil {
return err
}
return nil
}
// DependencyInfo represents high level information about an issue which is a dependency of another issue.
type DependencyInfo struct {
Issue `xorm:"extends"`
repo_model.Repository `xorm:"extends"`
}
// getParticipantIDsByIssue returns all userIDs who are participated in comments of an issue and issue author
func (issue *Issue) getParticipantIDsByIssue(ctx context.Context) ([]int64, error) {
// GetParticipantIDsByIssue returns all userIDs who are participated in comments of an issue and issue author
func (issue *Issue) GetParticipantIDsByIssue(ctx context.Context) ([]int64, error) {
if issue == nil {
return nil, nil
}
@ -2196,9 +2106,9 @@ func (issue *Issue) BlockingDependencies(ctx context.Context) (issueDeps []*Depe
func updateIssueClosedNum(ctx context.Context, issue *Issue) (err error) {
if issue.IsPull {
err = repoStatsCorrectNumClosed(ctx, issue.RepoID, true, "num_closed_pulls")
err = repo_model.StatsCorrectNumClosed(ctx, issue.RepoID, true, "num_closed_pulls")
} else {
err = repoStatsCorrectNumClosed(ctx, issue.RepoID, false, "num_closed_issues")
err = repo_model.StatsCorrectNumClosed(ctx, issue.RepoID, false, "num_closed_issues")
}
return
}
@ -2363,6 +2273,17 @@ func UpdateIssuesMigrationsByType(gitServiceType api.GitServiceType, originalAut
return err
}
func migratedIssueCond(tp api.GitServiceType) builder.Cond {
return builder.In("issue_id",
builder.Select("issue.id").
From("issue").
InnerJoin("repository", "issue.repo_id = repository.id").
Where(builder.Eq{
"repository.original_service_type": tp,
}),
)
}
// UpdateReactionsMigrationsByType updates all migrated repositories' reactions from gitServiceType to replace originalAuthorID to posterID
func UpdateReactionsMigrationsByType(gitServiceType api.GitServiceType, originalAuthorID string, userID int64) error {
_, err := db.GetEngine(db.DefaultContext).Table("reaction").
@ -2376,13 +2297,14 @@ func UpdateReactionsMigrationsByType(gitServiceType api.GitServiceType, original
return err
}
func deleteIssuesByRepoID(ctx context.Context, repoID int64) (attachmentPaths []string, err error) {
// DeleteIssuesByRepoID deletes issues by repositories id
func DeleteIssuesByRepoID(ctx context.Context, repoID int64) (attachmentPaths []string, err error) {
deleteCond := builder.Select("id").From("issue").Where(builder.Eq{"issue.repo_id": repoID})
sess := db.GetEngine(ctx)
// Delete content histories
if _, err = sess.In("issue_id", deleteCond).
Delete(&issues_model.ContentHistory{}); err != nil {
Delete(&ContentHistory{}); err != nil {
return
}
@ -2410,7 +2332,7 @@ func deleteIssuesByRepoID(ctx context.Context, repoID int64) (attachmentPaths []
}
if _, err = sess.In("issue_id", deleteCond).
Delete(&issues_model.Reaction{}); err != nil {
Delete(&Reaction{}); err != nil {
return
}
@ -2477,3 +2399,50 @@ func (issue *Issue) GetExternalName() string { return issue.OriginalAuthor }
// GetExternalID ExternalUserRemappable interface
func (issue *Issue) GetExternalID() int64 { return issue.OriginalAuthorID }
// CountOrphanedIssues count issues without a repo
func CountOrphanedIssues() (int64, error) {
return db.GetEngine(db.DefaultContext).Table("issue").
Join("LEFT", "repository", "issue.repo_id=repository.id").
Where(builder.IsNull{"repository.id"}).
Select("COUNT(`issue`.`id`)").
Count()
}
// DeleteOrphanedIssues delete issues without a repo
func DeleteOrphanedIssues() error {
ctx, committer, err := db.TxContext()
if err != nil {
return err
}
defer committer.Close()
var ids []int64
if err := db.GetEngine(ctx).Table("issue").Distinct("issue.repo_id").
Join("LEFT", "repository", "issue.repo_id=repository.id").
Where(builder.IsNull{"repository.id"}).GroupBy("issue.repo_id").
Find(&ids); err != nil {
return err
}
var attachmentPaths []string
for i := range ids {
paths, err := DeleteIssuesByRepoID(ctx, ids[i])
if err != nil {
return err
}
attachmentPaths = append(attachmentPaths, paths...)
}
if err := committer.Commit(); err != nil {
return err
}
committer.Close()
// Remove issue attachment files.
for i := range attachmentPaths {
admin_model.RemoveAllWithNotice(db.DefaultContext, "Delete issue attachment", attachmentPaths[i])
}
return nil
}

View file

@ -0,0 +1,32 @@
// Copyright 2017 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 issues
import "code.gitea.io/gitea/models/db"
// RecalculateIssueIndexForRepo create issue_index for repo if not exist and
// update it based on highest index of existing issues assigned to a repo
func RecalculateIssueIndexForRepo(repoID int64) error {
ctx, committer, err := db.TxContext()
if err != nil {
return err
}
defer committer.Close()
if err := db.UpsertResourceIndex(ctx, "issue_index", repoID); err != nil {
return err
}
var max int64
if _, err := db.GetEngine(ctx).Select(" MAX(`index`)").Table("issue").Where("repo_id=?", repoID).Get(&max); err != nil {
return err
}
if _, err := db.GetEngine(ctx).Exec("UPDATE `issue_index` SET max_index=? WHERE group_id=?", max, repoID); err != nil {
return err
}
return committer.Commit()
}

View file

@ -2,14 +2,13 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues
import (
"context"
"fmt"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container"
@ -20,11 +19,6 @@ import (
// IssueList defines a list of issues
type IssueList []*Issue
const (
// default variables number on IN () in SQL
defaultMaxInSize = 50
)
// get the repo IDs to be loaded later, these IDs are for issue.Repo and issue.PullRequest.HeadRepo
func (issues IssueList) getRepoIDs() []int64 {
repoIDs := make(map[int64]struct{}, len(issues))
@ -48,7 +42,7 @@ func (issues IssueList) loadRepositories(ctx context.Context) ([]*repo_model.Rep
repoMaps := make(map[int64]*repo_model.Repository, len(repoIDs))
left := len(repoIDs)
for left > 0 {
limit := defaultMaxInSize
limit := db.DefaultMaxInSize
if left < limit {
limit = left
}
@ -102,7 +96,7 @@ func (issues IssueList) loadPosters(ctx context.Context) error {
posterMaps := make(map[int64]*user_model.User, len(posterIDs))
left := len(posterIDs)
for left > 0 {
limit := defaultMaxInSize
limit := db.DefaultMaxInSize
if left < limit {
limit = left
}
@ -150,7 +144,7 @@ func (issues IssueList) loadLabels(ctx context.Context) error {
issueIDs := issues.getIssueIDs()
left := len(issueIDs)
for left > 0 {
limit := defaultMaxInSize
limit := db.DefaultMaxInSize
if left < limit {
limit = left
}
@ -205,10 +199,10 @@ func (issues IssueList) loadMilestones(ctx context.Context) error {
return nil
}
milestoneMaps := make(map[int64]*issues_model.Milestone, len(milestoneIDs))
milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
left := len(milestoneIDs)
for left > 0 {
limit := defaultMaxInSize
limit := db.DefaultMaxInSize
if left < limit {
limit = left
}
@ -242,7 +236,7 @@ func (issues IssueList) loadAssignees(ctx context.Context) error {
issueIDs := issues.getIssueIDs()
left := len(issueIDs)
for left > 0 {
limit := defaultMaxInSize
limit := db.DefaultMaxInSize
if left < limit {
limit = left
}
@ -298,7 +292,7 @@ func (issues IssueList) loadPullRequests(ctx context.Context) error {
pullRequestMaps := make(map[int64]*PullRequest, len(issuesIDs))
left := len(issuesIDs)
for left > 0 {
limit := defaultMaxInSize
limit := db.DefaultMaxInSize
if left < limit {
limit = left
}
@ -342,7 +336,7 @@ func (issues IssueList) loadAttachments(ctx context.Context) (err error) {
issuesIDs := issues.getIssueIDs()
left := len(issuesIDs)
for left > 0 {
limit := defaultMaxInSize
limit := db.DefaultMaxInSize
if left < limit {
limit = left
}
@ -387,7 +381,7 @@ func (issues IssueList) loadComments(ctx context.Context, cond builder.Cond) (er
issuesIDs := issues.getIssueIDs()
left := len(issuesIDs)
for left > 0 {
limit := defaultMaxInSize
limit := db.DefaultMaxInSize
if left < limit {
limit = left
}
@ -443,7 +437,7 @@ func (issues IssueList) loadTotalTrackedTimes(ctx context.Context) (err error) {
left := len(ids)
for left > 0 {
limit := defaultMaxInSize
limit := db.DefaultMaxInSize
if left < limit {
limit = left
}

View file

@ -2,11 +2,12 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues_test
import (
"testing"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting"
@ -16,10 +17,10 @@ import (
func TestIssueList_LoadRepositories(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
issueList := IssueList{
unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue),
unittest.AssertExistsAndLoadBean(t, &Issue{ID: 2}).(*Issue),
unittest.AssertExistsAndLoadBean(t, &Issue{ID: 4}).(*Issue),
issueList := issues_model.IssueList{
unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue),
unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}).(*issues_model.Issue),
unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4}).(*issues_model.Issue),
}
repos, err := issueList.LoadRepositories()
@ -33,9 +34,9 @@ func TestIssueList_LoadRepositories(t *testing.T) {
func TestIssueList_LoadAttributes(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
setting.Service.EnableTimetracking = true
issueList := IssueList{
unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue),
unittest.AssertExistsAndLoadBean(t, &Issue{ID: 4}).(*Issue),
issueList := issues_model.IssueList{
unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue),
unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4}).(*issues_model.Issue),
}
assert.NoError(t, issueList.LoadAttributes())
@ -43,7 +44,7 @@ func TestIssueList_LoadAttributes(t *testing.T) {
assert.EqualValues(t, issue.RepoID, issue.Repo.ID)
for _, label := range issue.Labels {
assert.EqualValues(t, issue.RepoID, label.RepoID)
unittest.AssertExistsAndLoadBean(t, &IssueLabel{IssueID: issue.ID, LabelID: label.ID})
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label.ID})
}
if issue.PosterID > 0 {
assert.EqualValues(t, issue.PosterID, issue.Poster.ID)

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues
import (
"code.gitea.io/gitea/models/db"

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues
import (
"context"

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues_test
import (
"context"
@ -29,18 +29,18 @@ func TestIssue_ReplaceLabels(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
testSuccess := func(issueID int64, labelIDs []int64) {
issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: issueID}).(*Issue)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issueID}).(*issues_model.Issue)
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository)
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
labels := make([]*Label, len(labelIDs))
labels := make([]*issues_model.Label, len(labelIDs))
for i, labelID := range labelIDs {
labels[i] = unittest.AssertExistsAndLoadBean(t, &Label{ID: labelID, RepoID: repo.ID}).(*Label)
labels[i] = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: labelID, RepoID: repo.ID}).(*issues_model.Label)
}
assert.NoError(t, ReplaceIssueLabels(issue, labels, doer))
unittest.AssertCount(t, &IssueLabel{IssueID: issueID}, len(labelIDs))
assert.NoError(t, issues_model.ReplaceIssueLabels(issue, labels, doer))
unittest.AssertCount(t, &issues_model.IssueLabel{IssueID: issueID}, len(labelIDs))
for _, labelID := range labelIDs {
unittest.AssertExistsAndLoadBean(t, &IssueLabel{IssueID: issueID, LabelID: labelID})
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issueID, LabelID: labelID})
}
}
@ -52,15 +52,15 @@ func TestIssue_ReplaceLabels(t *testing.T) {
func Test_GetIssueIDsByRepoID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
ids, err := GetIssueIDsByRepoID(db.DefaultContext, 1)
ids, err := issues_model.GetIssueIDsByRepoID(db.DefaultContext, 1)
assert.NoError(t, err)
assert.Len(t, ids, 5)
}
func TestIssueAPIURL(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue)
err := issue.LoadAttributes()
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue)
err := issue.LoadAttributes(db.DefaultContext)
assert.NoError(t, err)
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/issues/1", issue.APIURL())
@ -69,7 +69,7 @@ func TestIssueAPIURL(t *testing.T) {
func TestGetIssuesByIDs(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
testSuccess := func(expectedIssueIDs, nonExistentIssueIDs []int64) {
issues, err := GetIssuesByIDs(db.DefaultContext, append(expectedIssueIDs, nonExistentIssueIDs...))
issues, err := issues_model.GetIssuesByIDs(db.DefaultContext, append(expectedIssueIDs, nonExistentIssueIDs...))
assert.NoError(t, err)
actualIssueIDs := make([]int64, len(issues))
for i, issue := range issues {
@ -85,9 +85,9 @@ func TestGetParticipantIDsByIssue(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
checkParticipants := func(issueID int64, userIDs []int) {
issue, err := GetIssueByID(issueID)
issue, err := issues_model.GetIssueByID(db.DefaultContext, issueID)
assert.NoError(t, err)
participants, err := issue.getParticipantIDsByIssue(db.DefaultContext)
participants, err := issue.GetParticipantIDsByIssue(db.DefaultContext)
if assert.NoError(t, err) {
participantsIDs := make([]int, len(participants))
for i, uid := range participants {
@ -117,16 +117,16 @@ func TestIssue_ClearLabels(t *testing.T) {
}
for _, test := range tests {
assert.NoError(t, unittest.PrepareTestDatabase())
issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: test.issueID}).(*Issue)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: test.issueID}).(*issues_model.Issue)
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: test.doerID}).(*user_model.User)
assert.NoError(t, ClearIssueLabels(issue, doer))
unittest.AssertNotExistsBean(t, &IssueLabel{IssueID: test.issueID})
assert.NoError(t, issues_model.ClearIssueLabels(issue, doer))
unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{IssueID: test.issueID})
}
}
func TestUpdateIssueCols(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
issue := unittest.AssertExistsAndLoadBean(t, &Issue{}).(*Issue)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{}).(*issues_model.Issue)
const newTitle = "New Title for unit test"
issue.Title = newTitle
@ -135,10 +135,10 @@ func TestUpdateIssueCols(t *testing.T) {
issue.Content = "This should have no effect"
now := time.Now().Unix()
assert.NoError(t, UpdateIssueCols(db.DefaultContext, issue, "name"))
assert.NoError(t, issues_model.UpdateIssueCols(db.DefaultContext, issue, "name"))
then := time.Now().Unix()
updatedIssue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: issue.ID}).(*Issue)
updatedIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue.ID}).(*issues_model.Issue)
assert.EqualValues(t, newTitle, updatedIssue.Title)
assert.EqualValues(t, prevContent, updatedIssue.Content)
unittest.AssertInt64InRange(t, now, then, int64(updatedIssue.UpdatedUnix))
@ -147,18 +147,18 @@ func TestUpdateIssueCols(t *testing.T) {
func TestIssues(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
for _, test := range []struct {
Opts IssuesOptions
Opts issues_model.IssuesOptions
ExpectedIssueIDs []int64
}{
{
IssuesOptions{
issues_model.IssuesOptions{
AssigneeID: 1,
SortType: "oldest",
},
[]int64{1, 6},
},
{
IssuesOptions{
issues_model.IssuesOptions{
RepoCond: builder.In("repo_id", 1, 3),
SortType: "oldest",
ListOptions: db.ListOptions{
@ -169,7 +169,7 @@ func TestIssues(t *testing.T) {
[]int64{1, 2, 3, 5},
},
{
IssuesOptions{
issues_model.IssuesOptions{
LabelIDs: []int64{1},
ListOptions: db.ListOptions{
Page: 1,
@ -179,7 +179,7 @@ func TestIssues(t *testing.T) {
[]int64{2, 1},
},
{
IssuesOptions{
issues_model.IssuesOptions{
LabelIDs: []int64{1, 2},
ListOptions: db.ListOptions{
Page: 1,
@ -189,7 +189,7 @@ func TestIssues(t *testing.T) {
[]int64{}, // issues with **both** label 1 and 2, none of these issues matches, TODO: add more tests
},
} {
issues, err := Issues(&test.Opts)
issues, err := issues_model.Issues(&test.Opts)
assert.NoError(t, err)
if assert.Len(t, issues, len(test.ExpectedIssueIDs)) {
for i, issue := range issues {
@ -202,16 +202,16 @@ func TestIssues(t *testing.T) {
func TestGetUserIssueStats(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
for _, test := range []struct {
Opts UserIssueStatsOptions
ExpectedIssueStats IssueStats
Opts issues_model.UserIssueStatsOptions
ExpectedIssueStats issues_model.IssueStats
}{
{
UserIssueStatsOptions{
issues_model.UserIssueStatsOptions{
UserID: 1,
RepoIDs: []int64{1},
FilterMode: FilterModeAll,
FilterMode: issues_model.FilterModeAll,
},
IssueStats{
issues_model.IssueStats{
YourRepositoriesCount: 1, // 6
AssignCount: 1, // 6
CreateCount: 1, // 6
@ -220,13 +220,13 @@ func TestGetUserIssueStats(t *testing.T) {
},
},
{
UserIssueStatsOptions{
issues_model.UserIssueStatsOptions{
UserID: 1,
RepoIDs: []int64{1},
FilterMode: FilterModeAll,
FilterMode: issues_model.FilterModeAll,
IsClosed: true,
},
IssueStats{
issues_model.IssueStats{
YourRepositoriesCount: 1, // 6
AssignCount: 0,
CreateCount: 0,
@ -235,11 +235,11 @@ func TestGetUserIssueStats(t *testing.T) {
},
},
{
UserIssueStatsOptions{
issues_model.UserIssueStatsOptions{
UserID: 1,
FilterMode: FilterModeAssign,
FilterMode: issues_model.FilterModeAssign,
},
IssueStats{
issues_model.IssueStats{
YourRepositoriesCount: 1, // 6
AssignCount: 1, // 6
CreateCount: 1, // 6
@ -248,11 +248,11 @@ func TestGetUserIssueStats(t *testing.T) {
},
},
{
UserIssueStatsOptions{
issues_model.UserIssueStatsOptions{
UserID: 1,
FilterMode: FilterModeCreate,
FilterMode: issues_model.FilterModeCreate,
},
IssueStats{
issues_model.IssueStats{
YourRepositoriesCount: 1, // 6
AssignCount: 1, // 6
CreateCount: 1, // 6
@ -261,11 +261,11 @@ func TestGetUserIssueStats(t *testing.T) {
},
},
{
UserIssueStatsOptions{
issues_model.UserIssueStatsOptions{
UserID: 1,
FilterMode: FilterModeMention,
FilterMode: issues_model.FilterModeMention,
},
IssueStats{
issues_model.IssueStats{
YourRepositoriesCount: 1, // 6
AssignCount: 1, // 6
CreateCount: 1, // 6
@ -275,12 +275,12 @@ func TestGetUserIssueStats(t *testing.T) {
},
},
{
UserIssueStatsOptions{
issues_model.UserIssueStatsOptions{
UserID: 1,
FilterMode: FilterModeCreate,
FilterMode: issues_model.FilterModeCreate,
IssueIDs: []int64{1},
},
IssueStats{
issues_model.IssueStats{
YourRepositoriesCount: 1, // 1
AssignCount: 1, // 1
CreateCount: 1, // 1
@ -289,13 +289,13 @@ func TestGetUserIssueStats(t *testing.T) {
},
},
{
UserIssueStatsOptions{
issues_model.UserIssueStatsOptions{
UserID: 2,
Org: unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}).(*organization.Organization),
Team: unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 7}).(*organization.Team),
FilterMode: FilterModeAll,
FilterMode: issues_model.FilterModeAll,
},
IssueStats{
issues_model.IssueStats{
YourRepositoriesCount: 2,
AssignCount: 1,
CreateCount: 1,
@ -304,7 +304,7 @@ func TestGetUserIssueStats(t *testing.T) {
},
} {
t.Run(fmt.Sprintf("%#v", test.Opts), func(t *testing.T) {
stats, err := GetUserIssueStats(test.Opts)
stats, err := issues_model.GetUserIssueStats(test.Opts)
if !assert.NoError(t, err) {
return
}
@ -315,31 +315,31 @@ func TestGetUserIssueStats(t *testing.T) {
func TestIssue_loadTotalTimes(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
ms, err := GetIssueByID(2)
ms, err := issues_model.GetIssueByID(db.DefaultContext, 2)
assert.NoError(t, err)
assert.NoError(t, ms.loadTotalTimes(db.DefaultContext))
assert.NoError(t, ms.LoadTotalTimes(db.DefaultContext))
assert.Equal(t, int64(3682), ms.TotalTrackedTime)
}
func TestIssue_SearchIssueIDsByKeyword(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
total, ids, err := SearchIssueIDsByKeyword(context.TODO(), "issue2", []int64{1}, 10, 0)
total, ids, err := issues_model.SearchIssueIDsByKeyword(context.TODO(), "issue2", []int64{1}, 10, 0)
assert.NoError(t, err)
assert.EqualValues(t, 1, total)
assert.EqualValues(t, []int64{2}, ids)
total, ids, err = SearchIssueIDsByKeyword(context.TODO(), "first", []int64{1}, 10, 0)
total, ids, err = issues_model.SearchIssueIDsByKeyword(context.TODO(), "first", []int64{1}, 10, 0)
assert.NoError(t, err)
assert.EqualValues(t, 1, total)
assert.EqualValues(t, []int64{1}, ids)
total, ids, err = SearchIssueIDsByKeyword(context.TODO(), "for", []int64{1}, 10, 0)
total, ids, err = issues_model.SearchIssueIDsByKeyword(context.TODO(), "for", []int64{1}, 10, 0)
assert.NoError(t, err)
assert.EqualValues(t, 5, total)
assert.ElementsMatch(t, []int64{1, 2, 3, 5, 11}, ids)
// issue1's comment id 2
total, ids, err = SearchIssueIDsByKeyword(context.TODO(), "good", []int64{1}, 10, 0)
total, ids, err = issues_model.SearchIssueIDsByKeyword(context.TODO(), "good", []int64{1}, 10, 0)
assert.NoError(t, err)
assert.EqualValues(t, 1, total)
assert.EqualValues(t, []int64{1}, ids)
@ -349,23 +349,23 @@ func TestGetRepoIDsForIssuesOptions(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
for _, test := range []struct {
Opts IssuesOptions
Opts issues_model.IssuesOptions
ExpectedRepoIDs []int64
}{
{
IssuesOptions{
issues_model.IssuesOptions{
AssigneeID: 2,
},
[]int64{3, 32},
},
{
IssuesOptions{
issues_model.IssuesOptions{
RepoCond: builder.In("repo_id", 1, 2),
},
[]int64{1, 2},
},
} {
repoIDs, err := GetRepoIDsForIssuesOptions(&test.Opts, user)
repoIDs, err := issues_model.GetRepoIDsForIssuesOptions(&test.Opts, user)
assert.NoError(t, err)
if assert.Len(t, repoIDs, len(test.ExpectedRepoIDs)) {
for i, repoID := range repoIDs {
@ -375,20 +375,20 @@ func TestGetRepoIDsForIssuesOptions(t *testing.T) {
}
}
func testInsertIssue(t *testing.T, title, content string, expectIndex int64) *Issue {
var newIssue Issue
func testInsertIssue(t *testing.T, title, content string, expectIndex int64) *issues_model.Issue {
var newIssue issues_model.Issue
t.Run(title, func(t *testing.T) {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
issue := Issue{
issue := issues_model.Issue{
RepoID: repo.ID,
PosterID: user.ID,
Poster: user,
Title: title,
Content: content,
}
err := NewIssue(repo, &issue, nil, nil)
err := issues_model.NewIssue(repo, &issue, nil, nil)
assert.NoError(t, err)
has, err := db.GetEngine(db.DefaultContext).ID(issue.ID).Get(&newIssue)
@ -408,75 +408,23 @@ func TestIssue_InsertIssue(t *testing.T) {
// there are 5 issues and max index is 5 on repository 1, so this one should 6
issue := testInsertIssue(t, "my issue1", "special issue's comments?", 6)
_, err := db.GetEngine(db.DefaultContext).ID(issue.ID).Delete(new(Issue))
_, err := db.GetEngine(db.DefaultContext).ID(issue.ID).Delete(new(issues_model.Issue))
assert.NoError(t, err)
issue = testInsertIssue(t, `my issue2, this is my son's love \n \r \ `, "special issue's '' comments?", 7)
_, err = db.GetEngine(db.DefaultContext).ID(issue.ID).Delete(new(Issue))
_, err = db.GetEngine(db.DefaultContext).ID(issue.ID).Delete(new(issues_model.Issue))
assert.NoError(t, err)
}
func TestIssue_DeleteIssue(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
issueIDs, err := GetIssueIDsByRepoID(db.DefaultContext, 1)
assert.NoError(t, err)
assert.EqualValues(t, 5, len(issueIDs))
issue := &Issue{
RepoID: 1,
ID: issueIDs[2],
}
err = DeleteIssue(issue)
assert.NoError(t, err)
issueIDs, err = GetIssueIDsByRepoID(db.DefaultContext, 1)
assert.NoError(t, err)
assert.EqualValues(t, 4, len(issueIDs))
// check attachment removal
attachments, err := repo_model.GetAttachmentsByIssueID(db.DefaultContext, 4)
assert.NoError(t, err)
issue, err = GetIssueByID(4)
assert.NoError(t, err)
err = DeleteIssue(issue)
assert.NoError(t, err)
assert.EqualValues(t, 2, len(attachments))
for i := range attachments {
attachment, err := repo_model.GetAttachmentByUUID(db.DefaultContext, attachments[i].UUID)
assert.Error(t, err)
assert.True(t, repo_model.IsErrAttachmentNotExist(err))
assert.Nil(t, attachment)
}
// check issue dependencies
user, err := user_model.GetUserByID(1)
assert.NoError(t, err)
issue1, err := GetIssueByID(1)
assert.NoError(t, err)
issue2, err := GetIssueByID(2)
assert.NoError(t, err)
err = CreateIssueDependency(user, issue1, issue2)
assert.NoError(t, err)
left, err := IssueNoDependenciesLeft(db.DefaultContext, issue1)
assert.NoError(t, err)
assert.False(t, left)
err = DeleteIssue(&Issue{ID: 2})
assert.NoError(t, err)
left, err = IssueNoDependenciesLeft(db.DefaultContext, issue1)
assert.NoError(t, err)
assert.True(t, left)
}
func TestIssue_ResolveMentions(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
testSuccess := func(owner, repo, doer string, mentions []string, expected []int64) {
o := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: owner}).(*user_model.User)
r := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: o.ID, LowerName: repo}).(*repo_model.Repository)
issue := &Issue{RepoID: r.ID}
issue := &issues_model.Issue{RepoID: r.ID}
d := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: doer}).(*user_model.User)
resolved, err := ResolveIssueMentionsByVisibility(db.DefaultContext, issue, d, mentions)
resolved, err := issues_model.ResolveIssueMentionsByVisibility(db.DefaultContext, issue, d, mentions)
assert.NoError(t, err)
ids := make([]int64, len(resolved))
for i, user := range resolved {
@ -523,7 +471,7 @@ func TestCorrectIssueStats(t *testing.T) {
// Each new issues will have a constant description "Bugs are nasty"
// Which will be used later on.
issueAmount := maxQueryParameters + 10
issueAmount := issues_model.MaxQueryParameters + 10
var wg sync.WaitGroup
for i := 0; i < issueAmount; i++ {
@ -536,7 +484,7 @@ func TestCorrectIssueStats(t *testing.T) {
wg.Wait()
// Now we will get all issueID's that match the "Bugs are nasty" query.
total, ids, err := SearchIssueIDsByKeyword(context.TODO(), "Bugs are nasty", []int64{1}, issueAmount, 0)
total, ids, err := issues_model.SearchIssueIDsByKeyword(context.TODO(), "Bugs are nasty", []int64{1}, issueAmount, 0)
// Just to be sure.
assert.NoError(t, err)
@ -544,7 +492,7 @@ func TestCorrectIssueStats(t *testing.T) {
// Now we will call the GetIssueStats with these IDs and if working,
// get the correct stats back.
issueStats, err := GetIssueStats(&IssueStatsOptions{
issueStats, err := issues_model.GetIssueStats(&issues_model.IssueStatsOptions{
RepoID: 1,
IssueIDs: ids,
})
@ -556,19 +504,19 @@ func TestCorrectIssueStats(t *testing.T) {
func TestIssueForeignReference(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 4}).(*Issue)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4}).(*issues_model.Issue)
assert.NotEqualValues(t, issue.Index, issue.ID) // make sure they are different to avoid false positive
// it is fine for an issue to not have a foreign reference
err := issue.LoadAttributes()
err := issue.LoadAttributes(db.DefaultContext)
assert.NoError(t, err)
assert.Nil(t, issue.ForeignReference)
var foreignIndex int64 = 12345
_, err = GetIssueByForeignIndex(context.Background(), issue.RepoID, foreignIndex)
_, err = issues_model.GetIssueByForeignIndex(context.Background(), issue.RepoID, foreignIndex)
assert.True(t, foreignreference.IsErrLocalIndexNotExist(err))
_, err = db.GetEngine(db.DefaultContext).Insert(&foreignreference.ForeignReference{
err = db.Insert(db.DefaultContext, &foreignreference.ForeignReference{
LocalIndex: issue.Index,
ForeignIndex: strconv.FormatInt(foreignIndex, 10),
RepoID: issue.RepoID,
@ -576,12 +524,12 @@ func TestIssueForeignReference(t *testing.T) {
})
assert.NoError(t, err)
err = issue.LoadAttributes()
err = issue.LoadAttributes(db.DefaultContext)
assert.NoError(t, err)
assert.EqualValues(t, issue.ForeignReference.ForeignIndex, strconv.FormatInt(foreignIndex, 10))
found, err := GetIssueByForeignIndex(context.Background(), issue.RepoID, foreignIndex)
found, err := issues_model.GetIssueByForeignIndex(context.Background(), issue.RepoID, foreignIndex)
assert.NoError(t, err)
assert.EqualValues(t, found.Index, issue.Index)
}
@ -608,7 +556,7 @@ func TestLoadTotalTrackedTime(t *testing.T) {
func TestCountIssues(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
count, err := CountIssues(&IssuesOptions{})
count, err := issues_model.CountIssues(&issues_model.IssuesOptions{})
assert.NoError(t, err)
assert.EqualValues(t, 17, count)
}

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues
import (
"context"
@ -25,7 +25,8 @@ func init() {
db.RegisterModel(new(IssueUser))
}
func newIssueUsers(ctx context.Context, repo *repo_model.Repository, issue *Issue) error {
// NewIssueUsers inserts an issue related users
func NewIssueUsers(ctx context.Context, repo *repo_model.Repository, issue *Issue) error {
assignees, err := repo_model.GetRepoAssignees(ctx, repo)
if err != nil {
return fmt.Errorf("getAssignees: %v", err)

View file

@ -0,0 +1,62 @@
// Copyright 2017 The Gogs 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 issues_test
import (
"testing"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
)
func Test_NewIssueUsers(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
newIssue := &issues_model.Issue{
RepoID: repo.ID,
PosterID: 4,
Index: 6,
Title: "newTestIssueTitle",
Content: "newTestIssueContent",
}
// artificially insert new issue
unittest.AssertSuccessfulInsert(t, newIssue)
assert.NoError(t, issues_model.NewIssueUsers(db.DefaultContext, repo, newIssue))
// issue_user table should now have entries for new issue
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueUser{IssueID: newIssue.ID, UID: newIssue.PosterID})
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueUser{IssueID: newIssue.ID, UID: repo.OwnerID})
}
func TestUpdateIssueUserByRead(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue)
assert.NoError(t, issues_model.UpdateIssueUserByRead(4, issue.ID))
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueUser{IssueID: issue.ID, UID: 4}, "is_read=1")
assert.NoError(t, issues_model.UpdateIssueUserByRead(4, issue.ID))
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueUser{IssueID: issue.ID, UID: 4}, "is_read=1")
assert.NoError(t, issues_model.UpdateIssueUserByRead(unittest.NonexistentID, unittest.NonexistentID))
}
func TestUpdateIssueUsersByMentions(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue)
uids := []int64{2, 5}
assert.NoError(t, issues_model.UpdateIssueUsersByMentions(db.DefaultContext, issue.ID, uids))
for _, uid := range uids {
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueUser{IssueID: issue.ID, UID: uid}, "is_mentioned=1")
}
}

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues
import (
"context"
@ -125,7 +125,8 @@ func CountIssueWatchers(ctx context.Context, issueID int64) (int64, error) {
Join("INNER", "`user`", "`user`.id = `issue_watch`.user_id").Count(new(IssueWatch))
}
func removeIssueWatchersByRepoID(ctx context.Context, userID, repoID int64) error {
// RemoveIssueWatchersByRepoID remove issue watchers by repoID
func RemoveIssueWatchersByRepoID(ctx context.Context, userID, repoID int64) error {
_, err := db.GetEngine(ctx).
Join("INNER", "issue", "`issue`.id = `issue_watch`.issue_id AND `issue`.repo_id = ?", repoID).
Where("`issue_watch`.user_id = ?", userID).

View file

@ -2,12 +2,13 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues_test
import (
"testing"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
@ -16,28 +17,28 @@ import (
func TestCreateOrUpdateIssueWatch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
assert.NoError(t, CreateOrUpdateIssueWatch(3, 1, true))
iw := unittest.AssertExistsAndLoadBean(t, &IssueWatch{UserID: 3, IssueID: 1}).(*IssueWatch)
assert.NoError(t, issues_model.CreateOrUpdateIssueWatch(3, 1, true))
iw := unittest.AssertExistsAndLoadBean(t, &issues_model.IssueWatch{UserID: 3, IssueID: 1}).(*issues_model.IssueWatch)
assert.True(t, iw.IsWatching)
assert.NoError(t, CreateOrUpdateIssueWatch(1, 1, false))
iw = unittest.AssertExistsAndLoadBean(t, &IssueWatch{UserID: 1, IssueID: 1}).(*IssueWatch)
assert.NoError(t, issues_model.CreateOrUpdateIssueWatch(1, 1, false))
iw = unittest.AssertExistsAndLoadBean(t, &issues_model.IssueWatch{UserID: 1, IssueID: 1}).(*issues_model.IssueWatch)
assert.False(t, iw.IsWatching)
}
func TestGetIssueWatch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
_, exists, err := GetIssueWatch(db.DefaultContext, 9, 1)
_, exists, err := issues_model.GetIssueWatch(db.DefaultContext, 9, 1)
assert.True(t, exists)
assert.NoError(t, err)
iw, exists, err := GetIssueWatch(db.DefaultContext, 2, 2)
iw, exists, err := issues_model.GetIssueWatch(db.DefaultContext, 2, 2)
assert.True(t, exists)
assert.NoError(t, err)
assert.False(t, iw.IsWatching)
_, exists, err = GetIssueWatch(db.DefaultContext, 3, 1)
_, exists, err = issues_model.GetIssueWatch(db.DefaultContext, 3, 1)
assert.False(t, exists)
assert.NoError(t, err)
}
@ -45,22 +46,22 @@ func TestGetIssueWatch(t *testing.T) {
func TestGetIssueWatchers(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
iws, err := GetIssueWatchers(db.DefaultContext, 1, db.ListOptions{})
iws, err := issues_model.GetIssueWatchers(db.DefaultContext, 1, db.ListOptions{})
assert.NoError(t, err)
// Watcher is inactive, thus 0
assert.Len(t, iws, 0)
iws, err = GetIssueWatchers(db.DefaultContext, 2, db.ListOptions{})
iws, err = issues_model.GetIssueWatchers(db.DefaultContext, 2, db.ListOptions{})
assert.NoError(t, err)
// Watcher is explicit not watching
assert.Len(t, iws, 0)
iws, err = GetIssueWatchers(db.DefaultContext, 5, db.ListOptions{})
iws, err = issues_model.GetIssueWatchers(db.DefaultContext, 5, db.ListOptions{})
assert.NoError(t, err)
// Issue has no Watchers
assert.Len(t, iws, 0)
iws, err = GetIssueWatchers(db.DefaultContext, 7, db.ListOptions{})
iws, err = issues_model.GetIssueWatchers(db.DefaultContext, 7, db.ListOptions{})
assert.NoError(t, err)
// Issue has one watcher
assert.Len(t, iws, 1)

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues
import (
"context"
@ -55,15 +55,8 @@ func neuterCrossReferencesIds(ctx context.Context, ids []int64) error {
return err
}
// .___
// | | ______ ________ __ ____
// | |/ ___// ___/ | \_/ __ \
// | |\___ \ \___ \| | /\ ___/
// |___/____ >____ >____/ \___ >
// \/ \/ \/
//
func (issue *Issue) addCrossReferences(stdCtx context.Context, doer *user_model.User, removeOld bool) error {
// AddCrossReferences add cross repositories references.
func (issue *Issue) AddCrossReferences(stdCtx context.Context, doer *user_model.User, removeOld bool) error {
var commentType CommentType
if issue.IsPull {
commentType = CommentTypePullRef
@ -237,15 +230,8 @@ func (issue *Issue) verifyReferencedIssue(stdCtx context.Context, ctx *crossRefe
return refIssue, refAction, nil
}
// _________ __
// \_ ___ \ ____ _____ _____ ____ _____/ |_
// / \ \/ / _ \ / \ / \_/ __ \ / \ __\
// \ \___( <_> ) Y Y \ Y Y \ ___/| | \ |
// \______ /\____/|__|_| /__|_| /\___ >___| /__|
// \/ \/ \/ \/ \/
//
func (comment *Comment) addCrossReferences(stdCtx context.Context, doer *user_model.User, removeOld bool) error {
// AddCrossReferences add cross references
func (comment *Comment) AddCrossReferences(stdCtx context.Context, doer *user_model.User, removeOld bool) error {
if comment.Type != CommentTypeCode && comment.Type != CommentTypeComment {
return nil
}
@ -280,7 +266,7 @@ func (comment *Comment) LoadRefIssue() (err error) {
if comment.RefIssue != nil {
return nil
}
comment.RefIssue, err = GetIssueByID(comment.RefIssueID)
comment.RefIssue, err = GetIssueByID(db.DefaultContext, comment.RefIssueID)
if err == nil {
err = comment.RefIssue.LoadRepo(db.DefaultContext)
}

View file

@ -2,13 +2,14 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues_test
import (
"fmt"
"testing"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
@ -26,8 +27,8 @@ func TestXRef_AddCrossReferences(t *testing.T) {
// PR to close issue #1
content := fmt.Sprintf("content2, closes #%d", itarget.Index)
pr := testCreateIssue(t, 1, 2, "title2", content, true)
ref := unittest.AssertExistsAndLoadBean(t, &Comment{IssueID: itarget.ID, RefIssueID: pr.ID, RefCommentID: 0}).(*Comment)
assert.Equal(t, CommentTypePullRef, ref.Type)
ref := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: pr.ID, RefCommentID: 0}).(*issues_model.Comment)
assert.Equal(t, issues_model.CommentTypePullRef, ref.Type)
assert.Equal(t, pr.RepoID, ref.RefRepoID)
assert.True(t, ref.RefIsPull)
assert.Equal(t, references.XRefActionCloses, ref.RefAction)
@ -35,8 +36,8 @@ func TestXRef_AddCrossReferences(t *testing.T) {
// Comment on PR to reopen issue #1
content = fmt.Sprintf("content2, reopens #%d", itarget.Index)
c := testCreateComment(t, 1, 2, pr.ID, content)
ref = unittest.AssertExistsAndLoadBean(t, &Comment{IssueID: itarget.ID, RefIssueID: pr.ID, RefCommentID: c.ID}).(*Comment)
assert.Equal(t, CommentTypeCommentRef, ref.Type)
ref = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: pr.ID, RefCommentID: c.ID}).(*issues_model.Comment)
assert.Equal(t, issues_model.CommentTypeCommentRef, ref.Type)
assert.Equal(t, pr.RepoID, ref.RefRepoID)
assert.True(t, ref.RefIsPull)
assert.Equal(t, references.XRefActionReopens, ref.RefAction)
@ -44,8 +45,8 @@ func TestXRef_AddCrossReferences(t *testing.T) {
// Issue mentioning issue #1
content = fmt.Sprintf("content3, mentions #%d", itarget.Index)
i := testCreateIssue(t, 1, 2, "title3", content, false)
ref = unittest.AssertExistsAndLoadBean(t, &Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*Comment)
assert.Equal(t, CommentTypeIssueRef, ref.Type)
ref = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*issues_model.Comment)
assert.Equal(t, issues_model.CommentTypeIssueRef, ref.Type)
assert.Equal(t, pr.RepoID, ref.RefRepoID)
assert.False(t, ref.RefIsPull)
assert.Equal(t, references.XRefActionNone, ref.RefAction)
@ -56,8 +57,8 @@ func TestXRef_AddCrossReferences(t *testing.T) {
// 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 = unittest.AssertExistsAndLoadBean(t, &Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*Comment)
assert.Equal(t, CommentTypeIssueRef, ref.Type)
ref = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*issues_model.Comment)
assert.Equal(t, issues_model.CommentTypeIssueRef, ref.Type)
assert.Equal(t, i.RepoID, ref.RefRepoID)
assert.False(t, ref.RefIsPull)
assert.Equal(t, references.XRefActionNone, ref.RefAction)
@ -65,7 +66,7 @@ func TestXRef_AddCrossReferences(t *testing.T) {
// 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)
unittest.AssertNotExistsBean(t, &Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0})
unittest.AssertNotExistsBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0})
}
func TestXRef_NeuterCrossReferences(t *testing.T) {
@ -77,16 +78,16 @@ func TestXRef_NeuterCrossReferences(t *testing.T) {
// Issue mentioning issue #1
title := fmt.Sprintf("title2, mentions #%d", itarget.Index)
i := testCreateIssue(t, 1, 2, title, "content2", false)
ref := unittest.AssertExistsAndLoadBean(t, &Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*Comment)
assert.Equal(t, CommentTypeIssueRef, ref.Type)
ref := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*issues_model.Comment)
assert.Equal(t, issues_model.CommentTypeIssueRef, ref.Type)
assert.Equal(t, references.XRefActionNone, ref.RefAction)
d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
i.Title = "title2, no mentions"
assert.NoError(t, ChangeIssueTitle(i, d, title))
assert.NoError(t, issues_model.ChangeIssueTitle(i, d, title))
ref = unittest.AssertExistsAndLoadBean(t, &Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*Comment)
assert.Equal(t, CommentTypeIssueRef, ref.Type)
ref = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*issues_model.Comment)
assert.Equal(t, issues_model.CommentTypeIssueRef, ref.Type)
assert.Equal(t, references.XRefActionNeutered, ref.RefAction)
}
@ -98,25 +99,25 @@ func TestXRef_ResolveCrossReferences(t *testing.T) {
i1 := testCreateIssue(t, 1, 2, "title1", "content1", false)
i2 := testCreateIssue(t, 1, 2, "title2", "content2", false)
i3 := testCreateIssue(t, 1, 2, "title3", "content3", false)
_, err := ChangeIssueStatus(db.DefaultContext, i3, d, true)
_, err := issues_model.ChangeIssueStatus(db.DefaultContext, i3, d, true)
assert.NoError(t, err)
pr := testCreatePR(t, 1, 2, "titlepr", fmt.Sprintf("closes #%d", i1.Index))
rp := unittest.AssertExistsAndLoadBean(t, &Comment{IssueID: i1.ID, RefIssueID: pr.Issue.ID, RefCommentID: 0}).(*Comment)
rp := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i1.ID, RefIssueID: pr.Issue.ID, RefCommentID: 0}).(*issues_model.Comment)
c1 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("closes #%d", i2.Index))
r1 := unittest.AssertExistsAndLoadBean(t, &Comment{IssueID: i2.ID, RefIssueID: pr.Issue.ID, RefCommentID: c1.ID}).(*Comment)
r1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i2.ID, RefIssueID: pr.Issue.ID, RefCommentID: c1.ID}).(*issues_model.Comment)
// Must be ignored
c2 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("mentions #%d", i2.Index))
unittest.AssertExistsAndLoadBean(t, &Comment{IssueID: i2.ID, RefIssueID: pr.Issue.ID, RefCommentID: c2.ID})
unittest.AssertExistsAndLoadBean(t, &issues_model.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))
unittest.AssertExistsAndLoadBean(t, &Comment{IssueID: i3.ID, RefIssueID: pr.Issue.ID, RefCommentID: c3.ID})
unittest.AssertExistsAndLoadBean(t, &issues_model.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 := unittest.AssertExistsAndLoadBean(t, &Comment{IssueID: i3.ID, RefIssueID: pr.Issue.ID, RefCommentID: c4.ID}).(*Comment)
r4 := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i3.ID, RefIssueID: pr.Issue.ID, RefCommentID: c4.ID}).(*issues_model.Comment)
refs, err := pr.ResolveCrossReferences(db.DefaultContext)
assert.NoError(t, err)
@ -126,13 +127,13 @@ func TestXRef_ResolveCrossReferences(t *testing.T) {
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 {
func testCreateIssue(t *testing.T, repo, doer int64, title, content string, ispull bool) *issues_model.Issue {
r := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repo}).(*repo_model.Repository)
d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doer}).(*user_model.User)
idx, err := db.GetNextResourceIndex("issue_index", r.ID)
assert.NoError(t, err)
i := &Issue{
i := &issues_model.Issue{
RepoID: r.ID,
PosterID: d.ID,
Poster: d,
@ -145,39 +146,39 @@ func testCreateIssue(t *testing.T, repo, doer int64, title, content string, ispu
ctx, committer, err := db.TxContext()
assert.NoError(t, err)
defer committer.Close()
err = newIssue(ctx, d, NewIssueOptions{
err = issues_model.NewIssueWithIndex(ctx, d, issues_model.NewIssueOptions{
Repo: r,
Issue: i,
})
assert.NoError(t, err)
i, err = getIssueByID(ctx, i.ID)
i, err = issues_model.GetIssueByID(ctx, i.ID)
assert.NoError(t, err)
assert.NoError(t, i.addCrossReferences(ctx, d, false))
assert.NoError(t, i.AddCrossReferences(ctx, d, false))
assert.NoError(t, committer.Commit())
return i
}
func testCreatePR(t *testing.T, repo, doer int64, title, content string) *PullRequest {
func testCreatePR(t *testing.T, repo, doer int64, title, content string) *issues_model.PullRequest {
r := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repo}).(*repo_model.Repository)
d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doer}).(*user_model.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", Status: PullRequestStatusMergeable}
assert.NoError(t, NewPullRequest(db.DefaultContext, r, i, nil, nil, pr))
i := &issues_model.Issue{RepoID: r.ID, PosterID: d.ID, Poster: d, Title: title, Content: content, IsPull: true}
pr := &issues_model.PullRequest{HeadRepoID: repo, BaseRepoID: repo, HeadBranch: "head", BaseBranch: "base", Status: issues_model.PullRequestStatusMergeable}
assert.NoError(t, issues_model.NewPullRequest(db.DefaultContext, r, i, nil, nil, pr))
pr.Issue = i
return pr
}
func testCreateComment(t *testing.T, repo, doer, issue int64, content string) *Comment {
func testCreateComment(t *testing.T, repo, doer, issue int64, content string) *issues_model.Comment {
d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doer}).(*user_model.User)
i := unittest.AssertExistsAndLoadBean(t, &Issue{ID: issue}).(*Issue)
c := &Comment{Type: CommentTypeComment, PosterID: doer, Poster: d, IssueID: issue, Issue: i, Content: content}
i := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue}).(*issues_model.Issue)
c := &issues_model.Comment{Type: issues_model.CommentTypeComment, PosterID: doer, Poster: d, IssueID: issue, Issue: i, Content: content}
ctx, committer, err := db.TxContext()
assert.NoError(t, err)
defer committer.Close()
err = db.Insert(ctx, c)
assert.NoError(t, err)
assert.NoError(t, c.addCrossReferences(ctx, d, false))
assert.NoError(t, c.AddCrossReferences(ctx, d, false))
assert.NoError(t, committer.Commit())
return c
}

View file

@ -3,7 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues
import (
"context"
@ -21,6 +21,53 @@ import (
"xorm.io/builder"
)
// ErrRepoLabelNotExist represents a "RepoLabelNotExist" kind of error.
type ErrRepoLabelNotExist struct {
LabelID int64
RepoID int64
}
// IsErrRepoLabelNotExist checks if an error is a RepoErrLabelNotExist.
func IsErrRepoLabelNotExist(err error) bool {
_, ok := err.(ErrRepoLabelNotExist)
return ok
}
func (err ErrRepoLabelNotExist) Error() string {
return fmt.Sprintf("label does not exist [label_id: %d, repo_id: %d]", err.LabelID, err.RepoID)
}
// ErrOrgLabelNotExist represents a "OrgLabelNotExist" kind of error.
type ErrOrgLabelNotExist struct {
LabelID int64
OrgID int64
}
// IsErrOrgLabelNotExist checks if an error is a OrgErrLabelNotExist.
func IsErrOrgLabelNotExist(err error) bool {
_, ok := err.(ErrOrgLabelNotExist)
return ok
}
func (err ErrOrgLabelNotExist) Error() string {
return fmt.Sprintf("label does not exist [label_id: %d, org_id: %d]", err.LabelID, err.OrgID)
}
// ErrLabelNotExist represents a "LabelNotExist" kind of error.
type ErrLabelNotExist struct {
LabelID int64
}
// IsErrLabelNotExist checks if an error is a ErrLabelNotExist.
func IsErrLabelNotExist(err error) bool {
_, ok := err.(ErrLabelNotExist)
return ok
}
func (err ErrLabelNotExist) Error() string {
return fmt.Sprintf("label does not exist [label_id: %d]", err.LabelID)
}
// LabelColorPattern is a regexp witch can validate LabelColor
var LabelColorPattern = regexp.MustCompile("^#?(?:[0-9a-fA-F]{6}|[0-9a-fA-F]{3})$")
@ -671,7 +718,8 @@ func DeleteIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *use
return issue.LoadLabels(ctx)
}
func deleteLabelsByRepoID(ctx context.Context, repoID int64) error {
// DeleteLabelsByRepoID deletes labels of some repository
func DeleteLabelsByRepoID(ctx context.Context, repoID int64) error {
deleteCond := builder.Select("id").From("label").Where(builder.Eq{"label.repo_id": repoID})
if _, err := db.GetEngine(ctx).In("label_id", deleteCond).
@ -682,3 +730,107 @@ func deleteLabelsByRepoID(ctx context.Context, repoID int64) error {
_, err := db.DeleteByBean(ctx, &Label{RepoID: repoID})
return err
}
// CountOrphanedLabels return count of labels witch are broken and not accessible via ui anymore
func CountOrphanedLabels() (int64, error) {
noref, err := db.GetEngine(db.DefaultContext).Table("label").Where("repo_id=? AND org_id=?", 0, 0).Count("label.id")
if err != nil {
return 0, err
}
norepo, err := db.GetEngine(db.DefaultContext).Table("label").
Where(builder.And(
builder.Gt{"repo_id": 0},
builder.NotIn("repo_id", builder.Select("id").From("repository")),
)).
Count()
if err != nil {
return 0, err
}
noorg, err := db.GetEngine(db.DefaultContext).Table("label").
Where(builder.And(
builder.Gt{"org_id": 0},
builder.NotIn("org_id", builder.Select("id").From("user")),
)).
Count()
if err != nil {
return 0, err
}
return noref + norepo + noorg, nil
}
// DeleteOrphanedLabels delete labels witch are broken and not accessible via ui anymore
func DeleteOrphanedLabels() error {
// delete labels with no reference
if _, err := db.GetEngine(db.DefaultContext).Table("label").Where("repo_id=? AND org_id=?", 0, 0).Delete(new(Label)); err != nil {
return err
}
// delete labels with none existing repos
if _, err := db.GetEngine(db.DefaultContext).
Where(builder.And(
builder.Gt{"repo_id": 0},
builder.NotIn("repo_id", builder.Select("id").From("repository")),
)).
Delete(Label{}); err != nil {
return err
}
// delete labels with none existing orgs
if _, err := db.GetEngine(db.DefaultContext).
Where(builder.And(
builder.Gt{"org_id": 0},
builder.NotIn("org_id", builder.Select("id").From("user")),
)).
Delete(Label{}); err != nil {
return err
}
return nil
}
// CountOrphanedIssueLabels return count of IssueLabels witch have no label behind anymore
func CountOrphanedIssueLabels() (int64, error) {
return db.GetEngine(db.DefaultContext).Table("issue_label").
NotIn("label_id", builder.Select("id").From("label")).
Count()
}
// DeleteOrphanedIssueLabels delete IssueLabels witch have no label behind anymore
func DeleteOrphanedIssueLabels() error {
_, err := db.GetEngine(db.DefaultContext).
NotIn("label_id", builder.Select("id").From("label")).
Delete(IssueLabel{})
return err
}
// CountIssueLabelWithOutsideLabels count label comments with outside label
func CountIssueLabelWithOutsideLabels() (int64, error) {
return db.GetEngine(db.DefaultContext).Where(builder.Expr("(label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id)")).
Table("issue_label").
Join("inner", "label", "issue_label.label_id = label.id ").
Join("inner", "issue", "issue.id = issue_label.issue_id ").
Join("inner", "repository", "issue.repo_id = repository.id").
Count(new(IssueLabel))
}
// FixIssueLabelWithOutsideLabels fix label comments with outside label
func FixIssueLabelWithOutsideLabels() (int64, error) {
res, err := db.GetEngine(db.DefaultContext).Exec(`DELETE FROM issue_label WHERE issue_label.id IN (
SELECT il_too.id FROM (
SELECT il_too_too.id
FROM issue_label AS il_too_too
INNER JOIN label ON il_too_too.label_id = label.id
INNER JOIN issue on issue.id = il_too_too.issue_id
INNER JOIN repository on repository.id = issue.repo_id
WHERE
(label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id)
) AS il_too )`)
if err != nil {
return 0, err
}
return res.RowsAffected()
}

395
models/issues/label_test.go Normal file
View file

@ -0,0 +1,395 @@
// Copyright 2017 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 issues_test
import (
"html/template"
"testing"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"github.com/stretchr/testify/assert"
)
// TODO TestGetLabelTemplateFile
func TestLabel_CalOpenIssues(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label)
label.CalOpenIssues()
assert.EqualValues(t, 2, label.NumOpenIssues)
}
func TestLabel_ForegroundColor(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label)
assert.Equal(t, template.CSS("#000"), label.ForegroundColor())
label = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2}).(*issues_model.Label)
assert.Equal(t, template.CSS("#fff"), label.ForegroundColor())
}
func TestNewLabels(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
labels := []*issues_model.Label{
{RepoID: 2, Name: "labelName2", Color: "#123456"},
{RepoID: 3, Name: "labelName3", Color: "#123"},
{RepoID: 4, Name: "labelName4", Color: "ABCDEF"},
{RepoID: 5, Name: "labelName5", Color: "DEF"},
}
assert.Error(t, issues_model.NewLabel(db.DefaultContext, &issues_model.Label{RepoID: 3, Name: "invalid Color", Color: ""}))
assert.Error(t, issues_model.NewLabel(db.DefaultContext, &issues_model.Label{RepoID: 3, Name: "invalid Color", Color: "#45G"}))
assert.Error(t, issues_model.NewLabel(db.DefaultContext, &issues_model.Label{RepoID: 3, Name: "invalid Color", Color: "#12345G"}))
assert.Error(t, issues_model.NewLabel(db.DefaultContext, &issues_model.Label{RepoID: 3, Name: "invalid Color", Color: "45G"}))
assert.Error(t, issues_model.NewLabel(db.DefaultContext, &issues_model.Label{RepoID: 3, Name: "invalid Color", Color: "12345G"}))
for _, label := range labels {
unittest.AssertNotExistsBean(t, label)
}
assert.NoError(t, issues_model.NewLabels(labels...))
for _, label := range labels {
unittest.AssertExistsAndLoadBean(t, label, unittest.Cond("id = ?", label.ID))
}
unittest.CheckConsistencyFor(t, &issues_model.Label{}, &repo_model.Repository{})
}
func TestGetLabelByID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label, err := issues_model.GetLabelByID(db.DefaultContext, 1)
assert.NoError(t, err)
assert.EqualValues(t, 1, label.ID)
_, err = issues_model.GetLabelByID(db.DefaultContext, unittest.NonexistentID)
assert.True(t, issues_model.IsErrLabelNotExist(err))
}
func TestGetLabelInRepoByName(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label, err := issues_model.GetLabelInRepoByName(db.DefaultContext, 1, "label1")
assert.NoError(t, err)
assert.EqualValues(t, 1, label.ID)
assert.Equal(t, "label1", label.Name)
_, err = issues_model.GetLabelInRepoByName(db.DefaultContext, 1, "")
assert.True(t, issues_model.IsErrRepoLabelNotExist(err))
_, err = issues_model.GetLabelInRepoByName(db.DefaultContext, unittest.NonexistentID, "nonexistent")
assert.True(t, issues_model.IsErrRepoLabelNotExist(err))
}
func TestGetLabelInRepoByNames(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
labelIDs, err := issues_model.GetLabelIDsInRepoByNames(1, []string{"label1", "label2"})
assert.NoError(t, err)
assert.Len(t, labelIDs, 2)
assert.Equal(t, int64(1), labelIDs[0])
assert.Equal(t, int64(2), labelIDs[1])
}
func TestGetLabelInRepoByNamesDiscardsNonExistentLabels(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
// label3 doesn't exists.. See labels.yml
labelIDs, err := issues_model.GetLabelIDsInRepoByNames(1, []string{"label1", "label2", "label3"})
assert.NoError(t, err)
assert.Len(t, labelIDs, 2)
assert.Equal(t, int64(1), labelIDs[0])
assert.Equal(t, int64(2), labelIDs[1])
assert.NoError(t, err)
}
func TestGetLabelInRepoByID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label, err := issues_model.GetLabelInRepoByID(db.DefaultContext, 1, 1)
assert.NoError(t, err)
assert.EqualValues(t, 1, label.ID)
_, err = issues_model.GetLabelInRepoByID(db.DefaultContext, 1, -1)
assert.True(t, issues_model.IsErrRepoLabelNotExist(err))
_, err = issues_model.GetLabelInRepoByID(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID)
assert.True(t, issues_model.IsErrRepoLabelNotExist(err))
}
func TestGetLabelsInRepoByIDs(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
labels, err := issues_model.GetLabelsInRepoByIDs(1, []int64{1, 2, unittest.NonexistentID})
assert.NoError(t, err)
if assert.Len(t, labels, 2) {
assert.EqualValues(t, 1, labels[0].ID)
assert.EqualValues(t, 2, labels[1].ID)
}
}
func TestGetLabelsByRepoID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
testSuccess := func(repoID int64, sortType string, expectedIssueIDs []int64) {
labels, err := issues_model.GetLabelsByRepoID(db.DefaultContext, repoID, sortType, db.ListOptions{})
assert.NoError(t, err)
assert.Len(t, labels, len(expectedIssueIDs))
for i, label := range labels {
assert.EqualValues(t, expectedIssueIDs[i], label.ID)
}
}
testSuccess(1, "leastissues", []int64{2, 1})
testSuccess(1, "mostissues", []int64{1, 2})
testSuccess(1, "reversealphabetically", []int64{2, 1})
testSuccess(1, "default", []int64{1, 2})
}
// Org versions
func TestGetLabelInOrgByName(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label, err := issues_model.GetLabelInOrgByName(db.DefaultContext, 3, "orglabel3")
assert.NoError(t, err)
assert.EqualValues(t, 3, label.ID)
assert.Equal(t, "orglabel3", label.Name)
_, err = issues_model.GetLabelInOrgByName(db.DefaultContext, 3, "")
assert.True(t, issues_model.IsErrOrgLabelNotExist(err))
_, err = issues_model.GetLabelInOrgByName(db.DefaultContext, 0, "orglabel3")
assert.True(t, issues_model.IsErrOrgLabelNotExist(err))
_, err = issues_model.GetLabelInOrgByName(db.DefaultContext, -1, "orglabel3")
assert.True(t, issues_model.IsErrOrgLabelNotExist(err))
_, err = issues_model.GetLabelInOrgByName(db.DefaultContext, unittest.NonexistentID, "nonexistent")
assert.True(t, issues_model.IsErrOrgLabelNotExist(err))
}
func TestGetLabelInOrgByNames(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
labelIDs, err := issues_model.GetLabelIDsInOrgByNames(3, []string{"orglabel3", "orglabel4"})
assert.NoError(t, err)
assert.Len(t, labelIDs, 2)
assert.Equal(t, int64(3), labelIDs[0])
assert.Equal(t, int64(4), labelIDs[1])
}
func TestGetLabelInOrgByNamesDiscardsNonExistentLabels(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
// orglabel99 doesn't exists.. See labels.yml
labelIDs, err := issues_model.GetLabelIDsInOrgByNames(3, []string{"orglabel3", "orglabel4", "orglabel99"})
assert.NoError(t, err)
assert.Len(t, labelIDs, 2)
assert.Equal(t, int64(3), labelIDs[0])
assert.Equal(t, int64(4), labelIDs[1])
assert.NoError(t, err)
}
func TestGetLabelInOrgByID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label, err := issues_model.GetLabelInOrgByID(db.DefaultContext, 3, 3)
assert.NoError(t, err)
assert.EqualValues(t, 3, label.ID)
_, err = issues_model.GetLabelInOrgByID(db.DefaultContext, 3, -1)
assert.True(t, issues_model.IsErrOrgLabelNotExist(err))
_, err = issues_model.GetLabelInOrgByID(db.DefaultContext, 0, 3)
assert.True(t, issues_model.IsErrOrgLabelNotExist(err))
_, err = issues_model.GetLabelInOrgByID(db.DefaultContext, -1, 3)
assert.True(t, issues_model.IsErrOrgLabelNotExist(err))
_, err = issues_model.GetLabelInOrgByID(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID)
assert.True(t, issues_model.IsErrOrgLabelNotExist(err))
}
func TestGetLabelsInOrgByIDs(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
labels, err := issues_model.GetLabelsInOrgByIDs(3, []int64{3, 4, unittest.NonexistentID})
assert.NoError(t, err)
if assert.Len(t, labels, 2) {
assert.EqualValues(t, 3, labels[0].ID)
assert.EqualValues(t, 4, labels[1].ID)
}
}
func TestGetLabelsByOrgID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
testSuccess := func(orgID int64, sortType string, expectedIssueIDs []int64) {
labels, err := issues_model.GetLabelsByOrgID(db.DefaultContext, orgID, sortType, db.ListOptions{})
assert.NoError(t, err)
assert.Len(t, labels, len(expectedIssueIDs))
for i, label := range labels {
assert.EqualValues(t, expectedIssueIDs[i], label.ID)
}
}
testSuccess(3, "leastissues", []int64{3, 4})
testSuccess(3, "mostissues", []int64{4, 3})
testSuccess(3, "reversealphabetically", []int64{4, 3})
testSuccess(3, "default", []int64{3, 4})
var err error
_, err = issues_model.GetLabelsByOrgID(db.DefaultContext, 0, "leastissues", db.ListOptions{})
assert.True(t, issues_model.IsErrOrgLabelNotExist(err))
_, err = issues_model.GetLabelsByOrgID(db.DefaultContext, -1, "leastissues", db.ListOptions{})
assert.True(t, issues_model.IsErrOrgLabelNotExist(err))
}
//
func TestGetLabelsByIssueID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
labels, err := issues_model.GetLabelsByIssueID(db.DefaultContext, 1)
assert.NoError(t, err)
if assert.Len(t, labels, 1) {
assert.EqualValues(t, 1, labels[0].ID)
}
labels, err = issues_model.GetLabelsByIssueID(db.DefaultContext, unittest.NonexistentID)
assert.NoError(t, err)
assert.Len(t, labels, 0)
}
func TestUpdateLabel(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label)
// make sure update wont overwrite it
update := &issues_model.Label{
ID: label.ID,
Color: "#ffff00",
Name: "newLabelName",
Description: label.Description,
}
label.Color = update.Color
label.Name = update.Name
assert.NoError(t, issues_model.UpdateLabel(update))
newLabel := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label)
assert.EqualValues(t, label.ID, newLabel.ID)
assert.EqualValues(t, label.Color, newLabel.Color)
assert.EqualValues(t, label.Name, newLabel.Name)
assert.EqualValues(t, label.Description, newLabel.Description)
unittest.CheckConsistencyFor(t, &issues_model.Label{}, &repo_model.Repository{})
}
func TestDeleteLabel(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label)
assert.NoError(t, issues_model.DeleteLabel(label.RepoID, label.ID))
unittest.AssertNotExistsBean(t, &issues_model.Label{ID: label.ID, RepoID: label.RepoID})
assert.NoError(t, issues_model.DeleteLabel(label.RepoID, label.ID))
unittest.AssertNotExistsBean(t, &issues_model.Label{ID: label.ID})
assert.NoError(t, issues_model.DeleteLabel(unittest.NonexistentID, unittest.NonexistentID))
unittest.CheckConsistencyFor(t, &issues_model.Label{}, &repo_model.Repository{})
}
func TestHasIssueLabel(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
assert.True(t, issues_model.HasIssueLabel(db.DefaultContext, 1, 1))
assert.False(t, issues_model.HasIssueLabel(db.DefaultContext, 1, 2))
assert.False(t, issues_model.HasIssueLabel(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID))
}
func TestNewIssueLabel(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2}).(*issues_model.Label)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue)
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
// add new IssueLabel
prevNumIssues := label.NumIssues
assert.NoError(t, issues_model.NewIssueLabel(issue, label, doer))
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label.ID})
unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{
Type: issues_model.CommentTypeLabel,
PosterID: doer.ID,
IssueID: issue.ID,
LabelID: label.ID,
Content: "1",
})
label = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2}).(*issues_model.Label)
assert.EqualValues(t, prevNumIssues+1, label.NumIssues)
// re-add existing IssueLabel
assert.NoError(t, issues_model.NewIssueLabel(issue, label, doer))
unittest.CheckConsistencyFor(t, &issues_model.Issue{}, &issues_model.Label{})
}
func TestNewIssueLabels(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label)
label2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2}).(*issues_model.Label)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 5}).(*issues_model.Issue)
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
assert.NoError(t, issues_model.NewIssueLabels(issue, []*issues_model.Label{label1, label2}, doer))
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label1.ID})
unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{
Type: issues_model.CommentTypeLabel,
PosterID: doer.ID,
IssueID: issue.ID,
LabelID: label1.ID,
Content: "1",
})
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label1.ID})
label1 = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label)
assert.EqualValues(t, 3, label1.NumIssues)
assert.EqualValues(t, 1, label1.NumClosedIssues)
label2 = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2}).(*issues_model.Label)
assert.EqualValues(t, 1, label2.NumIssues)
assert.EqualValues(t, 1, label2.NumClosedIssues)
// corner case: test empty slice
assert.NoError(t, issues_model.NewIssueLabels(issue, []*issues_model.Label{}, doer))
unittest.CheckConsistencyFor(t, &issues_model.Issue{}, &issues_model.Label{})
}
func TestDeleteIssueLabel(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
testSuccess := func(labelID, issueID, doerID int64) {
label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: labelID}).(*issues_model.Label)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issueID}).(*issues_model.Issue)
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doerID}).(*user_model.User)
expectedNumIssues := label.NumIssues
expectedNumClosedIssues := label.NumClosedIssues
if unittest.BeanExists(t, &issues_model.IssueLabel{IssueID: issueID, LabelID: labelID}) {
expectedNumIssues--
if issue.IsClosed {
expectedNumClosedIssues--
}
}
ctx, committer, err := db.TxContext()
defer committer.Close()
assert.NoError(t, err)
assert.NoError(t, issues_model.DeleteIssueLabel(ctx, issue, label, doer))
assert.NoError(t, committer.Commit())
unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{IssueID: issueID, LabelID: labelID})
unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{
Type: issues_model.CommentTypeLabel,
PosterID: doerID,
IssueID: issueID,
LabelID: labelID,
}, `content=""`)
label = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: labelID}).(*issues_model.Label)
assert.EqualValues(t, expectedNumIssues, label.NumIssues)
assert.EqualValues(t, expectedNumClosedIssues, label.NumClosedIssues)
}
testSuccess(1, 1, 2)
testSuccess(2, 5, 2)
testSuccess(1, 1, 2) // delete non-existent IssueLabel
unittest.CheckConsistencyFor(t, &issues_model.Issue{}, &issues_model.Label{})
}

View file

@ -2,14 +2,20 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package issues
package issues_test
import (
"path/filepath"
"testing"
_ "code.gitea.io/gitea/models"
issues_model "code.gitea.io/gitea/models/issues"
_ "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
_ "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert"
)
func init() {
@ -17,14 +23,18 @@ func init() {
setting.LoadForTest()
}
func TestFixturesAreConsistent(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
unittest.CheckConsistencyFor(t,
&issues_model.Issue{},
&issues_model.PullRequest{},
&issues_model.Milestone{},
&issues_model.Label{},
)
}
func TestMain(m *testing.M) {
unittest.MainTest(m, &unittest.TestOptions{
GiteaRootPath: filepath.Join("..", ".."),
FixtureFiles: []string{
"reaction.yml",
"user.yml",
"repository.yml",
"milestone.yml",
},
})
}

View file

@ -292,11 +292,17 @@ func DeleteMilestoneByRepoID(repoID, id int64) error {
return err
}
numMilestones, err := countRepoMilestones(ctx, repo.ID)
numMilestones, err := CountMilestones(ctx, GetMilestonesOption{
RepoID: repo.ID,
State: api.StateAll,
})
if err != nil {
return err
}
numClosedMilestones, err := countRepoClosedMilestones(ctx, repo.ID)
numClosedMilestones, err := CountMilestones(ctx, GetMilestonesOption{
RepoID: repo.ID,
State: api.StateClosed,
})
if err != nil {
return err
}
@ -428,13 +434,6 @@ func GetMilestonesByRepoIDs(repoIDs []int64, page int, isClosed bool, sortType s
)
}
// ____ _ _
// / ___|| |_ __ _| |_ ___
// \___ \| __/ _` | __/ __|
// ___) | || (_| | |_\__ \
// |____/ \__\__,_|\__|___/
//
// MilestonesStats represents milestone statistic information.
type MilestonesStats struct {
OpenCount, ClosedCount int64
@ -503,23 +502,13 @@ func GetMilestonesStatsByRepoCondAndKw(repoCond builder.Cond, keyword string) (*
return stats, nil
}
func countRepoMilestones(ctx context.Context, repoID int64) (int64, error) {
// CountMilestones returns number of milestones in given repository with other options
func CountMilestones(ctx context.Context, opts GetMilestonesOption) (int64, error) {
return db.GetEngine(ctx).
Where("repo_id=?", repoID).
Where(opts.toCond()).
Count(new(Milestone))
}
func countRepoClosedMilestones(ctx context.Context, repoID int64) (int64, error) {
return db.GetEngine(ctx).
Where("repo_id=? AND is_closed=?", repoID, true).
Count(new(Milestone))
}
// CountRepoClosedMilestones returns number of closed milestones in given repository.
func CountRepoClosedMilestones(repoID int64) (int64, error) {
return countRepoClosedMilestones(db.DefaultContext, repoID)
}
// CountMilestonesByRepoCond map from repo conditions to number of milestones matching the options`
func CountMilestonesByRepoCond(repoCond builder.Cond, isClosed bool) (map[int64]int64, error) {
sess := db.GetEngine(db.DefaultContext).Where("is_closed = ?", isClosed)

View file

@ -2,44 +2,46 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package issues
package issues_test
import (
"sort"
"testing"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"github.com/stretchr/testify/assert"
"xorm.io/builder"
)
func TestMilestone_State(t *testing.T) {
assert.Equal(t, api.StateOpen, (&Milestone{IsClosed: false}).State())
assert.Equal(t, api.StateClosed, (&Milestone{IsClosed: true}).State())
assert.Equal(t, api.StateOpen, (&issues_model.Milestone{IsClosed: false}).State())
assert.Equal(t, api.StateClosed, (&issues_model.Milestone{IsClosed: true}).State())
}
func TestGetMilestoneByRepoID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
milestone, err := GetMilestoneByRepoID(db.DefaultContext, 1, 1)
milestone, err := issues_model.GetMilestoneByRepoID(db.DefaultContext, 1, 1)
assert.NoError(t, err)
assert.EqualValues(t, 1, milestone.ID)
assert.EqualValues(t, 1, milestone.RepoID)
_, err = GetMilestoneByRepoID(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID)
assert.True(t, IsErrMilestoneNotExist(err))
_, err = issues_model.GetMilestoneByRepoID(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID)
assert.True(t, issues_model.IsErrMilestoneNotExist(err))
}
func TestGetMilestonesByRepoID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
test := func(repoID int64, state api.StateType) {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository)
milestones, _, err := GetMilestones(GetMilestonesOption{
milestones, _, err := issues_model.GetMilestones(issues_model.GetMilestonesOption{
RepoID: repo.ID,
State: state,
})
@ -76,7 +78,7 @@ func TestGetMilestonesByRepoID(t *testing.T) {
test(3, api.StateClosed)
test(3, api.StateAll)
milestones, _, err := GetMilestones(GetMilestonesOption{
milestones, _, err := issues_model.GetMilestones(issues_model.GetMilestonesOption{
RepoID: unittest.NonexistentID,
State: api.StateOpen,
})
@ -87,9 +89,9 @@ func TestGetMilestonesByRepoID(t *testing.T) {
func TestGetMilestones(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
test := func(sortType string, sortCond func(*Milestone) int) {
test := func(sortType string, sortCond func(*issues_model.Milestone) int) {
for _, page := range []int{0, 1} {
milestones, _, err := GetMilestones(GetMilestonesOption{
milestones, _, err := issues_model.GetMilestones(issues_model.GetMilestonesOption{
ListOptions: db.ListOptions{
Page: page,
PageSize: setting.UI.IssuePagingNum,
@ -106,7 +108,7 @@ func TestGetMilestones(t *testing.T) {
}
assert.True(t, sort.IntsAreSorted(values))
milestones, _, err = GetMilestones(GetMilestonesOption{
milestones, _, err = issues_model.GetMilestones(issues_model.GetMilestonesOption{
ListOptions: db.ListOptions{
Page: page,
PageSize: setting.UI.IssuePagingNum,
@ -125,22 +127,22 @@ func TestGetMilestones(t *testing.T) {
assert.True(t, sort.IntsAreSorted(values))
}
}
test("furthestduedate", func(milestone *Milestone) int {
test("furthestduedate", func(milestone *issues_model.Milestone) int {
return -int(milestone.DeadlineUnix)
})
test("leastcomplete", func(milestone *Milestone) int {
test("leastcomplete", func(milestone *issues_model.Milestone) int {
return milestone.Completeness
})
test("mostcomplete", func(milestone *Milestone) int {
test("mostcomplete", func(milestone *issues_model.Milestone) int {
return -milestone.Completeness
})
test("leastissues", func(milestone *Milestone) int {
test("leastissues", func(milestone *issues_model.Milestone) int {
return milestone.NumIssues
})
test("mostissues", func(milestone *Milestone) int {
test("mostissues", func(milestone *issues_model.Milestone) int {
return -milestone.NumIssues
})
test("soonestduedate", func(milestone *Milestone) int {
test("soonestduedate", func(milestone *issues_model.Milestone) int {
return int(milestone.DeadlineUnix)
})
}
@ -149,7 +151,10 @@ func TestCountRepoMilestones(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
test := func(repoID int64) {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository)
count, err := countRepoMilestones(db.DefaultContext, repoID)
count, err := issues_model.CountMilestones(db.DefaultContext, issues_model.GetMilestonesOption{
RepoID: repoID,
State: api.StateAll,
})
assert.NoError(t, err)
assert.EqualValues(t, repo.NumMilestones, count)
}
@ -157,7 +162,10 @@ func TestCountRepoMilestones(t *testing.T) {
test(2)
test(3)
count, err := countRepoMilestones(db.DefaultContext, unittest.NonexistentID)
count, err := issues_model.CountMilestones(db.DefaultContext, issues_model.GetMilestonesOption{
RepoID: unittest.NonexistentID,
State: api.StateAll,
})
assert.NoError(t, err)
assert.EqualValues(t, 0, count)
}
@ -166,7 +174,10 @@ func TestCountRepoClosedMilestones(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
test := func(repoID int64) {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository)
count, err := CountRepoClosedMilestones(repoID)
count, err := issues_model.CountMilestones(db.DefaultContext, issues_model.GetMilestonesOption{
RepoID: repoID,
State: api.StateClosed,
})
assert.NoError(t, err)
assert.EqualValues(t, repo.NumClosedMilestones, count)
}
@ -174,7 +185,10 @@ func TestCountRepoClosedMilestones(t *testing.T) {
test(2)
test(3)
count, err := CountRepoClosedMilestones(unittest.NonexistentID)
count, err := issues_model.CountMilestones(db.DefaultContext, issues_model.GetMilestonesOption{
RepoID: unittest.NonexistentID,
State: api.StateClosed,
})
assert.NoError(t, err)
assert.EqualValues(t, 0, count)
}
@ -188,12 +202,12 @@ func TestCountMilestonesByRepoIDs(t *testing.T) {
repo1OpenCount, repo1ClosedCount := milestonesCount(1)
repo2OpenCount, repo2ClosedCount := milestonesCount(2)
openCounts, err := CountMilestonesByRepoCond(builder.In("repo_id", []int64{1, 2}), false)
openCounts, err := issues_model.CountMilestonesByRepoCond(builder.In("repo_id", []int64{1, 2}), false)
assert.NoError(t, err)
assert.EqualValues(t, repo1OpenCount, openCounts[1])
assert.EqualValues(t, repo2OpenCount, openCounts[2])
closedCounts, err := CountMilestonesByRepoCond(builder.In("repo_id", []int64{1, 2}), true)
closedCounts, err := issues_model.CountMilestonesByRepoCond(builder.In("repo_id", []int64{1, 2}), true)
assert.NoError(t, err)
assert.EqualValues(t, repo1ClosedCount, closedCounts[1])
assert.EqualValues(t, repo2ClosedCount, closedCounts[2])
@ -203,9 +217,9 @@ func TestGetMilestonesByRepoIDs(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
test := func(sortType string, sortCond func(*Milestone) int) {
test := func(sortType string, sortCond func(*issues_model.Milestone) int) {
for _, page := range []int{0, 1} {
openMilestones, err := GetMilestonesByRepoIDs([]int64{repo1.ID, repo2.ID}, page, false, sortType)
openMilestones, err := issues_model.GetMilestonesByRepoIDs([]int64{repo1.ID, repo2.ID}, page, false, sortType)
assert.NoError(t, err)
assert.Len(t, openMilestones, repo1.NumOpenMilestones+repo2.NumOpenMilestones)
values := make([]int, len(openMilestones))
@ -214,7 +228,7 @@ func TestGetMilestonesByRepoIDs(t *testing.T) {
}
assert.True(t, sort.IntsAreSorted(values))
closedMilestones, err := GetMilestonesByRepoIDs([]int64{repo1.ID, repo2.ID}, page, true, sortType)
closedMilestones, err := issues_model.GetMilestonesByRepoIDs([]int64{repo1.ID, repo2.ID}, page, true, sortType)
assert.NoError(t, err)
assert.Len(t, closedMilestones, repo1.NumClosedMilestones+repo2.NumClosedMilestones)
values = make([]int, len(closedMilestones))
@ -224,22 +238,22 @@ func TestGetMilestonesByRepoIDs(t *testing.T) {
assert.True(t, sort.IntsAreSorted(values))
}
}
test("furthestduedate", func(milestone *Milestone) int {
test("furthestduedate", func(milestone *issues_model.Milestone) int {
return -int(milestone.DeadlineUnix)
})
test("leastcomplete", func(milestone *Milestone) int {
test("leastcomplete", func(milestone *issues_model.Milestone) int {
return milestone.Completeness
})
test("mostcomplete", func(milestone *Milestone) int {
test("mostcomplete", func(milestone *issues_model.Milestone) int {
return -milestone.Completeness
})
test("leastissues", func(milestone *Milestone) int {
test("leastissues", func(milestone *issues_model.Milestone) int {
return milestone.NumIssues
})
test("mostissues", func(milestone *Milestone) int {
test("mostissues", func(milestone *issues_model.Milestone) int {
return -milestone.NumIssues
})
test("soonestduedate", func(milestone *Milestone) int {
test("soonestduedate", func(milestone *issues_model.Milestone) int {
return int(milestone.DeadlineUnix)
})
}
@ -249,7 +263,7 @@ func TestGetMilestonesStats(t *testing.T) {
test := func(repoID int64) {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository)
stats, err := GetMilestonesStatsByRepoCond(builder.And(builder.Eq{"repo_id": repoID}))
stats, err := issues_model.GetMilestonesStatsByRepoCond(builder.And(builder.Eq{"repo_id": repoID}))
assert.NoError(t, err)
assert.EqualValues(t, repo.NumMilestones-repo.NumClosedMilestones, stats.OpenCount)
assert.EqualValues(t, repo.NumClosedMilestones, stats.ClosedCount)
@ -258,7 +272,7 @@ func TestGetMilestonesStats(t *testing.T) {
test(2)
test(3)
stats, err := GetMilestonesStatsByRepoCond(builder.And(builder.Eq{"repo_id": unittest.NonexistentID}))
stats, err := issues_model.GetMilestonesStatsByRepoCond(builder.And(builder.Eq{"repo_id": unittest.NonexistentID}))
assert.NoError(t, err)
assert.EqualValues(t, 0, stats.OpenCount)
assert.EqualValues(t, 0, stats.ClosedCount)
@ -266,8 +280,75 @@ func TestGetMilestonesStats(t *testing.T) {
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
milestoneStats, err := GetMilestonesStatsByRepoCond(builder.In("repo_id", []int64{repo1.ID, repo2.ID}))
milestoneStats, err := issues_model.GetMilestonesStatsByRepoCond(builder.In("repo_id", []int64{repo1.ID, repo2.ID}))
assert.NoError(t, err)
assert.EqualValues(t, repo1.NumOpenMilestones+repo2.NumOpenMilestones, milestoneStats.OpenCount)
assert.EqualValues(t, repo1.NumClosedMilestones+repo2.NumClosedMilestones, milestoneStats.ClosedCount)
}
func TestNewMilestone(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
milestone := &issues_model.Milestone{
RepoID: 1,
Name: "milestoneName",
Content: "milestoneContent",
}
assert.NoError(t, issues_model.NewMilestone(milestone))
unittest.AssertExistsAndLoadBean(t, milestone)
unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: milestone.RepoID}, &issues_model.Milestone{})
}
func TestChangeMilestoneStatus(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}).(*issues_model.Milestone)
assert.NoError(t, issues_model.ChangeMilestoneStatus(milestone, true))
unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}, "is_closed=1")
unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: milestone.RepoID}, &issues_model.Milestone{})
assert.NoError(t, issues_model.ChangeMilestoneStatus(milestone, false))
unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}, "is_closed=0")
unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: milestone.RepoID}, &issues_model.Milestone{})
}
func TestDeleteMilestoneByRepoID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
assert.NoError(t, issues_model.DeleteMilestoneByRepoID(1, 1))
unittest.AssertNotExistsBean(t, &issues_model.Milestone{ID: 1})
unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: 1})
assert.NoError(t, issues_model.DeleteMilestoneByRepoID(unittest.NonexistentID, unittest.NonexistentID))
}
func TestUpdateMilestone(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}).(*issues_model.Milestone)
milestone.Name = " newMilestoneName "
milestone.Content = "newMilestoneContent"
assert.NoError(t, issues_model.UpdateMilestone(milestone, milestone.IsClosed))
milestone = unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}).(*issues_model.Milestone)
assert.EqualValues(t, "newMilestoneName", milestone.Name)
unittest.CheckConsistencyFor(t, &issues_model.Milestone{})
}
func TestUpdateMilestoneCounters(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{MilestoneID: 1},
"is_closed=0").(*issues_model.Issue)
issue.IsClosed = true
issue.ClosedUnix = timeutil.TimeStampNow()
_, err := db.GetEngine(db.DefaultContext).ID(issue.ID).Cols("is_closed", "closed_unix").Update(issue)
assert.NoError(t, err)
assert.NoError(t, issues_model.UpdateMilestoneCounters(db.DefaultContext, issue.MilestoneID))
unittest.CheckConsistencyFor(t, &issues_model.Milestone{})
issue.IsClosed = false
issue.ClosedUnix = 0
_, err = db.GetEngine(db.DefaultContext).ID(issue.ID).Cols("is_closed", "closed_unix").Update(issue)
assert.NoError(t, err)
assert.NoError(t, issues_model.UpdateMilestoneCounters(db.DefaultContext, issue.MilestoneID))
unittest.CheckConsistencyFor(t, &issues_model.Milestone{})
}

View file

@ -3,7 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues
import (
"context"
@ -25,6 +25,83 @@ import (
"xorm.io/builder"
)
// ErrPullRequestNotExist represents a "PullRequestNotExist" kind of error.
type ErrPullRequestNotExist struct {
ID int64
IssueID int64
HeadRepoID int64
BaseRepoID int64
HeadBranch string
BaseBranch string
}
// IsErrPullRequestNotExist checks if an error is a ErrPullRequestNotExist.
func IsErrPullRequestNotExist(err error) bool {
_, ok := err.(ErrPullRequestNotExist)
return ok
}
func (err ErrPullRequestNotExist) Error() string {
return fmt.Sprintf("pull request does not exist [id: %d, issue_id: %d, head_repo_id: %d, base_repo_id: %d, head_branch: %s, base_branch: %s]",
err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch)
}
// ErrPullRequestAlreadyExists represents a "PullRequestAlreadyExists"-error
type ErrPullRequestAlreadyExists struct {
ID int64
IssueID int64
HeadRepoID int64
BaseRepoID int64
HeadBranch string
BaseBranch string
}
// IsErrPullRequestAlreadyExists checks if an error is a ErrPullRequestAlreadyExists.
func IsErrPullRequestAlreadyExists(err error) bool {
_, ok := err.(ErrPullRequestAlreadyExists)
return ok
}
// Error does pretty-printing :D
func (err ErrPullRequestAlreadyExists) Error() string {
return fmt.Sprintf("pull request already exists for these targets [id: %d, issue_id: %d, head_repo_id: %d, base_repo_id: %d, head_branch: %s, base_branch: %s]",
err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch)
}
// ErrPullRequestHeadRepoMissing represents a "ErrPullRequestHeadRepoMissing" error
type ErrPullRequestHeadRepoMissing struct {
ID int64
HeadRepoID int64
}
// IsErrErrPullRequestHeadRepoMissing checks if an error is a ErrPullRequestHeadRepoMissing.
func IsErrErrPullRequestHeadRepoMissing(err error) bool {
_, ok := err.(ErrPullRequestHeadRepoMissing)
return ok
}
// Error does pretty-printing :D
func (err ErrPullRequestHeadRepoMissing) Error() string {
return fmt.Sprintf("pull request head repo missing [id: %d, head_repo_id: %d]",
err.ID, err.HeadRepoID)
}
// ErrPullWasClosed is used close a closed pull request
type ErrPullWasClosed struct {
ID int64
Index int64
}
// IsErrPullWasClosed checks if an error is a ErrErrPullWasClosed.
func IsErrPullWasClosed(err error) bool {
_, ok := err.(ErrPullWasClosed)
return ok
}
func (err ErrPullWasClosed) Error() string {
return fmt.Sprintf("Pull request [%d] %d was already closed", err.ID, err.Index)
}
// PullRequestType defines pull request type
type PullRequestType int
@ -98,7 +175,8 @@ func init() {
db.RegisterModel(new(PullRequest))
}
func deletePullsByBaseRepoID(ctx context.Context, repoID int64) error {
// DeletePullsByBaseRepoID deletes all pull requests by the base repository ID
func DeletePullsByBaseRepoID(ctx context.Context, repoID int64) error {
deleteCond := builder.Select("id").From("pull_request").Where(builder.Eq{"pull_request.base_repo_id": repoID})
// Delete scheduled auto merges
@ -219,7 +297,7 @@ func (pr *PullRequest) LoadIssueCtx(ctx context.Context) (err error) {
return nil
}
pr.Issue, err = getIssueByID(ctx, pr.IssueID)
pr.Issue, err = GetIssueByID(ctx, pr.IssueID)
if err == nil {
pr.Issue.PullRequest = pr
}
@ -420,14 +498,14 @@ func NewPullRequest(outerCtx context.Context, repo *repo_model.Repository, issue
defer committer.Close()
ctx.WithContext(outerCtx)
if err = newIssue(ctx, issue.Poster, NewIssueOptions{
if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{
Repo: repo,
Issue: issue,
LabelIDs: labelIDs,
Attachments: uuids,
IsPull: true,
}); err != nil {
if IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) {
if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) {
return err
}
return fmt.Errorf("newIssue: %v", err)
@ -691,3 +769,70 @@ func (pr *PullRequest) Mergeable() bool {
return pr.Status != PullRequestStatusChecking && pr.Status != PullRequestStatusConflict &&
pr.Status != PullRequestStatusError && !pr.IsWorkInProgress()
}
// HasEnoughApprovals returns true if pr has enough granted approvals.
func HasEnoughApprovals(ctx context.Context, protectBranch *git_model.ProtectedBranch, pr *PullRequest) bool {
if protectBranch.RequiredApprovals == 0 {
return true
}
return GetGrantedApprovalsCount(ctx, protectBranch, pr) >= protectBranch.RequiredApprovals
}
// GetGrantedApprovalsCount returns the number of granted approvals for pr. A granted approval must be authored by a user in an approval whitelist.
func GetGrantedApprovalsCount(ctx context.Context, protectBranch *git_model.ProtectedBranch, pr *PullRequest) int64 {
sess := db.GetEngine(ctx).Where("issue_id = ?", pr.IssueID).
And("type = ?", ReviewTypeApprove).
And("official = ?", true).
And("dismissed = ?", false)
if protectBranch.DismissStaleApprovals {
sess = sess.And("stale = ?", false)
}
approvals, err := sess.Count(new(Review))
if err != nil {
log.Error("GetGrantedApprovalsCount: %v", err)
return 0
}
return approvals
}
// MergeBlockedByRejectedReview returns true if merge is blocked by rejected reviews
func MergeBlockedByRejectedReview(ctx context.Context, protectBranch *git_model.ProtectedBranch, pr *PullRequest) bool {
if !protectBranch.BlockOnRejectedReviews {
return false
}
rejectExist, err := db.GetEngine(ctx).Where("issue_id = ?", pr.IssueID).
And("type = ?", ReviewTypeReject).
And("official = ?", true).
And("dismissed = ?", false).
Exist(new(Review))
if err != nil {
log.Error("MergeBlockedByRejectedReview: %v", err)
return true
}
return rejectExist
}
// MergeBlockedByOfficialReviewRequests block merge because of some review request to official reviewer
// of from official review
func MergeBlockedByOfficialReviewRequests(ctx context.Context, protectBranch *git_model.ProtectedBranch, pr *PullRequest) bool {
if !protectBranch.BlockOnOfficialReviewRequests {
return false
}
has, err := db.GetEngine(ctx).Where("issue_id = ?", pr.IssueID).
And("type = ?", ReviewTypeRequest).
And("official = ?", true).
Exist(new(Review))
if err != nil {
log.Error("MergeBlockedByOfficialReviewRequests: %v", err)
return true
}
return has
}
// MergeBlockedByOutdatedBranch returns true if merge is blocked by an outdated head branch
func MergeBlockedByOutdatedBranch(protectBranch *git_model.ProtectedBranch, pr *PullRequest) bool {
return protectBranch.BlockOnOutdatedBranch && pr.CommitsBehind > 0
}

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues
import (
"context"

View file

@ -2,12 +2,13 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues_test
import (
"testing"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
@ -15,7 +16,7 @@ import (
func TestPullRequest_LoadAttributes(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest)
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest)
assert.NoError(t, pr.LoadAttributes())
assert.NotNil(t, pr.Merger)
assert.Equal(t, pr.MergerID, pr.Merger.ID)
@ -23,7 +24,7 @@ func TestPullRequest_LoadAttributes(t *testing.T) {
func TestPullRequest_LoadIssue(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest)
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest)
assert.NoError(t, pr.LoadIssue())
assert.NotNil(t, pr.Issue)
assert.Equal(t, int64(2), pr.Issue.ID)
@ -34,7 +35,7 @@ func TestPullRequest_LoadIssue(t *testing.T) {
func TestPullRequest_LoadBaseRepo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest)
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest)
assert.NoError(t, pr.LoadBaseRepo())
assert.NotNil(t, pr.BaseRepo)
assert.Equal(t, pr.BaseRepoID, pr.BaseRepo.ID)
@ -45,7 +46,7 @@ func TestPullRequest_LoadBaseRepo(t *testing.T) {
func TestPullRequest_LoadHeadRepo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest)
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest)
assert.NoError(t, pr.LoadHeadRepo())
assert.NotNil(t, pr.HeadRepo)
assert.Equal(t, pr.HeadRepoID, pr.HeadRepo.ID)
@ -57,7 +58,7 @@ func TestPullRequest_LoadHeadRepo(t *testing.T) {
func TestPullRequestsNewest(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
prs, count, err := PullRequests(1, &PullRequestsOptions{
prs, count, err := issues_model.PullRequests(1, &issues_model.PullRequestsOptions{
ListOptions: db.ListOptions{
Page: 1,
},
@ -76,7 +77,7 @@ func TestPullRequestsNewest(t *testing.T) {
func TestPullRequestsOldest(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
prs, count, err := PullRequests(1, &PullRequestsOptions{
prs, count, err := issues_model.PullRequests(1, &issues_model.PullRequestsOptions{
ListOptions: db.ListOptions{
Page: 1,
},
@ -95,30 +96,30 @@ func TestPullRequestsOldest(t *testing.T) {
func TestGetUnmergedPullRequest(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
pr, err := GetUnmergedPullRequest(1, 1, "branch2", "master", PullRequestFlowGithub)
pr, err := issues_model.GetUnmergedPullRequest(1, 1, "branch2", "master", issues_model.PullRequestFlowGithub)
assert.NoError(t, err)
assert.Equal(t, int64(2), pr.ID)
_, err = GetUnmergedPullRequest(1, 9223372036854775807, "branch1", "master", PullRequestFlowGithub)
_, err = issues_model.GetUnmergedPullRequest(1, 9223372036854775807, "branch1", "master", issues_model.PullRequestFlowGithub)
assert.Error(t, err)
assert.True(t, IsErrPullRequestNotExist(err))
assert.True(t, issues_model.IsErrPullRequestNotExist(err))
}
func TestHasUnmergedPullRequestsByHeadInfo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
exist, err := HasUnmergedPullRequestsByHeadInfo(db.DefaultContext, 1, "branch2")
exist, err := issues_model.HasUnmergedPullRequestsByHeadInfo(db.DefaultContext, 1, "branch2")
assert.NoError(t, err)
assert.Equal(t, true, exist)
exist, err = HasUnmergedPullRequestsByHeadInfo(db.DefaultContext, 1, "not_exist_branch")
exist, err = issues_model.HasUnmergedPullRequestsByHeadInfo(db.DefaultContext, 1, "not_exist_branch")
assert.NoError(t, err)
assert.Equal(t, false, exist)
}
func TestGetUnmergedPullRequestsByHeadInfo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
prs, err := GetUnmergedPullRequestsByHeadInfo(1, "branch2")
prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(1, "branch2")
assert.NoError(t, err)
assert.Len(t, prs, 1)
for _, pr := range prs {
@ -129,7 +130,7 @@ func TestGetUnmergedPullRequestsByHeadInfo(t *testing.T) {
func TestGetUnmergedPullRequestsByBaseInfo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
prs, err := GetUnmergedPullRequestsByBaseInfo(1, "master")
prs, err := issues_model.GetUnmergedPullRequestsByBaseInfo(1, "master")
assert.NoError(t, err)
assert.Len(t, prs, 1)
pr := prs[0]
@ -140,51 +141,51 @@ func TestGetUnmergedPullRequestsByBaseInfo(t *testing.T) {
func TestGetPullRequestByIndex(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
pr, err := GetPullRequestByIndex(db.DefaultContext, 1, 2)
pr, err := issues_model.GetPullRequestByIndex(db.DefaultContext, 1, 2)
assert.NoError(t, err)
assert.Equal(t, int64(1), pr.BaseRepoID)
assert.Equal(t, int64(2), pr.Index)
_, err = GetPullRequestByIndex(db.DefaultContext, 9223372036854775807, 9223372036854775807)
_, err = issues_model.GetPullRequestByIndex(db.DefaultContext, 9223372036854775807, 9223372036854775807)
assert.Error(t, err)
assert.True(t, IsErrPullRequestNotExist(err))
assert.True(t, issues_model.IsErrPullRequestNotExist(err))
_, err = GetPullRequestByIndex(db.DefaultContext, 1, 0)
_, err = issues_model.GetPullRequestByIndex(db.DefaultContext, 1, 0)
assert.Error(t, err)
assert.True(t, IsErrPullRequestNotExist(err))
assert.True(t, issues_model.IsErrPullRequestNotExist(err))
}
func TestGetPullRequestByID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
pr, err := GetPullRequestByID(db.DefaultContext, 1)
pr, err := issues_model.GetPullRequestByID(db.DefaultContext, 1)
assert.NoError(t, err)
assert.Equal(t, int64(1), pr.ID)
assert.Equal(t, int64(2), pr.IssueID)
_, err = GetPullRequestByID(db.DefaultContext, 9223372036854775807)
_, err = issues_model.GetPullRequestByID(db.DefaultContext, 9223372036854775807)
assert.Error(t, err)
assert.True(t, IsErrPullRequestNotExist(err))
assert.True(t, issues_model.IsErrPullRequestNotExist(err))
}
func TestGetPullRequestByIssueID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
pr, err := GetPullRequestByIssueID(db.DefaultContext, 2)
pr, err := issues_model.GetPullRequestByIssueID(db.DefaultContext, 2)
assert.NoError(t, err)
assert.Equal(t, int64(2), pr.IssueID)
_, err = GetPullRequestByIssueID(db.DefaultContext, 9223372036854775807)
_, err = issues_model.GetPullRequestByIssueID(db.DefaultContext, 9223372036854775807)
assert.Error(t, err)
assert.True(t, IsErrPullRequestNotExist(err))
assert.True(t, issues_model.IsErrPullRequestNotExist(err))
}
func TestPullRequest_Update(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest)
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest)
pr.BaseBranch = "baseBranch"
pr.HeadBranch = "headBranch"
pr.Update()
pr = unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: pr.ID}).(*PullRequest)
pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID}).(*issues_model.PullRequest)
assert.Equal(t, "baseBranch", pr.BaseBranch)
assert.Equal(t, "headBranch", pr.HeadBranch)
unittest.CheckConsistencyFor(t, pr)
@ -192,14 +193,14 @@ func TestPullRequest_Update(t *testing.T) {
func TestPullRequest_UpdateCols(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
pr := &PullRequest{
pr := &issues_model.PullRequest{
ID: 1,
BaseBranch: "baseBranch",
HeadBranch: "headBranch",
}
assert.NoError(t, pr.UpdateCols("head_branch"))
pr = unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest)
pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest)
assert.Equal(t, "master", pr.BaseBranch)
assert.Equal(t, "headBranch", pr.HeadBranch)
unittest.CheckConsistencyFor(t, pr)
@ -208,17 +209,17 @@ func TestPullRequest_UpdateCols(t *testing.T) {
func TestPullRequestList_LoadAttributes(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
prs := []*PullRequest{
unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest),
unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 2}).(*PullRequest),
prs := []*issues_model.PullRequest{
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest),
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}).(*issues_model.PullRequest),
}
assert.NoError(t, PullRequestList(prs).LoadAttributes())
assert.NoError(t, issues_model.PullRequestList(prs).LoadAttributes())
for _, pr := range prs {
assert.NotNil(t, pr.Issue)
assert.Equal(t, pr.IssueID, pr.Issue.ID)
}
assert.NoError(t, PullRequestList([]*PullRequest{}).LoadAttributes())
assert.NoError(t, issues_model.PullRequestList([]*issues_model.PullRequest{}).LoadAttributes())
}
// TODO TestAddTestPullRequestTask
@ -226,7 +227,7 @@ func TestPullRequestList_LoadAttributes(t *testing.T) {
func TestPullRequest_IsWorkInProgress(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 2}).(*PullRequest)
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}).(*issues_model.PullRequest)
pr.LoadIssue()
assert.False(t, pr.IsWorkInProgress())
@ -241,7 +242,7 @@ func TestPullRequest_IsWorkInProgress(t *testing.T) {
func TestPullRequest_GetWorkInProgressPrefixWorkInProgress(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 2}).(*PullRequest)
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}).(*issues_model.PullRequest)
pr.LoadIssue()
assert.Empty(t, pr.GetWorkInProgressPrefix())
@ -253,3 +254,24 @@ func TestPullRequest_GetWorkInProgressPrefixWorkInProgress(t *testing.T) {
pr.Issue.Title = "[wip] " + original
assert.Equal(t, "[wip]", pr.GetWorkInProgressPrefix())
}
func TestDeleteOrphanedObjects(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
countBefore, err := db.GetEngine(db.DefaultContext).Count(&issues_model.PullRequest{})
assert.NoError(t, err)
_, err = db.GetEngine(db.DefaultContext).Insert(&issues_model.PullRequest{IssueID: 1000}, &issues_model.PullRequest{IssueID: 1001}, &issues_model.PullRequest{IssueID: 1003})
assert.NoError(t, err)
orphaned, err := db.CountOrphanedObjects("pull_request", "issue", "pull_request.issue_id=issue.id")
assert.NoError(t, err)
assert.EqualValues(t, 3, orphaned)
err = db.DeleteOrphanedObjects("pull_request", "issue", "pull_request.issue_id=issue.id")
assert.NoError(t, err)
countAfter, err := db.GetEngine(db.DefaultContext).Count(&issues_model.PullRequest{})
assert.NoError(t, err)
assert.EqualValues(t, countBefore, countAfter)
}

View file

@ -2,12 +2,13 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package issues
package issues_test
import (
"testing"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
@ -17,12 +18,12 @@ import (
)
func addReaction(t *testing.T, doerID, issueID, commentID int64, content string) {
var reaction *Reaction
var reaction *issues_model.Reaction
var err error
if commentID == 0 {
reaction, err = CreateIssueReaction(doerID, issueID, content)
reaction, err = issues_model.CreateIssueReaction(doerID, issueID, content)
} else {
reaction, err = CreateCommentReaction(doerID, issueID, commentID, content)
reaction, err = issues_model.CreateCommentReaction(doerID, issueID, commentID, content)
}
assert.NoError(t, err)
assert.NotNil(t, reaction)
@ -37,7 +38,7 @@ func TestIssueAddReaction(t *testing.T) {
addReaction(t, user1.ID, issue1ID, 0, "heart")
unittest.AssertExistsAndLoadBean(t, &Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1ID})
unittest.AssertExistsAndLoadBean(t, &issues_model.Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1ID})
}
func TestIssueAddDuplicateReaction(t *testing.T) {
@ -49,15 +50,15 @@ func TestIssueAddDuplicateReaction(t *testing.T) {
addReaction(t, user1.ID, issue1ID, 0, "heart")
reaction, err := CreateReaction(&ReactionOptions{
reaction, err := issues_model.CreateReaction(&issues_model.ReactionOptions{
DoerID: user1.ID,
IssueID: issue1ID,
Type: "heart",
})
assert.Error(t, err)
assert.Equal(t, ErrReactionAlreadyExist{Reaction: "heart"}, err)
assert.Equal(t, issues_model.ErrReactionAlreadyExist{Reaction: "heart"}, err)
existingR := unittest.AssertExistsAndLoadBean(t, &Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1ID}).(*Reaction)
existingR := unittest.AssertExistsAndLoadBean(t, &issues_model.Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1ID}).(*issues_model.Reaction)
assert.Equal(t, existingR.ID, reaction.ID)
}
@ -70,10 +71,10 @@ func TestIssueDeleteReaction(t *testing.T) {
addReaction(t, user1.ID, issue1ID, 0, "heart")
err := DeleteIssueReaction(user1.ID, issue1ID, "heart")
err := issues_model.DeleteIssueReaction(user1.ID, issue1ID, "heart")
assert.NoError(t, err)
unittest.AssertNotExistsBean(t, &Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1ID})
unittest.AssertNotExistsBean(t, &issues_model.Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1ID})
}
func TestIssueReactionCount(t *testing.T) {
@ -98,7 +99,7 @@ func TestIssueReactionCount(t *testing.T) {
addReaction(t, user4.ID, issueID, 0, "heart")
addReaction(t, ghost.ID, issueID, 0, "-1")
reactionsList, _, err := FindReactions(db.DefaultContext, FindReactionsOptions{
reactionsList, _, err := issues_model.FindReactions(db.DefaultContext, issues_model.FindReactionsOptions{
IssueID: issueID,
})
assert.NoError(t, err)
@ -128,7 +129,7 @@ func TestIssueCommentAddReaction(t *testing.T) {
addReaction(t, user1.ID, issue1ID, comment1ID, "heart")
unittest.AssertExistsAndLoadBean(t, &Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1ID, CommentID: comment1ID})
unittest.AssertExistsAndLoadBean(t, &issues_model.Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1ID, CommentID: comment1ID})
}
func TestIssueCommentDeleteReaction(t *testing.T) {
@ -147,7 +148,7 @@ func TestIssueCommentDeleteReaction(t *testing.T) {
addReaction(t, user3.ID, issue1ID, comment1ID, "heart")
addReaction(t, user4.ID, issue1ID, comment1ID, "+1")
reactionsList, _, err := FindReactions(db.DefaultContext, FindReactionsOptions{
reactionsList, _, err := issues_model.FindReactions(db.DefaultContext, issues_model.FindReactionsOptions{
IssueID: issue1ID,
CommentID: comment1ID,
})
@ -168,7 +169,7 @@ func TestIssueCommentReactionCount(t *testing.T) {
var comment1ID int64 = 1
addReaction(t, user1.ID, issue1ID, comment1ID, "heart")
assert.NoError(t, DeleteCommentReaction(user1.ID, issue1ID, comment1ID, "heart"))
assert.NoError(t, issues_model.DeleteCommentReaction(user1.ID, issue1ID, comment1ID, "heart"))
unittest.AssertNotExistsBean(t, &Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1ID, CommentID: comment1ID})
unittest.AssertNotExistsBean(t, &issues_model.Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1ID, CommentID: comment1ID})
}

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues
import (
"context"
@ -17,11 +17,47 @@ import (
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/builder"
)
// ErrReviewNotExist represents a "ReviewNotExist" kind of error.
type ErrReviewNotExist struct {
ID int64
}
// IsErrReviewNotExist checks if an error is a ErrReviewNotExist.
func IsErrReviewNotExist(err error) bool {
_, ok := err.(ErrReviewNotExist)
return ok
}
func (err ErrReviewNotExist) Error() string {
return fmt.Sprintf("review does not exist [id: %d]", err.ID)
}
// ErrNotValidReviewRequest an not allowed review request modify
type ErrNotValidReviewRequest struct {
Reason string
UserID int64
RepoID int64
}
// IsErrNotValidReviewRequest checks if an error is a ErrNotValidReviewRequest.
func IsErrNotValidReviewRequest(err error) bool {
_, ok := err.(ErrNotValidReviewRequest)
return ok
}
func (err ErrNotValidReviewRequest) Error() string {
return fmt.Sprintf("%s [user_id: %d, repo_id: %d]",
err.Reason,
err.UserID,
err.RepoID)
}
// ReviewType defines the sort of feedback a review gives
type ReviewType int
@ -105,7 +141,7 @@ func (r *Review) loadIssue(ctx context.Context) (err error) {
if r.Issue != nil {
return
}
r.Issue, err = getIssueByID(ctx, r.IssueID)
r.Issue, err = GetIssueByID(ctx, r.IssueID)
return
}
@ -967,3 +1003,16 @@ func (r *Review) GetExternalName() string { return r.OriginalAuthor }
// GetExternalID ExternalUserRemappable interface
func (r *Review) GetExternalID() int64 { return r.OriginalAuthorID }
// UpdateReviewsMigrationsByType updates reviews' migrations information via given git service type and original id and poster id
func UpdateReviewsMigrationsByType(tp structs.GitServiceType, originalAuthorID string, posterID int64) error {
_, err := db.GetEngine(db.DefaultContext).Table("review").
Where("original_author_id = ?", originalAuthorID).
And(migratedIssueCond(tp)).
Update(map[string]interface{}{
"reviewer_id": posterID,
"original_author": "",
"original_author_id": 0,
})
return err
}

View file

@ -2,12 +2,13 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues_test
import (
"testing"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
@ -16,34 +17,34 @@ import (
func TestGetReviewByID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
review, err := GetReviewByID(db.DefaultContext, 1)
review, err := issues_model.GetReviewByID(db.DefaultContext, 1)
assert.NoError(t, err)
assert.Equal(t, "Demo Review", review.Content)
assert.Equal(t, ReviewTypeApprove, review.Type)
assert.Equal(t, issues_model.ReviewTypeApprove, review.Type)
_, err = GetReviewByID(db.DefaultContext, 23892)
_, err = issues_model.GetReviewByID(db.DefaultContext, 23892)
assert.Error(t, err)
assert.True(t, IsErrReviewNotExist(err), "IsErrReviewNotExist")
assert.True(t, issues_model.IsErrReviewNotExist(err), "IsErrReviewNotExist")
}
func TestReview_LoadAttributes(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
review := unittest.AssertExistsAndLoadBean(t, &Review{ID: 1}).(*Review)
review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 1}).(*issues_model.Review)
assert.NoError(t, review.LoadAttributes(db.DefaultContext))
assert.NotNil(t, review.Issue)
assert.NotNil(t, review.Reviewer)
invalidReview1 := unittest.AssertExistsAndLoadBean(t, &Review{ID: 2}).(*Review)
invalidReview1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 2}).(*issues_model.Review)
assert.Error(t, invalidReview1.LoadAttributes(db.DefaultContext))
invalidReview2 := unittest.AssertExistsAndLoadBean(t, &Review{ID: 3}).(*Review)
invalidReview2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 3}).(*issues_model.Review)
assert.Error(t, invalidReview2.LoadAttributes(db.DefaultContext))
}
func TestReview_LoadCodeComments(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
review := unittest.AssertExistsAndLoadBean(t, &Review{ID: 4}).(*Review)
review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 4}).(*issues_model.Review)
assert.NoError(t, review.LoadAttributes(db.DefaultContext))
assert.NoError(t, review.LoadCodeComments(db.DefaultContext))
assert.Len(t, review.CodeComments, 1)
@ -51,18 +52,18 @@ func TestReview_LoadCodeComments(t *testing.T) {
}
func TestReviewType_Icon(t *testing.T) {
assert.Equal(t, "check", ReviewTypeApprove.Icon())
assert.Equal(t, "diff", ReviewTypeReject.Icon())
assert.Equal(t, "comment", ReviewTypeComment.Icon())
assert.Equal(t, "comment", ReviewTypeUnknown.Icon())
assert.Equal(t, "dot-fill", ReviewTypeRequest.Icon())
assert.Equal(t, "comment", ReviewType(6).Icon())
assert.Equal(t, "check", issues_model.ReviewTypeApprove.Icon())
assert.Equal(t, "diff", issues_model.ReviewTypeReject.Icon())
assert.Equal(t, "comment", issues_model.ReviewTypeComment.Icon())
assert.Equal(t, "comment", issues_model.ReviewTypeUnknown.Icon())
assert.Equal(t, "dot-fill", issues_model.ReviewTypeRequest.Icon())
assert.Equal(t, "comment", issues_model.ReviewType(6).Icon())
}
func TestFindReviews(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
reviews, err := FindReviews(db.DefaultContext, FindReviewOptions{
Type: ReviewTypeApprove,
reviews, err := issues_model.FindReviews(db.DefaultContext, issues_model.FindReviewOptions{
Type: issues_model.ReviewTypeApprove,
IssueID: 2,
ReviewerID: 1,
})
@ -73,66 +74,66 @@ func TestFindReviews(t *testing.T) {
func TestGetCurrentReview(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 2}).(*Issue)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}).(*issues_model.Issue)
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
review, err := GetCurrentReview(db.DefaultContext, user, issue)
review, err := issues_model.GetCurrentReview(db.DefaultContext, user, issue)
assert.NoError(t, err)
assert.NotNil(t, review)
assert.Equal(t, ReviewTypePending, review.Type)
assert.Equal(t, issues_model.ReviewTypePending, review.Type)
assert.Equal(t, "Pending Review", review.Content)
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 7}).(*user_model.User)
review2, err := GetCurrentReview(db.DefaultContext, user2, issue)
review2, err := issues_model.GetCurrentReview(db.DefaultContext, user2, issue)
assert.Error(t, err)
assert.True(t, IsErrReviewNotExist(err))
assert.True(t, issues_model.IsErrReviewNotExist(err))
assert.Nil(t, review2)
}
func TestCreateReview(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 2}).(*Issue)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}).(*issues_model.Issue)
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
review, err := CreateReview(db.DefaultContext, CreateReviewOptions{
review, err := issues_model.CreateReview(db.DefaultContext, issues_model.CreateReviewOptions{
Content: "New Review",
Type: ReviewTypePending,
Type: issues_model.ReviewTypePending,
Issue: issue,
Reviewer: user,
})
assert.NoError(t, err)
assert.Equal(t, "New Review", review.Content)
unittest.AssertExistsAndLoadBean(t, &Review{Content: "New Review"})
unittest.AssertExistsAndLoadBean(t, &issues_model.Review{Content: "New Review"})
}
func TestGetReviewersByIssueID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 3}).(*Issue)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3}).(*issues_model.Issue)
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User)
expectedReviews := []*Review{}
expectedReviews := []*issues_model.Review{}
expectedReviews = append(expectedReviews,
&Review{
&issues_model.Review{
Reviewer: user3,
Type: ReviewTypeReject,
Type: issues_model.ReviewTypeReject,
UpdatedUnix: 946684812,
},
&Review{
&issues_model.Review{
Reviewer: user4,
Type: ReviewTypeApprove,
Type: issues_model.ReviewTypeApprove,
UpdatedUnix: 946684813,
},
&Review{
&issues_model.Review{
Reviewer: user2,
Type: ReviewTypeReject,
Type: issues_model.ReviewTypeReject,
UpdatedUnix: 946684814,
})
allReviews, err := GetReviewersByIssueID(issue.ID)
allReviews, err := issues_model.GetReviewersByIssueID(issue.ID)
for _, reviewer := range allReviews {
assert.NoError(t, reviewer.LoadReviewer())
}
@ -149,53 +150,53 @@ func TestGetReviewersByIssueID(t *testing.T) {
func TestDismissReview(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
rejectReviewExample := unittest.AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review)
requestReviewExample := unittest.AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review)
approveReviewExample := unittest.AssertExistsAndLoadBean(t, &Review{ID: 8}).(*Review)
rejectReviewExample := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9}).(*issues_model.Review)
requestReviewExample := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11}).(*issues_model.Review)
approveReviewExample := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 8}).(*issues_model.Review)
assert.False(t, rejectReviewExample.Dismissed)
assert.False(t, requestReviewExample.Dismissed)
assert.False(t, approveReviewExample.Dismissed)
assert.NoError(t, DismissReview(rejectReviewExample, true))
rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review)
requestReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review)
assert.NoError(t, issues_model.DismissReview(rejectReviewExample, true))
rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9}).(*issues_model.Review)
requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11}).(*issues_model.Review)
assert.True(t, rejectReviewExample.Dismissed)
assert.False(t, requestReviewExample.Dismissed)
assert.NoError(t, DismissReview(requestReviewExample, true))
rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review)
requestReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review)
assert.NoError(t, issues_model.DismissReview(requestReviewExample, true))
rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9}).(*issues_model.Review)
requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11}).(*issues_model.Review)
assert.True(t, rejectReviewExample.Dismissed)
assert.False(t, requestReviewExample.Dismissed)
assert.False(t, approveReviewExample.Dismissed)
assert.NoError(t, DismissReview(requestReviewExample, true))
rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review)
requestReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review)
assert.NoError(t, issues_model.DismissReview(requestReviewExample, true))
rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9}).(*issues_model.Review)
requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11}).(*issues_model.Review)
assert.True(t, rejectReviewExample.Dismissed)
assert.False(t, requestReviewExample.Dismissed)
assert.False(t, approveReviewExample.Dismissed)
assert.NoError(t, DismissReview(requestReviewExample, false))
rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review)
requestReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review)
assert.NoError(t, issues_model.DismissReview(requestReviewExample, false))
rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9}).(*issues_model.Review)
requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11}).(*issues_model.Review)
assert.True(t, rejectReviewExample.Dismissed)
assert.False(t, requestReviewExample.Dismissed)
assert.False(t, approveReviewExample.Dismissed)
assert.NoError(t, DismissReview(requestReviewExample, false))
rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review)
requestReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review)
assert.NoError(t, issues_model.DismissReview(requestReviewExample, false))
rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9}).(*issues_model.Review)
requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11}).(*issues_model.Review)
assert.True(t, rejectReviewExample.Dismissed)
assert.False(t, requestReviewExample.Dismissed)
assert.False(t, approveReviewExample.Dismissed)
assert.NoError(t, DismissReview(rejectReviewExample, false))
assert.NoError(t, issues_model.DismissReview(rejectReviewExample, false))
assert.False(t, rejectReviewExample.Dismissed)
assert.False(t, requestReviewExample.Dismissed)
assert.False(t, approveReviewExample.Dismissed)
assert.NoError(t, DismissReview(approveReviewExample, true))
assert.NoError(t, issues_model.DismissReview(approveReviewExample, true))
assert.False(t, rejectReviewExample.Dismissed)
assert.False(t, requestReviewExample.Dismissed)
assert.True(t, approveReviewExample.Dismissed)

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues
import (
"context"
@ -215,7 +215,7 @@ func CreateIssueStopwatch(ctx context.Context, user *user_model.User, issue *Iss
return err
}
if exists {
issue, err := getIssueByID(ctx, sw.IssueID)
issue, err := GetIssueByID(ctx, sw.IssueID)
if err != nil {
return err
}

View file

@ -0,0 +1,79 @@
// Copyright 2020 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 issues_test
import (
"testing"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/timeutil"
"github.com/stretchr/testify/assert"
)
func TestCancelStopwatch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
user1, err := user_model.GetUserByID(1)
assert.NoError(t, err)
issue1, err := issues_model.GetIssueByID(db.DefaultContext, 1)
assert.NoError(t, err)
issue2, err := issues_model.GetIssueByID(db.DefaultContext, 2)
assert.NoError(t, err)
err = issues_model.CancelStopwatch(user1, issue1)
assert.NoError(t, err)
unittest.AssertNotExistsBean(t, &issues_model.Stopwatch{UserID: user1.ID, IssueID: issue1.ID})
_ = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{Type: issues_model.CommentTypeCancelTracking, PosterID: user1.ID, IssueID: issue1.ID})
assert.Nil(t, issues_model.CancelStopwatch(user1, issue2))
}
func TestStopwatchExists(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
assert.True(t, issues_model.StopwatchExists(1, 1))
assert.False(t, issues_model.StopwatchExists(1, 2))
}
func TestHasUserStopwatch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
exists, sw, err := issues_model.HasUserStopwatch(db.DefaultContext, 1)
assert.NoError(t, err)
assert.True(t, exists)
assert.Equal(t, int64(1), sw.ID)
exists, _, err = issues_model.HasUserStopwatch(db.DefaultContext, 3)
assert.NoError(t, err)
assert.False(t, exists)
}
func TestCreateOrStopIssueStopwatch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
user2, err := user_model.GetUserByID(2)
assert.NoError(t, err)
user3, err := user_model.GetUserByID(3)
assert.NoError(t, err)
issue1, err := issues_model.GetIssueByID(db.DefaultContext, 1)
assert.NoError(t, err)
issue2, err := issues_model.GetIssueByID(db.DefaultContext, 2)
assert.NoError(t, err)
assert.NoError(t, issues_model.CreateOrStopIssueStopwatch(user3, issue1))
sw := unittest.AssertExistsAndLoadBean(t, &issues_model.Stopwatch{UserID: 3, IssueID: 1}).(*issues_model.Stopwatch)
assert.LessOrEqual(t, sw.CreatedUnix, timeutil.TimeStampNow())
assert.NoError(t, issues_model.CreateOrStopIssueStopwatch(user2, issue2))
unittest.AssertNotExistsBean(t, &issues_model.Stopwatch{UserID: 2, IssueID: 2})
unittest.AssertExistsAndLoadBean(t, &issues_model.TrackedTime{UserID: 2, IssueID: 2})
}

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues
import (
"context"
@ -48,7 +48,7 @@ func (t *TrackedTime) LoadAttributes() (err error) {
func (t *TrackedTime) loadAttributes(ctx context.Context) (err error) {
if t.Issue == nil {
t.Issue, err = getIssueByID(ctx, t.IssueID)
t.Issue, err = GetIssueByID(ctx, t.IssueID)
if err != nil {
return
}

View file

@ -2,13 +2,14 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package issues_test
import (
"testing"
"time"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
@ -21,20 +22,20 @@ func TestAddTime(t *testing.T) {
user3, err := user_model.GetUserByID(3)
assert.NoError(t, err)
issue1, err := GetIssueByID(1)
issue1, err := issues_model.GetIssueByID(db.DefaultContext, 1)
assert.NoError(t, err)
// 3661 = 1h 1min 1s
trackedTime, err := AddTime(user3, issue1, 3661, time.Now())
trackedTime, err := issues_model.AddTime(user3, issue1, 3661, time.Now())
assert.NoError(t, err)
assert.Equal(t, int64(3), trackedTime.UserID)
assert.Equal(t, int64(1), trackedTime.IssueID)
assert.Equal(t, int64(3661), trackedTime.Time)
tt := unittest.AssertExistsAndLoadBean(t, &TrackedTime{UserID: 3, IssueID: 1}).(*TrackedTime)
tt := unittest.AssertExistsAndLoadBean(t, &issues_model.TrackedTime{UserID: 3, IssueID: 1}).(*issues_model.TrackedTime)
assert.Equal(t, int64(3661), tt.Time)
comment := unittest.AssertExistsAndLoadBean(t, &Comment{Type: CommentTypeAddTimeManual, PosterID: 3, IssueID: 1}).(*Comment)
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{Type: issues_model.CommentTypeAddTimeManual, PosterID: 3, IssueID: 1}).(*issues_model.Comment)
assert.Equal(t, comment.Content, "1 hour 1 minute")
}
@ -42,39 +43,39 @@ func TestGetTrackedTimes(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
// by Issue
times, err := GetTrackedTimes(db.DefaultContext, &FindTrackedTimesOptions{IssueID: 1})
times, err := issues_model.GetTrackedTimes(db.DefaultContext, &issues_model.FindTrackedTimesOptions{IssueID: 1})
assert.NoError(t, err)
assert.Len(t, times, 1)
assert.Equal(t, int64(400), times[0].Time)
times, err = GetTrackedTimes(db.DefaultContext, &FindTrackedTimesOptions{IssueID: -1})
times, err = issues_model.GetTrackedTimes(db.DefaultContext, &issues_model.FindTrackedTimesOptions{IssueID: -1})
assert.NoError(t, err)
assert.Len(t, times, 0)
// by User
times, err = GetTrackedTimes(db.DefaultContext, &FindTrackedTimesOptions{UserID: 1})
times, err = issues_model.GetTrackedTimes(db.DefaultContext, &issues_model.FindTrackedTimesOptions{UserID: 1})
assert.NoError(t, err)
assert.Len(t, times, 3)
assert.Equal(t, int64(400), times[0].Time)
times, err = GetTrackedTimes(db.DefaultContext, &FindTrackedTimesOptions{UserID: 3})
times, err = issues_model.GetTrackedTimes(db.DefaultContext, &issues_model.FindTrackedTimesOptions{UserID: 3})
assert.NoError(t, err)
assert.Len(t, times, 0)
// by Repo
times, err = GetTrackedTimes(db.DefaultContext, &FindTrackedTimesOptions{RepositoryID: 2})
times, err = issues_model.GetTrackedTimes(db.DefaultContext, &issues_model.FindTrackedTimesOptions{RepositoryID: 2})
assert.NoError(t, err)
assert.Len(t, times, 3)
assert.Equal(t, int64(1), times[0].Time)
issue, err := GetIssueByID(times[0].IssueID)
issue, err := issues_model.GetIssueByID(db.DefaultContext, times[0].IssueID)
assert.NoError(t, err)
assert.Equal(t, issue.RepoID, int64(2))
times, err = GetTrackedTimes(db.DefaultContext, &FindTrackedTimesOptions{RepositoryID: 1})
times, err = issues_model.GetTrackedTimes(db.DefaultContext, &issues_model.FindTrackedTimesOptions{RepositoryID: 1})
assert.NoError(t, err)
assert.Len(t, times, 5)
times, err = GetTrackedTimes(db.DefaultContext, &FindTrackedTimesOptions{RepositoryID: 10})
times, err = issues_model.GetTrackedTimes(db.DefaultContext, &issues_model.FindTrackedTimesOptions{RepositoryID: 10})
assert.NoError(t, err)
assert.Len(t, times, 0)
}
@ -82,7 +83,7 @@ func TestGetTrackedTimes(t *testing.T) {
func TestTotalTimes(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
total, err := TotalTimes(&FindTrackedTimesOptions{IssueID: 1})
total, err := issues_model.TotalTimes(&issues_model.FindTrackedTimesOptions{IssueID: 1})
assert.NoError(t, err)
assert.Len(t, total, 1)
for user, time := range total {
@ -90,7 +91,7 @@ func TestTotalTimes(t *testing.T) {
assert.Equal(t, "6 minutes 40 seconds", time)
}
total, err = TotalTimes(&FindTrackedTimesOptions{IssueID: 2})
total, err = issues_model.TotalTimes(&issues_model.FindTrackedTimesOptions{IssueID: 2})
assert.NoError(t, err)
assert.Len(t, total, 2)
for user, time := range total {
@ -103,7 +104,7 @@ func TestTotalTimes(t *testing.T) {
}
}
total, err = TotalTimes(&FindTrackedTimesOptions{IssueID: 5})
total, err = issues_model.TotalTimes(&issues_model.FindTrackedTimesOptions{IssueID: 5})
assert.NoError(t, err)
assert.Len(t, total, 1)
for user, time := range total {
@ -111,7 +112,7 @@ func TestTotalTimes(t *testing.T) {
assert.Equal(t, "1 second", time)
}
total, err = TotalTimes(&FindTrackedTimesOptions{IssueID: 4})
total, err = issues_model.TotalTimes(&issues_model.FindTrackedTimesOptions{IssueID: 4})
assert.NoError(t, err)
assert.Len(t, total, 2)
}

View file

@ -7,7 +7,6 @@ package models
import (
"testing"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
@ -28,10 +27,6 @@ func TestFixturesAreConsistent(t *testing.T) {
unittest.CheckConsistencyFor(t,
&user_model.User{},
&repo_model.Repository{},
&Issue{},
&PullRequest{},
&issues_model.Milestone{},
&Label{},
&organization.Team{},
&Action{})
}

View file

@ -10,8 +10,6 @@ import (
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/modules/structs"
"xorm.io/builder"
)
// InsertMilestones creates milestones of repository.
@ -41,7 +39,7 @@ func InsertMilestones(ms ...*issues_model.Milestone) (err error) {
}
// InsertIssues insert issues to database
func InsertIssues(issues ...*Issue) error {
func InsertIssues(issues ...*issues_model.Issue) error {
ctx, committer, err := db.TxContext()
if err != nil {
return err
@ -56,14 +54,14 @@ func InsertIssues(issues ...*Issue) error {
return committer.Commit()
}
func insertIssue(ctx context.Context, issue *Issue) error {
func insertIssue(ctx context.Context, issue *issues_model.Issue) error {
sess := db.GetEngine(ctx)
if _, err := sess.NoAutoTime().Insert(issue); err != nil {
return err
}
issueLabels := make([]IssueLabel, 0, len(issue.Labels))
issueLabels := make([]issues_model.IssueLabel, 0, len(issue.Labels))
for _, label := range issue.Labels {
issueLabels = append(issueLabels, IssueLabel{
issueLabels = append(issueLabels, issues_model.IssueLabel{
IssueID: issue.ID,
LabelID: label.ID,
})
@ -95,7 +93,7 @@ func insertIssue(ctx context.Context, issue *Issue) error {
}
// InsertIssueComments inserts many comments of issues.
func InsertIssueComments(comments []*Comment) error {
func InsertIssueComments(comments []*issues_model.Comment) error {
if len(comments) == 0 {
return nil
}
@ -127,7 +125,8 @@ func InsertIssueComments(comments []*Comment) error {
}
for issueID := range issueIDs {
if _, err := db.Exec(ctx, "UPDATE issue set num_comments = (SELECT count(*) FROM comment WHERE issue_id = ? AND `type`=?) WHERE id = ?", issueID, CommentTypeComment, issueID); err != nil {
if _, err := db.Exec(ctx, "UPDATE issue set num_comments = (SELECT count(*) FROM comment WHERE issue_id = ? AND `type`=?) WHERE id = ?",
issueID, issues_model.CommentTypeComment, issueID); err != nil {
return err
}
}
@ -135,7 +134,7 @@ func InsertIssueComments(comments []*Comment) error {
}
// InsertPullRequests inserted pull requests
func InsertPullRequests(prs ...*PullRequest) error {
func InsertPullRequests(prs ...*issues_model.PullRequest) error {
ctx, committer, err := db.TxContext()
if err != nil {
return err
@ -182,37 +181,13 @@ func InsertReleases(rels ...*Release) error {
return committer.Commit()
}
func migratedIssueCond(tp structs.GitServiceType) builder.Cond {
return builder.In("issue_id",
builder.Select("issue.id").
From("issue").
InnerJoin("repository", "issue.repo_id = repository.id").
Where(builder.Eq{
"repository.original_service_type": tp,
}),
)
}
// UpdateReviewsMigrationsByType updates reviews' migrations information via given git service type and original id and poster id
func UpdateReviewsMigrationsByType(tp structs.GitServiceType, originalAuthorID string, posterID int64) error {
_, err := db.GetEngine(db.DefaultContext).Table("review").
Where("original_author_id = ?", originalAuthorID).
And(migratedIssueCond(tp)).
Update(map[string]interface{}{
"reviewer_id": posterID,
"original_author": "",
"original_author_id": 0,
})
return err
}
// UpdateMigrationsByType updates all migrated repositories' posterid from gitServiceType to replace originalAuthorID to posterID
func UpdateMigrationsByType(tp structs.GitServiceType, externalUserID string, userID int64) error {
if err := UpdateIssuesMigrationsByType(tp, externalUserID, userID); err != nil {
if err := issues_model.UpdateIssuesMigrationsByType(tp, externalUserID, userID); err != nil {
return err
}
if err := UpdateCommentsMigrationsByType(tp, externalUserID, userID); err != nil {
if err := issues_model.UpdateCommentsMigrationsByType(tp, externalUserID, userID); err != nil {
return err
}
@ -220,8 +195,8 @@ func UpdateMigrationsByType(tp structs.GitServiceType, externalUserID string, us
return err
}
if err := UpdateReactionsMigrationsByType(tp, externalUserID, userID); err != nil {
if err := issues_model.UpdateReactionsMigrationsByType(tp, externalUserID, userID); err != nil {
return err
}
return UpdateReviewsMigrationsByType(tp, externalUserID, userID)
return issues_model.UpdateReviewsMigrationsByType(tp, externalUserID, userID)
}

View file

@ -41,7 +41,7 @@ func assertCreateIssues(t *testing.T, isPull bool) {
reponame := "repo1"
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: reponame}).(*repo_model.Repository)
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
label := unittest.AssertExistsAndLoadBean(t, &Label{ID: 1}).(*Label)
label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label)
milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}).(*issues_model.Milestone)
assert.EqualValues(t, milestone.ID, 1)
reaction := &issues_model.Reaction{
@ -51,7 +51,7 @@ func assertCreateIssues(t *testing.T, isPull bool) {
foreignIndex := int64(12345)
title := "issuetitle1"
is := &Issue{
is := &issues_model.Issue{
RepoID: repo.ID,
MilestoneID: milestone.ID,
Repo: repo,
@ -61,7 +61,7 @@ func assertCreateIssues(t *testing.T, isPull bool) {
PosterID: owner.ID,
Poster: owner,
IsClosed: true,
Labels: []*Label{label},
Labels: []*issues_model.Label{label},
Reactions: []*issues_model.Reaction{reaction},
ForeignReference: &foreignreference.ForeignReference{
ForeignIndex: strconv.FormatInt(foreignIndex, 10),
@ -72,9 +72,9 @@ func assertCreateIssues(t *testing.T, isPull bool) {
err := InsertIssues(is)
assert.NoError(t, err)
i := unittest.AssertExistsAndLoadBean(t, &Issue{Title: title}).(*Issue)
i := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{Title: title}).(*issues_model.Issue)
assert.Nil(t, i.ForeignReference)
err = i.LoadAttributes()
err = i.LoadAttributes(db.DefaultContext)
assert.NoError(t, err)
assert.EqualValues(t, strconv.FormatInt(foreignIndex, 10), i.ForeignReference.ForeignIndex)
unittest.AssertExistsAndLoadBean(t, &issues_model.Reaction{Type: "heart", UserID: owner.ID, IssueID: i.ID})
@ -90,7 +90,7 @@ func TestMigrate_CreateIssuesIsPullTrue(t *testing.T) {
func TestMigrate_InsertIssueComments(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue)
_ = issue.LoadRepo(db.DefaultContext)
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}).(*user_model.User)
reaction := &issues_model.Reaction{
@ -98,7 +98,7 @@ func TestMigrate_InsertIssueComments(t *testing.T) {
UserID: owner.ID,
}
comment := &Comment{
comment := &issues_model.Comment{
PosterID: owner.ID,
Poster: owner,
IssueID: issue.ID,
@ -106,13 +106,13 @@ func TestMigrate_InsertIssueComments(t *testing.T) {
Reactions: []*issues_model.Reaction{reaction},
}
err := InsertIssueComments([]*Comment{comment})
err := InsertIssueComments([]*issues_model.Comment{comment})
assert.NoError(t, err)
issueModified := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue)
issueModified := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue)
assert.EqualValues(t, issue.NumComments+1, issueModified.NumComments)
unittest.CheckConsistencyFor(t, &Issue{})
unittest.CheckConsistencyFor(t, &issues_model.Issue{})
}
func TestMigrate_InsertPullRequests(t *testing.T) {
@ -121,7 +121,7 @@ func TestMigrate_InsertPullRequests(t *testing.T) {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: reponame}).(*repo_model.Repository)
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
i := &Issue{
i := &issues_model.Issue{
RepoID: repo.ID,
Repo: repo,
Title: "title1",
@ -131,16 +131,16 @@ func TestMigrate_InsertPullRequests(t *testing.T) {
Poster: owner,
}
p := &PullRequest{
p := &issues_model.PullRequest{
Issue: i,
}
err := InsertPullRequests(p)
assert.NoError(t, err)
_ = unittest.AssertExistsAndLoadBean(t, &PullRequest{IssueID: i.ID}).(*PullRequest)
_ = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{IssueID: i.ID}).(*issues_model.PullRequest)
unittest.CheckConsistencyFor(t, &Issue{}, &PullRequest{})
unittest.CheckConsistencyFor(t, &issues_model.Issue{}, &issues_model.PullRequest{})
}
func TestMigrate_InsertReleases(t *testing.T) {

View file

@ -419,7 +419,7 @@ func EnsureUpToDate(x *xorm.Engine) error {
}
if currentDB < 0 {
return fmt.Errorf("Database has not been initialised")
return fmt.Errorf("Database has not been initialized")
}
if minDBVersion > currentDB {
@ -953,7 +953,7 @@ func dropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin
return nil
}
// modifyColumn will modify column's type or other propertity. SQLITE is not supported
// modifyColumn will modify column's type or other property. SQLITE is not supported
func modifyColumn(x *xorm.Engine, tableName string, col *schemas.Column) error {
var indexes map[string]*schemas.Index
var err error

View file

@ -131,7 +131,7 @@ func addBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error {
Authorize int
}
// getUserRepoPermission static function based on models.IsOfficialReviewer at 5d78792385
// getUserRepoPermission static function based on issues_model.IsOfficialReviewer at 5d78792385
getUserRepoPermission := func(sess *xorm.Session, repo *Repository, user *User) (Permission, error) {
var perm Permission

View file

@ -11,6 +11,7 @@ import (
"strconv"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
@ -66,9 +67,9 @@ type Notification struct {
UpdatedBy int64 `xorm:"INDEX NOT NULL"`
Issue *Issue `xorm:"-"`
Issue *issues_model.Issue `xorm:"-"`
Repository *repo_model.Repository `xorm:"-"`
Comment *Comment `xorm:"-"`
Comment *issues_model.Comment `xorm:"-"`
User *user_model.User `xorm:"-"`
CreatedUnix timeutil.TimeStamp `xorm:"created INDEX NOT NULL"`
@ -204,7 +205,7 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n
return err
}
issue, err := getIssueByID(ctx, issueID)
issue, err := issues_model.GetIssueByID(ctx, issueID)
if err != nil {
return err
}
@ -214,14 +215,14 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n
toNotify[receiverID] = struct{}{}
} else {
toNotify = make(map[int64]struct{}, 32)
issueWatches, err := GetIssueWatchersIDs(ctx, issueID, true)
issueWatches, err := issues_model.GetIssueWatchersIDs(ctx, issueID, true)
if err != nil {
return err
}
for _, id := range issueWatches {
toNotify[id] = struct{}{}
}
if !(issue.IsPull && HasWorkInProgressPrefix(issue.Title)) {
if !(issue.IsPull && issues_model.HasWorkInProgressPrefix(issue.Title)) {
repoWatches, err := repo_model.GetRepoWatchersIDs(ctx, issue.RepoID)
if err != nil {
return err
@ -230,7 +231,7 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n
toNotify[id] = struct{}{}
}
}
issueParticipants, err := issue.getParticipantIDsByIssue(ctx)
issueParticipants, err := issue.GetParticipantIDsByIssue(ctx)
if err != nil {
return err
}
@ -241,7 +242,7 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n
// dont notify user who cause notification
delete(toNotify, notificationAuthorID)
// explicit unwatch on issue
issueUnWatches, err := GetIssueWatchersIDs(ctx, issueID, false)
issueUnWatches, err := issues_model.GetIssueWatchersIDs(ctx, issueID, false)
if err != nil {
return err
}
@ -303,7 +304,7 @@ func notificationExists(notifications []*Notification, issueID, userID int64) bo
return false
}
func createIssueNotification(ctx context.Context, userID int64, issue *Issue, commentID, updatedByID int64) error {
func createIssueNotification(ctx context.Context, userID int64, issue *issues_model.Issue, commentID, updatedByID int64) error {
notification := &Notification{
UserID: userID,
RepoID: issue.RepoID,
@ -415,21 +416,21 @@ func (n *Notification) loadRepo(ctx context.Context) (err error) {
func (n *Notification) loadIssue(ctx context.Context) (err error) {
if n.Issue == nil && n.IssueID != 0 {
n.Issue, err = getIssueByID(ctx, n.IssueID)
n.Issue, err = issues_model.GetIssueByID(ctx, n.IssueID)
if err != nil {
return fmt.Errorf("getIssueByID [%d]: %v", n.IssueID, err)
}
return n.Issue.loadAttributes(ctx)
return n.Issue.LoadAttributes(ctx)
}
return nil
}
func (n *Notification) loadComment(ctx context.Context) (err error) {
if n.Comment == nil && n.CommentID != 0 {
n.Comment, err = GetCommentByID(ctx, n.CommentID)
n.Comment, err = issues_model.GetCommentByID(ctx, n.CommentID)
if err != nil {
if IsErrCommentNotExist(err) {
return ErrCommentNotExist{
if issues_model.IsErrCommentNotExist(err) {
return issues_model.ErrCommentNotExist{
ID: n.CommentID,
IssueID: n.IssueID,
}
@ -456,7 +457,7 @@ func (n *Notification) GetRepo() (*repo_model.Repository, error) {
}
// GetIssue returns the issue of the notification
func (n *Notification) GetIssue() (*Issue, error) {
func (n *Notification) GetIssue() (*issues_model.Issue, error) {
return n.Issue, n.loadIssue(db.DefaultContext)
}
@ -489,7 +490,7 @@ func (nl NotificationList) LoadAttributes() error {
var err error
for i := 0; i < len(nl); i++ {
err = nl[i].LoadAttributes()
if err != nil && !IsErrCommentNotExist(err) {
if err != nil && !issues_model.IsErrCommentNotExist(err) {
return err
}
}
@ -519,7 +520,7 @@ func (nl NotificationList) LoadRepos() (repo_model.RepositoryList, []int, error)
repos := make(map[int64]*repo_model.Repository, len(repoIDs))
left := len(repoIDs)
for left > 0 {
limit := defaultMaxInSize
limit := db.DefaultMaxInSize
if left < limit {
limit = left
}
@ -592,22 +593,22 @@ func (nl NotificationList) LoadIssues() ([]int, error) {
}
issueIDs := nl.getPendingIssueIDs()
issues := make(map[int64]*Issue, len(issueIDs))
issues := make(map[int64]*issues_model.Issue, len(issueIDs))
left := len(issueIDs)
for left > 0 {
limit := defaultMaxInSize
limit := db.DefaultMaxInSize
if left < limit {
limit = left
}
rows, err := db.GetEngine(db.DefaultContext).
In("id", issueIDs[:limit]).
Rows(new(Issue))
Rows(new(issues_model.Issue))
if err != nil {
return nil, err
}
for rows.Next() {
var issue Issue
var issue issues_model.Issue
err = rows.Scan(&issue)
if err != nil {
rows.Close()
@ -678,22 +679,22 @@ func (nl NotificationList) LoadComments() ([]int, error) {
}
commentIDs := nl.getPendingCommentIDs()
comments := make(map[int64]*Comment, len(commentIDs))
comments := make(map[int64]*issues_model.Comment, len(commentIDs))
left := len(commentIDs)
for left > 0 {
limit := defaultMaxInSize
limit := db.DefaultMaxInSize
if left < limit {
limit = left
}
rows, err := db.GetEngine(db.DefaultContext).
In("id", commentIDs[:limit]).
Rows(new(Comment))
Rows(new(issues_model.Comment))
if err != nil {
return nil, err
}
for rows.Next() {
var comment Comment
var comment issues_model.Comment
err = rows.Scan(&comment)
if err != nil {
rows.Close()
@ -747,6 +748,15 @@ func GetUIDsAndNotificationCounts(since, until timeutil.TimeStamp) ([]UserIDCoun
return res, db.GetEngine(db.DefaultContext).SQL(sql, since, until, NotificationStatusUnread).Find(&res)
}
// SetIssueReadBy sets issue to be read by given user.
func SetIssueReadBy(ctx context.Context, issueID, userID int64) error {
if err := issues_model.UpdateIssueUserByRead(userID, issueID); err != nil {
return err
}
return setIssueNotificationStatusReadIfUnread(ctx, userID, issueID)
}
func setIssueNotificationStatusReadIfUnread(ctx context.Context, userID, issueID int64) error {
notification, err := getIssueNotification(ctx, userID, issueID)
// ignore if not exists

View file

@ -8,6 +8,7 @@ import (
"testing"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
@ -16,14 +17,14 @@ import (
func TestCreateOrUpdateIssueNotifications(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue)
assert.NoError(t, CreateOrUpdateIssueNotifications(issue.ID, 0, 2, 0))
// User 9 is inactive, thus notifications for user 1 and 4 are created
notf := unittest.AssertExistsAndLoadBean(t, &Notification{UserID: 1, IssueID: issue.ID}).(*Notification)
assert.Equal(t, NotificationStatusUnread, notf.Status)
unittest.CheckConsistencyFor(t, &Issue{ID: issue.ID})
unittest.CheckConsistencyFor(t, &issues_model.Issue{ID: issue.ID})
notf = unittest.AssertExistsAndLoadBean(t, &Notification{UserID: 4, IssueID: issue.ID}).(*Notification)
assert.Equal(t, NotificationStatusUnread, notf.Status)

View file

@ -13,6 +13,7 @@ import (
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
@ -153,7 +154,7 @@ func removeAllRepositories(ctx context.Context, t *organization.Team) (err error
}
// Remove all IssueWatches a user has subscribed to in the repositories
if err = removeIssueWatchersByRepoID(ctx, user.ID, repo.ID); err != nil {
if err = issues_model.RemoveIssueWatchersByRepoID(ctx, user.ID, repo.ID); err != nil {
return err
}
}
@ -216,7 +217,7 @@ func removeRepository(ctx context.Context, t *organization.Team, repo *repo_mode
}
// Remove all IssueWatches a user has subscribed to in the repositories
if err := removeIssueWatchersByRepoID(ctx, teamUser.UID, repo.ID); err != nil {
if err := issues_model.RemoveIssueWatchersByRepoID(ctx, teamUser.UID, repo.ID); err != nil {
return err
}
}

View file

@ -281,7 +281,7 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
&access_model.Access{RepoID: repo.ID},
&Action{RepoID: repo.ID},
&repo_model.Collaboration{RepoID: repoID},
&Comment{RefRepoID: repoID},
&issues_model.Comment{RefRepoID: repoID},
&git_model.CommitStatus{RepoID: repoID},
&git_model.DeletedBranch{RepoID: repoID},
&webhook.HookTask{RepoID: repoID},
@ -306,18 +306,18 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
}
// Delete Labels and related objects
if err := deleteLabelsByRepoID(ctx, repoID); err != nil {
if err := issues_model.DeleteLabelsByRepoID(ctx, repoID); err != nil {
return err
}
// Delete Pulls and related objects
if err := deletePullsByBaseRepoID(ctx, repoID); err != nil {
if err := issues_model.DeletePullsByBaseRepoID(ctx, repoID); err != nil {
return err
}
// Delete Issues and related objects
var attachmentPaths []string
if attachmentPaths, err = deleteIssuesByRepoID(ctx, repoID); err != nil {
if attachmentPaths, err = issues_model.DeleteIssuesByRepoID(ctx, repoID); err != nil {
return err
}
@ -576,16 +576,11 @@ func repoStatsCorrectNum(ctx context.Context, id int64, isPull bool, field strin
}
func repoStatsCorrectNumClosedIssues(ctx context.Context, id int64) error {
return repoStatsCorrectNumClosed(ctx, id, false, "num_closed_issues")
return repo_model.StatsCorrectNumClosed(ctx, id, false, "num_closed_issues")
}
func repoStatsCorrectNumClosedPulls(ctx context.Context, id int64) error {
return repoStatsCorrectNumClosed(ctx, id, true, "num_closed_pulls")
}
func repoStatsCorrectNumClosed(ctx context.Context, id int64, isPull bool, field string) error {
_, err := db.GetEngine(ctx).Exec("UPDATE `repository` SET "+field+"=(SELECT COUNT(*) FROM `issue` WHERE repo_id=? AND is_closed=? AND is_pull=?) WHERE id=?", id, true, isPull, id)
return err
return repo_model.StatsCorrectNumClosed(ctx, id, true, "num_closed_pulls")
}
func statsQuery(args ...interface{}) func(context.Context) ([]map[string][]byte, error) {
@ -687,12 +682,11 @@ func CheckRepoStats(ctx context.Context) error {
continue
}
rawResult, err := e.Query("SELECT COUNT(*) FROM `repository` WHERE fork_id=?", repo.ID)
_, err = e.SQL("SELECT COUNT(*) FROM `repository` WHERE fork_id=?", repo.ID).Get(&repo.NumForks)
if err != nil {
log.Error("Select count of forks[%d]: %v", repo.ID, err)
continue
}
repo.NumForks = int(parseCountResult(rawResult))
if _, err = e.ID(repo.ID).Cols("num_forks").Update(repo); err != nil {
log.Error("UpdateRepository[%d]: %v", id, err)

View file

@ -25,6 +25,22 @@ import (
"code.gitea.io/gitea/modules/util"
)
// ErrUserDoesNotHaveAccessToRepo represets an error where the user doesn't has access to a given repo.
type ErrUserDoesNotHaveAccessToRepo struct {
UserID int64
RepoName string
}
// IsErrUserDoesNotHaveAccessToRepo checks if an error is a ErrRepoFileAlreadyExists.
func IsErrUserDoesNotHaveAccessToRepo(err error) bool {
_, ok := err.(ErrUserDoesNotHaveAccessToRepo)
return ok
}
func (err ErrUserDoesNotHaveAccessToRepo) Error() string {
return fmt.Sprintf("user doesn't have access to repo [user_id: %d, repo_name: %s]", err.UserID, err.RepoName)
}
var (
reservedRepoNames = []string{".", "..", "-"}
reservedRepoPatterns = []string{"*.git", "*.wiki", "*.rss", "*.atom"}
@ -743,3 +759,34 @@ func CountRepositories(ctx context.Context, opts CountRepositoryOptions) (int64,
}
return count, nil
}
// StatsCorrectNumClosed update repository's issue related numbers
func StatsCorrectNumClosed(ctx context.Context, id int64, isPull bool, field string) error {
_, err := db.Exec(ctx, "UPDATE `repository` SET "+field+"=(SELECT COUNT(*) FROM `issue` WHERE repo_id=? AND is_closed=? AND is_pull=?) WHERE id=?", id, true, isPull, id)
return err
}
// UpdateRepoIssueNumbers update repository issue numbers
func UpdateRepoIssueNumbers(ctx context.Context, repoID int64, isPull, isClosed bool) error {
e := db.GetEngine(ctx)
if isPull {
if _, err := e.ID(repoID).Decr("num_pulls").Update(new(Repository)); err != nil {
return err
}
if isClosed {
if _, err := e.ID(repoID).Decr("num_closed_pulls").Update(new(Repository)); err != nil {
return err
}
}
} else {
if _, err := e.ID(repoID).Decr("num_issues").Update(new(Repository)); err != nil {
return err
}
if isClosed {
if _, err := e.ID(repoID).Decr("num_closed_issues").Update(new(Repository)); err != nil {
return err
}
}
}
return nil
}

View file

@ -11,6 +11,7 @@ import (
"time"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
@ -29,15 +30,15 @@ type ActivityAuthorData struct {
// ActivityStats represets issue and pull request information.
type ActivityStats struct {
OpenedPRs PullRequestList
OpenedPRs issues_model.PullRequestList
OpenedPRAuthorCount int64
MergedPRs PullRequestList
MergedPRs issues_model.PullRequestList
MergedPRAuthorCount int64
OpenedIssues IssueList
OpenedIssues issues_model.IssueList
OpenedIssueAuthorCount int64
ClosedIssues IssueList
ClosedIssues issues_model.IssueList
ClosedIssueAuthorCount int64
UnresolvedIssues IssueList
UnresolvedIssues issues_model.IssueList
PublishedReleases []*Release
PublishedReleaseAuthorCount int64
Code *git.CodeActivityStats
@ -212,7 +213,7 @@ func (stats *ActivityStats) FillPullRequests(repoID int64, fromTime time.Time) e
// Merged pull requests
sess := pullRequestsForActivityStatement(repoID, fromTime, true)
sess.OrderBy("pull_request.merged_unix DESC")
stats.MergedPRs = make(PullRequestList, 0)
stats.MergedPRs = make(issues_model.PullRequestList, 0)
if err = sess.Find(&stats.MergedPRs); err != nil {
return err
}
@ -230,7 +231,7 @@ func (stats *ActivityStats) FillPullRequests(repoID int64, fromTime time.Time) e
// Opened pull requests
sess = pullRequestsForActivityStatement(repoID, fromTime, false)
sess.OrderBy("issue.created_unix ASC")
stats.OpenedPRs = make(PullRequestList, 0)
stats.OpenedPRs = make(issues_model.PullRequestList, 0)
if err = sess.Find(&stats.OpenedPRs); err != nil {
return err
}
@ -271,7 +272,7 @@ func (stats *ActivityStats) FillIssues(repoID int64, fromTime time.Time) error {
// Closed issues
sess := issuesForActivityStatement(repoID, fromTime, true, false)
sess.OrderBy("issue.closed_unix DESC")
stats.ClosedIssues = make(IssueList, 0)
stats.ClosedIssues = make(issues_model.IssueList, 0)
if err = sess.Find(&stats.ClosedIssues); err != nil {
return err
}
@ -286,7 +287,7 @@ func (stats *ActivityStats) FillIssues(repoID int64, fromTime time.Time) error {
// New issues
sess = issuesForActivityStatement(repoID, fromTime, false, false)
sess.OrderBy("issue.created_unix ASC")
stats.OpenedIssues = make(IssueList, 0)
stats.OpenedIssues = make(issues_model.IssueList, 0)
if err = sess.Find(&stats.OpenedIssues); err != nil {
return err
}
@ -312,7 +313,7 @@ func (stats *ActivityStats) FillUnresolvedIssues(repoID int64, fromTime time.Tim
sess.And("issue.is_pull = ?", prs)
}
sess.OrderBy("issue.updated_unix DESC")
stats.UnresolvedIssues = make(IssueList, 0)
stats.UnresolvedIssues = make(issues_model.IssueList, 0)
return sess.Find(&stats.UnresolvedIssues)
}

View file

@ -10,6 +10,7 @@ import (
"fmt"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
@ -101,7 +102,7 @@ func reconsiderRepoIssuesAssignee(ctx context.Context, repo *repo_model.Reposito
if _, err := db.GetEngine(ctx).Where(builder.Eq{"assignee_id": uid}).
In("issue_id", builder.Select("id").From("issue").Where(builder.Eq{"repo_id": repo.ID})).
Delete(&IssueAssignees{}); err != nil {
Delete(&issues_model.IssueAssignees{}); err != nil {
return fmt.Errorf("Could not delete assignee[%d] %v", uid, err)
}
return nil
@ -116,5 +117,5 @@ func reconsiderWatches(ctx context.Context, repo *repo_model.Repository, uid int
}
// Remove all IssueWatches a user has subscribed to in the repository
return removeIssueWatchersByRepoID(ctx, uid, repo.ID)
return issues_model.RemoveIssueWatchersByRepoID(ctx, uid, repo.ID)
}

View file

@ -10,6 +10,7 @@ import (
"os"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
@ -376,7 +377,7 @@ func TransferOwnership(doer *user_model.User, newOwnerName string, repo *repo_mo
INNER JOIN issue ON issue.id = com.issue_id
WHERE
com.type = ? AND issue.repo_id = ? AND ((label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != ?))
) AS il_too)`, CommentTypeLabel, repo.ID, newOwner.ID); err != nil {
) AS il_too)`, issues_model.CommentTypeLabel, repo.ID, newOwner.ID); err != nil {
return fmt.Errorf("Unable to remove old org label comments: %v", err)
}
}

View file

@ -97,7 +97,7 @@ func GetStatistic() (stats Statistic) {
stats.Counter.Issue = stats.Counter.IssueClosed + stats.Counter.IssueOpen
stats.Counter.Comment, _ = e.Count(new(Comment))
stats.Counter.Comment, _ = e.Count(new(issues_model.Comment))
stats.Counter.Oauth = 0
stats.Counter.Follow, _ = e.Count(new(user_model.Follow))
stats.Counter.Mirror, _ = e.Count(new(repo_model.Mirror))
@ -105,7 +105,7 @@ func GetStatistic() (stats Statistic) {
stats.Counter.AuthSource = auth.CountSources()
stats.Counter.Webhook, _ = e.Count(new(webhook.Webhook))
stats.Counter.Milestone, _ = e.Count(new(issues_model.Milestone))
stats.Counter.Label, _ = e.Count(new(Label))
stats.Counter.Label, _ = e.Count(new(issues_model.Label))
stats.Counter.HookTask, _ = e.Count(new(webhook.HookTask))
stats.Counter.Team, _ = e.Count(new(organization.Team))
stats.Counter.Attachment, _ = e.Count(new(repo_model.Attachment))

View file

@ -16,7 +16,7 @@ import (
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/models/issues"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
access_model "code.gitea.io/gitea/models/perm/access"
pull_model "code.gitea.io/gitea/models/pull"
@ -78,12 +78,12 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) {
&user_model.Follow{UserID: u.ID},
&user_model.Follow{FollowID: u.ID},
&Action{UserID: u.ID},
&IssueUser{UID: u.ID},
&issues_model.IssueUser{UID: u.ID},
&user_model.EmailAddress{UID: u.ID},
&user_model.UserOpenID{UID: u.ID},
&issues.Reaction{UserID: u.ID},
&issues_model.Reaction{UserID: u.ID},
&organization.TeamUser{UID: u.ID},
&Stopwatch{UserID: u.ID},
&issues_model.Stopwatch{UserID: u.ID},
&user_model.Setting{UserID: u.ID},
&pull_model.AutoMerge{DoerID: u.ID},
&pull_model.ReviewState{UserID: u.ID},
@ -101,8 +101,8 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) {
// Delete Comments
const batchSize = 50
for start := 0; ; start += batchSize {
comments := make([]*Comment, 0, batchSize)
if err = e.Where("type=? AND poster_id=?", CommentTypeComment, u.ID).Limit(batchSize, start).Find(&comments); err != nil {
comments := make([]*issues_model.Comment, 0, batchSize)
if err = e.Where("type=? AND poster_id=?", issues_model.CommentTypeComment, u.ID).Limit(batchSize, start).Find(&comments); err != nil {
return err
}
if len(comments) == 0 {
@ -110,14 +110,14 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) {
}
for _, comment := range comments {
if err = deleteComment(ctx, comment); err != nil {
if err = issues_model.DeleteComment(ctx, comment); err != nil {
return err
}
}
}
// Delete Reactions
if err = issues.DeleteReaction(ctx, &issues.ReactionOptions{DoerID: u.ID}); err != nil {
if err = issues_model.DeleteReaction(ctx, &issues_model.ReactionOptions{DoerID: u.ID}); err != nil {
return err
}
}
@ -189,7 +189,7 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) {
// ***** END: GPGPublicKey *****
// Clear assignee.
if _, err = db.DeleteByBean(ctx, &IssueAssignees{AssigneeID: u.ID}); err != nil {
if _, err = db.DeleteByBean(ctx, &issues_model.IssueAssignees{AssigneeID: u.ID}); err != nil {
return fmt.Errorf("clear assignee: %v", err)
}

View file

@ -18,6 +18,7 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
unit_model "code.gitea.io/gitea/models/unit"
@ -83,7 +84,7 @@ type Repository struct {
// CanWriteToBranch checks if the branch is writable by the user
func (r *Repository) CanWriteToBranch(user *user_model.User, branch string) bool {
return models.CanMaintainerWriteToBranch(r.Permission, branch, user)
return issues_model.CanMaintainerWriteToBranch(r.Permission, branch, user)
}
// CanEnableEditor returns true if repository is editable and user has proper access level.
@ -158,11 +159,11 @@ func (r *Repository) CanCommitToBranch(ctx context.Context, doer *user_model.Use
}
// CanUseTimetracker returns whether or not a user can use the timetracker.
func (r *Repository) CanUseTimetracker(issue *models.Issue, user *user_model.User) bool {
func (r *Repository) CanUseTimetracker(issue *issues_model.Issue, user *user_model.User) bool {
// Checking for following:
// 1. Is timetracker enabled
// 2. Is the user a contributor, admin, poster or assignee and do the repository policies require this?
isAssigned, _ := models.IsUserAssignedToIssue(db.DefaultContext, issue, user)
isAssigned, _ := issues_model.IsUserAssignedToIssue(db.DefaultContext, issue, user)
return r.Repository.IsTimetrackerEnabled() && (!r.Repository.AllowOnlyContributorsToTrackTime() ||
r.Permission.CanWriteIssuesOrPulls(issue.IsPull) || issue.IsPoster(user.ID) || isAssigned)
}
@ -379,24 +380,16 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) {
ctx.Data["Permission"] = &ctx.Repo.Permission
if repo.IsMirror {
// Check if the mirror has finsihed migrationg, only then we can
// lookup the mirror informtation the database.
finishedMigrating, err := models.HasFinishedMigratingTask(repo.ID)
if err != nil {
ctx.ServerError("HasFinishedMigratingTask", err)
return
}
if finishedMigrating {
ctx.Repo.Mirror, err = repo_model.GetMirrorByRepoID(ctx, repo.ID)
if err != nil {
ctx.ServerError("GetMirrorByRepoID", err)
return
}
ctx.Repo.Mirror, err = repo_model.GetMirrorByRepoID(ctx, repo.ID)
if err == nil {
ctx.Repo.Mirror.Repo = repo
ctx.Data["IsPullMirror"] = true
ctx.Data["MirrorEnablePrune"] = ctx.Repo.Mirror.EnablePrune
ctx.Data["MirrorInterval"] = ctx.Repo.Mirror.Interval
ctx.Data["Mirror"] = ctx.Repo.Mirror
} else if err != repo_model.ErrMirrorNotExist {
ctx.ServerError("GetMirrorByRepoID", err)
return
}
}

View file

@ -11,11 +11,11 @@ import (
"strings"
"time"
"code.gitea.io/gitea/models"
asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
@ -55,7 +55,7 @@ func ToBranch(repo *repo_model.Repository, b *git.Branch, c *git.Commit, bp *git
if err != nil {
return nil, err
}
canPush = models.CanMaintainerWriteToBranch(perms, b.Name, user)
canPush = issues_model.CanMaintainerWriteToBranch(perms, b.Name, user)
}
return &api.Branch{

View file

@ -9,7 +9,6 @@ import (
"net/url"
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
@ -23,7 +22,7 @@ import (
// it assumes some fields assigned with values:
// Required - Poster, Labels,
// Optional - Milestone, Assignee, PullRequest
func ToAPIIssue(issue *models.Issue) *api.Issue {
func ToAPIIssue(issue *issues_model.Issue) *api.Issue {
if err := issue.LoadLabels(db.DefaultContext); err != nil {
return &api.Issue{}
}
@ -100,7 +99,7 @@ func ToAPIIssue(issue *models.Issue) *api.Issue {
}
// ToAPIIssueList converts an IssueList to API format
func ToAPIIssueList(il models.IssueList) []*api.Issue {
func ToAPIIssueList(il issues_model.IssueList) []*api.Issue {
result := make([]*api.Issue, len(il))
for i := range il {
result[i] = ToAPIIssue(il[i])
@ -109,7 +108,7 @@ func ToAPIIssueList(il models.IssueList) []*api.Issue {
}
// ToTrackedTime converts TrackedTime to API format
func ToTrackedTime(t *models.TrackedTime) (apiT *api.TrackedTime) {
func ToTrackedTime(t *issues_model.TrackedTime) (apiT *api.TrackedTime) {
apiT = &api.TrackedTime{
ID: t.ID,
IssueID: t.IssueID,
@ -128,13 +127,13 @@ func ToTrackedTime(t *models.TrackedTime) (apiT *api.TrackedTime) {
}
// ToStopWatches convert Stopwatch list to api.StopWatches
func ToStopWatches(sws []*models.Stopwatch) (api.StopWatches, error) {
func ToStopWatches(sws []*issues_model.Stopwatch) (api.StopWatches, error) {
result := api.StopWatches(make([]api.StopWatch, 0, len(sws)))
issueCache := make(map[int64]*models.Issue)
issueCache := make(map[int64]*issues_model.Issue)
repoCache := make(map[int64]*repo_model.Repository)
var (
issue *models.Issue
issue *issues_model.Issue
repo *repo_model.Repository
ok bool
err error
@ -143,7 +142,7 @@ func ToStopWatches(sws []*models.Stopwatch) (api.StopWatches, error) {
for _, sw := range sws {
issue, ok = issueCache[sw.IssueID]
if !ok {
issue, err = models.GetIssueByID(sw.IssueID)
issue, err = issues_model.GetIssueByID(db.DefaultContext, sw.IssueID)
if err != nil {
return nil, err
}
@ -170,7 +169,7 @@ func ToStopWatches(sws []*models.Stopwatch) (api.StopWatches, error) {
}
// ToTrackedTimeList converts TrackedTimeList to API format
func ToTrackedTimeList(tl models.TrackedTimeList) api.TrackedTimeList {
func ToTrackedTimeList(tl issues_model.TrackedTimeList) api.TrackedTimeList {
result := make([]*api.TrackedTime, 0, len(tl))
for _, t := range tl {
result = append(result, ToTrackedTime(t))
@ -179,7 +178,7 @@ func ToTrackedTimeList(tl models.TrackedTimeList) api.TrackedTimeList {
}
// ToLabel converts Label to API format
func ToLabel(label *models.Label, repo *repo_model.Repository, org *user_model.User) *api.Label {
func ToLabel(label *issues_model.Label, repo *repo_model.Repository, org *user_model.User) *api.Label {
result := &api.Label{
ID: label.ID,
Name: label.Name,
@ -206,7 +205,7 @@ func ToLabel(label *models.Label, repo *repo_model.Repository, org *user_model.U
}
// ToLabelList converts list of Label to API format
func ToLabelList(labels []*models.Label, repo *repo_model.Repository, org *user_model.User) []*api.Label {
func ToLabelList(labels []*issues_model.Label, repo *repo_model.Repository, org *user_model.User) []*api.Label {
result := make([]*api.Label, len(labels))
for i := range labels {
result[i] = ToLabel(labels[i], repo, org)

View file

@ -5,16 +5,16 @@
package convert
import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
api "code.gitea.io/gitea/modules/structs"
)
// ToComment converts a models.Comment to the api.Comment format
func ToComment(c *models.Comment) *api.Comment {
// ToComment converts a issues_model.Comment to the api.Comment format
func ToComment(c *issues_model.Comment) *api.Comment {
return &api.Comment{
ID: c.ID,
Poster: ToUser(c.Poster, nil),
@ -27,8 +27,8 @@ func ToComment(c *models.Comment) *api.Comment {
}
}
// ToTimelineComment converts a models.Comment to the api.TimelineComment format
func ToTimelineComment(c *models.Comment, doer *user_model.User) *api.TimelineComment {
// ToTimelineComment converts a issues_model.Comment to the api.TimelineComment format
func ToTimelineComment(c *issues_model.Comment, doer *user_model.User) *api.TimelineComment {
err := c.LoadMilestone()
if err != nil {
log.Error("LoadMilestone: %v", err)
@ -105,7 +105,7 @@ func ToTimelineComment(c *models.Comment, doer *user_model.User) *api.TimelineCo
}
if c.RefIssueID != 0 {
issue, err := models.GetIssueByID(c.RefIssueID)
issue, err := issues_model.GetIssueByID(db.DefaultContext, c.RefIssueID)
if err != nil {
log.Error("GetIssueByID(%d): %v", c.RefIssueID, err)
return nil
@ -114,7 +114,7 @@ func ToTimelineComment(c *models.Comment, doer *user_model.User) *api.TimelineCo
}
if c.RefCommentID != 0 {
com, err := models.GetCommentByID(db.DefaultContext, c.RefCommentID)
com, err := issues_model.GetCommentByID(db.DefaultContext, c.RefCommentID)
if err != nil {
log.Error("GetCommentByID(%d): %v", c.RefCommentID, err)
return nil

View file

@ -9,7 +9,6 @@ import (
"testing"
"time"
"code.gitea.io/gitea/models"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
@ -22,7 +21,7 @@ import (
func TestLabel_ToLabel(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label := unittest.AssertExistsAndLoadBean(t, &models.Label{ID: 1}).(*models.Label)
label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label)
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: label.RepoID}).(*repo_model.Repository)
assert.Equal(t, &api.Label{
ID: label.ID,

View file

@ -8,7 +8,7 @@ import (
"context"
"fmt"
"code.gitea.io/gitea/models"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
user_model "code.gitea.io/gitea/models/user"
@ -20,7 +20,7 @@ import (
// ToAPIPullRequest assumes following fields have been assigned with valid values:
// Required - Issue
// Optional - Merger
func ToAPIPullRequest(ctx context.Context, pr *models.PullRequest, doer *user_model.User) *api.PullRequest {
func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User) *api.PullRequest {
var (
baseBranch *git.Branch
headBranch *git.Branch
@ -114,7 +114,7 @@ func ToAPIPullRequest(ctx context.Context, pr *models.PullRequest, doer *user_mo
}
}
if pr.Flow == models.PullRequestFlowAGit {
if pr.Flow == issues_model.PullRequestFlowAGit {
gitRepo, err := git.OpenRepository(ctx, pr.BaseRepo.RepoPath())
if err != nil {
log.Error("OpenRepository[%s]: %v", pr.GetGitRefName(), err)
@ -132,7 +132,7 @@ func ToAPIPullRequest(ctx context.Context, pr *models.PullRequest, doer *user_mo
apiPullRequest.Head.Name = ""
}
if pr.HeadRepo != nil && pr.Flow == models.PullRequestFlowGithub {
if pr.HeadRepo != nil && pr.Flow == issues_model.PullRequestFlowGithub {
p, err := access_model.GetUserRepoPermission(ctx, pr.HeadRepo, doer)
if err != nil {
log.Error("GetUserRepoPermission[%d]: %v", pr.HeadRepoID, err)

View file

@ -8,13 +8,13 @@ import (
"context"
"strings"
"code.gitea.io/gitea/models"
issues_model "code.gitea.io/gitea/models/issues"
user_model "code.gitea.io/gitea/models/user"
api "code.gitea.io/gitea/modules/structs"
)
// ToPullReview convert a review to api format
func ToPullReview(ctx context.Context, r *models.Review, doer *user_model.User) (*api.PullReview, error) {
func ToPullReview(ctx context.Context, r *issues_model.Review, doer *user_model.User) (*api.PullReview, error) {
if err := r.LoadAttributes(ctx); err != nil {
if !user_model.IsErrUserNotExist(err) {
return nil, err
@ -44,15 +44,15 @@ func ToPullReview(ctx context.Context, r *models.Review, doer *user_model.User)
}
switch r.Type {
case models.ReviewTypeApprove:
case issues_model.ReviewTypeApprove:
result.State = api.ReviewStateApproved
case models.ReviewTypeReject:
case issues_model.ReviewTypeReject:
result.State = api.ReviewStateRequestChanges
case models.ReviewTypeComment:
case issues_model.ReviewTypeComment:
result.State = api.ReviewStateComment
case models.ReviewTypePending:
case issues_model.ReviewTypePending:
result.State = api.ReviewStatePending
case models.ReviewTypeRequest:
case issues_model.ReviewTypeRequest:
result.State = api.ReviewStateRequestReview
}
@ -60,11 +60,11 @@ func ToPullReview(ctx context.Context, r *models.Review, doer *user_model.User)
}
// ToPullReviewList convert a list of review to it's api format
func ToPullReviewList(ctx context.Context, rl []*models.Review, doer *user_model.User) ([]*api.PullReview, error) {
func ToPullReviewList(ctx context.Context, rl []*issues_model.Review, doer *user_model.User) ([]*api.PullReview, error) {
result := make([]*api.PullReview, 0, len(rl))
for i := range rl {
// show pending reviews only for the user who created them
if rl[i].Type == models.ReviewTypePending && !(doer.IsAdmin || doer.ID == rl[i].ReviewerID) {
if rl[i].Type == issues_model.ReviewTypePending && !(doer.IsAdmin || doer.ID == rl[i].ReviewerID) {
continue
}
r, err := ToPullReview(ctx, rl[i], doer)
@ -77,7 +77,7 @@ func ToPullReviewList(ctx context.Context, rl []*models.Review, doer *user_model
}
// ToPullReviewCommentList convert the CodeComments of an review to it's api format
func ToPullReviewCommentList(ctx context.Context, review *models.Review, doer *user_model.User) ([]*api.PullReviewComment, error) {
func ToPullReviewCommentList(ctx context.Context, review *issues_model.Review, doer *user_model.User) ([]*api.PullReviewComment, error) {
if err := review.LoadAttributes(ctx); err != nil {
if !user_model.IsErrUserNotExist(err) {
return nil, err

View file

@ -7,7 +7,7 @@ package convert
import (
"testing"
"code.gitea.io/gitea/models"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
@ -21,7 +21,7 @@ func TestPullRequest_APIFormat(t *testing.T) {
// with HeadRepo
assert.NoError(t, unittest.PrepareTestDatabase())
headRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
pr := unittest.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 1}).(*models.PullRequest)
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest)
assert.NoError(t, pr.LoadAttributes())
assert.NoError(t, pr.LoadIssue())
apiPullRequest := ToAPIPullRequest(git.DefaultContext, pr, nil)
@ -35,7 +35,7 @@ func TestPullRequest_APIFormat(t *testing.T) {
}, apiPullRequest.Head)
// withOut HeadRepo
pr = unittest.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 1}).(*models.PullRequest)
pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest)
assert.NoError(t, pr.LoadIssue())
assert.NoError(t, pr.LoadAttributes())
// simulate fork deletion

View file

@ -9,6 +9,7 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/migrations"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/log"
@ -64,10 +65,10 @@ func genericOrphanCheck(name, subject, refobject, joincond string) consistencyCh
return consistencyCheck{
Name: name,
Counter: func() (int64, error) {
return models.CountOrphanedObjects(subject, refobject, joincond)
return db.CountOrphanedObjects(subject, refobject, joincond)
},
Fixer: func() (int64, error) {
err := models.DeleteOrphanedObjects(subject, refobject, joincond)
err := db.DeleteOrphanedObjects(subject, refobject, joincond)
return -1, err
},
}
@ -84,20 +85,20 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er
{
// find labels without existing repo or org
Name: "Orphaned Labels without existing repository or organisation",
Counter: models.CountOrphanedLabels,
Fixer: asFixer(models.DeleteOrphanedLabels),
Counter: issues_model.CountOrphanedLabels,
Fixer: asFixer(issues_model.DeleteOrphanedLabels),
},
{
// find IssueLabels without existing label
Name: "Orphaned Issue Labels without existing label",
Counter: models.CountOrphanedIssueLabels,
Fixer: asFixer(models.DeleteOrphanedIssueLabels),
Counter: issues_model.CountOrphanedIssueLabels,
Fixer: asFixer(issues_model.DeleteOrphanedIssueLabels),
},
{
// find issues without existing repository
Name: "Orphaned Issues without existing repository",
Counter: models.CountOrphanedIssues,
Fixer: asFixer(models.DeleteOrphanedIssues),
Counter: issues_model.CountOrphanedIssues,
Fixer: asFixer(issues_model.DeleteOrphanedIssues),
},
// find releases without existing repository
genericOrphanCheck("Orphaned Releases without existing repository",
@ -127,22 +128,22 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er
// find label comments with empty labels
{
Name: "Label comments with empty labels",
Counter: models.CountCommentTypeLabelWithEmptyLabel,
Fixer: models.FixCommentTypeLabelWithEmptyLabel,
Counter: issues_model.CountCommentTypeLabelWithEmptyLabel,
Fixer: issues_model.FixCommentTypeLabelWithEmptyLabel,
FixedMessage: "Fixed",
},
// find label comments with labels from outside the repository
{
Name: "Label comments with labels from outside the repository",
Counter: models.CountCommentTypeLabelWithOutsideLabels,
Fixer: models.FixCommentTypeLabelWithOutsideLabels,
Counter: issues_model.CountCommentTypeLabelWithOutsideLabels,
Fixer: issues_model.FixCommentTypeLabelWithOutsideLabels,
FixedMessage: "Removed",
},
// find issue_label with labels from outside the repository
{
Name: "IssueLabels with Labels from outside the repository",
Counter: models.CountIssueLabelWithOutsideLabels,
Fixer: models.FixIssueLabelWithOutsideLabels,
Counter: issues_model.CountIssueLabelWithOutsideLabels,
Fixer: issues_model.FixIssueLabelWithOutsideLabels,
FixedMessage: "Removed",
},
{

View file

@ -9,8 +9,8 @@ import (
"fmt"
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
@ -18,13 +18,13 @@ import (
"xorm.io/builder"
)
func iteratePRs(ctx context.Context, repo *repo_model.Repository, each func(*repo_model.Repository, *models.PullRequest) error) error {
func iteratePRs(ctx context.Context, repo *repo_model.Repository, each func(*repo_model.Repository, *issues_model.PullRequest) error) error {
return db.Iterate(
ctx,
new(models.PullRequest),
new(issues_model.PullRequest),
builder.Eq{"base_repo_id": repo.ID},
func(idx int, bean interface{}) error {
return each(repo, bean.(*models.PullRequest))
return each(repo, bean.(*issues_model.PullRequest))
},
)
}
@ -35,7 +35,7 @@ func checkPRMergeBase(ctx context.Context, logger log.Logger, autofix bool) erro
numPRsUpdated := 0
err := iterateRepositories(ctx, func(repo *repo_model.Repository) error {
numRepos++
return iteratePRs(ctx, repo, func(repo *repo_model.Repository, pr *models.PullRequest) error {
return iteratePRs(ctx, repo, func(repo *repo_model.Repository, pr *issues_model.PullRequest) error {
numPRs++
pr.BaseRepo = repo
repoPath := repo.RepoPath()

View file

@ -9,6 +9,7 @@ import (
"time"
"code.gitea.io/gitea/models"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/modules/convert"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/json"
@ -84,7 +85,7 @@ loop:
then = now
if setting.Service.EnableTimetracking {
usersStopwatches, err := models.GetUIDsAndStopwatch()
usersStopwatches, err := issues_model.GetUIDsAndStopwatch()
if err != nil {
log.Error("Unable to get GetUIDsAndStopwatch: %v", err)
return

View file

@ -26,7 +26,7 @@ func TestBleveIndexAndSearch(t *testing.T) {
defer indexer.Close()
if _, err := indexer.Init(); err != nil {
assert.Fail(t, "Unable to initialise bleve indexer: %v", err)
assert.Fail(t, "Unable to initialize bleve indexer: %v", err)
return
}

View file

@ -7,8 +7,8 @@ package issues
import (
"context"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
)
// DBIndexer implements Indexer interface to use database's like search
@ -44,7 +44,7 @@ func (i *DBIndexer) Close() {
// Search dummy function
func (i *DBIndexer) Search(ctx context.Context, kw string, repoIDs []int64, limit, start int) (*SearchResult, error) {
total, ids, err := models.SearchIssueIDsByKeyword(ctx, kw, repoIDs, limit, start)
total, ids, err := issues_model.SearchIssueIDsByKeyword(ctx, kw, repoIDs, limit, start)
if err != nil {
return nil, err
}

View file

@ -12,8 +12,8 @@ import (
"sync"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
@ -320,7 +320,7 @@ func populateIssueIndexer(ctx context.Context) {
// UpdateRepoIndexer add/update all issues of the repositories
func UpdateRepoIndexer(repo *repo_model.Repository) {
is, err := models.Issues(&models.IssuesOptions{
is, err := issues_model.Issues(&issues_model.IssuesOptions{
RepoID: repo.ID,
IsClosed: util.OptionalBoolNone,
IsPull: util.OptionalBoolNone,
@ -329,7 +329,7 @@ func UpdateRepoIndexer(repo *repo_model.Repository) {
log.Error("Issues: %v", err)
return
}
if err = models.IssueList(is).LoadDiscussComments(); err != nil {
if err = issues_model.IssueList(is).LoadDiscussComments(); err != nil {
log.Error("LoadComments: %v", err)
return
}
@ -339,10 +339,10 @@ func UpdateRepoIndexer(repo *repo_model.Repository) {
}
// UpdateIssueIndexer add/update an issue to the issue indexer
func UpdateIssueIndexer(issue *models.Issue) {
func UpdateIssueIndexer(issue *issues_model.Issue) {
var comments []string
for _, comment := range issue.Comments {
if comment.Type == models.CommentTypeComment {
if comment.Type == issues_model.CommentTypeComment {
comments = append(comments, comment.Content)
}
}
@ -362,7 +362,7 @@ func UpdateIssueIndexer(issue *models.Issue) {
// DeleteRepoIssueIndexer deletes repo's all issues indexes
func DeleteRepoIssueIndexer(repo *repo_model.Repository) {
var ids []int64
ids, err := models.GetIssueIDsByRepoID(db.DefaultContext, repo.ID)
ids, err := issues_model.GetIssueIDsByRepoID(db.DefaultContext, repo.ID)
if err != nil {
log.Error("getIssueIDsByRepoID failed: %v", err)
return

View file

@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/graceful"
@ -33,7 +34,7 @@ func NewNotifier() base.Notifier {
return &actionNotifier{}
}
func (a *actionNotifier) NotifyNewIssue(issue *models.Issue, mentions []*user_model.User) {
func (a *actionNotifier) NotifyNewIssue(issue *issues_model.Issue, mentions []*user_model.User) {
if err := issue.LoadPoster(); err != nil {
log.Error("issue.LoadPoster: %v", err)
return
@ -58,7 +59,7 @@ func (a *actionNotifier) NotifyNewIssue(issue *models.Issue, mentions []*user_mo
}
// NotifyIssueChangeStatus notifies close or reopen issue to notifiers
func (a *actionNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *models.Issue, actionComment *models.Comment, closeOrReopen bool) {
func (a *actionNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *issues_model.Issue, actionComment *issues_model.Comment, closeOrReopen bool) {
// Compose comment action, could be plain comment, close or reopen issue/pull request.
// This object will be used to notify watchers in the end of function.
act := &models.Action{
@ -92,7 +93,7 @@ func (a *actionNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *m
// NotifyCreateIssueComment notifies comment on an issue to notifiers
func (a *actionNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *repo_model.Repository,
issue *models.Issue, comment *models.Comment, mentions []*user_model.User,
issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User,
) {
act := &models.Action{
ActUserID: doer.ID,
@ -126,7 +127,7 @@ func (a *actionNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *r
}
}
func (a *actionNotifier) NotifyNewPullRequest(pull *models.PullRequest, mentions []*user_model.User) {
func (a *actionNotifier) NotifyNewPullRequest(pull *issues_model.PullRequest, mentions []*user_model.User) {
if err := pull.LoadIssue(); err != nil {
log.Error("pull.LoadIssue: %v", err)
return
@ -207,7 +208,7 @@ func (a *actionNotifier) NotifyForkRepository(doer *user_model.User, oldRepo, re
}
}
func (a *actionNotifier) NotifyPullRequestReview(pr *models.PullRequest, review *models.Review, comment *models.Comment, mentions []*user_model.User) {
func (a *actionNotifier) NotifyPullRequestReview(pr *issues_model.PullRequest, review *issues_model.Review, comment *issues_model.Comment, mentions []*user_model.User) {
ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("actionNotifier.NotifyPullRequestReview Pull[%d] #%d in [%d]", pr.ID, pr.Index, pr.BaseRepoID))
defer finished()
@ -239,7 +240,7 @@ func (a *actionNotifier) NotifyPullRequestReview(pr *models.PullRequest, review
}
}
if review.Type != models.ReviewTypeComment || strings.TrimSpace(comment.Content) != "" {
if review.Type != issues_model.ReviewTypeComment || strings.TrimSpace(comment.Content) != "" {
action := &models.Action{
ActUserID: review.Reviewer.ID,
ActUser: review.Reviewer,
@ -252,9 +253,9 @@ func (a *actionNotifier) NotifyPullRequestReview(pr *models.PullRequest, review
}
switch review.Type {
case models.ReviewTypeApprove:
case issues_model.ReviewTypeApprove:
action.OpType = models.ActionApprovePullRequest
case models.ReviewTypeReject:
case issues_model.ReviewTypeReject:
action.OpType = models.ActionRejectPullRequest
default:
action.OpType = models.ActionCommentPull
@ -268,7 +269,7 @@ func (a *actionNotifier) NotifyPullRequestReview(pr *models.PullRequest, review
}
}
func (*actionNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *user_model.User) {
func (*actionNotifier) NotifyMergePullRequest(pr *issues_model.PullRequest, doer *user_model.User) {
if err := models.NotifyWatchers(&models.Action{
ActUserID: doer.ID,
ActUser: doer,
@ -282,7 +283,7 @@ func (*actionNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *user
}
}
func (*actionNotifier) NotifyPullRevieweDismiss(doer *user_model.User, review *models.Review, comment *models.Comment) {
func (*actionNotifier) NotifyPullRevieweDismiss(doer *user_model.User, review *issues_model.Review, comment *issues_model.Comment) {
reviewerName := review.Reviewer.Name
if len(review.OriginalAuthor) > 0 {
reviewerName = review.OriginalAuthor

Some files were not shown because too many files have changed in this diff Show more