loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request 'fix: never set to nil: poster of an issue or comment; assignee of a comment' (#4729) from earl-warren/forgejo:wip-ghost-npe into forgejo

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/4729
Reviewed-by: Caesar Schinas <caesar@caesarschinas.com>
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>

+188 -24
+4 -8
models/issues/comment_list.go
··· 23 23 } 24 24 25 25 posterIDs := container.FilterSlice(comments, func(c *Comment) (int64, bool) { 26 - return c.PosterID, c.Poster == nil && c.PosterID > 0 26 + return c.PosterID, c.Poster == nil && user_model.IsValidUserID(c.PosterID) 27 27 }) 28 28 29 29 posterMaps, err := getPostersByIDs(ctx, posterIDs) ··· 33 33 34 34 for _, comment := range comments { 35 35 if comment.Poster == nil { 36 - comment.Poster = getPoster(comment.PosterID, posterMaps) 36 + comment.PosterID, comment.Poster = user_model.GetUserFromMap(comment.PosterID, posterMaps) 37 37 } 38 38 } 39 39 return nil ··· 165 165 166 166 func (comments CommentList) getAssigneeIDs() []int64 { 167 167 return container.FilterSlice(comments, func(comment *Comment) (int64, bool) { 168 - return comment.AssigneeID, comment.AssigneeID > 0 168 + return comment.AssigneeID, user_model.IsValidUserID(comment.AssigneeID) 169 169 }) 170 170 } 171 171 ··· 206 206 } 207 207 208 208 for _, comment := range comments { 209 - comment.Assignee = assignees[comment.AssigneeID] 210 - if comment.Assignee == nil { 211 - comment.AssigneeID = user_model.GhostUserID 212 - comment.Assignee = user_model.NewGhostUser() 213 - } 209 + comment.AssigneeID, comment.Assignee = user_model.GetUserFromMap(comment.AssigneeID, assignees) 214 210 } 215 211 return nil 216 212 }
+86
models/issues/comment_list_test.go
··· 1 + // Copyright 2024 The Forgejo Authors 2 + // SPDX-License-Identifier: MIT 3 + 4 + package issues 5 + 6 + import ( 7 + "testing" 8 + 9 + "code.gitea.io/gitea/models/db" 10 + repo_model "code.gitea.io/gitea/models/repo" 11 + "code.gitea.io/gitea/models/unittest" 12 + user_model "code.gitea.io/gitea/models/user" 13 + 14 + "github.com/stretchr/testify/assert" 15 + "github.com/stretchr/testify/require" 16 + ) 17 + 18 + func TestCommentListLoadUser(t *testing.T) { 19 + assert.NoError(t, unittest.PrepareTestDatabase()) 20 + 21 + issue := unittest.AssertExistsAndLoadBean(t, &Issue{}) 22 + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) 23 + doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) 24 + 25 + for _, testCase := range []struct { 26 + poster int64 27 + assignee int64 28 + user *user_model.User 29 + }{ 30 + { 31 + poster: user_model.ActionsUserID, 32 + assignee: user_model.ActionsUserID, 33 + user: user_model.NewActionsUser(), 34 + }, 35 + { 36 + poster: user_model.GhostUserID, 37 + assignee: user_model.GhostUserID, 38 + user: user_model.NewGhostUser(), 39 + }, 40 + { 41 + poster: doer.ID, 42 + assignee: doer.ID, 43 + user: doer, 44 + }, 45 + { 46 + poster: 0, 47 + assignee: 0, 48 + user: user_model.NewGhostUser(), 49 + }, 50 + { 51 + poster: -200, 52 + assignee: -200, 53 + user: user_model.NewGhostUser(), 54 + }, 55 + { 56 + poster: 200, 57 + assignee: 200, 58 + user: user_model.NewGhostUser(), 59 + }, 60 + } { 61 + t.Run(testCase.user.Name, func(t *testing.T) { 62 + comment, err := CreateComment(db.DefaultContext, &CreateCommentOptions{ 63 + Type: CommentTypeComment, 64 + Doer: testCase.user, 65 + Repo: repo, 66 + Issue: issue, 67 + Content: "Hello", 68 + }) 69 + assert.NoError(t, err) 70 + 71 + list := CommentList{comment} 72 + 73 + comment.PosterID = testCase.poster 74 + comment.Poster = nil 75 + assert.NoError(t, list.LoadPosters(db.DefaultContext)) 76 + require.NotNil(t, comment.Poster) 77 + assert.Equal(t, testCase.user.ID, comment.Poster.ID) 78 + 79 + comment.AssigneeID = testCase.assignee 80 + comment.Assignee = nil 81 + assert.NoError(t, list.loadAssignees(db.DefaultContext)) 82 + require.NotNil(t, comment.Assignee) 83 + assert.Equal(t, testCase.user.ID, comment.Assignee.ID) 84 + }) 85 + } 86 + }
+2 -16
models/issues/issue_list.go
··· 79 79 } 80 80 81 81 posterIDs := container.FilterSlice(issues, func(issue *Issue) (int64, bool) { 82 - return issue.PosterID, issue.Poster == nil && issue.PosterID > 0 82 + return issue.PosterID, issue.Poster == nil && user_model.IsValidUserID(issue.PosterID) 83 83 }) 84 84 85 85 posterMaps, err := getPostersByIDs(ctx, posterIDs) ··· 89 89 90 90 for _, issue := range issues { 91 91 if issue.Poster == nil { 92 - issue.Poster = getPoster(issue.PosterID, posterMaps) 92 + issue.PosterID, issue.Poster = user_model.GetUserFromMap(issue.PosterID, posterMaps) 93 93 } 94 94 } 95 95 return nil ··· 113 113 posterIDs = posterIDs[limit:] 114 114 } 115 115 return posterMaps, nil 116 - } 117 - 118 - func getPoster(posterID int64, posterMaps map[int64]*user_model.User) *user_model.User { 119 - if posterID == user_model.ActionsUserID { 120 - return user_model.NewActionsUser() 121 - } 122 - if posterID <= 0 { 123 - return nil 124 - } 125 - poster, ok := posterMaps[posterID] 126 - if !ok { 127 - return user_model.NewGhostUser() 128 - } 129 - return poster 130 116 } 131 117 132 118 func (issues IssueList) getIssueIDs() []int64 {
+49
models/issues/issue_list_test.go
··· 9 9 "code.gitea.io/gitea/models/db" 10 10 issues_model "code.gitea.io/gitea/models/issues" 11 11 "code.gitea.io/gitea/models/unittest" 12 + user_model "code.gitea.io/gitea/models/user" 12 13 "code.gitea.io/gitea/modules/setting" 13 14 14 15 "github.com/stretchr/testify/assert" 16 + "github.com/stretchr/testify/require" 15 17 ) 16 18 17 19 func TestIssueList_LoadRepositories(t *testing.T) { ··· 78 80 assert.Equal(t, issue.ID == 1, issue.IsRead, "unexpected is_read value for issue[%d]", issue.ID) 79 81 } 80 82 } 83 + 84 + func TestIssueListLoadUser(t *testing.T) { 85 + assert.NoError(t, unittest.PrepareTestDatabase()) 86 + 87 + issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{}) 88 + doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) 89 + 90 + for _, testCase := range []struct { 91 + poster int64 92 + user *user_model.User 93 + }{ 94 + { 95 + poster: user_model.ActionsUserID, 96 + user: user_model.NewActionsUser(), 97 + }, 98 + { 99 + poster: user_model.GhostUserID, 100 + user: user_model.NewGhostUser(), 101 + }, 102 + { 103 + poster: doer.ID, 104 + user: doer, 105 + }, 106 + { 107 + poster: 0, 108 + user: user_model.NewGhostUser(), 109 + }, 110 + { 111 + poster: -200, 112 + user: user_model.NewGhostUser(), 113 + }, 114 + { 115 + poster: 200, 116 + user: user_model.NewGhostUser(), 117 + }, 118 + } { 119 + t.Run(testCase.user.Name, func(t *testing.T) { 120 + list := issues_model.IssueList{issue} 121 + 122 + issue.PosterID = testCase.poster 123 + issue.Poster = nil 124 + assert.NoError(t, list.LoadPosters(db.DefaultContext)) 125 + require.NotNil(t, issue.Poster) 126 + assert.Equal(t, testCase.user.ID, issue.Poster.ID) 127 + }) 128 + } 129 + }
+14
models/user/user.go
··· 939 939 return users, err 940 940 } 941 941 942 + func IsValidUserID(id int64) bool { 943 + return id > 0 || id == GhostUserID || id == ActionsUserID 944 + } 945 + 946 + func GetUserFromMap(id int64, idMap map[int64]*User) (int64, *User) { 947 + if user, ok := idMap[id]; ok { 948 + return id, user 949 + } 950 + if id == ActionsUserID { 951 + return ActionsUserID, NewActionsUser() 952 + } 953 + return GhostUserID, NewGhostUser() 954 + } 955 + 942 956 // GetPossibleUserByID returns the user if id > 0 or return system usrs if id < 0 943 957 func GetPossibleUserByID(ctx context.Context, id int64) (*User, error) { 944 958 switch id {
+33
models/user/user_test.go
··· 35 35 assert.NotNil(t, user) 36 36 } 37 37 38 + func TestIsValidUserID(t *testing.T) { 39 + assert.False(t, user_model.IsValidUserID(-30)) 40 + assert.False(t, user_model.IsValidUserID(0)) 41 + assert.True(t, user_model.IsValidUserID(user_model.GhostUserID)) 42 + assert.True(t, user_model.IsValidUserID(user_model.ActionsUserID)) 43 + assert.True(t, user_model.IsValidUserID(200)) 44 + } 45 + 46 + func TestGetUserFromMap(t *testing.T) { 47 + id := int64(200) 48 + idMap := map[int64]*user_model.User{ 49 + id: {ID: id}, 50 + } 51 + 52 + ghostID := int64(user_model.GhostUserID) 53 + actionsID := int64(user_model.ActionsUserID) 54 + actualID, actualUser := user_model.GetUserFromMap(-20, idMap) 55 + assert.Equal(t, ghostID, actualID) 56 + assert.Equal(t, ghostID, actualUser.ID) 57 + 58 + actualID, actualUser = user_model.GetUserFromMap(0, idMap) 59 + assert.Equal(t, ghostID, actualID) 60 + assert.Equal(t, ghostID, actualUser.ID) 61 + 62 + actualID, actualUser = user_model.GetUserFromMap(ghostID, idMap) 63 + assert.Equal(t, ghostID, actualID) 64 + assert.Equal(t, ghostID, actualUser.ID) 65 + 66 + actualID, actualUser = user_model.GetUserFromMap(actionsID, idMap) 67 + assert.Equal(t, actionsID, actualID) 68 + assert.Equal(t, actionsID, actualUser.ID) 69 + } 70 + 38 71 func TestGetUserByName(t *testing.T) { 39 72 defer tests.AddFixtures("models/user/fixtures/")() 40 73 assert.NoError(t, unittest.PrepareTestDatabase())