Files
team/server/public/model/user_test.go
9ed85b6850 Cherry-pick Go 1.25.8 upgrade and FIPS password fixes to release-11.4 (#36123)
* ci: shard server Postgres tests into 4 parallel runners (#35739)

Cherry-pick of 00296f9b49 from master.

* updated go to version 1.25.8 (#35817)

Cherry-pick of 32a10bac62 from master.

* Move password hashers from server/v8 to server/public (#35805)

Cherry-pick of cca3f08574 from master.

* Generate instead of hard-coding test passwords, enforce new minimum for FIPS, shard CI, fix FIPS builds (#35905)

Cherry-pick of 1bdd985b41 from master.

* upgrade golangci-lint (#35845)

Cherry-pick of 157e0400b2 from master.

On release-11.4 (unlike 11.5), UpdatePostsChannelTypeByChannelId and
BackfillPostsChannelType exist in the elasticsearch/opensearch files,
so the imports they require (fmt, updatebyquery, scriptlanguage) must
be added — they were not in the cherry-pick diff since they already
existed on master.

Also updates html2text/tablewriter/olekukonko deps to fix a workspace
conflict where enterprise's Go 1.25.8 bump pulled in a newer html2text
that requires tablewriter ≥ 1.1 (was pinned to 0.0.5 via a replace
directive). Matches the subset of 866f759e4b (Mm 66662 bump deps)
relevant to this incompatibility.

* Fix FIPS-incompatible passwords and config in e2e test suites (#36001)

Cherry-pick of b51b4a8937 from master.

release-11.4 differences vs release-11.5:
- demo_plugin_installation.spec.ts also deleted (modify/delete conflict, git rm)
- authentication_2_spec.ts conflict: HEAD missing check() call; took
  incoming (check + FIPS-length passwords)
- member_invitation_ui_spec.ts: HEAD has 'Create Account' (capital A)
  matching release-11.4 button text; kept HEAD's button text with
  incoming's newTestPassword()

* fix(cypress): demo plugin (#36056)

Cherry-pick of 39f1979aa5 from master.

demo_plugin_installation.spec.ts was deleted in step 10 (modify/delete);
re-created here with both the FIPS-password change and the URL fix.

* test: clean up channel store data after TestChannelStore (#36066)

TestChannelStore sub-tests create channels, members, and team members
using fake TeamIds and UserIds (model.NewId() for non-existent rows).
These records are left in the database and cause integrity tests
(TestCheck*) running in the same binary to fail their full-table scans.

Register a t.Cleanup on TestChannelStore that purges the affected
tables entirely. A blanket purge is safe: the schema enforces no FK
constraints, and every test suite creates its own data independently.

* Fix command injection in server-test-template workflow (#36080)

Cherry-pick of 9223935f16 from master.

* fix(ci): restore testname format in sharded gotestsum runs (#36078)

run-shard-tests.sh called gotestsum directly without --format, so it
fell back to gotestsum's default (pkgname) instead of the testname
format set by the Makefile. Pass --format "${GOTESTSUM_FORMAT:-testname}"
to match the Makefile default.

Co-authored-by: Mattermost Build <[email protected]>

* fixup: drop demo_plugin playwright specs — master-only APIs

demo_plugin/helpers.ts and demo_plugin_installation.spec.ts use
installAndEnablePlugin and isPluginActive which don't exist on
release-11.4's PlaywrightExtended type. These files were re-created
by the step-11 cherry-pick of 39f1979aa5, but they don't compile on
this branch (same class of problem as actions.spec.ts in step 10).

* fixup: add missing await in PluginLinkTooltip test

The test added in 39f1979aa5 is missing an await on renderWithContext,
causing the pointer-events assertion to race. Fix from 0c7d3f27c6
(on a feature branch, not yet on master).

* fixup: drop pointer-events test incompatible with jsdom@20

The 'should not block interaction with elements outside the tooltip' test
checks getComputedStyle for pointer-events: none, which is set only in SCSS.
release-11.4 uses jest-environment-jsdom@29 (jsdom@20), which does not apply
stylesheets, so getComputedStyle always returns "". The test passes on
release-11.5+ only because they use jest-environment-jsdom@30 (jsdom@25+).

Drop the test rather than backport the jsdom upgrade.

* fixup: add missing i18n keys for elasticsearch/opensearch functions

BackfillPostsChannelType and UpdatePostsChannelTypeByChannelId exist on
release-11.4 (unlike release-11.5+). The golangci-lint cherry-pick added
new error strings to those functions referencing i18n keys that weren't
in en.json.

* fixup: run make i18n-extract to reorder keys correctly

* fixup: drop terms-and-privacy checkbox from MM-T1774 on release-11.4

The #signup-body-card-form-check-terms-and-privacy element did not exist
on release-11.4's signup page. It was added to master after this branch
was cut, so the check() call from the incoming cherry-pick causes the test
to fail with "element never found".

* fixup: remove UpdatePostsChannelTypeByChannelId/BackfillPostsChannelType

These functions were introduced by MM-67540 (allow searching public channel
messages without channel membership), which never landed on release-11.4.
The golangci-lint cherry-pick only modernizes existing code; the rerere
resolution incorrectly added the full function bodies from master.

Remove both functions and their now-unused imports (fmt, updatebyquery,
scriptlanguage) from elasticsearch.go and opensearch.go. Also remove the
two i18n keys added to support them.

Same treatment as release-11.5, where these functions were also absent.

---------

Co-authored-by: Mattermost Build <[email protected]>
2026-04-16 12:10:03 -03:00

648 lines
21 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
import (
"fmt"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/mattermost/mattermost/server/public/shared/mlog"
"github.com/mattermost/mattermost/server/public/shared/timezones"
)
func TestUserAuditable(t *testing.T) {
t.Run("zero value", func(t *testing.T) {
var u User
m := u.Auditable()
require.NotNil(t, m)
assert.Equal(t, "", m["remote_id"])
})
t.Run("values set", func(t *testing.T) {
id := NewId()
now := GetMillis()
u := User{
Id: id,
CreateAt: now,
UpdateAt: now,
DeleteAt: now,
Username: "some user_name",
Password: "some password",
AuthData: NewPointer("some_auth_data"),
AuthService: UserAuthServiceLdap,
Email: "[email protected]",
EmailVerified: true,
Position: "some position",
Roles: strings.Join([]string{ChannelAdminRoleId, SystemManagerRoleId}, ","),
AllowMarketing: true,
Props: StringMap{
"foo": "bar",
},
NotifyProps: StringMap{
"bar": "foo",
},
Locale: DefaultLocale,
Timezone: timezones.DefaultUserTimezone(),
MfaActive: true,
RemoteId: NewPointer("some_remote"),
}
m := u.Auditable()
expected := map[string]any{
"id": id,
"create_at": now,
"update_at": now,
"delete_at": now,
"username": "some user_name",
"auth_service": "ldap",
"email": "[email protected]",
"email_verified": true,
"position": "some position",
"roles": "channel_admin,system_manager",
"allow_marketing": true,
"props": StringMap{
"foo": "bar",
},
"notify_props": StringMap{
"bar": "foo",
},
"last_password_update": int64(0),
"last_picture_update": int64(0),
"failed_attempts": 0,
"locale": "en",
"timezone": StringMap(timezones.DefaultUserTimezone()),
"mfa_active": true,
"remote_id": "some_remote",
"last_activity_at": int64(0),
"is_bot": false,
"bot_description": "",
"bot_last_icon_update": int64(0),
"terms_of_service_id": "",
"terms_of_service_create_at": int64(0),
"disable_welcome_email": false,
}
assert.Equal(t, expected, m)
})
}
func TestUserLogClone(t *testing.T) {
t.Run("zero value", func(t *testing.T) {
var u User
l := u.LogClone()
require.NotNil(t, l)
m, ok := l.(map[string]any)
require.True(t, ok)
assert.Equal(t, "", m["remote_id"])
})
t.Run("values set", func(t *testing.T) {
id := NewId()
now := GetMillis()
u := User{
Id: id,
CreateAt: now,
UpdateAt: now,
DeleteAt: now,
Username: "some user_name",
Password: "some password",
AuthData: NewPointer("some_auth_data"),
AuthService: UserAuthServiceLdap,
Email: "[email protected]",
EmailVerified: true,
Position: "some position",
Roles: strings.Join([]string{ChannelAdminRoleId, SystemManagerRoleId}, ","),
AllowMarketing: true,
Props: StringMap{
"foo": "bar",
},
NotifyProps: StringMap{
"bar": "foo",
},
Locale: DefaultLocale,
Timezone: timezones.DefaultUserTimezone(),
MfaActive: true,
RemoteId: NewPointer("some_remote"),
}
l := u.LogClone()
m, ok := l.(map[string]any)
require.True(t, ok)
expected := map[string]any{
"id": id,
"create_at": now,
"update_at": now,
"delete_at": now,
"username": "some user_name",
"auth_data": "some_auth_data",
"auth_service": "ldap",
"email": "[email protected]",
"email_verified": true,
"position": "some position",
"roles": "channel_admin,system_manager",
"allow_marketing": true,
"props": StringMap{
"foo": "bar",
},
"notify_props": StringMap{
"bar": "foo",
},
"locale": "en",
"timezone": StringMap(timezones.DefaultUserTimezone()),
"mfa_active": true,
"remote_id": "some_remote",
}
assert.Equal(t, expected, m)
})
}
func TestUserDeepCopy(t *testing.T) {
id := NewId()
authData := "authdata"
mapKey := "key"
mapValue := "key"
user := &User{Id: id, AuthData: NewPointer(authData), Props: map[string]string{}, NotifyProps: map[string]string{}, Timezone: map[string]string{}}
user.Props[mapKey] = mapValue
user.NotifyProps[mapKey] = mapValue
user.Timezone[mapKey] = mapValue
copyUser := user.DeepCopy()
copyUser.Id = "someid"
*copyUser.AuthData = "changed"
copyUser.Props[mapKey] = "changed"
copyUser.NotifyProps[mapKey] = "changed"
copyUser.Timezone[mapKey] = "changed"
assert.Equal(t, id, user.Id)
assert.Equal(t, authData, *user.AuthData)
assert.Equal(t, mapValue, user.Props[mapKey])
assert.Equal(t, mapValue, user.NotifyProps[mapKey])
assert.Equal(t, mapValue, user.Timezone[mapKey])
user = &User{Id: id}
copyUser = user.DeepCopy()
assert.Equal(t, id, copyUser.Id)
}
type stubHasherFunc func(password string) (string, error)
func (f stubHasherFunc) Hash(password string) (string, error) { return f(password) }
func TestUserPreSave(t *testing.T) {
hasher := stubHasherFunc(func(password string) (string, error) {
return "hashed_" + password, nil
})
user := User{Password: "test"}
err := user.PreSave(hasher)
require.Nil(t, err)
assert.Equal(t, "hashed_test", user.Password)
user.Etag(true, true)
assert.NotNil(t, user.Timezone, "Timezone is nil")
assert.Equal(t, user.Timezone["useAutomaticTimezone"], "true", "Timezone is not set to default")
// Set default user with notify props
userWithDefaultNotifyProps := User{}
userWithDefaultNotifyProps.SetDefaultNotifications()
for notifyPropKey, expectedNotifyPropValue := range userWithDefaultNotifyProps.NotifyProps {
actualNotifyPropValue, ok := user.NotifyProps[notifyPropKey]
assert.True(t, ok, "Notify prop %s is not set", notifyPropKey)
assert.Equal(t, expectedNotifyPropValue, actualNotifyPropValue, "Notify prop %s is not set to default", notifyPropKey)
}
}
func TestUserPreSavePwdTooLong(t *testing.T) {
hasher := stubHasherFunc(func(password string) (string, error) {
return "", ErrPasswordTooLong
})
user := User{Password: strings.Repeat("1234567890", 8)}
err := user.PreSave(hasher)
require.NotNil(t, err)
assert.Equal(t, "model.user.pre_save.password_too_long.app_error", err.Id)
}
func TestUserPreUpdate(t *testing.T) {
user := User{Password: "test"}
user.PreUpdate()
// Set default user with notify props
userWithDefaultNotifyProps := User{}
userWithDefaultNotifyProps.SetDefaultNotifications()
for notifyPropKey, expectedNotifyPropValue := range userWithDefaultNotifyProps.NotifyProps {
actualNotifyPropValue, ok := user.NotifyProps[notifyPropKey]
assert.True(t, ok, "Notify prop %s is not set", notifyPropKey)
assert.Equal(t, expectedNotifyPropValue, actualNotifyPropValue, "Notify prop %s is not set to default", notifyPropKey)
}
}
func TestUserUpdateMentionKeysFromUsername(t *testing.T) {
user := User{Username: "user"}
user.SetDefaultNotifications()
assert.Equalf(t, user.NotifyProps["mention_keys"], "", "default mention keys are invalid: %v", user.NotifyProps["mention_keys"])
user.Username = "person"
user.UpdateMentionKeysFromUsername("user")
assert.Equalf(t, user.NotifyProps["mention_keys"], "", "mention keys are invalid after changing username: %v", user.NotifyProps["mention_keys"])
user.NotifyProps["mention_keys"] += ",mention"
user.UpdateMentionKeysFromUsername("person")
assert.Equalf(t, user.NotifyProps["mention_keys"], ",mention", "mention keys are invalid after adding extra mention keyword: %v", user.NotifyProps["mention_keys"])
user.Username = "user"
user.UpdateMentionKeysFromUsername("person")
assert.Equalf(t, user.NotifyProps["mention_keys"], ",mention", "mention keys are invalid after changing username with extra mention keyword: %v", user.NotifyProps["mention_keys"])
}
func TestUserIsValid(t *testing.T) {
user := User{}
appErr := user.IsValid()
require.True(t, HasExpectedUserIsValidError(appErr, "id", "", user.Id), "expected user is valid error: %s", appErr.Error())
user.Id = NewId()
appErr = user.IsValid()
require.True(t, HasExpectedUserIsValidError(appErr, "create_at", user.Id, user.CreateAt), "expected user is valid error: %s", appErr.Error())
user.CreateAt = GetMillis()
appErr = user.IsValid()
require.True(t, HasExpectedUserIsValidError(appErr, "update_at", user.Id, user.UpdateAt), "expected user is valid error: %s", appErr.Error())
user.UpdateAt = GetMillis()
appErr = user.IsValid()
require.True(t, HasExpectedUserIsValidError(appErr, "username", user.Id, user.Username), "expected user is valid error: %s", appErr.Error())
user.Username = NewUsername() + "^hello#"
appErr = user.IsValid()
require.True(t, HasExpectedUserIsValidError(appErr, "username", user.Id, user.Username), "expected user is valid error: %s", appErr.Error())
user.Username = NewUsername()
appErr = user.IsValid()
require.True(t, HasExpectedUserIsValidError(appErr, "email", user.Id, user.Email), "expected user is valid error: %s", appErr.Error())
user.Email = strings.Repeat("01234567890", 20)
appErr = user.IsValid()
require.True(t, HasExpectedUserIsValidError(appErr, "email", user.Id, user.Email), "expected user is valid error: %s", appErr.Error())
user.Email = "[email protected]"
user.Nickname = strings.Repeat("a", 65)
appErr = user.IsValid()
require.True(t, HasExpectedUserIsValidError(appErr, "nickname", user.Id, user.Nickname), "expected user is valid error: %s", appErr.Error())
user.Nickname = strings.Repeat("a", 64)
require.Nil(t, user.IsValid())
user.FirstName = ""
user.LastName = ""
require.Nil(t, user.IsValid())
user.Email = NewId()
appErr = user.IsValid()
require.True(t, HasExpectedUserIsValidError(appErr, "email", user.Id, user.Email), "expected user is valid error: %s", appErr.Error())
user.RemoteId = NewPointer(NewId())
require.Nil(t, user.IsValid())
user.FirstName = strings.Repeat("a", 65)
appErr = user.IsValid()
require.True(t, HasExpectedUserIsValidError(appErr, "first_name", user.Id, user.FirstName), "expected user is valid error: %s", appErr.Error())
user.FirstName = strings.Repeat("a", 64)
user.LastName = strings.Repeat("a", 65)
appErr = user.IsValid()
require.True(t, HasExpectedUserIsValidError(appErr, "last_name", user.Id, user.LastName), "expected user is valid error: %s", appErr.Error())
user.LastName = strings.Repeat("a", 64)
user.Position = strings.Repeat("a", 128)
require.Nil(t, user.IsValid())
user.Position = strings.Repeat("a", 129)
appErr = user.IsValid()
require.True(t, HasExpectedUserIsValidError(appErr, "position", user.Id, user.Position), "expected user is valid error: %s", appErr.Error())
user.Position = ""
user.Roles = strings.Repeat("a", UserRolesMaxLength)
appErr = user.IsValid()
require.Nil(t, appErr)
user.Roles = strings.Repeat("a", UserRolesMaxLength+1)
appErr = user.IsValid()
require.True(t, HasExpectedUserIsValidError(appErr, "roles_limit", user.Id, user.Roles), "expected user is valid error: %s", appErr.Error())
}
func TestUserSanitizeInput(t *testing.T) {
user := User{}
user.CreateAt = GetMillis()
user.UpdateAt = GetMillis()
user.DeleteAt = GetMillis()
user.LastPasswordUpdate = GetMillis()
user.LastPictureUpdate = GetMillis()
user.Username = "username"
user.Email = " [email protected] "
user.Nickname = "nickname"
user.FirstName = "firstname"
user.LastName = "lastname"
user.RemoteId = NewPointer(NewId())
user.Position = "position"
user.Roles = "system_admin"
user.AuthData = NewPointer("authdata")
user.AuthService = "saml"
user.EmailVerified = true
user.FailedAttempts = 10
user.LastActivityAt = GetMillis()
user.MfaUsedTimestamps = StringArray{"1234", "4566"}
user.SanitizeInput(false)
// these fields should be reset
require.Equal(t, NewPointer(""), user.AuthData)
require.Equal(t, "", user.AuthService)
require.False(t, user.EmailVerified)
require.Equal(t, NewPointer(""), user.RemoteId)
require.Equal(t, int64(0), user.CreateAt)
require.Equal(t, int64(0), user.UpdateAt)
require.Equal(t, int64(0), user.DeleteAt)
require.Equal(t, int64(0), user.LastPasswordUpdate)
require.Equal(t, int64(0), user.LastPictureUpdate)
require.Equal(t, int64(0), user.LastActivityAt)
require.Equal(t, 0, user.FailedAttempts)
require.Equal(t, StringArray{}, user.MfaUsedTimestamps)
// these fields should remain intact
require.Equal(t, "[email protected]", user.Email)
require.Equal(t, "username", user.Username)
require.Equal(t, "nickname", user.Nickname)
require.Equal(t, "firstname", user.FirstName)
require.Equal(t, "lastname", user.LastName)
require.Equal(t, "position", user.Position)
}
func HasExpectedUserIsValidError(err *AppError, fieldName, userId string, fieldValue any) bool {
if err == nil {
return false
}
return err.Where == "User.IsValid" &&
err.Id == fmt.Sprintf("model.user.is_valid.%s.app_error", fieldName) &&
err.StatusCode == http.StatusBadRequest &&
(userId == "" || err.DetailedError == fmt.Sprintf("user_id=%s %s=%v", userId, fieldName, fieldValue))
}
func TestUserGetFullName(t *testing.T) {
user := User{}
assert.Equal(t, user.GetFullName(), "", "Full name should be blank")
user.FirstName = "first"
assert.Equal(t, user.GetFullName(), "first", "Full name should be first name")
user.FirstName = ""
user.LastName = "last"
assert.Equal(t, user.GetFullName(), "last", "Full name should be last name")
user.FirstName = "first"
assert.Equal(t, user.GetFullName(), "first last", "Full name should be first name and last name")
}
func TestUserGetDisplayName(t *testing.T) {
user := User{Username: "username"}
assert.Equal(t, user.GetDisplayName(ShowFullName), "username", "Display name should be username")
assert.Equal(t, user.GetDisplayName(ShowNicknameFullName), "username", "Display name should be username")
assert.Equal(t, user.GetDisplayName(ShowUsername), "username", "Display name should be username")
user.FirstName = "first"
user.LastName = "last"
assert.Equal(t, user.GetDisplayName(ShowFullName), "first last", "Display name should be full name")
assert.Equal(t, user.GetDisplayName(ShowNicknameFullName), "first last", "Display name should be full name since there is no nickname")
assert.Equal(t, user.GetDisplayName(ShowUsername), "username", "Display name should be username")
user.Nickname = "nickname"
assert.Equal(t, user.GetDisplayName(ShowNicknameFullName), "nickname", "Display name should be nickname")
}
func TestUserGetDisplayNameWithPrefix(t *testing.T) {
user := User{Username: "username"}
assert.Equal(t, user.GetDisplayNameWithPrefix(ShowFullName, "@"), "@username", "Display name should be username")
assert.Equal(t, user.GetDisplayNameWithPrefix(ShowNicknameFullName, "@"), "@username", "Display name should be username")
assert.Equal(t, user.GetDisplayNameWithPrefix(ShowUsername, "@"), "@username", "Display name should be username")
user.FirstName = "first"
user.LastName = "last"
assert.Equal(t, user.GetDisplayNameWithPrefix(ShowFullName, "@"), "first last", "Display name should be full name")
assert.Equal(t, user.GetDisplayNameWithPrefix(ShowNicknameFullName, "@"), "first last", "Display name should be full name since there is no nickname")
assert.Equal(t, user.GetDisplayNameWithPrefix(ShowUsername, "@"), "@username", "Display name should be username")
user.Nickname = "nickname"
assert.Equal(t, user.GetDisplayNameWithPrefix(ShowNicknameFullName, "@"), "nickname", "Display name should be nickname")
}
type usernamesTest struct {
value string
expected bool
expectedWhenRemote bool
}
var usernames = []usernamesTest{
{"spin-punch", true, true},
{"sp", true, true},
{"s", true, true},
{"1spin-punch", true, true},
{"-spin-punch", true, true},
{".spin-punch", true, true},
{"Spin-punch", false, false},
{"spin punch-", false, false},
{"spin_punch", true, true},
{"spin", true, true},
{"PUNCH", false, false},
{"spin.punch", true, true},
{"spin'punch", false, false},
{"spin*punch", false, false},
{"all", false, false},
{"system", false, false},
{"spin:punch", false, true},
}
func TestValidUsername(t *testing.T) {
for _, v := range usernames {
if IsValidUsername(v.value) != v.expected {
t.Errorf("expect %v as %v", v.value, v.expected)
}
}
for _, v := range usernames {
if IsValidUsernameAllowRemote(v.value) != v.expectedWhenRemote {
t.Errorf("expect %v as %v", v.value, v.expectedWhenRemote)
}
}
}
func TestNormalizeUsername(t *testing.T) {
assert.Equal(t, NormalizeUsername("Spin-punch"), "spin-punch", "didn't normalize username properly")
assert.Equal(t, NormalizeUsername("PUNCH"), "punch", "didn't normalize username properly")
assert.Equal(t, NormalizeUsername("spin"), "spin", "didn't normalize username properly")
}
func TestNormalizeEmail(t *testing.T) {
assert.Equal(t, NormalizeEmail("[email protected]"), "[email protected]", "didn't normalize email properly")
assert.Equal(t, NormalizeEmail("[email protected]"), "[email protected]", "didn't normalize email properly")
assert.Equal(t, NormalizeEmail("[email protected]"), "[email protected]", "didn't normalize email properly")
}
func TestCleanUsername(t *testing.T) {
logger := mlog.CreateConsoleTestLogger(t)
assert.Equal(t, CleanUsername(logger, "Spin-punch"), "spin-punch", "didn't clean name properly")
assert.Equal(t, CleanUsername(logger, "PUNCH"), "punch", "didn't clean name properly")
assert.Equal(t, CleanUsername(logger, "spin'punch"), "spin-punch", "didn't clean name properly")
assert.Equal(t, CleanUsername(logger, "spin"), "spin", "didn't clean name properly")
assert.Len(t, CleanUsername(logger, "all"), 27, "didn't clean name properly")
}
func TestRoles(t *testing.T) {
require.True(t, IsValidUserRoles("team_user"))
require.False(t, IsValidUserRoles("system_admin"))
require.True(t, IsValidUserRoles("system_user system_admin"))
require.False(t, IsInRole("system_admin junk", "admin"))
require.True(t, IsInRole("system_admin junk", "system_admin"))
require.False(t, IsInRole("admin", "system_admin"))
}
func TestIsValidLocale(t *testing.T) {
for _, test := range []struct {
Name string
Locale string
Expected bool
}{
{
Name: "empty locale",
Locale: "",
Expected: true,
},
{
Name: "locale with only language",
Locale: "fr",
Expected: true,
},
{
Name: "locale with region",
Locale: "en-DE", // English, as used in Germany
Expected: true,
},
{
Name: "invalid locale",
Locale: "'",
Expected: false,
},
// Note that the following cases are all valid language tags, but they're considered invalid here because of
// the max length of the User.Locale field.
{
Name: "locale with extended language subtag",
Locale: "zh-yue-HK", // Chinese, Cantonese, as used in Hong Kong
Expected: false,
},
{
Name: "locale with script",
Locale: "hy-Latn-IT-arevela", // Eastern Armenian written in Latin script, as used in Italy
Expected: false,
},
{
Name: "locale with variant",
Locale: "sl-rozaj-biske", // San Giorgio dialect of Resian dialect of Slovenian
Expected: false,
},
{
Name: "locale with extension",
Locale: "de-DE-u-co-phonebk", // German, as used in Germany, using German phonebook sort order
Expected: false,
},
} {
t.Run(test.Name, func(t *testing.T) {
assert.Equal(t, test.Expected, IsValidLocale(test.Locale))
})
}
}
func TestUserSlice(t *testing.T) {
t.Run("FilterByActive", func(t *testing.T) {
user0 := &User{Id: "user0", DeleteAt: 0, IsBot: true}
user1 := &User{Id: "user1", DeleteAt: 0, IsBot: true}
user2 := &User{Id: "user2", DeleteAt: 1, IsBot: false}
slice := UserSlice([]*User{user0, user1, user2})
activeUsers := slice.FilterByActive(true)
assert.Len(t, activeUsers, 2)
for _, user := range activeUsers {
assert.True(t, user.DeleteAt == 0)
}
inactiveUsers := slice.FilterByActive(false)
assert.Len(t, inactiveUsers, 1)
for _, user := range inactiveUsers {
assert.True(t, user.DeleteAt != 0)
}
nonBotUsers := slice.FilterWithoutBots()
assert.Len(t, nonBotUsers, 1)
})
}
func TestValidateCustomStatus(t *testing.T) {
t.Run("ValidateCustomStatus", func(t *testing.T) {
user0 := &User{Id: "user0", DeleteAt: 0, IsBot: true}
user0.Props = map[string]string{UserPropsKeyCustomStatus: ""}
assert.True(t, user0.ValidateCustomStatus())
user0.Props[UserPropsKeyCustomStatus] = "hello"
assert.False(t, user0.ValidateCustomStatus())
user0.Props[UserPropsKeyCustomStatus] = "{\"emoji\":{\"foo\":\"bar\"}}"
assert.True(t, user0.ValidateCustomStatus())
user0.Props[UserPropsKeyCustomStatus] = "{\"text\": \"hello\"}"
assert.True(t, user0.ValidateCustomStatus())
user0.Props[UserPropsKeyCustomStatus] = "{\"wrong\": \"hello\"}"
assert.True(t, user0.ValidateCustomStatus())
})
}
func TestSanitizeProfile(t *testing.T) {
t.Run("should correctly sanitize email and remote email", func(t *testing.T) {
user := &User{
Email: "[email protected]",
Props: StringMap{UserPropsKeyRemoteEmail: "[email protected]"},
}
user.SanitizeProfile(nil, false)
require.Equal(t, "[email protected]", user.Email)
require.Equal(t, "[email protected]", user.Props[UserPropsKeyRemoteEmail])
user.SanitizeProfile(map[string]bool{"email": false}, false)
require.Empty(t, user.Email)
require.Empty(t, user.Props[UserPropsKeyRemoteEmail])
})
}