Files
gitea/tests/integration/user_settings_test.go
T
silverwind 8abda62312 test: consolidate Go tests (#39962)
Consolidate all Go tests. Coverage is identical to before. Go tests are
around 5% faster while being 7% smaller. No non-test code is touched.
2026-10-24 13:48:19 +00:00

401 lines
12 KiB
Go

// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package integration
import (
"net/http"
"strings"
"testing"
auth_model "gitea.dev/models/auth"
"gitea.dev/models/unittest"
"gitea.dev/modules/container"
"gitea.dev/modules/setting"
"gitea.dev/modules/test"
"gitea.dev/tests"
"github.com/stretchr/testify/assert"
)
// Validate that each navbar setting is correct. This checks that the
// appropriate context is passed everywhere the navbar is rendered
func assertNavbar(t *testing.T, doc *HTMLDoc) {
// Only show the account page if users can change their email notifications, delete themselves, or manage credentials
if setting.Admin.UserDisabledFeatures.Contains(setting.UserFeatureDeletion, setting.UserFeatureManageCredentials) && !setting.Service.EnableNotifyMail {
AssertHTMLElement(t, doc, ".menu a[href='/user/settings/account']", false)
} else {
AssertHTMLElement(t, doc, ".menu a[href='/user/settings/account']", true)
}
if setting.Admin.UserDisabledFeatures.Contains(setting.UserFeatureManageMFA, setting.UserFeatureManageCredentials) {
AssertHTMLElement(t, doc, ".menu a[href='/user/settings/security']", false)
} else {
AssertHTMLElement(t, doc, ".menu a[href='/user/settings/security']", true)
}
if setting.Admin.UserDisabledFeatures.Contains(setting.UserFeatureManageSSHKeys, setting.UserFeatureManageGPGKeys) {
AssertHTMLElement(t, doc, ".menu a[href='/user/settings/keys']", false)
} else {
AssertHTMLElement(t, doc, ".menu a[href='/user/settings/keys']", true)
}
}
func WithDisabledFeatures(t *testing.T, features ...string) {
t.Helper()
global := setting.Admin.UserDisabledFeatures
user := setting.Admin.ExternalUserDisableFeatures
setting.Admin.UserDisabledFeatures = container.SetOf(features...)
setting.Admin.ExternalUserDisableFeatures = setting.Admin.UserDisabledFeatures
t.Cleanup(func() {
setting.Admin.UserDisabledFeatures = global
setting.Admin.ExternalUserDisableFeatures = user
})
}
func TestRevokeOAuth2GrantOfOtherUser(t *testing.T) {
defer tests.PrepareTestEnv(t)()
// grant 1 belongs to user1, so user2 must not be able to act on it
session := loginUser(t, "user2")
req := NewRequestWithValues(t, "POST", "/user/settings/applications/oauth2/1/revoke/1", nil)
session.MakeRequest(t, req, http.StatusNotFound)
grant := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Grant{ID: 1})
assert.EqualValues(t, 1, grant.UserID)
}
func TestUserSettingsAccount(t *testing.T) {
defer tests.PrepareTestEnv(t)()
for _, testCase := range []struct {
name string
features []string
credentials bool
deleteSelector string
deletion bool
}{
{"all features enabled", nil, true, `form[action="/user/settings/account/delete"]`, true},
{"credentials disabled", []string{setting.UserFeatureManageCredentials}, false, `form[action="/user/settings/account/delete"]`, true},
{"deletion disabled", []string{setting.UserFeatureDeletion}, true, "#delete-form", false},
} {
t.Run(testCase.name, func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
if len(testCase.features) > 0 {
WithDisabledFeatures(t, testCase.features...)
}
session := loginUser(t, "user2")
req := NewRequest(t, "GET", "/user/settings/account")
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
if len(testCase.features) == 0 {
// account navbar should display
AssertHTMLElement(t, doc, ".menu a[href='/user/settings/account']", true)
} else {
assertNavbar(t, doc)
}
AssertHTMLElement(t, doc, "#password", testCase.credentials)
AssertHTMLElement(t, doc, "#email", testCase.credentials)
AssertHTMLElement(t, doc, testCase.deleteSelector, testCase.deletion)
})
}
t.Run("deletion, credentials and email notifications are disabled", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
mail := setting.Service.EnableNotifyMail
setting.Service.EnableNotifyMail = false
defer func() {
setting.Service.EnableNotifyMail = mail
}()
WithDisabledFeatures(t, setting.UserFeatureDeletion, setting.UserFeatureManageCredentials)
session := loginUser(t, "user2")
req := NewRequest(t, "GET", "/user/settings/account")
session.MakeRequest(t, req, http.StatusNotFound)
})
}
func TestUserSettingsUpdatePassword(t *testing.T) {
defer tests.PrepareTestEnv(t)()
t.Run("enabled", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
session := loginUser(t, "user2")
req := NewRequestWithValues(t, "POST", "/user/settings/account", map[string]string{
"old_password": "password",
"password": "password",
"retype": "password",
})
session.MakeRequest(t, req, http.StatusSeeOther)
})
t.Run("credentials disabled", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
WithDisabledFeatures(t, setting.UserFeatureManageCredentials)
session := loginUser(t, "user2")
req := NewRequest(t, "POST", "/user/settings/account")
session.MakeRequest(t, req, http.StatusNotFound)
})
}
func TestUserSettingsUpdateEmail(t *testing.T) {
defer tests.PrepareTestEnv(t)()
t.Run("credentials disabled", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
WithDisabledFeatures(t, setting.UserFeatureManageCredentials)
session := loginUser(t, "user2")
req := NewRequest(t, "POST", "/user/settings/account/email")
session.MakeRequest(t, req, http.StatusNotFound)
})
for _, testCase := range []struct {
name string
id string
}{
{"primary email not found", "9999"},
{"primary email not owned by user", "6"},
} {
t.Run(testCase.name, func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
session := loginUser(t, "user2")
req := NewRequestWithValues(t, "POST", "/user/settings/account/email", map[string]string{
"_method": "PRIMARY",
"id": testCase.id,
})
resp := session.MakeRequest(t, req, http.StatusSeeOther)
assert.Equal(t, "/user/settings/account", resp.Header().Get("Location"))
flashMsg := session.GetCookieFlashMessage()
assert.Equal(t, "The selected email address could not be found.", flashMsg.ErrorMsg)
})
}
}
func TestUserSettingsDeleteEmail(t *testing.T) {
defer tests.PrepareTestEnv(t)()
t.Run("credentials disabled", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
WithDisabledFeatures(t, setting.UserFeatureManageCredentials)
session := loginUser(t, "user2")
req := NewRequest(t, "POST", "/user/settings/account/email/delete")
session.MakeRequest(t, req, http.StatusNotFound)
})
}
func TestUserSettingsDelete(t *testing.T) {
defer tests.PrepareTestEnv(t)()
t.Run("deletion disabled", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
WithDisabledFeatures(t, setting.UserFeatureDeletion)
session := loginUser(t, "user2")
req := NewRequest(t, "POST", "/user/settings/account/delete")
session.MakeRequest(t, req, http.StatusNotFound)
})
}
func TestUserSettingsPages(t *testing.T) {
for _, page := range []string{"appearance", "packages", "packages/rules/add", "organization", "repos", "blocked_users"} {
t.Run(page, func(t *testing.T) {
defer tests.PrepareTestEnv(t)()
session := loginUser(t, "user2")
req := NewRequest(t, "GET", "/user/settings/"+page)
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
assertNavbar(t, doc)
})
}
}
func TestUserSettingsSecurity(t *testing.T) {
defer tests.PrepareTestEnv(t)()
for _, testCase := range []struct {
name string
feature string
register bool
}{
{"credentials disabled", setting.UserFeatureManageCredentials, true},
{"mfa disabled", setting.UserFeatureManageMFA, false},
} {
t.Run(testCase.name, func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
WithDisabledFeatures(t, testCase.feature)
session := loginUser(t, "user2")
req := NewRequest(t, "GET", "/user/settings/security")
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
assertNavbar(t, doc)
AssertHTMLElement(t, doc, "#register-webauthn", testCase.register)
})
}
t.Run("credentials and mfa disabled", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
WithDisabledFeatures(t, setting.UserFeatureManageCredentials, setting.UserFeatureManageMFA)
session := loginUser(t, "user2")
req := NewRequest(t, "GET", "/user/settings/security")
session.MakeRequest(t, req, http.StatusNotFound)
})
}
func TestUserSettingsApplications(t *testing.T) {
defer tests.PrepareTestEnv(t)()
t.Run("Applications", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
session := loginUser(t, "user2")
req := NewRequest(t, "GET", "/user/settings/applications")
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
assertNavbar(t, doc)
})
t.Run("RegenerateAccessToken", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
session := loginUser(t, "user2")
before := unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{ID: 3, UID: 2})
req := NewRequestWithValues(t, "POST", "/user/settings/applications/regenerate", map[string]string{
"id": "3",
})
session.MakeRequest(t, req, http.StatusOK)
after := unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{ID: 3, UID: 2})
assert.Equal(t, before.Name, after.Name)
assert.Equal(t, before.Scope, after.Scope)
assert.NotEqual(t, before.TokenHash, after.TokenHash)
assert.NotEqual(t, before.TokenSalt, after.TokenSalt)
})
t.Run("OAuth2", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
session := loginUser(t, "user2")
t.Run("OAuth2ApplicationShow", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
req := NewRequest(t, "GET", "/user/settings/applications/oauth2/2")
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
assertNavbar(t, doc)
})
t.Run("OAuthApplicationsEdit", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
t.Run("Invalid URL", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
req := NewRequestWithValues(t, "POST", "/user/settings/applications/oauth2/2", map[string]string{
"application_name": "Test native app",
"redirect_uris": "ftp://127.0.0.1",
"confidential_client": "false",
})
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
msg := strings.TrimSpace(doc.Find(".ui.message.flash-message").Text())
assert.Equal(t, `RedirectURIs: "ftp://127.0.0.1" is not a valid URL.`, msg)
})
t.Run("OK", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
defer test.MockVariableValue(&setting.OAuth2.CustomSchemes, []string{"my-app"})()
for _, redirectURI := range []string{"http://127.0.0.1", "my-app://127.0.0.1"} {
req := NewRequestWithValues(t, "POST", "/user/settings/applications/oauth2/2", map[string]string{
"application_name": "Test native app",
"redirect_uris": redirectURI,
"confidential_client": "false",
})
session.MakeRequest(t, req, http.StatusSeeOther)
}
})
})
})
}
func TestUserSettingsKeys(t *testing.T) {
defer tests.PrepareTestEnv(t)()
for _, testCase := range []struct {
name string
feature string
ssh bool
gpg bool
}{
{"all enabled", "", true, true},
{"ssh keys disabled", setting.UserFeatureManageSSHKeys, false, true},
{"gpg keys disabled", setting.UserFeatureManageGPGKeys, true, false},
} {
t.Run(testCase.name, func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
if testCase.feature != "" {
WithDisabledFeatures(t, testCase.feature)
}
session := loginUser(t, "user2")
req := NewRequest(t, "GET", "/user/settings/keys")
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
assertNavbar(t, doc)
AssertHTMLElement(t, doc, "#add-ssh-button", testCase.ssh)
AssertHTMLElement(t, doc, "#add-gpg-key-panel", testCase.gpg)
})
}
t.Run("ssh & gpg keys disabled", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
WithDisabledFeatures(t, setting.UserFeatureManageSSHKeys, setting.UserFeatureManageGPGKeys)
session := loginUser(t, "user2")
req := NewRequest(t, "GET", "/user/settings/keys")
_ = session.MakeRequest(t, req, http.StatusNotFound)
})
}
func TestUserSettingsSecrets(t *testing.T) {
defer tests.PrepareTestEnv(t)()
session := loginUser(t, "user2")
req := NewRequest(t, "GET", "/user/settings/actions/secrets")
if setting.Actions.Enabled {
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
assertNavbar(t, doc)
} else {
session.MakeRequest(t, req, http.StatusNotFound)
}
}