code.gitea.io/gitea@v1.22.3/models/user/user_test.go (about)

     1  // Copyright 2017 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package user_test
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"math/rand"
    10  	"strings"
    11  	"testing"
    12  	"time"
    13  
    14  	"code.gitea.io/gitea/models/auth"
    15  	"code.gitea.io/gitea/models/db"
    16  	"code.gitea.io/gitea/models/unittest"
    17  	user_model "code.gitea.io/gitea/models/user"
    18  	"code.gitea.io/gitea/modules/auth/password/hash"
    19  	"code.gitea.io/gitea/modules/container"
    20  	"code.gitea.io/gitea/modules/optional"
    21  	"code.gitea.io/gitea/modules/setting"
    22  	"code.gitea.io/gitea/modules/structs"
    23  	"code.gitea.io/gitea/modules/timeutil"
    24  
    25  	"github.com/stretchr/testify/assert"
    26  )
    27  
    28  func TestOAuth2Application_LoadUser(t *testing.T) {
    29  	assert.NoError(t, unittest.PrepareTestDatabase())
    30  	app := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ID: 1})
    31  	user, err := user_model.GetUserByID(db.DefaultContext, app.UID)
    32  	assert.NoError(t, err)
    33  	assert.NotNil(t, user)
    34  }
    35  
    36  func TestGetUserEmailsByNames(t *testing.T) {
    37  	assert.NoError(t, unittest.PrepareTestDatabase())
    38  
    39  	// ignore none active user email
    40  	assert.ElementsMatch(t, []string{"user8@example.com"}, user_model.GetUserEmailsByNames(db.DefaultContext, []string{"user8", "user9"}))
    41  	assert.ElementsMatch(t, []string{"user8@example.com", "user5@example.com"}, user_model.GetUserEmailsByNames(db.DefaultContext, []string{"user8", "user5"}))
    42  
    43  	assert.ElementsMatch(t, []string{"user8@example.com"}, user_model.GetUserEmailsByNames(db.DefaultContext, []string{"user8", "org7"}))
    44  }
    45  
    46  func TestCanCreateOrganization(t *testing.T) {
    47  	assert.NoError(t, unittest.PrepareTestDatabase())
    48  
    49  	admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
    50  	assert.True(t, admin.CanCreateOrganization())
    51  
    52  	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
    53  	assert.True(t, user.CanCreateOrganization())
    54  	// Disable user create organization permission.
    55  	user.AllowCreateOrganization = false
    56  	assert.False(t, user.CanCreateOrganization())
    57  
    58  	setting.Admin.DisableRegularOrgCreation = true
    59  	user.AllowCreateOrganization = true
    60  	assert.True(t, admin.CanCreateOrganization())
    61  	assert.False(t, user.CanCreateOrganization())
    62  }
    63  
    64  func TestSearchUsers(t *testing.T) {
    65  	assert.NoError(t, unittest.PrepareTestDatabase())
    66  	testSuccess := func(opts *user_model.SearchUserOptions, expectedUserOrOrgIDs []int64) {
    67  		users, _, err := user_model.SearchUsers(db.DefaultContext, opts)
    68  		assert.NoError(t, err)
    69  		cassText := fmt.Sprintf("ids: %v, opts: %v", expectedUserOrOrgIDs, opts)
    70  		if assert.Len(t, users, len(expectedUserOrOrgIDs), "case: %s", cassText) {
    71  			for i, expectedID := range expectedUserOrOrgIDs {
    72  				assert.EqualValues(t, expectedID, users[i].ID, "case: %s", cassText)
    73  			}
    74  		}
    75  	}
    76  
    77  	// test orgs
    78  	testOrgSuccess := func(opts *user_model.SearchUserOptions, expectedOrgIDs []int64) {
    79  		opts.Type = user_model.UserTypeOrganization
    80  		testSuccess(opts, expectedOrgIDs)
    81  	}
    82  
    83  	testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1, PageSize: 2}},
    84  		[]int64{3, 6})
    85  
    86  	testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 2, PageSize: 2}},
    87  		[]int64{7, 17})
    88  
    89  	testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 3, PageSize: 2}},
    90  		[]int64{19, 25})
    91  
    92  	testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 4, PageSize: 2}},
    93  		[]int64{26, 41})
    94  
    95  	testOrgSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 5, PageSize: 2}},
    96  		[]int64{})
    97  
    98  	// test users
    99  	testUserSuccess := func(opts *user_model.SearchUserOptions, expectedUserIDs []int64) {
   100  		opts.Type = user_model.UserTypeIndividual
   101  		testSuccess(opts, expectedUserIDs)
   102  	}
   103  
   104  	testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}},
   105  		[]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})
   106  
   107  	testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(false)},
   108  		[]int64{9})
   109  
   110  	testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
   111  		[]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})
   112  
   113  	testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
   114  		[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
   115  
   116  	// order by name asc default
   117  	testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
   118  		[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
   119  
   120  	testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsAdmin: optional.Some(true)},
   121  		[]int64{1})
   122  
   123  	testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsRestricted: optional.Some(true)},
   124  		[]int64{29})
   125  
   126  	testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsProhibitLogin: optional.Some(true)},
   127  		[]int64{37})
   128  
   129  	testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: optional.Some(true)},
   130  		[]int64{24})
   131  }
   132  
   133  func TestEmailNotificationPreferences(t *testing.T) {
   134  	assert.NoError(t, unittest.PrepareTestDatabase())
   135  
   136  	for _, test := range []struct {
   137  		expected string
   138  		userID   int64
   139  	}{
   140  		{user_model.EmailNotificationsEnabled, 1},
   141  		{user_model.EmailNotificationsEnabled, 2},
   142  		{user_model.EmailNotificationsOnMention, 3},
   143  		{user_model.EmailNotificationsOnMention, 4},
   144  		{user_model.EmailNotificationsEnabled, 5},
   145  		{user_model.EmailNotificationsEnabled, 6},
   146  		{user_model.EmailNotificationsDisabled, 7},
   147  		{user_model.EmailNotificationsEnabled, 8},
   148  		{user_model.EmailNotificationsOnMention, 9},
   149  	} {
   150  		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: test.userID})
   151  		assert.Equal(t, test.expected, user.EmailNotificationsPreference)
   152  	}
   153  }
   154  
   155  func TestHashPasswordDeterministic(t *testing.T) {
   156  	b := make([]byte, 16)
   157  	u := &user_model.User{}
   158  	algos := hash.RecommendedHashAlgorithms
   159  	for j := 0; j < len(algos); j++ {
   160  		u.PasswdHashAlgo = algos[j]
   161  		for i := 0; i < 50; i++ {
   162  			// generate a random password
   163  			rand.Read(b)
   164  			pass := string(b)
   165  
   166  			// save the current password in the user - hash it and store the result
   167  			u.SetPassword(pass)
   168  			r1 := u.Passwd
   169  
   170  			// run again
   171  			u.SetPassword(pass)
   172  			r2 := u.Passwd
   173  
   174  			assert.NotEqual(t, r1, r2)
   175  			assert.True(t, u.ValidatePassword(pass))
   176  		}
   177  	}
   178  }
   179  
   180  func BenchmarkHashPassword(b *testing.B) {
   181  	// BenchmarkHashPassword ensures that it takes a reasonable amount of time
   182  	// to hash a password - in order to protect from brute-force attacks.
   183  	pass := "password1337"
   184  	u := &user_model.User{Passwd: pass}
   185  	b.ResetTimer()
   186  	for i := 0; i < b.N; i++ {
   187  		u.SetPassword(pass)
   188  	}
   189  }
   190  
   191  func TestNewGitSig(t *testing.T) {
   192  	users := make([]*user_model.User, 0, 20)
   193  	err := db.GetEngine(db.DefaultContext).Find(&users)
   194  	assert.NoError(t, err)
   195  
   196  	for _, user := range users {
   197  		sig := user.NewGitSig()
   198  		assert.NotContains(t, sig.Name, "<")
   199  		assert.NotContains(t, sig.Name, ">")
   200  		assert.NotContains(t, sig.Name, "\n")
   201  		assert.NotEqual(t, len(strings.TrimSpace(sig.Name)), 0)
   202  	}
   203  }
   204  
   205  func TestDisplayName(t *testing.T) {
   206  	users := make([]*user_model.User, 0, 20)
   207  	err := db.GetEngine(db.DefaultContext).Find(&users)
   208  	assert.NoError(t, err)
   209  
   210  	for _, user := range users {
   211  		displayName := user.DisplayName()
   212  		assert.Equal(t, strings.TrimSpace(displayName), displayName)
   213  		if len(strings.TrimSpace(user.FullName)) == 0 {
   214  			assert.Equal(t, user.Name, displayName)
   215  		}
   216  		assert.NotEqual(t, len(strings.TrimSpace(displayName)), 0)
   217  	}
   218  }
   219  
   220  func TestCreateUserInvalidEmail(t *testing.T) {
   221  	user := &user_model.User{
   222  		Name:               "GiteaBot",
   223  		Email:              "GiteaBot@gitea.io\r\n",
   224  		Passwd:             ";p['////..-++']",
   225  		IsAdmin:            false,
   226  		Theme:              setting.UI.DefaultTheme,
   227  		MustChangePassword: false,
   228  	}
   229  
   230  	err := user_model.CreateUser(db.DefaultContext, user)
   231  	assert.Error(t, err)
   232  	assert.True(t, user_model.IsErrEmailCharIsNotSupported(err))
   233  }
   234  
   235  func TestCreateUserEmailAlreadyUsed(t *testing.T) {
   236  	assert.NoError(t, unittest.PrepareTestDatabase())
   237  
   238  	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
   239  
   240  	// add new user with user2's email
   241  	user.Name = "testuser"
   242  	user.LowerName = strings.ToLower(user.Name)
   243  	user.ID = 0
   244  	err := user_model.CreateUser(db.DefaultContext, user)
   245  	assert.Error(t, err)
   246  	assert.True(t, user_model.IsErrEmailAlreadyUsed(err))
   247  }
   248  
   249  func TestCreateUserCustomTimestamps(t *testing.T) {
   250  	assert.NoError(t, unittest.PrepareTestDatabase())
   251  
   252  	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
   253  
   254  	// Add new user with a custom creation timestamp.
   255  	var creationTimestamp timeutil.TimeStamp = 12345
   256  	user.Name = "testuser"
   257  	user.LowerName = strings.ToLower(user.Name)
   258  	user.ID = 0
   259  	user.Email = "unique@example.com"
   260  	user.CreatedUnix = creationTimestamp
   261  	err := user_model.CreateUser(db.DefaultContext, user)
   262  	assert.NoError(t, err)
   263  
   264  	fetched, err := user_model.GetUserByID(context.Background(), user.ID)
   265  	assert.NoError(t, err)
   266  	assert.Equal(t, creationTimestamp, fetched.CreatedUnix)
   267  	assert.Equal(t, creationTimestamp, fetched.UpdatedUnix)
   268  }
   269  
   270  func TestCreateUserWithoutCustomTimestamps(t *testing.T) {
   271  	assert.NoError(t, unittest.PrepareTestDatabase())
   272  
   273  	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
   274  
   275  	// There is no way to use a mocked time for the XORM auto-time functionality,
   276  	// so use the real clock to approximate the expected timestamp.
   277  	timestampStart := time.Now().Unix()
   278  
   279  	// Add new user without a custom creation timestamp.
   280  	user.Name = "Testuser"
   281  	user.LowerName = strings.ToLower(user.Name)
   282  	user.ID = 0
   283  	user.Email = "unique@example.com"
   284  	user.CreatedUnix = 0
   285  	user.UpdatedUnix = 0
   286  	err := user_model.CreateUser(db.DefaultContext, user)
   287  	assert.NoError(t, err)
   288  
   289  	timestampEnd := time.Now().Unix()
   290  
   291  	fetched, err := user_model.GetUserByID(context.Background(), user.ID)
   292  	assert.NoError(t, err)
   293  
   294  	assert.LessOrEqual(t, timestampStart, fetched.CreatedUnix)
   295  	assert.LessOrEqual(t, fetched.CreatedUnix, timestampEnd)
   296  
   297  	assert.LessOrEqual(t, timestampStart, fetched.UpdatedUnix)
   298  	assert.LessOrEqual(t, fetched.UpdatedUnix, timestampEnd)
   299  }
   300  
   301  func TestGetUserIDsByNames(t *testing.T) {
   302  	assert.NoError(t, unittest.PrepareTestDatabase())
   303  
   304  	// ignore non existing
   305  	IDs, err := user_model.GetUserIDsByNames(db.DefaultContext, []string{"user1", "user2", "none_existing_user"}, true)
   306  	assert.NoError(t, err)
   307  	assert.Equal(t, []int64{1, 2}, IDs)
   308  
   309  	// ignore non existing
   310  	IDs, err = user_model.GetUserIDsByNames(db.DefaultContext, []string{"user1", "do_not_exist"}, false)
   311  	assert.Error(t, err)
   312  	assert.Equal(t, []int64(nil), IDs)
   313  }
   314  
   315  func TestGetMaileableUsersByIDs(t *testing.T) {
   316  	assert.NoError(t, unittest.PrepareTestDatabase())
   317  
   318  	results, err := user_model.GetMaileableUsersByIDs(db.DefaultContext, []int64{1, 4}, false)
   319  	assert.NoError(t, err)
   320  	assert.Len(t, results, 1)
   321  	if len(results) > 1 {
   322  		assert.Equal(t, results[0].ID, 1)
   323  	}
   324  
   325  	results, err = user_model.GetMaileableUsersByIDs(db.DefaultContext, []int64{1, 4}, true)
   326  	assert.NoError(t, err)
   327  	assert.Len(t, results, 2)
   328  	if len(results) > 2 {
   329  		assert.Equal(t, results[0].ID, 1)
   330  		assert.Equal(t, results[1].ID, 4)
   331  	}
   332  }
   333  
   334  func TestNewUserRedirect(t *testing.T) {
   335  	// redirect to a completely new name
   336  	assert.NoError(t, unittest.PrepareTestDatabase())
   337  
   338  	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
   339  	assert.NoError(t, user_model.NewUserRedirect(db.DefaultContext, user.ID, user.Name, "newusername"))
   340  
   341  	unittest.AssertExistsAndLoadBean(t, &user_model.Redirect{
   342  		LowerName:      user.LowerName,
   343  		RedirectUserID: user.ID,
   344  	})
   345  	unittest.AssertExistsAndLoadBean(t, &user_model.Redirect{
   346  		LowerName:      "olduser1",
   347  		RedirectUserID: user.ID,
   348  	})
   349  }
   350  
   351  func TestNewUserRedirect2(t *testing.T) {
   352  	// redirect to previously used name
   353  	assert.NoError(t, unittest.PrepareTestDatabase())
   354  
   355  	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
   356  	assert.NoError(t, user_model.NewUserRedirect(db.DefaultContext, user.ID, user.Name, "olduser1"))
   357  
   358  	unittest.AssertExistsAndLoadBean(t, &user_model.Redirect{
   359  		LowerName:      user.LowerName,
   360  		RedirectUserID: user.ID,
   361  	})
   362  	unittest.AssertNotExistsBean(t, &user_model.Redirect{
   363  		LowerName:      "olduser1",
   364  		RedirectUserID: user.ID,
   365  	})
   366  }
   367  
   368  func TestNewUserRedirect3(t *testing.T) {
   369  	// redirect for a previously-unredirected user
   370  	assert.NoError(t, unittest.PrepareTestDatabase())
   371  
   372  	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
   373  	assert.NoError(t, user_model.NewUserRedirect(db.DefaultContext, user.ID, user.Name, "newusername"))
   374  
   375  	unittest.AssertExistsAndLoadBean(t, &user_model.Redirect{
   376  		LowerName:      user.LowerName,
   377  		RedirectUserID: user.ID,
   378  	})
   379  }
   380  
   381  func TestGetUserByOpenID(t *testing.T) {
   382  	assert.NoError(t, unittest.PrepareTestDatabase())
   383  
   384  	_, err := user_model.GetUserByOpenID(db.DefaultContext, "https://unknown")
   385  	if assert.Error(t, err) {
   386  		assert.True(t, user_model.IsErrUserNotExist(err))
   387  	}
   388  
   389  	user, err := user_model.GetUserByOpenID(db.DefaultContext, "https://user1.domain1.tld")
   390  	if assert.NoError(t, err) {
   391  		assert.Equal(t, int64(1), user.ID)
   392  	}
   393  
   394  	user, err = user_model.GetUserByOpenID(db.DefaultContext, "https://domain1.tld/user2/")
   395  	if assert.NoError(t, err) {
   396  		assert.Equal(t, int64(2), user.ID)
   397  	}
   398  }
   399  
   400  func TestFollowUser(t *testing.T) {
   401  	assert.NoError(t, unittest.PrepareTestDatabase())
   402  
   403  	testSuccess := func(follower, followed *user_model.User) {
   404  		assert.NoError(t, user_model.FollowUser(db.DefaultContext, follower, followed))
   405  		unittest.AssertExistsAndLoadBean(t, &user_model.Follow{UserID: follower.ID, FollowID: followed.ID})
   406  	}
   407  
   408  	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
   409  	user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
   410  	user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
   411  
   412  	testSuccess(user4, user2)
   413  	testSuccess(user5, user2)
   414  
   415  	assert.NoError(t, user_model.FollowUser(db.DefaultContext, user2, user2))
   416  
   417  	unittest.CheckConsistencyFor(t, &user_model.User{})
   418  }
   419  
   420  func TestUnfollowUser(t *testing.T) {
   421  	assert.NoError(t, unittest.PrepareTestDatabase())
   422  
   423  	testSuccess := func(followerID, followedID int64) {
   424  		assert.NoError(t, user_model.UnfollowUser(db.DefaultContext, followerID, followedID))
   425  		unittest.AssertNotExistsBean(t, &user_model.Follow{UserID: followerID, FollowID: followedID})
   426  	}
   427  	testSuccess(4, 2)
   428  	testSuccess(5, 2)
   429  	testSuccess(2, 2)
   430  
   431  	unittest.CheckConsistencyFor(t, &user_model.User{})
   432  }
   433  
   434  func TestIsUserVisibleToViewer(t *testing.T) {
   435  	assert.NoError(t, unittest.PrepareTestDatabase())
   436  
   437  	user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})   // admin, public
   438  	user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})   // normal, public
   439  	user20 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 20}) // public, same team as user31
   440  	user29 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 29}) // public, is restricted
   441  	user31 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 31}) // private, same team as user20
   442  	user33 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 33}) // limited, follows 31
   443  
   444  	test := func(u, viewer *user_model.User, expected bool) {
   445  		name := func(u *user_model.User) string {
   446  			if u == nil {
   447  				return "<nil>"
   448  			}
   449  			return u.Name
   450  		}
   451  		assert.Equal(t, expected, user_model.IsUserVisibleToViewer(db.DefaultContext, u, viewer), "user %v should be visible to viewer %v: %v", name(u), name(viewer), expected)
   452  	}
   453  
   454  	// admin viewer
   455  	test(user1, user1, true)
   456  	test(user20, user1, true)
   457  	test(user31, user1, true)
   458  	test(user33, user1, true)
   459  
   460  	// non admin viewer
   461  	test(user4, user4, true)
   462  	test(user20, user4, true)
   463  	test(user31, user4, false)
   464  	test(user33, user4, true)
   465  	test(user4, nil, true)
   466  
   467  	// public user
   468  	test(user4, user20, true)
   469  	test(user4, user31, true)
   470  	test(user4, user33, true)
   471  
   472  	// limited user
   473  	test(user33, user33, true)
   474  	test(user33, user4, true)
   475  	test(user33, user29, false)
   476  	test(user33, nil, false)
   477  
   478  	// private user
   479  	test(user31, user31, true)
   480  	test(user31, user4, false)
   481  	test(user31, user20, true)
   482  	test(user31, user29, false)
   483  	test(user31, user33, true)
   484  	test(user31, nil, false)
   485  }
   486  
   487  func Test_ValidateUser(t *testing.T) {
   488  	oldSetting := setting.Service.AllowedUserVisibilityModesSlice
   489  	defer func() {
   490  		setting.Service.AllowedUserVisibilityModesSlice = oldSetting
   491  	}()
   492  	setting.Service.AllowedUserVisibilityModesSlice = []bool{true, false, true}
   493  	kases := map[*user_model.User]bool{
   494  		{ID: 1, Visibility: structs.VisibleTypePublic}:  true,
   495  		{ID: 2, Visibility: structs.VisibleTypeLimited}: false,
   496  		{ID: 2, Visibility: structs.VisibleTypePrivate}: true,
   497  	}
   498  	for kase, expected := range kases {
   499  		assert.EqualValues(t, expected, nil == user_model.ValidateUser(kase), fmt.Sprintf("case: %+v", kase))
   500  	}
   501  }
   502  
   503  func Test_NormalizeUserFromEmail(t *testing.T) {
   504  	testCases := []struct {
   505  		Input             string
   506  		Expected          string
   507  		IsNormalizedValid bool
   508  	}{
   509  		{"name@example.com", "name", true},
   510  		{"test'`´name", "testname", true},
   511  		{"Sinéad.O'Connor", "Sinead.OConnor", true},
   512  		{"Æsir", "AEsir", true},
   513  		{"éé", "ee", true}, // \u00e9\u0065\u0301
   514  		{"Awareness Hub", "Awareness-Hub", true},
   515  		{"double__underscore", "double__underscore", false}, // We should consider squashing double non-alpha characters
   516  		{".bad.", ".bad.", false},
   517  		{"new😀user", "new😀user", false}, // No plans to support
   518  		{`"quoted"`, `"quoted"`, false}, // No plans to support
   519  	}
   520  	for _, testCase := range testCases {
   521  		normalizedName, err := user_model.NormalizeUserName(testCase.Input)
   522  		assert.NoError(t, err)
   523  		assert.EqualValues(t, testCase.Expected, normalizedName)
   524  		if testCase.IsNormalizedValid {
   525  			assert.NoError(t, user_model.IsUsableUsername(normalizedName))
   526  		} else {
   527  			assert.Error(t, user_model.IsUsableUsername(normalizedName))
   528  		}
   529  	}
   530  }
   531  
   532  func TestDisabledUserFeatures(t *testing.T) {
   533  	assert.NoError(t, unittest.PrepareTestDatabase())
   534  
   535  	testValues := container.SetOf(setting.UserFeatureDeletion,
   536  		setting.UserFeatureManageSSHKeys,
   537  		setting.UserFeatureManageGPGKeys)
   538  
   539  	oldSetting := setting.Admin.ExternalUserDisableFeatures
   540  	defer func() {
   541  		setting.Admin.ExternalUserDisableFeatures = oldSetting
   542  	}()
   543  	setting.Admin.ExternalUserDisableFeatures = testValues
   544  
   545  	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
   546  
   547  	assert.Len(t, setting.Admin.UserDisabledFeatures.Values(), 0)
   548  
   549  	// no features should be disabled with a plain login type
   550  	assert.LessOrEqual(t, user.LoginType, auth.Plain)
   551  	assert.Len(t, user_model.DisabledFeaturesWithLoginType(user).Values(), 0)
   552  	for _, f := range testValues.Values() {
   553  		assert.False(t, user_model.IsFeatureDisabledWithLoginType(user, f))
   554  	}
   555  
   556  	// check disabled features with external login type
   557  	user.LoginType = auth.OAuth2
   558  
   559  	// all features should be disabled
   560  	assert.NotEmpty(t, user_model.DisabledFeaturesWithLoginType(user).Values())
   561  	for _, f := range testValues.Values() {
   562  		assert.True(t, user_model.IsFeatureDisabledWithLoginType(user, f))
   563  	}
   564  }