1
1
mirror of https://github.com/go-gitea/gitea synced 2025-07-05 18:17:19 +00:00

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

This commit is contained in:
Anthony Wang
2022-06-13 16:49:36 -05:00
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) Jimmy Praet <jimmy.praet@telenet.be> (@jpraet)
Leon Hofmeister <dev.lh@web.de> (@delvh) Leon Hofmeister <dev.lh@web.de> (@delvh)
Gusted <williamzijl7@hotmail.com) (@Gusted) Gusted <williamzijl7@hotmail.com) (@Gusted)
singuliere <singuliere@autistici.org> (@singuliere)
silentcode <silentcode@senga.org> (@silentcodeg) silentcode <silentcode@senga.org> (@silentcodeg)
Wim <wim@42.be> (@42wim) 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 this is the intended configuration file complete the [database] section.`, setting.CustomConf)
} }
if err := db.InitEngine(ctx); err != nil { 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 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. 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 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 Gitea service.
The main intention of these commands is to easily add a temporary logger to investigate problems on running systems where a restart 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" "net/url"
"testing" "testing"
"code.gitea.io/gitea/models" issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
@ -23,9 +23,9 @@ import (
func TestAPIListRepoComments(t *testing.T) { func TestAPIListRepoComments(t *testing.T) {
defer prepareTestEnv(t)() defer prepareTestEnv(t)()
comment := unittest.AssertExistsAndLoadBean(t, &models.Comment{}, comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{},
unittest.Cond("type = ?", models.CommentTypeComment)).(*models.Comment) unittest.Cond("type = ?", issues_model.CommentTypeComment)).(*issues_model.Comment)
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: comment.IssueID}).(*models.Issue) 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) 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) 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) DecodeJSON(t, resp, &apiComments)
assert.Len(t, apiComments, 2) assert.Len(t, apiComments, 2)
for _, apiComment := range apiComments { for _, apiComment := range apiComments {
c := &models.Comment{ID: apiComment.ID} c := &issues_model.Comment{ID: apiComment.ID}
unittest.AssertExistsAndLoadBean(t, c, unittest.AssertExistsAndLoadBean(t, c,
unittest.Cond("type = ?", models.CommentTypeComment)) unittest.Cond("type = ?", issues_model.CommentTypeComment))
unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: c.IssueID, RepoID: repo.ID}) unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: c.IssueID, RepoID: repo.ID})
} }
// test before and since filters // test before and since filters
@ -69,9 +69,9 @@ func TestAPIListRepoComments(t *testing.T) {
func TestAPIListIssueComments(t *testing.T) { func TestAPIListIssueComments(t *testing.T) {
defer prepareTestEnv(t)() defer prepareTestEnv(t)()
comment := unittest.AssertExistsAndLoadBean(t, &models.Comment{}, comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{},
unittest.Cond("type = ?", models.CommentTypeComment)).(*models.Comment) unittest.Cond("type = ?", issues_model.CommentTypeComment)).(*issues_model.Comment)
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: comment.IssueID}).(*models.Issue) 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) 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) 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 var comments []*api.Comment
DecodeJSON(t, resp, &comments) DecodeJSON(t, resp, &comments)
expectedCount := unittest.GetCount(t, &models.Comment{IssueID: issue.ID}, expectedCount := unittest.GetCount(t, &issues_model.Comment{IssueID: issue.ID},
unittest.Cond("type = ?", models.CommentTypeComment)) unittest.Cond("type = ?", issues_model.CommentTypeComment))
assert.EqualValues(t, expectedCount, len(comments)) assert.EqualValues(t, expectedCount, len(comments))
} }
@ -91,7 +91,7 @@ func TestAPICreateComment(t *testing.T) {
defer prepareTestEnv(t)() defer prepareTestEnv(t)()
const commentBody = "Comment body" 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) 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) 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 var updatedComment api.Comment
DecodeJSON(t, resp, &updatedComment) DecodeJSON(t, resp, &updatedComment)
assert.EqualValues(t, commentBody, updatedComment.Body) 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) { func TestAPIGetComment(t *testing.T) {
defer prepareTestEnv(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()) assert.NoError(t, comment.LoadIssue())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: comment.Issue.RepoID}).(*repo_model.Repository) 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) 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)() defer prepareTestEnv(t)()
const newCommentBody = "This is the new comment body" const newCommentBody = "This is the new comment body"
comment := unittest.AssertExistsAndLoadBean(t, &models.Comment{}, comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{},
unittest.Cond("type = ?", models.CommentTypeComment)).(*models.Comment) unittest.Cond("type = ?", issues_model.CommentTypeComment)).(*issues_model.Comment)
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: comment.IssueID}).(*models.Issue) 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) 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) 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) DecodeJSON(t, resp, &updatedComment)
assert.EqualValues(t, comment.ID, updatedComment.ID) assert.EqualValues(t, comment.ID, updatedComment.ID)
assert.EqualValues(t, newCommentBody, updatedComment.Body) 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) { func TestAPIDeleteComment(t *testing.T) {
defer prepareTestEnv(t)() defer prepareTestEnv(t)()
comment := unittest.AssertExistsAndLoadBean(t, &models.Comment{}, comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{},
unittest.Cond("type = ?", models.CommentTypeComment)).(*models.Comment) unittest.Cond("type = ?", issues_model.CommentTypeComment)).(*issues_model.Comment)
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: comment.IssueID}).(*models.Issue) 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) 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) 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) repoOwner.Name, repo.Name, comment.ID, token)
session.MakeRequest(t, req, http.StatusNoContent) 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) { func TestAPIListIssueTimeline(t *testing.T) {
defer prepareTestEnv(t)() defer prepareTestEnv(t)()
// load comment // 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) 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) 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 // lists extracted directly from DB are the same
var comments []*api.TimelineComment var comments []*api.TimelineComment
DecodeJSON(t, resp, &comments) 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)) assert.EqualValues(t, expectedCount, len(comments))
} }

View File

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

View File

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

View File

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

View File

@ -9,7 +9,7 @@ import (
"net/http" "net/http"
"testing" "testing"
"code.gitea.io/gitea/models" issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
@ -21,18 +21,18 @@ import (
func TestAPIIssueSubscriptions(t *testing.T) { func TestAPIIssueSubscriptions(t *testing.T) {
defer prepareTestEnv(t)() defer prepareTestEnv(t)()
issue1 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 1}).(*models.Issue) issue1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue)
issue2 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 2}).(*models.Issue) issue2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}).(*issues_model.Issue)
issue3 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 3}).(*models.Issue) issue3 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3}).(*issues_model.Issue)
issue4 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 4}).(*models.Issue) issue4 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4}).(*issues_model.Issue)
issue5 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 8}).(*models.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) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue1.PosterID}).(*user_model.User)
session := loginUser(t, owner.Name) session := loginUser(t, owner.Name)
token := getTokenForLoggedInUser(t, session) 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) 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) 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" "testing"
"time" "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" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user" 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) resp := session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
var apiIssues []*api.Issue var apiIssues []*api.Issue
DecodeJSON(t, resp, &apiIssues) 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 { 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 // test milestone filter
@ -91,7 +92,7 @@ func TestAPICreateIssue(t *testing.T) {
assert.Equal(t, body, apiIssue.Body) assert.Equal(t, body, apiIssue.Body)
assert.Equal(t, title, apiIssue.Title) assert.Equal(t, title, apiIssue.Title)
unittest.AssertExistsAndLoadBean(t, &models.Issue{ unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{
RepoID: repoBefore.ID, RepoID: repoBefore.ID,
AssigneeID: owner.ID, AssigneeID: owner.ID,
Content: body, Content: body,
@ -106,10 +107,10 @@ func TestAPICreateIssue(t *testing.T) {
func TestAPIEditIssue(t *testing.T) { func TestAPIEditIssue(t *testing.T) {
defer prepareTestEnv(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) 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) 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, int64(1019307200), int64(issueBefore.DeadlineUnix))
assert.Equal(t, api.StateOpen, issueBefore.State()) assert.Equal(t, api.StateOpen, issueBefore.State())
@ -137,12 +138,12 @@ func TestAPIEditIssue(t *testing.T) {
var apiIssue api.Issue var apiIssue api.Issue
DecodeJSON(t, resp, &apiIssue) 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) repoAfter := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID}).(*repo_model.Repository)
// check deleted user // check deleted user
assert.Equal(t, int64(500), issueAfter.PosterID) 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), issueAfter.PosterID)
assert.Equal(t, int64(-1), issueBefore.PosterID) assert.Equal(t, int64(-1), issueBefore.PosterID)
assert.Equal(t, int64(-1), apiIssue.Poster.ID) assert.Equal(t, int64(-1), apiIssue.Poster.ID)

View File

@ -10,8 +10,8 @@ import (
"testing" "testing"
"time" "time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
@ -23,7 +23,7 @@ func TestAPIGetTrackedTimes(t *testing.T) {
defer prepareTestEnv(t)() defer prepareTestEnv(t)()
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) 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)) assert.NoError(t, issue2.LoadRepo(db.DefaultContext))
session := loginUser(t, user2.Name) session := loginUser(t, user2.Name)
@ -33,7 +33,7 @@ func TestAPIGetTrackedTimes(t *testing.T) {
resp := session.MakeRequest(t, req, http.StatusOK) resp := session.MakeRequest(t, req, http.StatusOK)
var apiTimes api.TrackedTimeList var apiTimes api.TrackedTimeList
DecodeJSON(t, resp, &apiTimes) 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.NoError(t, err)
assert.Len(t, apiTimes, 3) assert.Len(t, apiTimes, 3)
@ -64,8 +64,8 @@ func TestAPIGetTrackedTimes(t *testing.T) {
func TestAPIDeleteTrackedTime(t *testing.T) { func TestAPIDeleteTrackedTime(t *testing.T) {
defer prepareTestEnv(t)() defer prepareTestEnv(t)()
time6 := unittest.AssertExistsAndLoadBean(t, &models.TrackedTime{ID: 6}).(*models.TrackedTime) time6 := unittest.AssertExistsAndLoadBean(t, &issues_model.TrackedTime{ID: 6}).(*issues_model.TrackedTime)
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)) assert.NoError(t, issue2.LoadRepo(db.DefaultContext))
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) 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) 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) 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) 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) session.MakeRequest(t, req, http.StatusNoContent)
// Delete non existing time // Delete non existing time
session.MakeRequest(t, req, http.StatusNotFound) session.MakeRequest(t, req, http.StatusNotFound)
// Reset time of user 2 on issue 2 // 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.NoError(t, err)
assert.Equal(t, int64(3661), trackedSeconds) 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.StatusNoContent)
session.MakeRequest(t, req, http.StatusNotFound) 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.NoError(t, err)
assert.Equal(t, int64(0), trackedSeconds) assert.Equal(t, int64(0), trackedSeconds)
} }
@ -99,7 +99,7 @@ func TestAPIDeleteTrackedTime(t *testing.T) {
func TestAPIAddTrackedTimes(t *testing.T) { func TestAPIAddTrackedTimes(t *testing.T) {
defer prepareTestEnv(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)) assert.NoError(t, issue2.LoadRepo(db.DefaultContext))
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)

View File

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

View File

@ -9,7 +9,8 @@ import (
"net/http" "net/http"
"testing" "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" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/json"
@ -20,8 +21,8 @@ import (
func TestAPIPullReview(t *testing.T) { func TestAPIPullReview(t *testing.T) {
defer prepareTestEnv(t)() defer prepareTestEnv(t)()
pullIssue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 3}).(*models.Issue) pullIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3}).(*issues_model.Issue)
assert.NoError(t, pullIssue.LoadAttributes()) assert.NoError(t, pullIssue.LoadAttributes(db.DefaultContext))
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue.RepoID}).(*repo_model.Repository) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue.RepoID}).(*repo_model.Repository)
// test ListPullReviews // test ListPullReviews
@ -64,7 +65,7 @@ func TestAPIPullReview(t *testing.T) {
assert.EqualValues(t, *reviews[5], review) assert.EqualValues(t, *reviews[5], review)
// test GetPullReviewComments // 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) 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) resp = session.MakeRequest(t, req, http.StatusOK)
var reviewComments []*api.PullReviewComment var reviewComments []*api.PullReviewComment
@ -199,8 +200,8 @@ func TestAPIPullReview(t *testing.T) {
// test get review requests // test get review requests
// to make it simple, use same api with get review // to make it simple, use same api with get review
pullIssue12 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 12}).(*models.Issue) pullIssue12 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 12}).(*issues_model.Issue)
assert.NoError(t, pullIssue12.LoadAttributes()) assert.NoError(t, pullIssue12.LoadAttributes(db.DefaultContext))
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue12.RepoID}).(*repo_model.Repository) 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) 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) { func TestAPIPullReviewRequest(t *testing.T) {
defer prepareTestEnv(t)() defer prepareTestEnv(t)()
pullIssue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 3}).(*models.Issue) pullIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3}).(*issues_model.Issue)
assert.NoError(t, pullIssue.LoadAttributes()) assert.NoError(t, pullIssue.LoadAttributes(db.DefaultContext))
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue.RepoID}).(*repo_model.Repository) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue.RepoID}).(*repo_model.Repository)
// Test add Review Request // Test add Review Request
@ -268,8 +269,8 @@ func TestAPIPullReviewRequest(t *testing.T) {
session.MakeRequest(t, req, http.StatusNoContent) session.MakeRequest(t, req, http.StatusNoContent)
// Test team review request // Test team review request
pullIssue12 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 12}).(*models.Issue) pullIssue12 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 12}).(*issues_model.Issue)
assert.NoError(t, pullIssue12.LoadAttributes()) assert.NoError(t, pullIssue12.LoadAttributes(db.DefaultContext))
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue12.RepoID}).(*repo_model.Repository) repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue12.RepoID}).(*repo_model.Repository)
// Test add Team Review Request // Test add Team Review Request

View File

@ -9,7 +9,7 @@ import (
"net/http" "net/http"
"testing" "testing"
"code.gitea.io/gitea/models" issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
@ -33,7 +33,7 @@ func TestAPIViewPulls(t *testing.T) {
var pulls []*api.PullRequest var pulls []*api.PullRequest
DecodeJSON(t, resp, &pulls) 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) assert.Len(t, pulls, expectedLen)
} }
@ -42,7 +42,7 @@ func TestAPIMergePullWIP(t *testing.T) {
defer prepareTestEnv(t)() defer prepareTestEnv(t)()
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) 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) 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() pr.LoadIssue()
issue_service.ChangeTitle(pr.Issue, owner, setting.Repository.PullRequest.WorkInProgressPrefixes[0]+" "+pr.Issue.Title) issue_service.ChangeTitle(pr.Issue, owner, setting.Repository.PullRequest.WorkInProgressPrefixes[0]+" "+pr.Issue.Title)

View File

@ -9,7 +9,7 @@ import (
"net/http" "net/http"
"testing" "testing"
"code.gitea.io/gitea/models" issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/organization"
access_model "code.gitea.io/gitea/models/perm/access" access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo" 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, &repo_model.Repository{OwnerID: userID})
unittest.AssertNotExistsBean(t, &access_model.Access{UserID: userID}) unittest.AssertNotExistsBean(t, &access_model.Access{UserID: userID})
unittest.AssertNotExistsBean(t, &organization.OrgUser{UID: 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, &organization.TeamUser{UID: userID})
unittest.AssertNotExistsBean(t, &repo_model.Star{UID: userID}) unittest.AssertNotExistsBean(t, &repo_model.Star{UID: userID})
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -15,6 +15,7 @@ import (
"time" "time"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/organization"
access_model "code.gitea.io/gitea/models/perm/access" access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
@ -76,7 +77,7 @@ type Action struct {
RepoID int64 `xorm:"INDEX"` RepoID int64 `xorm:"INDEX"`
Repo *repo_model.Repository `xorm:"-"` Repo *repo_model.Repository `xorm:"-"`
CommentID int64 `xorm:"INDEX"` CommentID int64 `xorm:"INDEX"`
Comment *Comment `xorm:"-"` Comment *issues_model.Comment `xorm:"-"`
IsDeleted bool `xorm:"INDEX NOT NULL DEFAULT false"` IsDeleted bool `xorm:"INDEX NOT NULL DEFAULT false"`
RefName string RefName string
IsPrivate bool `xorm:"INDEX NOT NULL DEFAULT false"` IsPrivate bool `xorm:"INDEX NOT NULL DEFAULT false"`
@ -223,7 +224,7 @@ func (a *Action) getCommentLink(ctx context.Context) string {
return "#" return "#"
} }
if a.Comment == nil && a.CommentID != 0 { 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 { if a.Comment != nil {
return a.Comment.HTMLURL() return a.Comment.HTMLURL()
@ -238,7 +239,7 @@ func (a *Action) getCommentLink(ctx context.Context) string {
return "#" return "#"
} }
issue, err := getIssueByID(ctx, issueID) issue, err := issues_model.GetIssueByID(ctx, issueID)
if err != nil { if err != nil {
return "#" return "#"
} }
@ -295,7 +296,7 @@ func (a *Action) GetIssueInfos() []string {
// with the action. // with the action.
func (a *Action) GetIssueTitle() string { func (a *Action) GetIssueTitle() string {
index, _ := strconv.ParseInt(a.GetIssueInfos()[0], 10, 64) 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 { if err != nil {
log.Error("GetIssueByIndex: %v", err) log.Error("GetIssueByIndex: %v", err)
return "500 when get issue" return "500 when get issue"
@ -307,7 +308,7 @@ func (a *Action) GetIssueTitle() string {
// this action. // this action.
func (a *Action) GetIssueContent() string { func (a *Action) GetIssueContent() string {
index, _ := strconv.ParseInt(a.GetIssueInfos()[0], 10, 64) 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 { if err != nil {
log.Error("GetIssueByIndex: %v", err) log.Error("GetIssueByIndex: %v", err)
return "500 when get issue" return "500 when get issue"
@ -572,3 +573,20 @@ func NotifyWatchersActions(acts []*Action) error {
} }
return committer.Commit() 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.NoError(t, err)
assert.Len(t, actions, 0) 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 package models
import ( import (
admin_model "code.gitea.io/gitea/models/admin"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
@ -14,151 +13,6 @@ import (
"xorm.io/builder" "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 // CountNullArchivedRepository counts the number of repositories with is_archived is null
func CountNullArchivedRepository() (int64, error) { func CountNullArchivedRepository() (int64, error) {
return db.GetEngine(db.DefaultContext).Where(builder.IsNull{"is_archived"}).Count(new(repo_model.Repository)) 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}) 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 // CountActionCreatedUnixString count actions where created_unix is an empty string
func CountActionCreatedUnixString() (int64, error) { func CountActionCreatedUnixString() (int64, error) {
if setting.Database.UseSQLite3 { 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. // 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 // An atomic UPSERT operation (INSERT/UPDATE) is the only operation
// that ensures that the key is actually locked. // that ensures that the key is actually locked.
switch { switch {
case setting.Database.UseSQLite3 || setting.Database.UsePostgreSQL: 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", "VALUES (?,1) ON CONFLICT (group_id) DO UPDATE SET max_index = %s.max_index+1",
tableName, tableName), groupID) tableName, tableName), groupID)
case setting.Database.UseMySQL: 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), "VALUES (?,1) ON DUPLICATE KEY UPDATE max_index = max_index+1", tableName),
groupID) groupID)
case setting.Database.UseMSSQL: case setting.Database.UseMSSQL:
// https://weblogs.sqlteam.com/dang/2009/01/31/upsert-race-condition-with-merge/ // 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 "+ "USING (SELECT ? AS group_id) AS src "+
"ON src.group_id = target.group_id "+ "ON src.group_id = target.group_id "+
"WHEN MATCHED THEN UPDATE SET target.max_index = target.max_index+1 "+ "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 // getNextResourceIndex return the next index
func getNextResourceIndex(tableName string, groupID int64) (int64, error) { func getNextResourceIndex(tableName string, groupID int64) (int64, error) {
sess := x.NewSession() ctx, commiter, err := TxContext()
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)
if err != nil { if err != nil {
return 0, err 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 return 0, err
} }
var curIdx int64 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 { if err != nil {
return 0, err return 0, err
} }
if !has { if !has {
return 0, ErrResouceOutdated return 0, ErrResouceOutdated
} }
if err := sess.Commit(); err != nil { if err := commiter.Commit(); err != nil {
return 0, err return 0, err
} }
return curIdx, nil return curIdx, nil

View File

@ -10,6 +10,11 @@ import (
"xorm.io/xorm" "xorm.io/xorm"
) )
const (
// DefaultMaxInSize represents default variables number on IN () in SQL
DefaultMaxInSize = 50
)
// Paginator is the base for different ListOptions types // Paginator is the base for different ListOptions types
type Paginator interface { type Paginator interface {
GetSkipTake() (skip, take int) 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) 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" 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 // ErrInvalidMergeStyle represents an error if merging with disabled merge strategy
type ErrInvalidMergeStyle struct { type ErrInvalidMergeStyle struct {
ID int64 ID int64
@ -830,29 +658,6 @@ func (err ErrPullRequestHasMerged) Error() string {
err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch) 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) 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 { func (err ErrUploadNotExist) Error() string {
return fmt.Sprintf("attachment does not exist [id: %d, uuid: %s]", err.ID, err.UUID) 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 ( import (
"testing" "testing"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git" git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest" "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) repo1 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
assert.Equal(t, "main", repo1.DefaultBranch) 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) 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) assert.Equal(t, "main", pull.BaseBranch)
renamedBranch := unittest.AssertExistsAndLoadBean(t, &git_model.RenamedBranch{ID: 2}).(*git_model.RenamedBranch) renamedBranch := unittest.AssertExistsAndLoadBean(t, &git_model.RenamedBranch{ID: 2}).(*git_model.RenamedBranch)

View File

@ -8,6 +8,7 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
_ "code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/unittest" "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 // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package models package issues
import ( import (
"context" "context"

View File

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

View File

@ -4,7 +4,7 @@
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package models package issues
import ( import (
"context" "context"
@ -16,7 +16,6 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git" 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/organization"
project_model "code.gitea.io/gitea/models/project" project_model "code.gitea.io/gitea/models/project"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
@ -34,6 +33,22 @@ import (
"xorm.io/xorm" "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. // CommentType defines whether a comment is just a simple comment, an action (like close) or a reference.
type CommentType int type CommentType int
@ -216,8 +231,8 @@ type Comment struct {
Project *project_model.Project `xorm:"-"` Project *project_model.Project `xorm:"-"`
OldMilestoneID int64 OldMilestoneID int64
MilestoneID int64 MilestoneID int64
OldMilestone *issues_model.Milestone `xorm:"-"` OldMilestone *Milestone `xorm:"-"`
Milestone *issues_model.Milestone `xorm:"-"` Milestone *Milestone `xorm:"-"`
TimeID int64 TimeID int64
Time *TrackedTime `xorm:"-"` Time *TrackedTime `xorm:"-"`
AssigneeID int64 AssigneeID int64
@ -250,8 +265,8 @@ type Comment struct {
// Reference issue in commit message // Reference issue in commit message
CommitSHA string `xorm:"VARCHAR(40)"` CommitSHA string `xorm:"VARCHAR(40)"`
Attachments []*repo_model.Attachment `xorm:"-"` Attachments []*repo_model.Attachment `xorm:"-"`
Reactions issues_model.ReactionList `xorm:"-"` Reactions ReactionList `xorm:"-"`
// For view issue page. // For view issue page.
ShowRole RoleDescriptor `xorm:"-"` ShowRole RoleDescriptor `xorm:"-"`
@ -299,7 +314,7 @@ func (c *Comment) LoadIssueCtx(ctx context.Context) (err error) {
if c.Issue != nil { if c.Issue != nil {
return nil return nil
} }
c.Issue, err = getIssueByID(ctx, c.IssueID) c.Issue, err = GetIssueByID(ctx, c.IssueID)
return return
} }
@ -503,7 +518,7 @@ func (c *Comment) LoadProject() error {
// LoadMilestone if comment.Type is CommentTypeMilestone, then load milestone // LoadMilestone if comment.Type is CommentTypeMilestone, then load milestone
func (c *Comment) LoadMilestone() error { func (c *Comment) LoadMilestone() error {
if c.OldMilestoneID > 0 { if c.OldMilestoneID > 0 {
var oldMilestone issues_model.Milestone var oldMilestone Milestone
has, err := db.GetEngine(db.DefaultContext).ID(c.OldMilestoneID).Get(&oldMilestone) has, err := db.GetEngine(db.DefaultContext).ID(c.OldMilestoneID).Get(&oldMilestone)
if err != nil { if err != nil {
return err return err
@ -513,7 +528,7 @@ func (c *Comment) LoadMilestone() error {
} }
if c.MilestoneID > 0 { if c.MilestoneID > 0 {
var milestone issues_model.Milestone var milestone Milestone
has, err := db.GetEngine(db.DefaultContext).ID(c.MilestoneID).Get(&milestone) has, err := db.GetEngine(db.DefaultContext).ID(c.MilestoneID).Get(&milestone)
if err != nil { if err != nil {
return err return err
@ -625,7 +640,7 @@ func (c *Comment) LoadDepIssueDetails() (err error) {
if c.DependentIssueID <= 0 || c.DependentIssue != nil { if c.DependentIssueID <= 0 || c.DependentIssue != nil {
return nil return nil
} }
c.DependentIssue, err = getIssueByID(db.DefaultContext, c.DependentIssueID) c.DependentIssue, err = GetIssueByID(db.DefaultContext, c.DependentIssueID)
return err return err
} }
@ -643,7 +658,7 @@ func (c *Comment) loadReactions(ctx context.Context, repo *repo_model.Repository
if c.Reactions != nil { if c.Reactions != nil {
return nil return nil
} }
c.Reactions, _, err = issues_model.FindReactions(ctx, issues_model.FindReactionsOptions{ c.Reactions, _, err = FindReactions(ctx, FindReactionsOptions{
IssueID: c.IssueID, IssueID: c.IssueID,
CommentID: c.ID, CommentID: c.ID,
}) })
@ -823,7 +838,7 @@ func CreateCommentCtx(ctx context.Context, opts *CreateCommentOptions) (_ *Comme
return nil, err 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 return nil, err
} }
@ -1128,7 +1143,7 @@ func UpdateComment(c *Comment, doer *user_model.User) error {
if err := c.LoadIssueCtx(ctx); err != nil { if err := c.LoadIssueCtx(ctx); err != nil {
return err return err
} }
if err := c.addCrossReferences(ctx, doer, true); err != nil { if err := c.AddCrossReferences(ctx, doer, true); err != nil {
return err return err
} }
if err := committer.Commit(); err != nil { if err := committer.Commit(); err != nil {
@ -1139,27 +1154,13 @@ func UpdateComment(c *Comment, doer *user_model.User) error {
} }
// DeleteComment deletes the comment // DeleteComment deletes the comment
func DeleteComment(comment *Comment) error { func DeleteComment(ctx context.Context, 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 {
e := db.GetEngine(ctx) e := db.GetEngine(ctx)
if _, err := e.ID(comment.ID).NoAutoCondition().Delete(comment); err != nil { if _, err := e.ID(comment.ID).NoAutoCondition().Delete(comment); err != nil {
return err return err
} }
if _, err := db.DeleteByBean(ctx, &issues_model.ContentHistory{ if _, err := db.DeleteByBean(ctx, &ContentHistory{
CommentID: comment.ID, CommentID: comment.ID,
}); err != nil { }); err != nil {
return err return err
@ -1170,7 +1171,11 @@ func deleteComment(ctx context.Context, comment *Comment) error {
return err 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 return err
} }
@ -1178,7 +1183,7 @@ func deleteComment(ctx context.Context, comment *Comment) error {
return err 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 // 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 // GetExternalID ExternalUserRemappable interface
func (c *Comment) GetExternalID() int64 { return c.OriginalAuthorID } 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 // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package models package issues
import ( import (
"context" "context"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container" "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)) posterMaps := make(map[int64]*user_model.User, len(posterIDs))
left := len(posterIDs) left := len(posterIDs)
for left > 0 { for left > 0 {
limit := defaultMaxInSize limit := db.DefaultMaxInSize
if left < limit { if left < limit {
limit = left limit = left
} }
@ -80,7 +79,7 @@ func (comments CommentList) getLabelIDs() []int64 {
return container.KeysInt64(ids) 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 { if len(comments) == 0 {
return nil return nil
} }
@ -89,7 +88,7 @@ func (comments CommentList) loadLabels(ctx context.Context) error {
commentLabels := make(map[int64]*Label, len(labelIDs)) commentLabels := make(map[int64]*Label, len(labelIDs))
left := len(labelIDs) left := len(labelIDs)
for left > 0 { for left > 0 {
limit := defaultMaxInSize limit := db.DefaultMaxInSize
if left < limit { if left < limit {
limit = left limit = left
} }
@ -140,10 +139,10 @@ func (comments CommentList) loadMilestones(ctx context.Context) error {
return nil return nil
} }
milestoneMaps := make(map[int64]*issues_model.Milestone, len(milestoneIDs)) milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
left := len(milestoneIDs) left := len(milestoneIDs)
for left > 0 { for left > 0 {
limit := defaultMaxInSize limit := db.DefaultMaxInSize
if left < limit { if left < limit {
limit = left limit = left
} }
@ -183,10 +182,10 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error {
return nil return nil
} }
milestoneMaps := make(map[int64]*issues_model.Milestone, len(milestoneIDs)) milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
left := len(milestoneIDs) left := len(milestoneIDs)
for left > 0 { for left > 0 {
limit := defaultMaxInSize limit := db.DefaultMaxInSize
if left < limit { if left < limit {
limit = left limit = left
} }
@ -225,7 +224,7 @@ func (comments CommentList) loadAssignees(ctx context.Context) error {
assignees := make(map[int64]*user_model.User, len(assigneeIDs)) assignees := make(map[int64]*user_model.User, len(assigneeIDs))
left := len(assigneeIDs) left := len(assigneeIDs)
for left > 0 { for left > 0 {
limit := defaultMaxInSize limit := db.DefaultMaxInSize
if left < limit { if left < limit {
limit = left limit = left
} }
@ -299,7 +298,7 @@ func (comments CommentList) loadIssues(ctx context.Context) error {
issues := make(map[int64]*Issue, len(issueIDs)) issues := make(map[int64]*Issue, len(issueIDs))
left := len(issueIDs) left := len(issueIDs)
for left > 0 { for left > 0 {
limit := defaultMaxInSize limit := db.DefaultMaxInSize
if left < limit { if left < limit {
limit = left limit = left
} }
@ -357,7 +356,7 @@ func (comments CommentList) loadDependentIssues(ctx context.Context) error {
issues := make(map[int64]*Issue, len(issueIDs)) issues := make(map[int64]*Issue, len(issueIDs))
left := len(issueIDs) left := len(issueIDs)
for left > 0 { for left > 0 {
limit := defaultMaxInSize limit := db.DefaultMaxInSize
if left < limit { if left < limit {
limit = left limit = left
} }
@ -406,7 +405,7 @@ func (comments CommentList) loadAttachments(ctx context.Context) (err error) {
commentsIDs := comments.getCommentIDs() commentsIDs := comments.getCommentIDs()
left := len(commentsIDs) left := len(commentsIDs)
for left > 0 { for left > 0 {
limit := defaultMaxInSize limit := db.DefaultMaxInSize
if left < limit { if left < limit {
limit = left limit = left
} }
@ -449,7 +448,7 @@ func (comments CommentList) getReviewIDs() []int64 {
return container.KeysInt64(ids) 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 { if len(comments) == 0 {
return nil return nil
} }
@ -458,7 +457,7 @@ func (comments CommentList) loadReviews(ctx context.Context) error {
reviews := make(map[int64]*Review, len(reviewIDs)) reviews := make(map[int64]*Review, len(reviewIDs))
left := len(reviewIDs) left := len(reviewIDs)
for left > 0 { for left > 0 {
limit := defaultMaxInSize limit := db.DefaultMaxInSize
if left < limit { if left < limit {
limit = left limit = left
} }

View File

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

View File

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

View File

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

View File

@ -3,7 +3,7 @@
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package models package issues
import ( import (
"context" "context"
@ -16,7 +16,6 @@ import (
admin_model "code.gitea.io/gitea/models/admin" admin_model "code.gitea.io/gitea/models/admin"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/foreignreference" "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/organization"
"code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access" 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/log"
"code.gitea.io/gitea/modules/references" "code.gitea.io/gitea/modules/references"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
@ -38,6 +36,71 @@ import (
"xorm.io/xorm" "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. // Issue represents an issue or pull request of repository.
type Issue struct { type Issue struct {
ID int64 `xorm:"pk autoincr"` ID int64 `xorm:"pk autoincr"`
@ -47,14 +110,14 @@ type Issue struct {
PosterID int64 `xorm:"INDEX"` PosterID int64 `xorm:"INDEX"`
Poster *user_model.User `xorm:"-"` Poster *user_model.User `xorm:"-"`
OriginalAuthor string OriginalAuthor string
OriginalAuthorID int64 `xorm:"index"` OriginalAuthorID int64 `xorm:"index"`
Title string `xorm:"name"` Title string `xorm:"name"`
Content string `xorm:"LONGTEXT"` Content string `xorm:"LONGTEXT"`
RenderedContent string `xorm:"-"` RenderedContent string `xorm:"-"`
Labels []*Label `xorm:"-"` Labels []*Label `xorm:"-"`
MilestoneID int64 `xorm:"INDEX"` MilestoneID int64 `xorm:"INDEX"`
Milestone *issues_model.Milestone `xorm:"-"` Milestone *Milestone `xorm:"-"`
Project *project_model.Project `xorm:"-"` Project *project_model.Project `xorm:"-"`
Priority int Priority int
AssigneeID int64 `xorm:"-"` AssigneeID int64 `xorm:"-"`
Assignee *user_model.User `xorm:"-"` Assignee *user_model.User `xorm:"-"`
@ -73,7 +136,7 @@ type Issue struct {
Attachments []*repo_model.Attachment `xorm:"-"` Attachments []*repo_model.Attachment `xorm:"-"`
Comments []*Comment `xorm:"-"` Comments []*Comment `xorm:"-"`
Reactions issues_model.ReactionList `xorm:"-"` Reactions ReactionList `xorm:"-"`
TotalTrackedTime int64 `xorm:"-"` TotalTrackedTime int64 `xorm:"-"`
Assignees []*user_model.User `xorm:"-"` Assignees []*user_model.User `xorm:"-"`
ForeignReference *foreignreference.ForeignReference `xorm:"-"` ForeignReference *foreignreference.ForeignReference `xorm:"-"`
@ -107,7 +170,8 @@ func init() {
db.RegisterModel(new(IssueIndex)) 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} opts := FindTrackedTimesOptions{IssueID: issue.ID}
issue.TotalTrackedTime, err = opts.toSession(db.GetEngine(ctx)).SumInt(&TrackedTime{}, "time") issue.TotalTrackedTime, err = opts.toSession(db.GetEngine(ctx)).SumInt(&TrackedTime{}, "time")
if err != nil { if err != nil {
@ -237,7 +301,7 @@ func (issue *Issue) loadReactions(ctx context.Context) (err error) {
if issue.Reactions != nil { if issue.Reactions != nil {
return nil return nil
} }
reactions, _, err := issues_model.FindReactions(ctx, issues_model.FindReactionsOptions{ reactions, _, err := FindReactions(ctx, FindReactionsOptions{
IssueID: issue.ID, IssueID: issue.ID,
}) })
if err != nil { if err != nil {
@ -247,7 +311,7 @@ func (issue *Issue) loadReactions(ctx context.Context) (err error) {
return err return err
} }
// Load reaction user data // 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 return err
} }
@ -292,15 +356,16 @@ func (issue *Issue) loadForeignReference(ctx context.Context) (err error) {
func (issue *Issue) loadMilestone(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 { if (issue.Milestone == nil || issue.Milestone.ID != issue.MilestoneID) && issue.MilestoneID > 0 {
issue.Milestone, err = issues_model.GetMilestoneByRepoID(ctx, issue.RepoID, issue.MilestoneID) issue.Milestone, err = GetMilestoneByRepoID(ctx, issue.RepoID, issue.MilestoneID)
if err != nil && !issues_model.IsErrMilestoneNotExist(err) { if err != nil && !IsErrMilestoneNotExist(err) {
return fmt.Errorf("getMilestoneByRepoID [repo_id: %d, milestone_id: %d]: %v", issue.RepoID, issue.MilestoneID, err) return fmt.Errorf("getMilestoneByRepoID [repo_id: %d, milestone_id: %d]: %v", issue.RepoID, issue.MilestoneID, err)
} }
} }
return nil 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 { if err = issue.LoadRepo(ctx); err != nil {
return return
} }
@ -345,7 +410,7 @@ func (issue *Issue) loadAttributes(ctx context.Context) (err error) {
return err return err
} }
if issue.isTimetrackerEnabled(ctx) { if issue.isTimetrackerEnabled(ctx) {
if err = issue.loadTotalTimes(ctx); err != nil { if err = issue.LoadTotalTimes(ctx); err != nil {
return err return err
} }
} }
@ -357,11 +422,6 @@ func (issue *Issue) loadAttributes(ctx context.Context) (err error) {
return issue.loadReactions(ctx) 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. // LoadMilestone load milestone of this issue.
func (issue *Issue) LoadMilestone() error { func (issue *Issue) LoadMilestone() error {
return issue.loadMilestone(db.DefaultContext) return issue.loadMilestone(db.DefaultContext)
@ -590,15 +650,6 @@ func ReplaceIssueLabels(issue *Issue, labels []*Label, doer *user_model.User) (e
return committer.Commit() 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 // UpdateIssueCols updates cols of issue
func UpdateIssueCols(ctx context.Context, issue *Issue, cols ...string) error { func UpdateIssueCols(ctx context.Context, issue *Issue, cols ...string) error {
if _, err := db.GetEngine(ctx).ID(issue.ID).Cols(cols...).Update(issue); err != nil { 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) { func changeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isClosed, isMergePull bool) (*Comment, error) {
// Reload the issue // Reload the issue
currentIssue, err := getIssueByID(ctx, issue.ID) currentIssue, err := GetIssueByID(ctx, issue.ID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -666,7 +717,7 @@ func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.Use
// Update issue count of milestone // Update issue count of milestone
if issue.MilestoneID > 0 { 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 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 { if _, err = CreateCommentCtx(ctx, opts); err != nil {
return fmt.Errorf("createComment: %v", err) 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 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 // 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 { 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 { if err != nil {
return err return err
} }
@ -815,12 +866,12 @@ func ChangeIssueContent(issue *Issue, doer *user_model.User, content string) (er
} }
defer committer.Close() defer committer.Close()
hasContentHistory, err := issues_model.HasIssueContentHistory(ctx, issue.ID, 0) hasContentHistory, err := HasIssueContentHistory(ctx, issue.ID, 0)
if err != nil { if err != nil {
return fmt.Errorf("HasIssueContentHistory: %v", err) return fmt.Errorf("HasIssueContentHistory: %v", err)
} }
if !hasContentHistory { 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 { issue.CreatedUnix, issue.Content, true); err != nil {
return fmt.Errorf("SaveIssueContentHistory: %v", err) 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) 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 { timeutil.TimeStampNow(), issue.Content, false); err != nil {
return fmt.Errorf("SaveIssueContentHistory: %v", err) 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) return fmt.Errorf("addCrossReferences: %v", err)
} }
@ -907,13 +958,14 @@ type NewIssueOptions struct {
IsPull bool 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) e := db.GetEngine(ctx)
opts.Issue.Title = strings.TrimSpace(opts.Issue.Title) opts.Issue.Title = strings.TrimSpace(opts.Issue.Title)
if opts.Issue.MilestoneID > 0 { if opts.Issue.MilestoneID > 0 {
milestone, err := issues_model.GetMilestoneByRepoID(ctx, opts.Issue.RepoID, opts.Issue.MilestoneID) milestone, err := GetMilestoneByRepoID(ctx, opts.Issue.RepoID, opts.Issue.MilestoneID)
if err != nil && !issues_model.IsErrMilestoneNotExist(err) { if err != nil && !IsErrMilestoneNotExist(err) {
return fmt.Errorf("getMilestoneByID: %v", 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 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 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 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 err
} }
return opts.Issue.addCrossReferences(ctx, doer, false) 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()
} }
// NewIssue creates new issue with labels for repository. // 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() defer committer.Close()
if err = newIssue(ctx, issue.Poster, NewIssueOptions{ if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{
Repo: repo, Repo: repo,
Issue: issue, Issue: issue,
LabelIDs: labelIDs, LabelIDs: labelIDs,
Attachments: uuids, Attachments: uuids,
}); err != nil { }); err != nil {
if IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) { if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) {
return err return err
} }
return fmt.Errorf("newIssue: %v", err) return fmt.Errorf("newIssue: %v", err)
@ -1114,10 +1141,11 @@ func GetIssueWithAttrsByIndex(repoID, index int64) (*Issue, error) {
if err != nil { if err != nil {
return nil, err 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) issue := new(Issue)
has, err := db.GetEngine(ctx).ID(id).Get(issue) has, err := db.GetEngine(ctx).ID(id).Get(issue)
if err != nil { 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. // GetIssueWithAttrsByID returns an issue with attributes by given ID.
func GetIssueWithAttrsByID(id int64) (*Issue, error) { func GetIssueWithAttrsByID(id int64) (*Issue, error) {
issue, err := getIssueByID(db.DefaultContext, id) issue, err := GetIssueByID(db.DefaultContext, id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return issue, issue.loadAttributes(db.DefaultContext) return issue, issue.LoadAttributes(db.DefaultContext)
}
// GetIssueByID returns an issue by given ID.
func GetIssueByID(id int64) (*Issue, error) {
return getIssueByID(db.DefaultContext, id)
} }
// GetIssuesByIDs return issues with the given IDs. // 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. // IssuesOptions represents options of an issue.
type IssuesOptions struct { type IssuesOptions struct { //nolint
db.ListOptions db.ListOptions
RepoID int64 // overwrites RepoCond if not 0 RepoID int64 // overwrites RepoCond if not 0
RepoCond builder.Cond RepoCond builder.Cond
@ -1534,7 +1557,7 @@ func GetParticipantsIDsByIssueID(issueID int64) ([]int64, error) {
// IsUserParticipantsOfIssue return true if user is participants of an issue // IsUserParticipantsOfIssue return true if user is participants of an issue
func IsUserParticipantsOfIssue(user *user_model.User, issue *Issue) bool { func IsUserParticipantsOfIssue(user *user_model.User, issue *Issue) bool {
userIDs, err := issue.getParticipantIDsByIssue(db.DefaultContext) userIDs, err := issue.GetParticipantIDsByIssue(db.DefaultContext)
if err != nil { if err != nil {
log.Error(err.Error()) log.Error(err.Error())
return false return false
@ -1577,17 +1600,6 @@ const (
FilterModeYourRepositories 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. // IssueStatsOptions contains parameters accepted by GetIssueStats.
type IssueStatsOptions struct { type IssueStatsOptions struct {
RepoID int64 RepoID int64
@ -1602,14 +1614,15 @@ type IssueStatsOptions struct {
} }
const ( const (
// MaxQueryParameters represents the max query parameters
// When queries are broken down in parts because of the number // When queries are broken down in parts because of the number
// of parameters, attempt to break by this amount // of parameters, attempt to break by this amount
maxQueryParameters = 300 MaxQueryParameters = 300
) )
// GetIssueStats returns issue statistic information by given conditions. // GetIssueStats returns issue statistic information by given conditions.
func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) { func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) {
if len(opts.IssueIDs) <= maxQueryParameters { if len(opts.IssueIDs) <= MaxQueryParameters {
return getIssueStatsChunk(opts, opts.IssueIDs) return getIssueStatsChunk(opts, opts.IssueIDs)
} }
@ -1619,7 +1632,7 @@ func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) {
// ids in a temporary table and join from them. // ids in a temporary table and join from them.
accum := &IssueStats{} accum := &IssueStats{}
for i := 0; i < len(opts.IssueIDs); { for i := 0; i < len(opts.IssueIDs); {
chunk := i + maxQueryParameters chunk := i + MaxQueryParameters
if chunk > len(opts.IssueIDs) { if chunk > len(opts.IssueIDs) {
chunk = len(opts.IssueIDs) chunk = len(opts.IssueIDs)
} }
@ -1950,7 +1963,7 @@ func UpdateIssueByAPI(issue *Issue, doer *user_model.User) (statusChangeComment
} }
// Reload the issue // Reload the issue
currentIssue, err := getIssueByID(ctx, issue.ID) currentIssue, err := GetIssueByID(ctx, issue.ID)
if err != nil { if err != nil {
return nil, false, err 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 nil, false, err
} }
return statusChangeComment, titleChanged, committer.Commit() return statusChangeComment, titleChanged, committer.Commit()
@ -2016,22 +2029,8 @@ func UpdateIssueDeadline(issue *Issue, deadlineUnix timeutil.TimeStamp, doer *us
return committer.Commit() return committer.Commit()
} }
// DeleteIssue deletes the issue // DeleteInIssue delete records in beans with external key issue_id = ?
func DeleteIssue(issue *Issue) error { func DeleteInIssue(ctx context.Context, issueID int64, beans ...interface{}) 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 {
e := db.GetEngine(ctx) e := db.GetEngine(ctx)
for _, bean := range beans { for _, bean := range beans {
if _, err := e.In("issue_id", issueID).Delete(bean); err != nil { 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 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. // DependencyInfo represents high level information about an issue which is a dependency of another issue.
type DependencyInfo struct { type DependencyInfo struct {
Issue `xorm:"extends"` Issue `xorm:"extends"`
repo_model.Repository `xorm:"extends"` repo_model.Repository `xorm:"extends"`
} }
// getParticipantIDsByIssue returns all userIDs who are participated in comments of an issue and issue author // GetParticipantIDsByIssue returns all userIDs who are participated in comments of an issue and issue author
func (issue *Issue) getParticipantIDsByIssue(ctx context.Context) ([]int64, error) { func (issue *Issue) GetParticipantIDsByIssue(ctx context.Context) ([]int64, error) {
if issue == nil { if issue == nil {
return nil, 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) { func updateIssueClosedNum(ctx context.Context, issue *Issue) (err error) {
if issue.IsPull { if issue.IsPull {
err = repoStatsCorrectNumClosed(ctx, issue.RepoID, true, "num_closed_pulls") err = repo_model.StatsCorrectNumClosed(ctx, issue.RepoID, true, "num_closed_pulls")
} else { } else {
err = repoStatsCorrectNumClosed(ctx, issue.RepoID, false, "num_closed_issues") err = repo_model.StatsCorrectNumClosed(ctx, issue.RepoID, false, "num_closed_issues")
} }
return return
} }
@ -2363,6 +2273,17 @@ func UpdateIssuesMigrationsByType(gitServiceType api.GitServiceType, originalAut
return err 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 // UpdateReactionsMigrationsByType updates all migrated repositories' reactions from gitServiceType to replace originalAuthorID to posterID
func UpdateReactionsMigrationsByType(gitServiceType api.GitServiceType, originalAuthorID string, userID int64) error { func UpdateReactionsMigrationsByType(gitServiceType api.GitServiceType, originalAuthorID string, userID int64) error {
_, err := db.GetEngine(db.DefaultContext).Table("reaction"). _, err := db.GetEngine(db.DefaultContext).Table("reaction").
@ -2376,13 +2297,14 @@ func UpdateReactionsMigrationsByType(gitServiceType api.GitServiceType, original
return err 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}) deleteCond := builder.Select("id").From("issue").Where(builder.Eq{"issue.repo_id": repoID})
sess := db.GetEngine(ctx) sess := db.GetEngine(ctx)
// Delete content histories // Delete content histories
if _, err = sess.In("issue_id", deleteCond). if _, err = sess.In("issue_id", deleteCond).
Delete(&issues_model.ContentHistory{}); err != nil { Delete(&ContentHistory{}); err != nil {
return return
} }
@ -2410,7 +2332,7 @@ func deleteIssuesByRepoID(ctx context.Context, repoID int64) (attachmentPaths []
} }
if _, err = sess.In("issue_id", deleteCond). if _, err = sess.In("issue_id", deleteCond).
Delete(&issues_model.Reaction{}); err != nil { Delete(&Reaction{}); err != nil {
return return
} }
@ -2477,3 +2399,50 @@ func (issue *Issue) GetExternalName() string { return issue.OriginalAuthor }
// GetExternalID ExternalUserRemappable interface // GetExternalID ExternalUserRemappable interface
func (issue *Issue) GetExternalID() int64 { return issue.OriginalAuthorID } 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 // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package models package issues
import ( import (
"context" "context"
"fmt" "fmt"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/container"
@ -20,11 +19,6 @@ import (
// IssueList defines a list of issues // IssueList defines a list of issues
type IssueList []*Issue 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 // get the repo IDs to be loaded later, these IDs are for issue.Repo and issue.PullRequest.HeadRepo
func (issues IssueList) getRepoIDs() []int64 { func (issues IssueList) getRepoIDs() []int64 {
repoIDs := make(map[int64]struct{}, len(issues)) 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)) repoMaps := make(map[int64]*repo_model.Repository, len(repoIDs))
left := len(repoIDs) left := len(repoIDs)
for left > 0 { for left > 0 {
limit := defaultMaxInSize limit := db.DefaultMaxInSize
if left < limit { if left < limit {
limit = left limit = left
} }
@ -102,7 +96,7 @@ func (issues IssueList) loadPosters(ctx context.Context) error {
posterMaps := make(map[int64]*user_model.User, len(posterIDs)) posterMaps := make(map[int64]*user_model.User, len(posterIDs))
left := len(posterIDs) left := len(posterIDs)
for left > 0 { for left > 0 {
limit := defaultMaxInSize limit := db.DefaultMaxInSize
if left < limit { if left < limit {
limit = left limit = left
} }
@ -150,7 +144,7 @@ func (issues IssueList) loadLabels(ctx context.Context) error {
issueIDs := issues.getIssueIDs() issueIDs := issues.getIssueIDs()
left := len(issueIDs) left := len(issueIDs)
for left > 0 { for left > 0 {
limit := defaultMaxInSize limit := db.DefaultMaxInSize
if left < limit { if left < limit {
limit = left limit = left
} }
@ -205,10 +199,10 @@ func (issues IssueList) loadMilestones(ctx context.Context) error {
return nil return nil
} }
milestoneMaps := make(map[int64]*issues_model.Milestone, len(milestoneIDs)) milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
left := len(milestoneIDs) left := len(milestoneIDs)
for left > 0 { for left > 0 {
limit := defaultMaxInSize limit := db.DefaultMaxInSize
if left < limit { if left < limit {
limit = left limit = left
} }
@ -242,7 +236,7 @@ func (issues IssueList) loadAssignees(ctx context.Context) error {
issueIDs := issues.getIssueIDs() issueIDs := issues.getIssueIDs()
left := len(issueIDs) left := len(issueIDs)
for left > 0 { for left > 0 {
limit := defaultMaxInSize limit := db.DefaultMaxInSize
if left < limit { if left < limit {
limit = left limit = left
} }
@ -298,7 +292,7 @@ func (issues IssueList) loadPullRequests(ctx context.Context) error {
pullRequestMaps := make(map[int64]*PullRequest, len(issuesIDs)) pullRequestMaps := make(map[int64]*PullRequest, len(issuesIDs))
left := len(issuesIDs) left := len(issuesIDs)
for left > 0 { for left > 0 {
limit := defaultMaxInSize limit := db.DefaultMaxInSize
if left < limit { if left < limit {
limit = left limit = left
} }
@ -342,7 +336,7 @@ func (issues IssueList) loadAttachments(ctx context.Context) (err error) {
issuesIDs := issues.getIssueIDs() issuesIDs := issues.getIssueIDs()
left := len(issuesIDs) left := len(issuesIDs)
for left > 0 { for left > 0 {
limit := defaultMaxInSize limit := db.DefaultMaxInSize
if left < limit { if left < limit {
limit = left limit = left
} }
@ -387,7 +381,7 @@ func (issues IssueList) loadComments(ctx context.Context, cond builder.Cond) (er
issuesIDs := issues.getIssueIDs() issuesIDs := issues.getIssueIDs()
left := len(issuesIDs) left := len(issuesIDs)
for left > 0 { for left > 0 {
limit := defaultMaxInSize limit := db.DefaultMaxInSize
if left < limit { if left < limit {
limit = left limit = left
} }
@ -443,7 +437,7 @@ func (issues IssueList) loadTotalTrackedTimes(ctx context.Context) (err error) {
left := len(ids) left := len(ids)
for left > 0 { for left > 0 {
limit := defaultMaxInSize limit := db.DefaultMaxInSize
if left < limit { if left < limit {
limit = left limit = left
} }

View File

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package models package issues
import ( import (
"context" "context"
@ -25,7 +25,8 @@ func init() {
db.RegisterModel(new(IssueUser)) 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) assignees, err := repo_model.GetRepoAssignees(ctx, repo)
if err != nil { if err != nil {
return fmt.Errorf("getAssignees: %v", err) 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 // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package models package issues
import ( import (
"context" "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)) 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). _, err := db.GetEngine(ctx).
Join("INNER", "issue", "`issue`.id = `issue_watch`.issue_id AND `issue`.repo_id = ?", repoID). Join("INNER", "issue", "`issue`.id = `issue_watch`.issue_id AND `issue`.repo_id = ?", repoID).
Where("`issue_watch`.user_id = ?", userID). Where("`issue_watch`.user_id = ?", userID).

View File

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

View File

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

View File

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

View File

@ -3,7 +3,7 @@
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package models package issues
import ( import (
"context" "context"
@ -21,6 +21,53 @@ import (
"xorm.io/builder" "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 // LabelColorPattern is a regexp witch can validate LabelColor
var LabelColorPattern = regexp.MustCompile("^#?(?:[0-9a-fA-F]{6}|[0-9a-fA-F]{3})$") 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) 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}) deleteCond := builder.Select("id").From("label").Where(builder.Eq{"label.repo_id": repoID})
if _, err := db.GetEngine(ctx).In("label_id", deleteCond). 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}) _, err := db.DeleteByBean(ctx, &Label{RepoID: repoID})
return err 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 // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package issues package issues_test
import ( import (
"path/filepath" "path/filepath"
"testing" "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/unittest"
_ "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert"
) )
func init() { func init() {
@ -17,14 +23,18 @@ func init() {
setting.LoadForTest() 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) { func TestMain(m *testing.M) {
unittest.MainTest(m, &unittest.TestOptions{ unittest.MainTest(m, &unittest.TestOptions{
GiteaRootPath: filepath.Join("..", ".."), 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 return err
} }
numMilestones, err := countRepoMilestones(ctx, repo.ID) numMilestones, err := CountMilestones(ctx, GetMilestonesOption{
RepoID: repo.ID,
State: api.StateAll,
})
if err != nil { if err != nil {
return err return err
} }
numClosedMilestones, err := countRepoClosedMilestones(ctx, repo.ID) numClosedMilestones, err := CountMilestones(ctx, GetMilestonesOption{
RepoID: repo.ID,
State: api.StateClosed,
})
if err != nil { if err != nil {
return err return err
} }
@ -428,13 +434,6 @@ func GetMilestonesByRepoIDs(repoIDs []int64, page int, isClosed bool, sortType s
) )
} }
// ____ _ _
// / ___|| |_ __ _| |_ ___
// \___ \| __/ _` | __/ __|
// ___) | || (_| | |_\__ \
// |____/ \__\__,_|\__|___/
//
// MilestonesStats represents milestone statistic information. // MilestonesStats represents milestone statistic information.
type MilestonesStats struct { type MilestonesStats struct {
OpenCount, ClosedCount int64 OpenCount, ClosedCount int64
@ -503,23 +502,13 @@ func GetMilestonesStatsByRepoCondAndKw(repoCond builder.Cond, keyword string) (*
return stats, nil 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). return db.GetEngine(ctx).
Where("repo_id=?", repoID). Where(opts.toCond()).
Count(new(Milestone)) 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` // CountMilestonesByRepoCond map from repo conditions to number of milestones matching the options`
func CountMilestonesByRepoCond(repoCond builder.Cond, isClosed bool) (map[int64]int64, error) { func CountMilestonesByRepoCond(repoCond builder.Cond, isClosed bool) (map[int64]int64, error) {
sess := db.GetEngine(db.DefaultContext).Where("is_closed = ?", isClosed) 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 // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package issues package issues_test
import ( import (
"sort" "sort"
"testing" "testing"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"xorm.io/builder" "xorm.io/builder"
) )
func TestMilestone_State(t *testing.T) { func TestMilestone_State(t *testing.T) {
assert.Equal(t, api.StateOpen, (&Milestone{IsClosed: false}).State()) assert.Equal(t, api.StateOpen, (&issues_model.Milestone{IsClosed: false}).State())
assert.Equal(t, api.StateClosed, (&Milestone{IsClosed: true}).State()) assert.Equal(t, api.StateClosed, (&issues_model.Milestone{IsClosed: true}).State())
} }
func TestGetMilestoneByRepoID(t *testing.T) { func TestGetMilestoneByRepoID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) 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.NoError(t, err)
assert.EqualValues(t, 1, milestone.ID) assert.EqualValues(t, 1, milestone.ID)
assert.EqualValues(t, 1, milestone.RepoID) assert.EqualValues(t, 1, milestone.RepoID)
_, err = GetMilestoneByRepoID(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID) _, err = issues_model.GetMilestoneByRepoID(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID)
assert.True(t, IsErrMilestoneNotExist(err)) assert.True(t, issues_model.IsErrMilestoneNotExist(err))
} }
func TestGetMilestonesByRepoID(t *testing.T) { func TestGetMilestonesByRepoID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
test := func(repoID int64, state api.StateType) { test := func(repoID int64, state api.StateType) {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository) 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, RepoID: repo.ID,
State: state, State: state,
}) })
@ -76,7 +78,7 @@ func TestGetMilestonesByRepoID(t *testing.T) {
test(3, api.StateClosed) test(3, api.StateClosed)
test(3, api.StateAll) test(3, api.StateAll)
milestones, _, err := GetMilestones(GetMilestonesOption{ milestones, _, err := issues_model.GetMilestones(issues_model.GetMilestonesOption{
RepoID: unittest.NonexistentID, RepoID: unittest.NonexistentID,
State: api.StateOpen, State: api.StateOpen,
}) })
@ -87,9 +89,9 @@ func TestGetMilestonesByRepoID(t *testing.T) {
func TestGetMilestones(t *testing.T) { func TestGetMilestones(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) 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} { for _, page := range []int{0, 1} {
milestones, _, err := GetMilestones(GetMilestonesOption{ milestones, _, err := issues_model.GetMilestones(issues_model.GetMilestonesOption{
ListOptions: db.ListOptions{ ListOptions: db.ListOptions{
Page: page, Page: page,
PageSize: setting.UI.IssuePagingNum, PageSize: setting.UI.IssuePagingNum,
@ -106,7 +108,7 @@ func TestGetMilestones(t *testing.T) {
} }
assert.True(t, sort.IntsAreSorted(values)) assert.True(t, sort.IntsAreSorted(values))
milestones, _, err = GetMilestones(GetMilestonesOption{ milestones, _, err = issues_model.GetMilestones(issues_model.GetMilestonesOption{
ListOptions: db.ListOptions{ ListOptions: db.ListOptions{
Page: page, Page: page,
PageSize: setting.UI.IssuePagingNum, PageSize: setting.UI.IssuePagingNum,
@ -125,22 +127,22 @@ func TestGetMilestones(t *testing.T) {
assert.True(t, sort.IntsAreSorted(values)) assert.True(t, sort.IntsAreSorted(values))
} }
} }
test("furthestduedate", func(milestone *Milestone) int { test("furthestduedate", func(milestone *issues_model.Milestone) int {
return -int(milestone.DeadlineUnix) return -int(milestone.DeadlineUnix)
}) })
test("leastcomplete", func(milestone *Milestone) int { test("leastcomplete", func(milestone *issues_model.Milestone) int {
return milestone.Completeness return milestone.Completeness
}) })
test("mostcomplete", func(milestone *Milestone) int { test("mostcomplete", func(milestone *issues_model.Milestone) int {
return -milestone.Completeness return -milestone.Completeness
}) })
test("leastissues", func(milestone *Milestone) int { test("leastissues", func(milestone *issues_model.Milestone) int {
return milestone.NumIssues return milestone.NumIssues
}) })
test("mostissues", func(milestone *Milestone) int { test("mostissues", func(milestone *issues_model.Milestone) int {
return -milestone.NumIssues return -milestone.NumIssues
}) })
test("soonestduedate", func(milestone *Milestone) int { test("soonestduedate", func(milestone *issues_model.Milestone) int {
return int(milestone.DeadlineUnix) return int(milestone.DeadlineUnix)
}) })
} }
@ -149,7 +151,10 @@ func TestCountRepoMilestones(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
test := func(repoID int64) { test := func(repoID int64) {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository) 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.NoError(t, err)
assert.EqualValues(t, repo.NumMilestones, count) assert.EqualValues(t, repo.NumMilestones, count)
} }
@ -157,7 +162,10 @@ func TestCountRepoMilestones(t *testing.T) {
test(2) test(2)
test(3) 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.NoError(t, err)
assert.EqualValues(t, 0, count) assert.EqualValues(t, 0, count)
} }
@ -166,7 +174,10 @@ func TestCountRepoClosedMilestones(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
test := func(repoID int64) { test := func(repoID int64) {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository) 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.NoError(t, err)
assert.EqualValues(t, repo.NumClosedMilestones, count) assert.EqualValues(t, repo.NumClosedMilestones, count)
} }
@ -174,7 +185,10 @@ func TestCountRepoClosedMilestones(t *testing.T) {
test(2) test(2)
test(3) 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.NoError(t, err)
assert.EqualValues(t, 0, count) assert.EqualValues(t, 0, count)
} }
@ -188,12 +202,12 @@ func TestCountMilestonesByRepoIDs(t *testing.T) {
repo1OpenCount, repo1ClosedCount := milestonesCount(1) repo1OpenCount, repo1ClosedCount := milestonesCount(1)
repo2OpenCount, repo2ClosedCount := milestonesCount(2) 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.NoError(t, err)
assert.EqualValues(t, repo1OpenCount, openCounts[1]) assert.EqualValues(t, repo1OpenCount, openCounts[1])
assert.EqualValues(t, repo2OpenCount, openCounts[2]) 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.NoError(t, err)
assert.EqualValues(t, repo1ClosedCount, closedCounts[1]) assert.EqualValues(t, repo1ClosedCount, closedCounts[1])
assert.EqualValues(t, repo2ClosedCount, closedCounts[2]) assert.EqualValues(t, repo2ClosedCount, closedCounts[2])
@ -203,9 +217,9 @@ func TestGetMilestonesByRepoIDs(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*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} { 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.NoError(t, err)
assert.Len(t, openMilestones, repo1.NumOpenMilestones+repo2.NumOpenMilestones) assert.Len(t, openMilestones, repo1.NumOpenMilestones+repo2.NumOpenMilestones)
values := make([]int, len(openMilestones)) values := make([]int, len(openMilestones))
@ -214,7 +228,7 @@ func TestGetMilestonesByRepoIDs(t *testing.T) {
} }
assert.True(t, sort.IntsAreSorted(values)) 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.NoError(t, err)
assert.Len(t, closedMilestones, repo1.NumClosedMilestones+repo2.NumClosedMilestones) assert.Len(t, closedMilestones, repo1.NumClosedMilestones+repo2.NumClosedMilestones)
values = make([]int, len(closedMilestones)) values = make([]int, len(closedMilestones))
@ -224,22 +238,22 @@ func TestGetMilestonesByRepoIDs(t *testing.T) {
assert.True(t, sort.IntsAreSorted(values)) assert.True(t, sort.IntsAreSorted(values))
} }
} }
test("furthestduedate", func(milestone *Milestone) int { test("furthestduedate", func(milestone *issues_model.Milestone) int {
return -int(milestone.DeadlineUnix) return -int(milestone.DeadlineUnix)
}) })
test("leastcomplete", func(milestone *Milestone) int { test("leastcomplete", func(milestone *issues_model.Milestone) int {
return milestone.Completeness return milestone.Completeness
}) })
test("mostcomplete", func(milestone *Milestone) int { test("mostcomplete", func(milestone *issues_model.Milestone) int {
return -milestone.Completeness return -milestone.Completeness
}) })
test("leastissues", func(milestone *Milestone) int { test("leastissues", func(milestone *issues_model.Milestone) int {
return milestone.NumIssues return milestone.NumIssues
}) })
test("mostissues", func(milestone *Milestone) int { test("mostissues", func(milestone *issues_model.Milestone) int {
return -milestone.NumIssues return -milestone.NumIssues
}) })
test("soonestduedate", func(milestone *Milestone) int { test("soonestduedate", func(milestone *issues_model.Milestone) int {
return int(milestone.DeadlineUnix) return int(milestone.DeadlineUnix)
}) })
} }
@ -249,7 +263,7 @@ func TestGetMilestonesStats(t *testing.T) {
test := func(repoID int64) { test := func(repoID int64) {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository) 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.NoError(t, err)
assert.EqualValues(t, repo.NumMilestones-repo.NumClosedMilestones, stats.OpenCount) assert.EqualValues(t, repo.NumMilestones-repo.NumClosedMilestones, stats.OpenCount)
assert.EqualValues(t, repo.NumClosedMilestones, stats.ClosedCount) assert.EqualValues(t, repo.NumClosedMilestones, stats.ClosedCount)
@ -258,7 +272,7 @@ func TestGetMilestonesStats(t *testing.T) {
test(2) test(2)
test(3) 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.NoError(t, err)
assert.EqualValues(t, 0, stats.OpenCount) assert.EqualValues(t, 0, stats.OpenCount)
assert.EqualValues(t, 0, stats.ClosedCount) 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) repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*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.NoError(t, err)
assert.EqualValues(t, repo1.NumOpenMilestones+repo2.NumOpenMilestones, milestoneStats.OpenCount) assert.EqualValues(t, repo1.NumOpenMilestones+repo2.NumOpenMilestones, milestoneStats.OpenCount)
assert.EqualValues(t, repo1.NumClosedMilestones+repo2.NumClosedMilestones, milestoneStats.ClosedCount) 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 // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package models package issues
import ( import (
"context" "context"
@ -25,6 +25,83 @@ import (
"xorm.io/builder" "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 // PullRequestType defines pull request type
type PullRequestType int type PullRequestType int
@ -98,7 +175,8 @@ func init() {
db.RegisterModel(new(PullRequest)) 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}) deleteCond := builder.Select("id").From("pull_request").Where(builder.Eq{"pull_request.base_repo_id": repoID})
// Delete scheduled auto merges // Delete scheduled auto merges
@ -219,7 +297,7 @@ func (pr *PullRequest) LoadIssueCtx(ctx context.Context) (err error) {
return nil return nil
} }
pr.Issue, err = getIssueByID(ctx, pr.IssueID) pr.Issue, err = GetIssueByID(ctx, pr.IssueID)
if err == nil { if err == nil {
pr.Issue.PullRequest = pr pr.Issue.PullRequest = pr
} }
@ -420,14 +498,14 @@ func NewPullRequest(outerCtx context.Context, repo *repo_model.Repository, issue
defer committer.Close() defer committer.Close()
ctx.WithContext(outerCtx) ctx.WithContext(outerCtx)
if err = newIssue(ctx, issue.Poster, NewIssueOptions{ if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{
Repo: repo, Repo: repo,
Issue: issue, Issue: issue,
LabelIDs: labelIDs, LabelIDs: labelIDs,
Attachments: uuids, Attachments: uuids,
IsPull: true, IsPull: true,
}); err != nil { }); err != nil {
if IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) { if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) {
return err return err
} }
return fmt.Errorf("newIssue: %v", err) return fmt.Errorf("newIssue: %v", err)
@ -691,3 +769,70 @@ func (pr *PullRequest) Mergeable() bool {
return pr.Status != PullRequestStatusChecking && pr.Status != PullRequestStatusConflict && return pr.Status != PullRequestStatusChecking && pr.Status != PullRequestStatusConflict &&
pr.Status != PullRequestStatusError && !pr.IsWorkInProgress() 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 // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package models package issues
import ( import (
"context" "context"

View File

@ -2,12 +2,13 @@
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package models package issues_test
import ( import (
"testing" "testing"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -15,7 +16,7 @@ import (
func TestPullRequest_LoadAttributes(t *testing.T) { func TestPullRequest_LoadAttributes(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) 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.NoError(t, pr.LoadAttributes())
assert.NotNil(t, pr.Merger) assert.NotNil(t, pr.Merger)
assert.Equal(t, pr.MergerID, pr.Merger.ID) assert.Equal(t, pr.MergerID, pr.Merger.ID)
@ -23,7 +24,7 @@ func TestPullRequest_LoadAttributes(t *testing.T) {
func TestPullRequest_LoadIssue(t *testing.T) { func TestPullRequest_LoadIssue(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) 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.NoError(t, pr.LoadIssue())
assert.NotNil(t, pr.Issue) assert.NotNil(t, pr.Issue)
assert.Equal(t, int64(2), pr.Issue.ID) assert.Equal(t, int64(2), pr.Issue.ID)
@ -34,7 +35,7 @@ func TestPullRequest_LoadIssue(t *testing.T) {
func TestPullRequest_LoadBaseRepo(t *testing.T) { func TestPullRequest_LoadBaseRepo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) 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.NoError(t, pr.LoadBaseRepo())
assert.NotNil(t, pr.BaseRepo) assert.NotNil(t, pr.BaseRepo)
assert.Equal(t, pr.BaseRepoID, pr.BaseRepo.ID) assert.Equal(t, pr.BaseRepoID, pr.BaseRepo.ID)
@ -45,7 +46,7 @@ func TestPullRequest_LoadBaseRepo(t *testing.T) {
func TestPullRequest_LoadHeadRepo(t *testing.T) { func TestPullRequest_LoadHeadRepo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) 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.NoError(t, pr.LoadHeadRepo())
assert.NotNil(t, pr.HeadRepo) assert.NotNil(t, pr.HeadRepo)
assert.Equal(t, pr.HeadRepoID, pr.HeadRepo.ID) assert.Equal(t, pr.HeadRepoID, pr.HeadRepo.ID)
@ -57,7 +58,7 @@ func TestPullRequest_LoadHeadRepo(t *testing.T) {
func TestPullRequestsNewest(t *testing.T) { func TestPullRequestsNewest(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
prs, count, err := PullRequests(1, &PullRequestsOptions{ prs, count, err := issues_model.PullRequests(1, &issues_model.PullRequestsOptions{
ListOptions: db.ListOptions{ ListOptions: db.ListOptions{
Page: 1, Page: 1,
}, },
@ -76,7 +77,7 @@ func TestPullRequestsNewest(t *testing.T) {
func TestPullRequestsOldest(t *testing.T) { func TestPullRequestsOldest(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
prs, count, err := PullRequests(1, &PullRequestsOptions{ prs, count, err := issues_model.PullRequests(1, &issues_model.PullRequestsOptions{
ListOptions: db.ListOptions{ ListOptions: db.ListOptions{
Page: 1, Page: 1,
}, },
@ -95,30 +96,30 @@ func TestPullRequestsOldest(t *testing.T) {
func TestGetUnmergedPullRequest(t *testing.T) { func TestGetUnmergedPullRequest(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) 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.NoError(t, err)
assert.Equal(t, int64(2), pr.ID) 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.Error(t, err)
assert.True(t, IsErrPullRequestNotExist(err)) assert.True(t, issues_model.IsErrPullRequestNotExist(err))
} }
func TestHasUnmergedPullRequestsByHeadInfo(t *testing.T) { func TestHasUnmergedPullRequestsByHeadInfo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) 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.NoError(t, err)
assert.Equal(t, true, exist) 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.NoError(t, err)
assert.Equal(t, false, exist) assert.Equal(t, false, exist)
} }
func TestGetUnmergedPullRequestsByHeadInfo(t *testing.T) { func TestGetUnmergedPullRequestsByHeadInfo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
prs, err := GetUnmergedPullRequestsByHeadInfo(1, "branch2") prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(1, "branch2")
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, prs, 1) assert.Len(t, prs, 1)
for _, pr := range prs { for _, pr := range prs {
@ -129,7 +130,7 @@ func TestGetUnmergedPullRequestsByHeadInfo(t *testing.T) {
func TestGetUnmergedPullRequestsByBaseInfo(t *testing.T) { func TestGetUnmergedPullRequestsByBaseInfo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
prs, err := GetUnmergedPullRequestsByBaseInfo(1, "master") prs, err := issues_model.GetUnmergedPullRequestsByBaseInfo(1, "master")
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, prs, 1) assert.Len(t, prs, 1)
pr := prs[0] pr := prs[0]
@ -140,51 +141,51 @@ func TestGetUnmergedPullRequestsByBaseInfo(t *testing.T) {
func TestGetPullRequestByIndex(t *testing.T) { func TestGetPullRequestByIndex(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) 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.NoError(t, err)
assert.Equal(t, int64(1), pr.BaseRepoID) assert.Equal(t, int64(1), pr.BaseRepoID)
assert.Equal(t, int64(2), pr.Index) 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.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.Error(t, err)
assert.True(t, IsErrPullRequestNotExist(err)) assert.True(t, issues_model.IsErrPullRequestNotExist(err))
} }
func TestGetPullRequestByID(t *testing.T) { func TestGetPullRequestByID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
pr, err := GetPullRequestByID(db.DefaultContext, 1) pr, err := issues_model.GetPullRequestByID(db.DefaultContext, 1)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, int64(1), pr.ID) assert.Equal(t, int64(1), pr.ID)
assert.Equal(t, int64(2), pr.IssueID) assert.Equal(t, int64(2), pr.IssueID)
_, err = GetPullRequestByID(db.DefaultContext, 9223372036854775807) _, err = issues_model.GetPullRequestByID(db.DefaultContext, 9223372036854775807)
assert.Error(t, err) assert.Error(t, err)
assert.True(t, IsErrPullRequestNotExist(err)) assert.True(t, issues_model.IsErrPullRequestNotExist(err))
} }
func TestGetPullRequestByIssueID(t *testing.T) { func TestGetPullRequestByIssueID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
pr, err := GetPullRequestByIssueID(db.DefaultContext, 2) pr, err := issues_model.GetPullRequestByIssueID(db.DefaultContext, 2)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, int64(2), pr.IssueID) assert.Equal(t, int64(2), pr.IssueID)
_, err = GetPullRequestByIssueID(db.DefaultContext, 9223372036854775807) _, err = issues_model.GetPullRequestByIssueID(db.DefaultContext, 9223372036854775807)
assert.Error(t, err) assert.Error(t, err)
assert.True(t, IsErrPullRequestNotExist(err)) assert.True(t, issues_model.IsErrPullRequestNotExist(err))
} }
func TestPullRequest_Update(t *testing.T) { func TestPullRequest_Update(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) 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.BaseBranch = "baseBranch"
pr.HeadBranch = "headBranch" pr.HeadBranch = "headBranch"
pr.Update() 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, "baseBranch", pr.BaseBranch)
assert.Equal(t, "headBranch", pr.HeadBranch) assert.Equal(t, "headBranch", pr.HeadBranch)
unittest.CheckConsistencyFor(t, pr) unittest.CheckConsistencyFor(t, pr)
@ -192,14 +193,14 @@ func TestPullRequest_Update(t *testing.T) {
func TestPullRequest_UpdateCols(t *testing.T) { func TestPullRequest_UpdateCols(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
pr := &PullRequest{ pr := &issues_model.PullRequest{
ID: 1, ID: 1,
BaseBranch: "baseBranch", BaseBranch: "baseBranch",
HeadBranch: "headBranch", HeadBranch: "headBranch",
} }
assert.NoError(t, pr.UpdateCols("head_branch")) 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, "master", pr.BaseBranch)
assert.Equal(t, "headBranch", pr.HeadBranch) assert.Equal(t, "headBranch", pr.HeadBranch)
unittest.CheckConsistencyFor(t, pr) unittest.CheckConsistencyFor(t, pr)
@ -208,17 +209,17 @@ func TestPullRequest_UpdateCols(t *testing.T) {
func TestPullRequestList_LoadAttributes(t *testing.T) { func TestPullRequestList_LoadAttributes(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
prs := []*PullRequest{ prs := []*issues_model.PullRequest{
unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest), unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest),
unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 2}).(*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 { for _, pr := range prs {
assert.NotNil(t, pr.Issue) assert.NotNil(t, pr.Issue)
assert.Equal(t, pr.IssueID, pr.Issue.ID) 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 // TODO TestAddTestPullRequestTask
@ -226,7 +227,7 @@ func TestPullRequestList_LoadAttributes(t *testing.T) {
func TestPullRequest_IsWorkInProgress(t *testing.T) { func TestPullRequest_IsWorkInProgress(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) 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() pr.LoadIssue()
assert.False(t, pr.IsWorkInProgress()) assert.False(t, pr.IsWorkInProgress())
@ -241,7 +242,7 @@ func TestPullRequest_IsWorkInProgress(t *testing.T) {
func TestPullRequest_GetWorkInProgressPrefixWorkInProgress(t *testing.T) { func TestPullRequest_GetWorkInProgressPrefixWorkInProgress(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) 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() pr.LoadIssue()
assert.Empty(t, pr.GetWorkInProgressPrefix()) assert.Empty(t, pr.GetWorkInProgressPrefix())
@ -253,3 +254,24 @@ func TestPullRequest_GetWorkInProgressPrefixWorkInProgress(t *testing.T) {
pr.Issue.Title = "[wip] " + original pr.Issue.Title = "[wip] " + original
assert.Equal(t, "[wip]", pr.GetWorkInProgressPrefix()) 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 // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package issues package issues_test
import ( import (
"testing" "testing"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
@ -17,12 +18,12 @@ import (
) )
func addReaction(t *testing.T, doerID, issueID, commentID int64, content string) { func addReaction(t *testing.T, doerID, issueID, commentID int64, content string) {
var reaction *Reaction var reaction *issues_model.Reaction
var err error var err error
if commentID == 0 { if commentID == 0 {
reaction, err = CreateIssueReaction(doerID, issueID, content) reaction, err = issues_model.CreateIssueReaction(doerID, issueID, content)
} else { } else {
reaction, err = CreateCommentReaction(doerID, issueID, commentID, content) reaction, err = issues_model.CreateCommentReaction(doerID, issueID, commentID, content)
} }
assert.NoError(t, err) assert.NoError(t, err)
assert.NotNil(t, reaction) assert.NotNil(t, reaction)
@ -37,7 +38,7 @@ func TestIssueAddReaction(t *testing.T) {
addReaction(t, user1.ID, issue1ID, 0, "heart") 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) { func TestIssueAddDuplicateReaction(t *testing.T) {
@ -49,15 +50,15 @@ func TestIssueAddDuplicateReaction(t *testing.T) {
addReaction(t, user1.ID, issue1ID, 0, "heart") addReaction(t, user1.ID, issue1ID, 0, "heart")
reaction, err := CreateReaction(&ReactionOptions{ reaction, err := issues_model.CreateReaction(&issues_model.ReactionOptions{
DoerID: user1.ID, DoerID: user1.ID,
IssueID: issue1ID, IssueID: issue1ID,
Type: "heart", Type: "heart",
}) })
assert.Error(t, err) 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) assert.Equal(t, existingR.ID, reaction.ID)
} }
@ -70,10 +71,10 @@ func TestIssueDeleteReaction(t *testing.T) {
addReaction(t, user1.ID, issue1ID, 0, "heart") 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) 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) { func TestIssueReactionCount(t *testing.T) {
@ -98,7 +99,7 @@ func TestIssueReactionCount(t *testing.T) {
addReaction(t, user4.ID, issueID, 0, "heart") addReaction(t, user4.ID, issueID, 0, "heart")
addReaction(t, ghost.ID, issueID, 0, "-1") 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, IssueID: issueID,
}) })
assert.NoError(t, err) assert.NoError(t, err)
@ -128,7 +129,7 @@ func TestIssueCommentAddReaction(t *testing.T) {
addReaction(t, user1.ID, issue1ID, comment1ID, "heart") 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) { func TestIssueCommentDeleteReaction(t *testing.T) {
@ -147,7 +148,7 @@ func TestIssueCommentDeleteReaction(t *testing.T) {
addReaction(t, user3.ID, issue1ID, comment1ID, "heart") addReaction(t, user3.ID, issue1ID, comment1ID, "heart")
addReaction(t, user4.ID, issue1ID, comment1ID, "+1") 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, IssueID: issue1ID,
CommentID: comment1ID, CommentID: comment1ID,
}) })
@ -168,7 +169,7 @@ func TestIssueCommentReactionCount(t *testing.T) {
var comment1ID int64 = 1 var comment1ID int64 = 1
addReaction(t, user1.ID, issue1ID, comment1ID, "heart") 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 // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package models package issues
import ( import (
"context" "context"
@ -17,11 +17,47 @@ import (
"code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"xorm.io/builder" "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 // ReviewType defines the sort of feedback a review gives
type ReviewType int type ReviewType int
@ -105,7 +141,7 @@ func (r *Review) loadIssue(ctx context.Context) (err error) {
if r.Issue != nil { if r.Issue != nil {
return return
} }
r.Issue, err = getIssueByID(ctx, r.IssueID) r.Issue, err = GetIssueByID(ctx, r.IssueID)
return return
} }
@ -967,3 +1003,16 @@ func (r *Review) GetExternalName() string { return r.OriginalAuthor }
// GetExternalID ExternalUserRemappable interface // GetExternalID ExternalUserRemappable interface
func (r *Review) GetExternalID() int64 { return r.OriginalAuthorID } 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 // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package models package issues_test
import ( import (
"testing" "testing"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
@ -16,34 +17,34 @@ import (
func TestGetReviewByID(t *testing.T) { func TestGetReviewByID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
review, err := GetReviewByID(db.DefaultContext, 1) review, err := issues_model.GetReviewByID(db.DefaultContext, 1)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "Demo Review", review.Content) 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.Error(t, err)
assert.True(t, IsErrReviewNotExist(err), "IsErrReviewNotExist") assert.True(t, issues_model.IsErrReviewNotExist(err), "IsErrReviewNotExist")
} }
func TestReview_LoadAttributes(t *testing.T) { func TestReview_LoadAttributes(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) 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.NoError(t, review.LoadAttributes(db.DefaultContext))
assert.NotNil(t, review.Issue) assert.NotNil(t, review.Issue)
assert.NotNil(t, review.Reviewer) 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)) 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)) assert.Error(t, invalidReview2.LoadAttributes(db.DefaultContext))
} }
func TestReview_LoadCodeComments(t *testing.T) { func TestReview_LoadCodeComments(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) 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.LoadAttributes(db.DefaultContext))
assert.NoError(t, review.LoadCodeComments(db.DefaultContext)) assert.NoError(t, review.LoadCodeComments(db.DefaultContext))
assert.Len(t, review.CodeComments, 1) assert.Len(t, review.CodeComments, 1)
@ -51,18 +52,18 @@ func TestReview_LoadCodeComments(t *testing.T) {
} }
func TestReviewType_Icon(t *testing.T) { func TestReviewType_Icon(t *testing.T) {
assert.Equal(t, "check", ReviewTypeApprove.Icon()) assert.Equal(t, "check", issues_model.ReviewTypeApprove.Icon())
assert.Equal(t, "diff", ReviewTypeReject.Icon()) assert.Equal(t, "diff", issues_model.ReviewTypeReject.Icon())
assert.Equal(t, "comment", ReviewTypeComment.Icon()) assert.Equal(t, "comment", issues_model.ReviewTypeComment.Icon())
assert.Equal(t, "comment", ReviewTypeUnknown.Icon()) assert.Equal(t, "comment", issues_model.ReviewTypeUnknown.Icon())
assert.Equal(t, "dot-fill", ReviewTypeRequest.Icon()) assert.Equal(t, "dot-fill", issues_model.ReviewTypeRequest.Icon())
assert.Equal(t, "comment", ReviewType(6).Icon()) assert.Equal(t, "comment", issues_model.ReviewType(6).Icon())
} }
func TestFindReviews(t *testing.T) { func TestFindReviews(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
reviews, err := FindReviews(db.DefaultContext, FindReviewOptions{ reviews, err := issues_model.FindReviews(db.DefaultContext, issues_model.FindReviewOptions{
Type: ReviewTypeApprove, Type: issues_model.ReviewTypeApprove,
IssueID: 2, IssueID: 2,
ReviewerID: 1, ReviewerID: 1,
}) })
@ -73,66 +74,66 @@ func TestFindReviews(t *testing.T) {
func TestGetCurrentReview(t *testing.T) { func TestGetCurrentReview(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) 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) 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.NoError(t, err)
assert.NotNil(t, review) 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) assert.Equal(t, "Pending Review", review.Content)
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 7}).(*user_model.User) 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.Error(t, err)
assert.True(t, IsErrReviewNotExist(err)) assert.True(t, issues_model.IsErrReviewNotExist(err))
assert.Nil(t, review2) assert.Nil(t, review2)
} }
func TestCreateReview(t *testing.T) { func TestCreateReview(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) 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) 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", Content: "New Review",
Type: ReviewTypePending, Type: issues_model.ReviewTypePending,
Issue: issue, Issue: issue,
Reviewer: user, Reviewer: user,
}) })
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "New Review", review.Content) 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) { func TestGetReviewersByIssueID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) 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) user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*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) user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User)
expectedReviews := []*Review{} expectedReviews := []*issues_model.Review{}
expectedReviews = append(expectedReviews, expectedReviews = append(expectedReviews,
&Review{ &issues_model.Review{
Reviewer: user3, Reviewer: user3,
Type: ReviewTypeReject, Type: issues_model.ReviewTypeReject,
UpdatedUnix: 946684812, UpdatedUnix: 946684812,
}, },
&Review{ &issues_model.Review{
Reviewer: user4, Reviewer: user4,
Type: ReviewTypeApprove, Type: issues_model.ReviewTypeApprove,
UpdatedUnix: 946684813, UpdatedUnix: 946684813,
}, },
&Review{ &issues_model.Review{
Reviewer: user2, Reviewer: user2,
Type: ReviewTypeReject, Type: issues_model.ReviewTypeReject,
UpdatedUnix: 946684814, UpdatedUnix: 946684814,
}) })
allReviews, err := GetReviewersByIssueID(issue.ID) allReviews, err := issues_model.GetReviewersByIssueID(issue.ID)
for _, reviewer := range allReviews { for _, reviewer := range allReviews {
assert.NoError(t, reviewer.LoadReviewer()) assert.NoError(t, reviewer.LoadReviewer())
} }
@ -149,53 +150,53 @@ func TestGetReviewersByIssueID(t *testing.T) {
func TestDismissReview(t *testing.T) { func TestDismissReview(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
rejectReviewExample := unittest.AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review) rejectReviewExample := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9}).(*issues_model.Review)
requestReviewExample := unittest.AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review) requestReviewExample := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11}).(*issues_model.Review)
approveReviewExample := unittest.AssertExistsAndLoadBean(t, &Review{ID: 8}).(*Review) approveReviewExample := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 8}).(*issues_model.Review)
assert.False(t, rejectReviewExample.Dismissed) assert.False(t, rejectReviewExample.Dismissed)
assert.False(t, requestReviewExample.Dismissed) assert.False(t, requestReviewExample.Dismissed)
assert.False(t, approveReviewExample.Dismissed) assert.False(t, approveReviewExample.Dismissed)
assert.NoError(t, DismissReview(rejectReviewExample, true)) assert.NoError(t, issues_model.DismissReview(rejectReviewExample, true))
rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review) rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9}).(*issues_model.Review)
requestReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review) requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11}).(*issues_model.Review)
assert.True(t, rejectReviewExample.Dismissed) assert.True(t, rejectReviewExample.Dismissed)
assert.False(t, requestReviewExample.Dismissed) assert.False(t, requestReviewExample.Dismissed)
assert.NoError(t, DismissReview(requestReviewExample, true)) assert.NoError(t, issues_model.DismissReview(requestReviewExample, true))
rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review) rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9}).(*issues_model.Review)
requestReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review) requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11}).(*issues_model.Review)
assert.True(t, rejectReviewExample.Dismissed) assert.True(t, rejectReviewExample.Dismissed)
assert.False(t, requestReviewExample.Dismissed) assert.False(t, requestReviewExample.Dismissed)
assert.False(t, approveReviewExample.Dismissed) assert.False(t, approveReviewExample.Dismissed)
assert.NoError(t, DismissReview(requestReviewExample, true)) assert.NoError(t, issues_model.DismissReview(requestReviewExample, true))
rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review) rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9}).(*issues_model.Review)
requestReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review) requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11}).(*issues_model.Review)
assert.True(t, rejectReviewExample.Dismissed) assert.True(t, rejectReviewExample.Dismissed)
assert.False(t, requestReviewExample.Dismissed) assert.False(t, requestReviewExample.Dismissed)
assert.False(t, approveReviewExample.Dismissed) assert.False(t, approveReviewExample.Dismissed)
assert.NoError(t, DismissReview(requestReviewExample, false)) assert.NoError(t, issues_model.DismissReview(requestReviewExample, false))
rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review) rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9}).(*issues_model.Review)
requestReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review) requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11}).(*issues_model.Review)
assert.True(t, rejectReviewExample.Dismissed) assert.True(t, rejectReviewExample.Dismissed)
assert.False(t, requestReviewExample.Dismissed) assert.False(t, requestReviewExample.Dismissed)
assert.False(t, approveReviewExample.Dismissed) assert.False(t, approveReviewExample.Dismissed)
assert.NoError(t, DismissReview(requestReviewExample, false)) assert.NoError(t, issues_model.DismissReview(requestReviewExample, false))
rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review) rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9}).(*issues_model.Review)
requestReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review) requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11}).(*issues_model.Review)
assert.True(t, rejectReviewExample.Dismissed) assert.True(t, rejectReviewExample.Dismissed)
assert.False(t, requestReviewExample.Dismissed) assert.False(t, requestReviewExample.Dismissed)
assert.False(t, approveReviewExample.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, rejectReviewExample.Dismissed)
assert.False(t, requestReviewExample.Dismissed) assert.False(t, requestReviewExample.Dismissed)
assert.False(t, approveReviewExample.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, rejectReviewExample.Dismissed)
assert.False(t, requestReviewExample.Dismissed) assert.False(t, requestReviewExample.Dismissed)
assert.True(t, approveReviewExample.Dismissed) assert.True(t, approveReviewExample.Dismissed)

