aboutsummaryrefslogtreecommitdiffstats
path: root/models/user
diff options
context:
space:
mode:
Diffstat (limited to 'models/user')
-rw-r--r--models/user/avatar.go7
-rw-r--r--models/user/badge.go2
-rw-r--r--models/user/email_address.go55
-rw-r--r--models/user/email_address_test.go10
-rw-r--r--models/user/follow.go60
-rw-r--r--models/user/search.go9
-rw-r--r--models/user/setting.go5
-rw-r--r--models/user/setting_options.go (renamed from models/user/setting_keys.go)8
-rw-r--r--models/user/setting_test.go8
-rw-r--r--models/user/user.go102
-rw-r--r--models/user/user_list.go5
-rw-r--r--models/user/user_system.go10
-rw-r--r--models/user/user_test.go144
13 files changed, 248 insertions, 177 deletions
diff --git a/models/user/avatar.go b/models/user/avatar.go
index 2a41b99129..542bd93b98 100644
--- a/models/user/avatar.go
+++ b/models/user/avatar.go
@@ -5,7 +5,6 @@ package user
import (
"context"
- "crypto/md5"
"fmt"
"image/png"
"io"
@@ -61,7 +60,9 @@ func GenerateRandomAvatar(ctx context.Context, u *User) error {
// AvatarLinkWithSize returns a link to the user's avatar with size. size <= 0 means default size
func (u *User) AvatarLinkWithSize(ctx context.Context, size int) string {
- if u.IsGhost() || u.IsGiteaActions() {
+ // ghost user was deleted, Gitea actions is a bot user, 0 means the user should be a virtual user
+ // which comes from git configure information
+ if u.IsGhost() || u.IsGiteaActions() || u.ID <= 0 {
return avatars.DefaultAvatarLink()
}
@@ -104,7 +105,7 @@ func (u *User) IsUploadAvatarChanged(data []byte) bool {
if !u.UseCustomAvatar || len(u.Avatar) == 0 {
return true
}
- avatarID := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%d-%x", u.ID, md5.Sum(data)))))
+ avatarID := avatar.HashAvatar(u.ID, data)
return u.Avatar != avatarID
}
diff --git a/models/user/badge.go b/models/user/badge.go
index 3ff3530a36..e475ceb748 100644
--- a/models/user/badge.go
+++ b/models/user/badge.go
@@ -19,7 +19,7 @@ type Badge struct {
}
// UserBadge represents a user badge
-type UserBadge struct { //nolint:revive
+type UserBadge struct { //nolint:revive // export stutter
ID int64 `xorm:"pk autoincr"`
BadgeID int64
UserID int64 `xorm:"INDEX"`
diff --git a/models/user/email_address.go b/models/user/email_address.go
index 2ba6a56450..cb423945f8 100644
--- a/models/user/email_address.go
+++ b/models/user/email_address.go
@@ -256,15 +256,9 @@ func IsEmailUsed(ctx context.Context, email string) (bool, error) {
// ActivateEmail activates the email address to given user.
func ActivateEmail(ctx context.Context, email *EmailAddress) error {
- ctx, committer, err := db.TxContext(ctx)
- if err != nil {
- return err
- }
- defer committer.Close()
- if err := updateActivation(ctx, email, true); err != nil {
- return err
- }
- return committer.Commit()
+ return db.WithTx(ctx, func(ctx context.Context) error {
+ return updateActivation(ctx, email, true)
+ })
}
func updateActivation(ctx context.Context, email *EmailAddress, activate bool) error {
@@ -305,33 +299,30 @@ func makeEmailPrimaryInternal(ctx context.Context, emailID int64, isActive bool)
return ErrUserNotExist{UID: email.UID}
}
- ctx, committer, err := db.TxContext(ctx)
- if err != nil {
- return err
- }
- defer committer.Close()
- sess := db.GetEngine(ctx)
+ return db.WithTx(ctx, func(ctx context.Context) error {
+ sess := db.GetEngine(ctx)
- // 1. Update user table
- user.Email = email.Email
- if _, err = sess.ID(user.ID).Cols("email").Update(user); err != nil {
- return err
- }
+ // 1. Update user table
+ user.Email = email.Email
+ if _, err := sess.ID(user.ID).Cols("email").Update(user); err != nil {
+ return err
+ }
- // 2. Update old primary email
- if _, err = sess.Where("uid=? AND is_primary=?", email.UID, true).Cols("is_primary").Update(&EmailAddress{
- IsPrimary: false,
- }); err != nil {
- return err
- }
+ // 2. Update old primary email
+ if _, err := sess.Where("uid=? AND is_primary=?", email.UID, true).Cols("is_primary").Update(&EmailAddress{
+ IsPrimary: false,
+ }); err != nil {
+ return err
+ }
- // 3. update new primary email
- email.IsPrimary = true
- if _, err = sess.ID(email.ID).Cols("is_primary").Update(email); err != nil {
- return err
- }
+ // 3. update new primary email
+ email.IsPrimary = true
+ if _, err := sess.ID(email.ID).Cols("is_primary").Update(email); err != nil {
+ return err
+ }
- return committer.Commit()
+ return nil
+ })
}
// ChangeInactivePrimaryEmail replaces the inactive primary email of a given user
diff --git a/models/user/email_address_test.go b/models/user/email_address_test.go
index d72d873de2..c0666246b0 100644
--- a/models/user/email_address_test.go
+++ b/models/user/email_address_test.go
@@ -4,6 +4,7 @@
package user_test
import (
+ "slices"
"testing"
"code.gitea.io/gitea/models/db"
@@ -100,12 +101,7 @@ func TestListEmails(t *testing.T) {
assert.Greater(t, count, int64(5))
contains := func(match func(s *user_model.SearchEmailResult) bool) bool {
- for _, v := range emails {
- if match(v) {
- return true
- }
- }
- return false
+ return slices.ContainsFunc(emails, match)
}
assert.True(t, contains(func(s *user_model.SearchEmailResult) bool { return s.UID == 18 }))
@@ -205,7 +201,7 @@ func TestEmailAddressValidate(t *testing.T) {
}
for kase, err := range kases {
t.Run(kase, func(t *testing.T) {
- assert.EqualValues(t, err, user_model.ValidateEmail(kase))
+ assert.Equal(t, err, user_model.ValidateEmail(kase))
})
}
}
diff --git a/models/user/follow.go b/models/user/follow.go
index cf9672109a..e098caab5b 100644
--- a/models/user/follow.go
+++ b/models/user/follow.go
@@ -38,24 +38,20 @@ func FollowUser(ctx context.Context, user, follow *User) (err error) {
return ErrBlockedUser
}
- ctx, committer, err := db.TxContext(ctx)
- if err != nil {
- return err
- }
- defer committer.Close()
-
- if err = db.Insert(ctx, &Follow{UserID: user.ID, FollowID: follow.ID}); err != nil {
- return err
- }
-
- if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?", follow.ID); err != nil {
- return err
- }
-
- if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following + 1 WHERE id = ?", user.ID); err != nil {
- return err
- }
- return committer.Commit()
+ return db.WithTx(ctx, func(ctx context.Context) error {
+ if err = db.Insert(ctx, &Follow{UserID: user.ID, FollowID: follow.ID}); err != nil {
+ return err
+ }
+
+ if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?", follow.ID); err != nil {
+ return err
+ }
+
+ if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following + 1 WHERE id = ?", user.ID); err != nil {
+ return err
+ }
+ return nil
+ })
}
// UnfollowUser unmarks someone as another's follower.
@@ -64,22 +60,18 @@ func UnfollowUser(ctx context.Context, userID, followID int64) (err error) {
return nil
}
- ctx, committer, err := db.TxContext(ctx)
- if err != nil {
- return err
- }
- defer committer.Close()
+ return db.WithTx(ctx, func(ctx context.Context) error {
+ if _, err = db.DeleteByBean(ctx, &Follow{UserID: userID, FollowID: followID}); err != nil {
+ return err
+ }
- if _, err = db.DeleteByBean(ctx, &Follow{UserID: userID, FollowID: followID}); err != nil {
- return err
- }
+ if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?", followID); err != nil {
+ return err
+ }
- if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?", followID); err != nil {
- return err
- }
-
- if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following - 1 WHERE id = ?", userID); err != nil {
- return err
- }
- return committer.Commit()
+ if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following - 1 WHERE id = ?", userID); err != nil {
+ return err
+ }
+ return nil
+ })
}
diff --git a/models/user/search.go b/models/user/search.go
index 85915f4020..cfd0d011bc 100644
--- a/models/user/search.go
+++ b/models/user/search.go
@@ -45,13 +45,14 @@ func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Sess
var cond builder.Cond
cond = builder.Eq{"type": opts.Type}
if opts.IncludeReserved {
- if opts.Type == UserTypeIndividual {
+ switch opts.Type {
+ case UserTypeIndividual:
cond = cond.Or(builder.Eq{"type": UserTypeUserReserved}).Or(
builder.Eq{"type": UserTypeBot},
).Or(
builder.Eq{"type": UserTypeRemoteUser},
)
- } else if opts.Type == UserTypeOrganization {
+ case UserTypeOrganization:
cond = cond.Or(builder.Eq{"type": UserTypeOrganizationReserved})
}
}
@@ -136,7 +137,7 @@ func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Sess
// SearchUsers takes options i.e. keyword and part of user name to search,
// it returns results in given range and number of total results.
-func SearchUsers(ctx context.Context, opts *SearchUserOptions) (users []*User, _ int64, _ error) {
+func SearchUsers(ctx context.Context, opts SearchUserOptions) (users []*User, _ int64, _ error) {
sessCount := opts.toSearchQueryBase(ctx)
defer sessCount.Close()
count, err := sessCount.Count(new(User))
@@ -151,7 +152,7 @@ func SearchUsers(ctx context.Context, opts *SearchUserOptions) (users []*User, _
sessQuery := opts.toSearchQueryBase(ctx).OrderBy(opts.OrderBy.String())
defer sessQuery.Close()
if opts.Page > 0 {
- sessQuery = db.SetSessionPagination(sessQuery, opts)
+ sessQuery = db.SetSessionPagination(sessQuery, &opts)
}
// the sql may contain JOIN, so we must only select User related columns
diff --git a/models/user/setting.go b/models/user/setting.go
index b4af0e5ccd..c65afae76c 100644
--- a/models/user/setting.go
+++ b/models/user/setting.go
@@ -5,6 +5,7 @@ package user
import (
"context"
+ "errors"
"fmt"
"strings"
@@ -114,10 +115,10 @@ func GetUserAllSettings(ctx context.Context, uid int64) (map[string]*Setting, er
func validateUserSettingKey(key string) error {
if len(key) == 0 {
- return fmt.Errorf("setting key must be set")
+ return errors.New("setting key must be set")
}
if strings.ToLower(key) != key {
- return fmt.Errorf("setting key should be lowercase")
+ return errors.New("setting key should be lowercase")
}
return nil
}
diff --git a/models/user/setting_keys.go b/models/user/setting_options.go
index 3149aae18b..7be5039329 100644
--- a/models/user/setting_keys.go
+++ b/models/user/setting_options.go
@@ -10,6 +10,7 @@ const (
SettingsKeyDiffWhitespaceBehavior = "diff.whitespace_behaviour"
// SettingsKeyShowOutdatedComments is the setting key wether or not to show outdated comments in PRs
SettingsKeyShowOutdatedComments = "comment_code.show_outdated"
+
// UserActivityPubPrivPem is user's private key
UserActivityPubPrivPem = "activitypub.priv_pem"
// UserActivityPubPubPem is user's public key
@@ -18,4 +19,11 @@ const (
SignupIP = "signup.ip"
// SignupUserAgent is the user agent that the user signed up with
SignupUserAgent = "signup.user_agent"
+
+ SettingsKeyCodeViewShowFileTree = "code_view.show_file_tree"
+
+ SettingsKeyEmailNotificationGiteaActions = "email_notification.gitea_actions"
+ SettingEmailNotificationGiteaActionsAll = "all"
+ SettingEmailNotificationGiteaActionsFailureOnly = "failure-only" // Default for actions email preference
+ SettingEmailNotificationGiteaActionsDisabled = "disabled"
)
diff --git a/models/user/setting_test.go b/models/user/setting_test.go
index c607d9fd00..3c199013f3 100644
--- a/models/user/setting_test.go
+++ b/models/user/setting_test.go
@@ -30,15 +30,15 @@ func TestSettings(t *testing.T) {
settings, err := user_model.GetSettings(db.DefaultContext, 99, []string{keyName})
assert.NoError(t, err)
assert.Len(t, settings, 1)
- assert.EqualValues(t, newSetting.SettingValue, settings[keyName].SettingValue)
+ assert.Equal(t, newSetting.SettingValue, settings[keyName].SettingValue)
settingValue, err := user_model.GetUserSetting(db.DefaultContext, 99, keyName)
assert.NoError(t, err)
- assert.EqualValues(t, newSetting.SettingValue, settingValue)
+ assert.Equal(t, newSetting.SettingValue, settingValue)
settingValue, err = user_model.GetUserSetting(db.DefaultContext, 99, "no_such")
assert.NoError(t, err)
- assert.EqualValues(t, "", settingValue)
+ assert.Empty(t, settingValue)
// updated setting
updatedSetting := &user_model.Setting{UserID: 99, SettingKey: keyName, SettingValue: "Updated"}
@@ -49,7 +49,7 @@ func TestSettings(t *testing.T) {
settings, err = user_model.GetUserAllSettings(db.DefaultContext, 99)
assert.NoError(t, err)
assert.Len(t, settings, 1)
- assert.EqualValues(t, updatedSetting.SettingValue, settings[updatedSetting.SettingKey].SettingValue)
+ assert.Equal(t, updatedSetting.SettingValue, settings[updatedSetting.SettingKey].SettingValue)
// delete setting
err = user_model.DeleteUserSetting(db.DefaultContext, 99, keyName)
diff --git a/models/user/user.go b/models/user/user.go
index 3c72aa7cc4..c362cbc6d2 100644
--- a/models/user/user.go
+++ b/models/user/user.go
@@ -247,19 +247,20 @@ func (u *User) MaxCreationLimit() int {
return u.MaxRepoCreation
}
-// CanCreateRepo returns if user login can create a repository
-// NOTE: functions calling this assume a failure due to repository count limit; if new checks are added, those functions should be revised
-func (u *User) CanCreateRepo() bool {
+// CanCreateRepoIn checks whether the doer(u) can create a repository in the owner
+// NOTE: functions calling this assume a failure due to repository count limit; it ONLY checks the repo number LIMIT, if new checks are added, those functions should be revised
+func (u *User) CanCreateRepoIn(owner *User) bool {
if u.IsAdmin {
return true
}
- if u.MaxRepoCreation <= -1 {
- if setting.Repository.MaxCreationLimit <= -1 {
+ const noLimit = -1
+ if owner.MaxRepoCreation == noLimit {
+ if setting.Repository.MaxCreationLimit == noLimit {
return true
}
- return u.NumRepos < setting.Repository.MaxCreationLimit
+ return owner.NumRepos < setting.Repository.MaxCreationLimit
}
- return u.NumRepos < u.MaxRepoCreation
+ return owner.NumRepos < owner.MaxRepoCreation
}
// CanCreateOrganization returns true if user can create organisation.
@@ -272,13 +273,12 @@ func (u *User) CanEditGitHook() bool {
return !setting.DisableGitHooks && (u.IsAdmin || u.AllowGitHook)
}
-// CanForkRepo returns if user login can fork a repository
-// It checks especially that the user can create repos, and potentially more
-func (u *User) CanForkRepo() bool {
+// CanForkRepoIn ONLY checks repository count limit
+func (u *User) CanForkRepoIn(owner *User) bool {
if setting.Repository.AllowForkWithoutMaximumLimit {
return true
}
- return u.CanCreateRepo()
+ return u.CanCreateRepoIn(owner)
}
// CanImportLocal returns true if user can migrate repository by local path.
@@ -828,6 +828,21 @@ func IsLastAdminUser(ctx context.Context, user *User) bool {
type CountUserFilter struct {
LastLoginSince *int64
IsAdmin optional.Option[bool]
+ IsActive optional.Option[bool]
+}
+
+// HasUsers checks whether there are any users in the database, or only one user exists.
+func HasUsers(ctx context.Context) (ret struct {
+ HasAnyUser, HasOnlyOneUser bool
+}, err error,
+) {
+ res, err := db.GetEngine(ctx).Table(&User{}).Cols("id").Limit(2).Query()
+ if err != nil {
+ return ret, fmt.Errorf("error checking user existence: %w", err)
+ }
+ ret.HasAnyUser = len(res) != 0
+ ret.HasOnlyOneUser = len(res) == 1
+ return ret, nil
}
// CountUsers returns number of users.
@@ -848,6 +863,10 @@ func countUsers(ctx context.Context, opts *CountUserFilter) int64 {
if opts.IsAdmin.Has() {
cond = cond.And(builder.Eq{"is_admin": opts.IsAdmin.Value()})
}
+
+ if opts.IsActive.Has() {
+ cond = cond.And(builder.Eq{"is_active": opts.IsActive.Value()})
+ }
}
count, err := sess.Where(cond).Count(new(User))
@@ -1146,13 +1165,7 @@ func ValidateCommitsWithEmails(ctx context.Context, oldCommits []*git.Commit) ([
}
for _, c := range oldCommits {
- user, ok := emailUserMap[c.Author.Email]
- if !ok {
- user = &User{
- Name: c.Author.Name,
- Email: c.Author.Email,
- }
- }
+ user := emailUserMap.GetByEmail(c.Author.Email) // FIXME: why ValidateCommitsWithEmails uses "Author", but ParseCommitsWithSignature uses "Committer"?
newCommits = append(newCommits, &UserCommit{
User: user,
Commit: c,
@@ -1161,19 +1174,29 @@ func ValidateCommitsWithEmails(ctx context.Context, oldCommits []*git.Commit) ([
return newCommits, nil
}
-func GetUsersByEmails(ctx context.Context, emails []string) (map[string]*User, error) {
+type EmailUserMap struct {
+ m map[string]*User
+}
+
+func (eum *EmailUserMap) GetByEmail(email string) *User {
+ return eum.m[strings.ToLower(email)]
+}
+
+func GetUsersByEmails(ctx context.Context, emails []string) (*EmailUserMap, error) {
if len(emails) == 0 {
return nil, nil
}
needCheckEmails := make(container.Set[string])
needCheckUserNames := make(container.Set[string])
+ noReplyAddressSuffix := "@" + strings.ToLower(setting.Service.NoReplyAddress)
for _, email := range emails {
- if strings.HasSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress)) {
- username := strings.TrimSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress))
- needCheckUserNames.Add(username)
+ emailLower := strings.ToLower(email)
+ if noReplyUserNameLower, ok := strings.CutSuffix(emailLower, noReplyAddressSuffix); ok {
+ needCheckUserNames.Add(noReplyUserNameLower)
+ needCheckEmails.Add(emailLower)
} else {
- needCheckEmails.Add(strings.ToLower(email))
+ needCheckEmails.Add(emailLower)
}
}
@@ -1187,31 +1210,30 @@ func GetUsersByEmails(ctx context.Context, emails []string) (map[string]*User, e
for _, email := range emailAddresses {
userIDs.Add(email.UID)
}
- users, err := GetUsersMapByIDs(ctx, userIDs.Values())
- if err != nil {
- return nil, err
- }
-
results := make(map[string]*User, len(emails))
- for _, email := range emailAddresses {
- user := users[email.UID]
- if user != nil {
- if user.KeepEmailPrivate {
- results[user.LowerName+"@"+setting.Service.NoReplyAddress] = user
- } else {
- results[email.Email] = user
+
+ if len(userIDs) > 0 {
+ users, err := GetUsersMapByIDs(ctx, userIDs.Values())
+ if err != nil {
+ return nil, err
+ }
+
+ for _, email := range emailAddresses {
+ user := users[email.UID]
+ if user != nil {
+ results[email.LowerEmail] = user
}
}
}
- users = make(map[int64]*User, len(needCheckUserNames))
+ users := make(map[int64]*User, len(needCheckUserNames))
if err := db.GetEngine(ctx).In("lower_name", needCheckUserNames.Values()).Find(&users); err != nil {
return nil, err
}
for _, user := range users {
- results[user.LowerName+"@"+setting.Service.NoReplyAddress] = user
+ results[strings.ToLower(user.GetPlaceholderEmail())] = user
}
- return results, nil
+ return &EmailUserMap{results}, nil
}
// GetUserByEmail returns the user object by given e-mail if exists.
@@ -1232,8 +1254,8 @@ func GetUserByEmail(ctx context.Context, email string) (*User, error) {
}
// Finally, if email address is the protected email address:
- if strings.HasSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress)) {
- username := strings.TrimSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress))
+ if strings.HasSuffix(email, "@"+setting.Service.NoReplyAddress) {
+ username := strings.TrimSuffix(email, "@"+setting.Service.NoReplyAddress)
user := &User{}
has, err := db.GetEngine(ctx).Where("lower_name=?", username).Get(user)
if err != nil {
diff --git a/models/user/user_list.go b/models/user/user_list.go
index 4241905058..1b6a27dd86 100644
--- a/models/user/user_list.go
+++ b/models/user/user_list.go
@@ -17,10 +17,7 @@ func GetUsersMapByIDs(ctx context.Context, userIDs []int64) (map[int64]*User, er
left := len(userIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
err := db.GetEngine(ctx).
In("id", userIDs[:limit]).
Find(&userMaps)
diff --git a/models/user/user_system.go b/models/user/user_system.go
index 6fbfd9e69e..e07274d291 100644
--- a/models/user/user_system.go
+++ b/models/user/user_system.go
@@ -10,8 +10,8 @@ import (
)
const (
- GhostUserID = -1
- GhostUserName = "Ghost"
+ GhostUserID int64 = -1
+ GhostUserName = "Ghost"
)
// NewGhostUser creates and returns a fake user for someone has deleted their account.
@@ -36,9 +36,9 @@ func (u *User) IsGhost() bool {
}
const (
- ActionsUserID = -2
- ActionsUserName = "gitea-actions"
- ActionsUserEmail = "teabot@gitea.io"
+ ActionsUserID int64 = -2
+ ActionsUserName = "gitea-actions"
+ ActionsUserEmail = "teabot@gitea.io"
)
func IsGiteaActionsUserName(name string) bool {
diff --git a/models/user/user_test.go b/models/user/user_test.go
index 1132c02f28..7944fc4b73 100644
--- a/models/user/user_test.go
+++ b/models/user/user_test.go
@@ -19,9 +19,11 @@ import (
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/timeutil"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestIsUsableUsername(t *testing.T) {
@@ -47,14 +49,48 @@ func TestOAuth2Application_LoadUser(t *testing.T) {
assert.NotNil(t, user)
}
-func TestGetUserEmailsByNames(t *testing.T) {
+func TestUserEmails(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
+ t.Run("GetUserEmailsByNames", func(t *testing.T) {
+ // ignore none active user email
+ assert.ElementsMatch(t, []string{"user8@example.com"}, user_model.GetUserEmailsByNames(db.DefaultContext, []string{"user8", "user9"}))
+ assert.ElementsMatch(t, []string{"user8@example.com", "user5@example.com"}, user_model.GetUserEmailsByNames(db.DefaultContext, []string{"user8", "user5"}))
+ assert.ElementsMatch(t, []string{"user8@example.com"}, user_model.GetUserEmailsByNames(db.DefaultContext, []string{"user8", "org7"}))
+ })
+ t.Run("GetUsersByEmails", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.Service.NoReplyAddress, "NoReply.gitea.internal")()
+ testGetUserByEmail := func(t *testing.T, email string, uid int64) {
+ m, err := user_model.GetUsersByEmails(db.DefaultContext, []string{email})
+ require.NoError(t, err)
+ user := m.GetByEmail(email)
+ if uid == 0 {
+ require.Nil(t, user)
+ return
+ }
+ require.NotNil(t, user)
+ assert.Equal(t, uid, user.ID)
+ }
+ cases := []struct {
+ Email string
+ UID int64
+ }{
+ {"UseR1@example.com", 1},
+ {"user1-2@example.COM", 1},
+ {"USER2@" + setting.Service.NoReplyAddress, 2},
+ {"user4@example.com", 4},
+ {"no-such", 0},
+ }
+ for _, c := range cases {
+ t.Run(c.Email, func(t *testing.T) {
+ testGetUserByEmail(t, c.Email, c.UID)
+ })
+ }
- // ignore none active user email
- assert.ElementsMatch(t, []string{"user8@example.com"}, user_model.GetUserEmailsByNames(db.DefaultContext, []string{"user8", "user9"}))
- assert.ElementsMatch(t, []string{"user8@example.com", "user5@example.com"}, user_model.GetUserEmailsByNames(db.DefaultContext, []string{"user8", "user5"}))
-
- assert.ElementsMatch(t, []string{"user8@example.com"}, user_model.GetUserEmailsByNames(db.DefaultContext, []string{"user8", "org7"}))
+ t.Run("NoReplyConflict", func(t *testing.T) {
+ setting.Service.NoReplyAddress = "example.com"
+ testGetUserByEmail(t, "user1-2@example.COM", 1)
+ })
+ })
}
func TestCanCreateOrganization(t *testing.T) {
@@ -77,73 +113,73 @@ func TestCanCreateOrganization(t *testing.T) {
func TestSearchUsers(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- testSuccess := func(opts *user_model.SearchUserOptions, expectedUserOrOrgIDs []int64) {
+ testSuccess := func(opts user_model.SearchUserOptions, expectedUserOrOrgIDs []int64) {
users, _, err := user_model.SearchUsers(db.DefaultContext, opts)
assert.NoError(t, err)
cassText := fmt.Sprintf("ids: %v, opts: %v", expectedUserOrOrgIDs, opts)
if assert.Len(t, users, len(expectedUserOrOrgIDs), "case: %s", cassText) {
for i, expectedID := range expectedUserOrOrgIDs {
- assert.EqualValues(t, expectedID, users[i].ID, "case: %s", cassText)
+ assert.Equal(t, expectedID, users[i].ID, "case: %s", cassText)
}
}
}
// test orgs
- testOrgSuccess := func(opts *user_model.SearchUserOptions, expectedOrgIDs []int64) {
+ testOrgSuccess := func(opts user_model.SearchUserOptions, expectedOrgIDs []int64) {
opts.Type = user_model.UserTypeOrganization
testSuccess(opts, expectedOrgIDs)
}
- testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1, PageSize: 2}},
+ testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1, PageSize: 2}},
[]int64{3, 6})
- testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 2, PageSize: 2}},
+ testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 2, PageSize: 2}},
[]int64{7, 17})
- testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 3, PageSize: 2}},
+ testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 3, PageSize: 2}},
[]int64{19, 25})
- testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 4, PageSize: 2}},
+ testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 4, PageSize: 2}},
[]int64{26, 41})
- testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 5, PageSize: 2}},
+ testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 5, PageSize: 2}},
[]int64{42})
- testOrgSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 6, PageSize: 2}},
+ testOrgSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 6, PageSize: 2}},
[]int64{})
// test users
- testUserSuccess := func(opts *user_model.SearchUserOptions, expectedUserIDs []int64) {
+ testUserSuccess := func(opts user_model.SearchUserOptions, expectedUserIDs []int64) {
opts.Type = user_model.UserTypeIndividual
testSuccess(opts, expectedUserIDs)
}
- testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}},
+ testUserSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}},
[]int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40})
- testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(false)},
+ testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(false)},
[]int64{9})
- testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
+ testUserSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
[]int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40})
- testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
+ testUserSuccess(user_model.SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
// order by name asc default
- testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
+ testUserSuccess(user_model.SearchUserOptions{Keyword: "user1", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
- testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsAdmin: optional.Some(true)},
+ testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsAdmin: optional.Some(true)},
[]int64{1})
- testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsRestricted: optional.Some(true)},
+ testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsRestricted: optional.Some(true)},
[]int64{29})
- testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsProhibitLogin: optional.Some(true)},
+ testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsProhibitLogin: optional.Some(true)},
[]int64{37})
- testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: optional.Some(true)},
+ testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: optional.Some(true)},
[]int64{24})
}
@@ -173,9 +209,9 @@ func TestHashPasswordDeterministic(t *testing.T) {
b := make([]byte, 16)
u := &user_model.User{}
algos := hash.RecommendedHashAlgorithms
- for j := 0; j < len(algos); j++ {
+ for j := range algos {
u.PasswdHashAlgo = algos[j]
- for i := 0; i < 50; i++ {
+ for range 50 {
// generate a random password
rand.Read(b)
pass := string(b)
@@ -502,18 +538,15 @@ func TestIsUserVisibleToViewer(t *testing.T) {
}
func Test_ValidateUser(t *testing.T) {
- oldSetting := setting.Service.AllowedUserVisibilityModesSlice
- defer func() {
- setting.Service.AllowedUserVisibilityModesSlice = oldSetting
- }()
- setting.Service.AllowedUserVisibilityModesSlice = []bool{true, false, true}
+ defer test.MockVariableValue(&setting.Service.AllowedUserVisibilityModesSlice, []bool{true, false, true})()
+
kases := map[*user_model.User]bool{
{ID: 1, Visibility: structs.VisibleTypePublic}: true,
{ID: 2, Visibility: structs.VisibleTypeLimited}: false,
{ID: 2, Visibility: structs.VisibleTypePrivate}: true,
}
for kase, expected := range kases {
- assert.EqualValues(t, expected, nil == user_model.ValidateUser(kase), "case: %+v", kase)
+ assert.Equal(t, expected, nil == user_model.ValidateUser(kase), "case: %+v", kase)
}
}
@@ -537,7 +570,7 @@ func Test_NormalizeUserFromEmail(t *testing.T) {
for _, testCase := range testCases {
normalizedName, err := user_model.NormalizeUserName(testCase.Input)
assert.NoError(t, err)
- assert.EqualValues(t, testCase.Expected, normalizedName)
+ assert.Equal(t, testCase.Expected, normalizedName)
if testCase.IsNormalizedValid {
assert.NoError(t, user_model.IsUsableUsername(normalizedName))
} else {
@@ -564,7 +597,7 @@ func TestEmailTo(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.result, func(t *testing.T) {
testUser := &user_model.User{FullName: testCase.fullName, Email: testCase.mail}
- assert.EqualValues(t, testCase.result, testUser.EmailTo())
+ assert.Equal(t, testCase.result, testUser.EmailTo())
})
}
}
@@ -575,12 +608,7 @@ func TestDisabledUserFeatures(t *testing.T) {
testValues := container.SetOf(setting.UserFeatureDeletion,
setting.UserFeatureManageSSHKeys,
setting.UserFeatureManageGPGKeys)
-
- oldSetting := setting.Admin.ExternalUserDisableFeatures
- defer func() {
- setting.Admin.ExternalUserDisableFeatures = oldSetting
- }()
- setting.Admin.ExternalUserDisableFeatures = testValues
+ defer test.MockVariableValue(&setting.Admin.ExternalUserDisableFeatures, testValues)()
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
@@ -616,3 +644,37 @@ func TestGetInactiveUsers(t *testing.T) {
assert.NoError(t, err)
assert.Empty(t, users)
}
+
+func TestCanCreateRepo(t *testing.T) {
+ defer test.MockVariableValue(&setting.Repository.MaxCreationLimit)()
+ const noLimit = -1
+ doerNormal := &user_model.User{}
+ doerAdmin := &user_model.User{IsAdmin: true}
+ t.Run("NoGlobalLimit", func(t *testing.T) {
+ setting.Repository.MaxCreationLimit = noLimit
+
+ assert.True(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: noLimit}))
+ assert.False(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 0}))
+ assert.True(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 100}))
+
+ assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: noLimit}))
+ assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 0}))
+ assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 100}))
+ })
+
+ t.Run("GlobalLimit50", func(t *testing.T) {
+ setting.Repository.MaxCreationLimit = 50
+
+ assert.True(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: noLimit}))
+ assert.False(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 60, MaxRepoCreation: noLimit})) // limited by global limit
+ assert.False(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 0}))
+ assert.True(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 100}))
+ assert.True(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 60, MaxRepoCreation: 100}))
+
+ assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: noLimit}))
+ assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 60, MaxRepoCreation: noLimit}))
+ assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 0}))
+ assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 100}))
+ assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 60, MaxRepoCreation: 100}))
+ })
+}