forgejo/services/user/update_test.go
Mathieu Fenniak 51d0188533 refactor: replace Value() from Option[T] with Get() & ValueOrZeroValue() (#11218)
`Option[T]` currently exposes a method `Value()` which is permitted to be called on an option that has a value, and an option that doesn't have a value.  This API is awkward because the behaviour if the option doesn't have a value isn't clear to the caller, and, because almost all accesses end up being `.Has()?` then `OK, use .Value()`.

`Get() (bool, T)` is added as a better replacement, which both returns whether the option has a value, and the value if present.  Most call-sites are rewritten to this form.

`ValueOrZeroValue()` is a direct replacement that has the same behaviour that `Value()` had, but describes the behaviour if the value is missing.

In addition to the current API being awkward, the core reason for this change is that `Value()` conflicts with the `Value()` function from the `driver.Valuer` interface.  If this interface was implemented, it would allow `Option[T]` to be used to represent a nullable field in an xorm bean struct (requires: https://code.forgejo.org/xorm/xorm/pulls/66).

_Note:_ changes are extensive in this PR, but are almost all changes are easy, mechanical transitions from `.Has()` to `.Get()`.  All of this work was performed by hand.

## Checklist

The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org).

### Tests

- I added test coverage for Go changes...
  - [ ] in their respective `*_test.go` for unit tests.
  - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- I added test coverage for JavaScript changes...
  - [ ] in `web_src/js/*.test.js` if it can be unit tested.
  - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [ ] This change will be noticed by a Forgejo user or admin (feature, bug fix, performance, etc.). I suggest to include a release note for this change.
- [x] This change is not visible to a Forgejo user or admin (refactor, dependency upgrade, etc.). I think there is no need to add a release note for this change.

*The decision if the pull request will be shown in the release notes is up to the mergers / release team.*

The content of the `release-notes/<pull request number>.md` file will serve as the basis for the release notes. If the file does not exist, the title of the pull request will be used instead.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/11218
Reviewed-by: Otto <otto@codeberg.org>
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Co-committed-by: Mathieu Fenniak <mathieu@fenniak.net>
2026-02-10 16:41:21 +01:00

121 lines
5.5 KiB
Go

// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package user
import (
"testing"
"forgejo.org/models/db"
"forgejo.org/models/unittest"
user_model "forgejo.org/models/user"
password_module "forgejo.org/modules/auth/password"
"forgejo.org/modules/optional"
"forgejo.org/modules/structs"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestUpdateUser(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
require.Error(t, UpdateUser(db.DefaultContext, admin, &UpdateOptions{
IsAdmin: optional.Some(false),
}))
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 28})
opts := &UpdateOptions{
KeepEmailPrivate: optional.Some(false),
FullName: optional.Some("Changed Name"),
Website: optional.Some("https://gitea.com/"),
Location: optional.Some("location"),
Description: optional.Some("description"),
AllowGitHook: optional.Some(true),
AllowImportLocal: optional.Some(true),
MaxRepoCreation: optional.Some(10),
IsRestricted: optional.Some(true),
IsActive: optional.Some(false),
IsAdmin: optional.Some(true),
Visibility: optional.Some(structs.VisibleTypePrivate),
KeepActivityPrivate: optional.Some(true),
Language: optional.Some("lang"),
Theme: optional.Some("theme"),
DiffViewStyle: optional.Some("split"),
AllowCreateOrganization: optional.Some(false),
EmailNotificationsPreference: optional.Some("disabled"),
SetLastLogin: true,
}
require.NoError(t, UpdateUser(db.DefaultContext, user, opts))
assert.Equal(t, opts.KeepEmailPrivate.ValueOrZeroValue(), user.KeepEmailPrivate)
assert.Equal(t, opts.FullName.ValueOrZeroValue(), user.FullName)
assert.Equal(t, opts.Website.ValueOrZeroValue(), user.Website)
assert.Equal(t, opts.Location.ValueOrZeroValue(), user.Location)
assert.Equal(t, opts.Description.ValueOrZeroValue(), user.Description)
assert.Equal(t, opts.AllowGitHook.ValueOrZeroValue(), user.AllowGitHook)
assert.Equal(t, opts.AllowImportLocal.ValueOrZeroValue(), user.AllowImportLocal)
assert.Equal(t, opts.MaxRepoCreation.ValueOrZeroValue(), user.MaxRepoCreation)
assert.Equal(t, opts.IsRestricted.ValueOrZeroValue(), user.IsRestricted)
assert.Equal(t, opts.IsActive.ValueOrZeroValue(), user.IsActive)
assert.Equal(t, opts.IsAdmin.ValueOrZeroValue(), user.IsAdmin)
assert.Equal(t, opts.Visibility.ValueOrZeroValue(), user.Visibility)
assert.Equal(t, opts.KeepActivityPrivate.ValueOrZeroValue(), user.KeepActivityPrivate)
assert.Equal(t, opts.Language.ValueOrZeroValue(), user.Language)
assert.Equal(t, opts.Theme.ValueOrZeroValue(), user.Theme)
assert.Equal(t, opts.DiffViewStyle.ValueOrZeroValue(), user.DiffViewStyle)
assert.Equal(t, opts.AllowCreateOrganization.ValueOrZeroValue(), user.AllowCreateOrganization)
assert.Equal(t, opts.EmailNotificationsPreference.ValueOrZeroValue(), user.EmailNotificationsPreference)
user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 28})
assert.Equal(t, opts.KeepEmailPrivate.ValueOrZeroValue(), user.KeepEmailPrivate)
assert.Equal(t, opts.FullName.ValueOrZeroValue(), user.FullName)
assert.Equal(t, opts.Website.ValueOrZeroValue(), user.Website)
assert.Equal(t, opts.Location.ValueOrZeroValue(), user.Location)
assert.Equal(t, opts.Description.ValueOrZeroValue(), user.Description)
assert.Equal(t, opts.AllowGitHook.ValueOrZeroValue(), user.AllowGitHook)
assert.Equal(t, opts.AllowImportLocal.ValueOrZeroValue(), user.AllowImportLocal)
assert.Equal(t, opts.MaxRepoCreation.ValueOrZeroValue(), user.MaxRepoCreation)
assert.Equal(t, opts.IsRestricted.ValueOrZeroValue(), user.IsRestricted)
assert.Equal(t, opts.IsActive.ValueOrZeroValue(), user.IsActive)
assert.Equal(t, opts.IsAdmin.ValueOrZeroValue(), user.IsAdmin)
assert.Equal(t, opts.Visibility.ValueOrZeroValue(), user.Visibility)
assert.Equal(t, opts.KeepActivityPrivate.ValueOrZeroValue(), user.KeepActivityPrivate)
assert.Equal(t, opts.Language.ValueOrZeroValue(), user.Language)
assert.Equal(t, opts.Theme.ValueOrZeroValue(), user.Theme)
assert.Equal(t, opts.DiffViewStyle.ValueOrZeroValue(), user.DiffViewStyle)
assert.Equal(t, opts.AllowCreateOrganization.ValueOrZeroValue(), user.AllowCreateOrganization)
assert.Equal(t, opts.EmailNotificationsPreference.ValueOrZeroValue(), user.EmailNotificationsPreference)
}
func TestUpdateAuth(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 28})
userCopy := *user
require.NoError(t, UpdateAuth(db.DefaultContext, user, &UpdateAuthOptions{
LoginName: optional.Some("new-login"),
}))
assert.Equal(t, "new-login", user.LoginName)
require.NoError(t, UpdateAuth(db.DefaultContext, user, &UpdateAuthOptions{
Password: optional.Some("%$DRZUVB576tfzgu"),
MustChangePassword: optional.Some(true),
}))
assert.True(t, user.MustChangePassword)
assert.NotEqual(t, userCopy.Passwd, user.Passwd)
assert.NotEqual(t, userCopy.Salt, user.Salt)
require.NoError(t, UpdateAuth(db.DefaultContext, user, &UpdateAuthOptions{
ProhibitLogin: optional.Some(true),
}))
assert.True(t, user.ProhibitLogin)
require.ErrorIs(t, UpdateAuth(db.DefaultContext, user, &UpdateAuthOptions{
Password: optional.Some("aaaa"),
}), password_module.ErrMinLength)
}