fix login error

This commit is contained in:
tamakyi
2026-09-15 13:10:57 +08:00
parent 0e0c16f786
commit 5ee2dc1540
14 changed files with 801 additions and 68 deletions
+115
View File
@@ -6,6 +6,7 @@ package db
import (
"context"
"errors"
"testing"
"time"
@@ -470,3 +471,117 @@ func TestHarassmentSettingNormalize(t *testing.T) {
require.Nil(t, err)
require.Equal(t, HarassmentSettingNone, got.HarassmentSetting, "历史空值应被归一为 none")
}
// TestDeactivatePurgesUser 覆盖自助停用的删除语义。
//
// 停用 = 清空内容 + 保留用户行作「身份占位」(软删除),因此:
// - 该账号的提问等内容必须被物理删除;
// - 用户行必须仍在(Unscoped 可见,deleted_at 有效),邮箱/域名继续被占用 → 防止他人抢占;
// - 用同一邮箱/域名重新注册必须得到「账号已停用」的明确错误,而不是底层的唯一键 500;
// - 站长可通过 Reactivate 把账号恢复成空账号。
func TestDeactivatePurgesUser(t *testing.T) {
db, cleanup := newTestDB(t)
defer func() { _ = cleanup("users", "questions", "feature_controls") }()
ctx := context.Background()
usersStore := NewUsersStore(db)
questionsStore := NewQuestionsStore(db)
opts := CreateUserOptions{
Name: "E99p1ant",
Password: "super_secret",
Email: "[email protected]",
Avatar: "avater.png",
Domain: "purge",
Background: "background.png",
Intro: "Be cool, but also be warm.",
}
require.Nil(t, usersStore.Create(ctx, opts))
user, err := usersStore.GetByEmail(ctx, opts.Email)
require.Nil(t, err)
_, err = questionsStore.Create(ctx, CreateQuestionOptions{UserID: user.ID, Content: "hello"})
require.Nil(t, err)
require.Nil(t, usersStore.Deactivate(ctx, user.ID))
var n int64
// 身份占位必须保留(Unscoped 可见),且处于软删除状态
var placeholder User
require.Nil(t, db.WithContext(ctx).Unscoped().Where("email = ?", opts.Email).First(&placeholder).Error)
require.True(t, placeholder.DeletedAt.Valid, "停用后用户行应保留为软删除的身份占位")
require.Equal(t, user.ID, placeholder.ID)
require.Nil(t, db.WithContext(ctx).Unscoped().Model(&Question{}).Where("user_id = ?", user.ID).Count(&n).Error)
require.EqualValues(t, 0, n, "停用后该用户的提问应被物理删除")
// 普通查询已看不见该占位行
_, err = usersStore.GetByEmail(ctx, opts.Email)
require.Equal(t, ErrUserNotExists, err)
// 同一邮箱 / 域名重新注册:给出明确的「已停用」错误,而不是底层唯一键冲突
err = usersStore.Create(ctx, opts)
require.True(t, errors.Is(err, ErrDeactivatedEmail), "同邮箱重注册应返回 ErrDeactivatedEmail,实际: %v", err)
require.False(t, errors.Is(err, ErrDuplicateEmail), "不应退化成普通重复邮箱错误")
err = usersStore.Create(ctx, CreateUserOptions{
Name: "E99p1ant",
Password: "super_secret",
Email: "[email protected]",
Avatar: "avater.png",
Domain: opts.Domain,
Background: "background.png",
Intro: "Be cool, but also be warm.",
})
require.True(t, errors.Is(err, ErrDeactivatedDomain), "同域名重注册应返回 ErrDeactivatedDomain,实际: %v", err)
// 站长恢复:占位行清掉 deleted_at,账号重新可见
require.Nil(t, usersStore.Reactivate(ctx, user.ID))
restored, err := usersStore.GetByEmail(ctx, opts.Email)
require.Nil(t, err)
require.Equal(t, user.ID, restored.ID)
require.False(t, restored.DeletedAt.Valid)
// 恢复后该邮箱已被活跃账号占用 → 普通重复错误
require.True(t, errors.Is(usersStore.Create(ctx, opts), ErrDuplicateEmail))
// 未被停用的账号无法重复恢复
require.Equal(t, ErrUserNotExists, usersStore.Reactivate(ctx, user.ID))
}
// TestBanUserAndCleanupRemovesRow 封禁与停用的唯一差别:封禁连用户行一起物理删除,
// 该邮箱/域名被彻底释放。
func TestBanUserAndCleanupRemovesRow(t *testing.T) {
db, cleanup := newTestDB(t)
defer func() { _ = cleanup("users", "questions", "feature_controls") }()
ctx := context.Background()
usersStore := NewUsersStore(db)
questionsStore := NewQuestionsStore(db)
opts := CreateUserOptions{
Name: "E99p1ant",
Password: "super_secret",
Email: "[email protected]",
Avatar: "avater.png",
Domain: "ban",
Background: "background.png",
Intro: "Be cool, but also be warm.",
}
require.Nil(t, usersStore.Create(ctx, opts))
user, err := usersStore.GetByEmail(ctx, opts.Email)
require.Nil(t, err)
_, err = questionsStore.Create(ctx, CreateQuestionOptions{UserID: user.ID, Content: "hello"})
require.Nil(t, err)
require.Nil(t, usersStore.BanUserAndCleanup(ctx, user.ID))
var n int64
require.Nil(t, db.WithContext(ctx).Unscoped().Model(&User{}).Where("email = ?", opts.Email).Count(&n).Error)
require.EqualValues(t, 0, n, "封禁后用户行应被物理删除")
require.Nil(t, db.WithContext(ctx).Unscoped().Model(&Question{}).Where("user_id = ?", user.ID).Count(&n).Error)
require.EqualValues(t, 0, n, "封禁后该用户的提问应被物理删除")
require.Nil(t, usersStore.Create(ctx, opts), "封禁后应能用同一邮箱重新注册")
}