// Copyright 2026 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package mailer import ( "mime" "net/url" "regexp" "testing" "gitea.dev/models/auth" "gitea.dev/models/unittest" user_model "gitea.dev/models/user" "gitea.dev/modules/setting" "gitea.dev/modules/test" sender_service "gitea.dev/services/mailer/sender" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func assertMailSubject(t *testing.T, expected, actual string) { t.Helper() decoded, err := (&mime.WordDecoder{}).DecodeHeader(actual) require.NoError(t, err) assert.Equal(t, expected, decoded) } func TestSendRegisterNotifyMailPasswordLink(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) defer test.MockVariableValue(&setting.MailService, &setting.Mailer{})() defer test.MockVariableValue(&setting.AppURL, "https://example.com/")() defer test.MockVariableValue(&setting.Service.ActiveCodeLives, 180)() defer test.MockVariableValue(&setting.Service.ResetPwdCodeLives, 180)() var sent []*sender_service.Message defer test.MockVariableValue(&SendAsync, func(msgs ...*sender_service.Message) { sent = append(sent, msgs...) })() u := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) SendRegisterNotifyMail(u) require.Len(t, sent, 1) assertMailSubject(t, "海南荣刻科技有限公司|账号信息", sent[0].Subject) assert.Contains(t, sent[0].Body, "海南荣刻科技有限公司已为您开通代码协作平台账号") assert.Contains(t, sent[0].Body, "用户名:user2") assert.NotContains(t, sent[0].Body, "/user/forgot_password") codePattern := regexp.MustCompile(`user/recover_account\?code=([^"]+)`) match := codePattern.FindStringSubmatch(sent[0].Body) if !assert.Len(t, match, 2) { return } code, err := url.QueryUnescape(match[1]) require.NoError(t, err) verifiedUser := user_model.VerifyUserTimeLimitCode(t.Context(), &user_model.TimeLimitCodeOptions{ Purpose: user_model.TimeLimitCodeResetPassword, }, code) require.NotNil(t, verifiedUser, "rendered code: %q", match[1]) assert.Equal(t, u.ID, verifiedUser.ID) } func TestSendRegisterNotifyMailExternalAccountUsesForgotPassword(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) defer test.MockVariableValue(&setting.MailService, &setting.Mailer{})() defer test.MockVariableValue(&setting.AppURL, "https://example.com/")() defer test.MockVariableValue(&setting.Service.ActiveCodeLives, 180)() defer test.MockVariableValue(&setting.Service.ResetPwdCodeLives, 180)() var sent []*sender_service.Message defer test.MockVariableValue(&SendAsync, func(msgs ...*sender_service.Message) { sent = append(sent, msgs...) })() u := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) u.LoginType = auth.LDAP SendRegisterNotifyMail(u) require.Len(t, sent, 1) assertMailSubject(t, "海南荣刻科技有限公司|账号信息", sent[0].Subject) assert.Contains(t, sent[0].Body, "https://example.com/user/forgot_password") assert.NotContains(t, sent[0].Body, "/user/recover_account?code=") } func TestSendResetPasswordMailBrandingAndLink(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) defer test.MockVariableValue(&setting.MailService, &setting.Mailer{})() defer test.MockVariableValue(&setting.AppURL, "https://example.com/")() defer test.MockVariableValue(&setting.Service.ActiveCodeLives, 180)() defer test.MockVariableValue(&setting.Service.ResetPwdCodeLives, 180)() var sent []*sender_service.Message defer test.MockVariableValue(&SendAsync, func(msgs ...*sender_service.Message) { sent = append(sent, msgs...) })() u := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) SendResetPasswordMail(u) require.Len(t, sent, 1) assertMailSubject(t, "海南荣刻科技有限公司|账号访问链接", sent[0].Subject) assert.Contains(t, sent[0].Body, "我们收到了一次账号访问设置请求") codePattern := regexp.MustCompile(`user/recover_account\?code=([^"]+)`) match := codePattern.FindStringSubmatch(sent[0].Body) if !assert.Len(t, match, 2) { return } code, err := url.QueryUnescape(match[1]) require.NoError(t, err) verifiedUser := user_model.VerifyUserTimeLimitCode(t.Context(), &user_model.TimeLimitCodeOptions{ Purpose: user_model.TimeLimitCodeResetPassword, }, code) require.NotNil(t, verifiedUser, "rendered code: %q", match[1]) assert.Equal(t, u.ID, verifiedUser.ID) }