View File

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

View File

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

View File

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

View File

@ -10,8 +10,6 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues" issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"xorm.io/builder"
) )
// InsertMilestones creates milestones of repository. // InsertMilestones creates milestones of repository.
@ -41,7 +39,7 @@ func InsertMilestones(ms ...*issues_model.Milestone) (err error) {
} }
// InsertIssues insert issues to database // InsertIssues insert issues to database
func InsertIssues(issues ...*Issue) error { func InsertIssues(issues ...*issues_model.Issue) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext()
if err != nil { if err != nil {
return err return err
@ -56,14 +54,14 @@ func InsertIssues(issues ...*Issue) error {
return committer.Commit() 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) sess := db.GetEngine(ctx)
if _, err := sess.NoAutoTime().Insert(issue); err != nil { if _, err := sess.NoAutoTime().Insert(issue); err != nil {
return err return err
} }
issueLabels := make([]IssueLabel, 0, len(issue.Labels)) issueLabels := make([]issues_model.IssueLabel, 0, len(issue.Labels))
for _, label := range issue.Labels { for _, label := range issue.Labels {
issueLabels = append(issueLabels, IssueLabel{ issueLabels = append(issueLabels, issues_model.IssueLabel{
IssueID: issue.ID, IssueID: issue.ID,
LabelID: label.ID, LabelID: label.ID,
}) })
@ -95,7 +93,7 @@ func insertIssue(ctx context.Context, issue *Issue) error {
} }
// InsertIssueComments inserts many comments of issues. // InsertIssueComments inserts many comments of issues.
func InsertIssueComments(comments []*Comment) error { func InsertIssueComments(comments []*issues_model.Comment) error {
if len(comments) == 0 { if len(comments) == 0 {
return nil return nil
} }
@ -127,7 +125,8 @@ func InsertIssueComments(comments []*Comment) error {
} }
for issueID := range issueIDs { 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 return err
} }
} }
@ -135,7 +134,7 @@ func InsertIssueComments(comments []*Comment) error {
} }
// InsertPullRequests inserted pull requests // InsertPullRequests inserted pull requests
func InsertPullRequests(prs ...*PullRequest) error { func InsertPullRequests(prs ...*issues_model.PullRequest) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext()
if err != nil { if err != nil {
return err return err
@ -182,37 +181,13 @@ func InsertReleases(rels ...*Release) error {
return committer.Commit() 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 // UpdateMigrationsByType updates all migrated repositories' posterid from gitServiceType to replace originalAuthorID to posterID
func UpdateMigrationsByType(tp structs.GitServiceType, externalUserID string, userID int64) error { 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 return err
} }
if err := UpdateCommentsMigrationsByType(tp, externalUserID, userID); err != nil { if err := issues_model.UpdateCommentsMigrationsByType(tp, externalUserID, userID); err != nil {
return err return err
} }
@ -220,8 +195,8 @@ func UpdateMigrationsByType(tp structs.GitServiceType, externalUserID string, us
return err return err
} }
if err := UpdateReactionsMigrationsByType(tp, externalUserID, userID); err != nil { if err := issues_model.UpdateReactionsMigrationsByType(tp, externalUserID, userID); err != nil {
return err 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" reponame := "repo1"
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: reponame}).(*repo_model.Repository) 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) 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) milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}).(*issues_model.Milestone)
assert.EqualValues(t, milestone.ID, 1) assert.EqualValues(t, milestone.ID, 1)
reaction := &issues_model.Reaction{ reaction := &issues_model.Reaction{
@ -51,7 +51,7 @@ func assertCreateIssues(t *testing.T, isPull bool) {
foreignIndex := int64(12345) foreignIndex := int64(12345)
title := "issuetitle1" title := "issuetitle1"
is := &Issue{ is := &issues_model.Issue{
RepoID: repo.ID, RepoID: repo.ID,
MilestoneID: milestone.ID, MilestoneID: milestone.ID,
Repo: repo, Repo: repo,
@ -61,7 +61,7 @@ func assertCreateIssues(t *testing.T, isPull bool) {
PosterID: owner.ID, PosterID: owner.ID,
Poster: owner, Poster: owner,
IsClosed: true, IsClosed: true,
Labels: []*Label{label}, Labels: []*issues_model.Label{label},
Reactions: []*issues_model.Reaction{reaction}, Reactions: []*issues_model.Reaction{reaction},
ForeignReference: &foreignreference.ForeignReference{ ForeignReference: &foreignreference.ForeignReference{
ForeignIndex: strconv.FormatInt(foreignIndex, 10), ForeignIndex: strconv.FormatInt(foreignIndex, 10),
@ -72,9 +72,9 @@ func assertCreateIssues(t *testing.T, isPull bool) {
err := InsertIssues(is) err := InsertIssues(is)
assert.NoError(t, err) 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) assert.Nil(t, i.ForeignReference)
err = i.LoadAttributes() err = i.LoadAttributes(db.DefaultContext)
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, strconv.FormatInt(foreignIndex, 10), i.ForeignReference.ForeignIndex) assert.EqualValues(t, strconv.FormatInt(foreignIndex, 10), i.ForeignReference.ForeignIndex)
unittest.AssertExistsAndLoadBean(t, &issues_model.Reaction{Type: "heart", UserID: owner.ID, IssueID: i.ID}) 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) { func TestMigrate_InsertIssueComments(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) 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) _ = issue.LoadRepo(db.DefaultContext)
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}).(*user_model.User) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}).(*user_model.User)
reaction := &issues_model.Reaction{ reaction := &issues_model.Reaction{
@ -98,7 +98,7 @@ func TestMigrate_InsertIssueComments(t *testing.T) {
UserID: owner.ID, UserID: owner.ID,
} }
comment := &Comment{ comment := &issues_model.Comment{
PosterID: owner.ID, PosterID: owner.ID,
Poster: owner, Poster: owner,
IssueID: issue.ID, IssueID: issue.ID,
@ -106,13 +106,13 @@ func TestMigrate_InsertIssueComments(t *testing.T) {
Reactions: []*issues_model.Reaction{reaction}, Reactions: []*issues_model.Reaction{reaction},
} }
err := InsertIssueComments([]*Comment{comment}) err := InsertIssueComments([]*issues_model.Comment{comment})
assert.NoError(t, err) 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) assert.EqualValues(t, issue.NumComments+1, issueModified.NumComments)
unittest.CheckConsistencyFor(t, &Issue{}) unittest.CheckConsistencyFor(t, &issues_model.Issue{})
} }
func TestMigrate_InsertPullRequests(t *testing.T) { 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) 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) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
i := &Issue{ i := &issues_model.Issue{
RepoID: repo.ID, RepoID: repo.ID,
Repo: repo, Repo: repo,
Title: "title1", Title: "title1",
@ -131,16 +131,16 @@ func TestMigrate_InsertPullRequests(t *testing.T) {
Poster: owner, Poster: owner,
} }
p := &PullRequest{ p := &issues_model.PullRequest{
Issue: i, Issue: i,
} }
err := InsertPullRequests(p) err := InsertPullRequests(p)
assert.NoError(t, err) 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) { func TestMigrate_InsertReleases(t *testing.T) {

View File

@ -419,7 +419,7 @@ func EnsureUpToDate(x *xorm.Engine) error {
} }
if currentDB < 0 { if currentDB < 0 {
return fmt.Errorf("Database has not been initialised") return fmt.Errorf("Database has not been initialized")
} }
if minDBVersion > currentDB { if minDBVersion > currentDB {
@ -953,7 +953,7 @@ func dropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin
return nil 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 { func modifyColumn(x *xorm.Engine, tableName string, col *schemas.Column) error {
var indexes map[string]*schemas.Index var indexes map[string]*schemas.Index
var err error var err error

View File

@ -131,7 +131,7 @@ func addBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error {
Authorize int 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) { getUserRepoPermission := func(sess *xorm.Session, repo *Repository, user *User) (Permission, error) {
var perm Permission var perm Permission

View File

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

View File

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

View File

@ -13,6 +13,7 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git" 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/organization"
access_model "code.gitea.io/gitea/models/perm/access" access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo" 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 // 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 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 // 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 return err
} }
} }

View File

@ -281,7 +281,7 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
&access_model.Access{RepoID: repo.ID}, &access_model.Access{RepoID: repo.ID},
&Action{RepoID: repo.ID}, &Action{RepoID: repo.ID},
&repo_model.Collaboration{RepoID: repoID}, &repo_model.Collaboration{RepoID: repoID},
&Comment{RefRepoID: repoID}, &issues_model.Comment{RefRepoID: repoID},
&git_model.CommitStatus{RepoID: repoID}, &git_model.CommitStatus{RepoID: repoID},
&git_model.DeletedBranch{RepoID: repoID}, &git_model.DeletedBranch{RepoID: repoID},
&webhook.HookTask{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 // Delete Labels and related objects
if err := deleteLabelsByRepoID(ctx, repoID); err != nil { if err := issues_model.DeleteLabelsByRepoID(ctx, repoID); err != nil {
return err return err
} }
// Delete Pulls and related objects // Delete Pulls and related objects
if err := deletePullsByBaseRepoID(ctx, repoID); err != nil { if err := issues_model.DeletePullsByBaseRepoID(ctx, repoID); err != nil {
return err return err
} }
// Delete Issues and related objects // Delete Issues and related objects
var attachmentPaths []string var attachmentPaths []string
if attachmentPaths, err = deleteIssuesByRepoID(ctx, repoID); err != nil { if attachmentPaths, err = issues_model.DeleteIssuesByRepoID(ctx, repoID); err != nil {
return err 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 { 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 { func repoStatsCorrectNumClosedPulls(ctx context.Context, id int64) error {
return repoStatsCorrectNumClosed(ctx, id, true, "num_closed_pulls") return repo_model.StatsCorrectNumClosed(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
} }
func statsQuery(args ...interface{}) func(context.Context) ([]map[string][]byte, error) { func statsQuery(args ...interface{}) func(context.Context) ([]map[string][]byte, error) {
@ -687,12 +682,11 @@ func CheckRepoStats(ctx context.Context) error {
continue 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 { if err != nil {
log.Error("Select count of forks[%d]: %v", repo.ID, err) log.Error("Select count of forks[%d]: %v", repo.ID, err)
continue continue
} }
repo.NumForks = int(parseCountResult(rawResult))
if _, err = e.ID(repo.ID).Cols("num_forks").Update(repo); err != nil { if _, err = e.ID(repo.ID).Cols("num_forks").Update(repo); err != nil {
log.Error("UpdateRepository[%d]: %v", id, err) log.Error("UpdateRepository[%d]: %v", id, err)

View File

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

View File

@ -10,6 +10,7 @@ import (
"fmt" "fmt"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access" access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo" 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}). 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})). 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 fmt.Errorf("Could not delete assignee[%d] %v", uid, err)
} }
return nil 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 // 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" "os"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/organization"
access_model "code.gitea.io/gitea/models/perm/access" access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo" 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 INNER JOIN issue ON issue.id = com.issue_id
WHERE 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 != ?)) 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) 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.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.Oauth = 0
stats.Counter.Follow, _ = e.Count(new(user_model.Follow)) stats.Counter.Follow, _ = e.Count(new(user_model.Follow))
stats.Counter.Mirror, _ = e.Count(new(repo_model.Mirror)) stats.Counter.Mirror, _ = e.Count(new(repo_model.Mirror))
@ -105,7 +105,7 @@ func GetStatistic() (stats Statistic) {
stats.Counter.AuthSource = auth.CountSources() stats.Counter.AuthSource = auth.CountSources()
stats.Counter.Webhook, _ = e.Count(new(webhook.Webhook)) stats.Counter.Webhook, _ = e.Count(new(webhook.Webhook))
stats.Counter.Milestone, _ = e.Count(new(issues_model.Milestone)) 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.HookTask, _ = e.Count(new(webhook.HookTask))
stats.Counter.Team, _ = e.Count(new(organization.Team)) stats.Counter.Team, _ = e.Count(new(organization.Team))
stats.Counter.Attachment, _ = e.Count(new(repo_model.Attachment)) stats.Counter.Attachment, _ = e.Count(new(repo_model.Attachment))

View File

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

View File

@ -18,6 +18,7 @@ import (
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git" 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" access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
unit_model "code.gitea.io/gitea/models/unit" 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 // CanWriteToBranch checks if the branch is writable by the user
func (r *Repository) CanWriteToBranch(user *user_model.User, branch string) bool { 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. // 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. // 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: // Checking for following:
// 1. Is timetracker enabled // 1. Is timetracker enabled
// 2. Is the user a contributor, admin, poster or assignee and do the repository policies require this? // 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() || return r.Repository.IsTimetrackerEnabled() && (!r.Repository.AllowOnlyContributorsToTrackTime() ||
r.Permission.CanWriteIssuesOrPulls(issue.IsPull) || issue.IsPoster(user.ID) || isAssigned) 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 ctx.Data["Permission"] = &ctx.Repo.Permission
if repo.IsMirror { if repo.IsMirror {
ctx.Repo.Mirror, err = repo_model.GetMirrorByRepoID(ctx, repo.ID)
// Check if the mirror has finsihed migrationg, only then we can if err == nil {
// 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.Repo = repo ctx.Repo.Mirror.Repo = repo
ctx.Data["IsPullMirror"] = true
ctx.Data["MirrorEnablePrune"] = ctx.Repo.Mirror.EnablePrune ctx.Data["MirrorEnablePrune"] = ctx.Repo.Mirror.EnablePrune
ctx.Data["MirrorInterval"] = ctx.Repo.Mirror.Interval ctx.Data["MirrorInterval"] = ctx.Repo.Mirror.Interval
ctx.Data["Mirror"] = ctx.Repo.Mirror 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" "strings"
"time" "time"
"code.gitea.io/gitea/models"
asymkey_model "code.gitea.io/gitea/models/asymkey" asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git" 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/organization"
"code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access" 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 { if err != nil {
return nil, err return nil, err
} }
canPush = models.CanMaintainerWriteToBranch(perms, b.Name, user) canPush = issues_model.CanMaintainerWriteToBranch(perms, b.Name, user)
} }
return &api.Branch{ return &api.Branch{

View File

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

View File

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

View File

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

View File

@ -8,7 +8,7 @@ import (
"context" "context"
"fmt" "fmt"
"code.gitea.io/gitea/models" issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access" access_model "code.gitea.io/gitea/models/perm/access"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
@ -20,7 +20,7 @@ import (
// ToAPIPullRequest assumes following fields have been assigned with valid values: // ToAPIPullRequest assumes following fields have been assigned with valid values:
// Required - Issue // Required - Issue
// Optional - Merger // 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 ( var (
baseBranch *git.Branch baseBranch *git.Branch
headBranch *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()) gitRepo, err := git.OpenRepository(ctx, pr.BaseRepo.RepoPath())
if err != nil { if err != nil {
log.Error("OpenRepository[%s]: %v", pr.GetGitRefName(), err) 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 = "" 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) p, err := access_model.GetUserRepoPermission(ctx, pr.HeadRepo, doer)
if err != nil { if err != nil {
log.Error("GetUserRepoPermission[%d]: %v", pr.HeadRepoID, err) log.Error("GetUserRepoPermission[%d]: %v", pr.HeadRepoID, err)

View File

@ -8,13 +8,13 @@ import (
"context" "context"
"strings" "strings"
"code.gitea.io/gitea/models" issues_model "code.gitea.io/gitea/models/issues"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
) )
// ToPullReview convert a review to api format // 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 err := r.LoadAttributes(ctx); err != nil {
if !user_model.IsErrUserNotExist(err) { if !user_model.IsErrUserNotExist(err) {
return nil, err return nil, err
@ -44,15 +44,15 @@ func ToPullReview(ctx context.Context, r *models.Review, doer *user_model.User)
} }
switch r.Type { switch r.Type {
case models.ReviewTypeApprove: case issues_model.ReviewTypeApprove:
result.State = api.ReviewStateApproved result.State = api.ReviewStateApproved
case models.ReviewTypeReject: case issues_model.ReviewTypeReject:
result.State = api.ReviewStateRequestChanges result.State = api.ReviewStateRequestChanges
case models.ReviewTypeComment: case issues_model.ReviewTypeComment:
result.State = api.ReviewStateComment result.State = api.ReviewStateComment
case models.ReviewTypePending: case issues_model.ReviewTypePending:
result.State = api.ReviewStatePending result.State = api.ReviewStatePending
case models.ReviewTypeRequest: case issues_model.ReviewTypeRequest:
result.State = api.ReviewStateRequestReview 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 // 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)) result := make([]*api.PullReview, 0, len(rl))
for i := range rl { for i := range rl {
// show pending reviews only for the user who created them // 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 continue
} }
r, err := ToPullReview(ctx, rl[i], doer) 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 // 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 err := review.LoadAttributes(ctx); err != nil {
if !user_model.IsErrUserNotExist(err) { if !user_model.IsErrUserNotExist(err) {
return nil, err return nil, err

View File

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

View File

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

View File

@ -9,8 +9,8 @@ import (
"fmt" "fmt"
"strings" "strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
@ -18,13 +18,13 @@ import (
"xorm.io/builder" "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( return db.Iterate(
ctx, ctx,
new(models.PullRequest), new(issues_model.PullRequest),
builder.Eq{"base_repo_id": repo.ID}, builder.Eq{"base_repo_id": repo.ID},
func(idx int, bean interface{}) error { 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 numPRsUpdated := 0
err := iterateRepositories(ctx, func(repo *repo_model.Repository) error { err := iterateRepositories(ctx, func(repo *repo_model.Repository) error {
numRepos++ 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++ numPRs++
pr.BaseRepo = repo pr.BaseRepo = repo
repoPath := repo.RepoPath() repoPath := repo.RepoPath()

View File

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

View File

@ -26,7 +26,7 @@ func TestBleveIndexAndSearch(t *testing.T) {
defer indexer.Close() defer indexer.Close()
if _, err := indexer.Init(); err != nil { 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 return
} }

View File

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

View File

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

View File

@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/graceful"
@ -33,7 +34,7 @@ func NewNotifier() base.Notifier {
return &actionNotifier{} 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 { if err := issue.LoadPoster(); err != nil {
log.Error("issue.LoadPoster: %v", err) log.Error("issue.LoadPoster: %v", err)
return return
@ -58,7 +59,7 @@ func (a *actionNotifier) NotifyNewIssue(issue *models.Issue, mentions []*user_mo
} }
// NotifyIssueChangeStatus notifies close or reopen issue to notifiers // 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. // 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. // This object will be used to notify watchers in the end of function.
act := &models.Action{ 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 // NotifyCreateIssueComment notifies comment on an issue to notifiers
func (a *actionNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *repo_model.Repository, 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{ act := &models.Action{
ActUserID: doer.ID, 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 { if err := pull.LoadIssue(); err != nil {
log.Error("pull.LoadIssue: %v", err) log.Error("pull.LoadIssue: %v", err)
return 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)) 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() 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{ action := &models.Action{
ActUserID: review.Reviewer.ID, ActUserID: review.Reviewer.ID,
ActUser: review.Reviewer, ActUser: review.Reviewer,
@ -252,9 +253,9 @@ func (a *actionNotifier) NotifyPullRequestReview(pr *models.PullRequest, review
} }
switch review.Type { switch review.Type {
case models.ReviewTypeApprove: case issues_model.ReviewTypeApprove:
action.OpType = models.ActionApprovePullRequest action.OpType = models.ActionApprovePullRequest
case models.ReviewTypeReject: case issues_model.ReviewTypeReject:
action.OpType = models.ActionRejectPullRequest action.OpType = models.ActionRejectPullRequest
default: default:
action.OpType = models.ActionCommentPull 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{ if err := models.NotifyWatchers(&models.Action{
ActUserID: doer.ID, ActUserID: doer.ID,
ActUser: doer, 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 reviewerName := review.Reviewer.Name
if len(review.OriginalAuthor) > 0 { if len(review.OriginalAuthor) > 0 {
reviewerName = review.OriginalAuthor reviewerName = review.OriginalAuthor

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