test: add repository tests to improve coverage from 46.6% to 74%

New test files:
- custom_field_repository_test.go: 10 tests for CustomFieldRepository & UserCustomFieldValueRepository
- login_log_repository_test.go: 3 tests for ListCursor, ListByUserIDCursor, ListAllForExport
- operation_log_repository_test.go: 1 test for ListCursor
- role_repository_test.go: 2 tests for GetAncestorIDs, GetAncestors
- social_account_repository_test.go: 8 CRUD tests
- theme_repository_test.go: 10 tests for ThemeConfigRepository
- user_role_repository_test.go: 1 test for DeleteByUserAndRole

Modified test files:
- device_repository_test.go: Added ListAllCursor tests
- user_repository_test.go: Added AdvancedSearch tests
- webhook_repository_test.go: Added ListByCreatorPaginated test

Updated documentation with new coverage status.
This commit is contained in:
2026-04-11 21:58:28 +08:00
parent b1311ea144
commit 289aab2930
11 changed files with 1630 additions and 0 deletions

View File

@@ -3,6 +3,7 @@ package repository
import (
"context"
"testing"
"time"
"gorm.io/gorm"
@@ -401,3 +402,259 @@ func TestUserRepository_Search_LikePattern(t *testing.T) {
// Should not error and should escape properly
_ = users
}
// TestUserRepository_GetByIDs 测试批量获取用户
func TestUserRepository_GetByIDs(t *testing.T) {
db := setupTestDB(t)
repo := NewUserRepository(db)
ctx := context.Background()
u1 := &domain.User{Username: "batchuser1", Password: "hash", Status: domain.UserStatusActive}
u2 := &domain.User{Username: "batchuser2", Password: "hash", Status: domain.UserStatusActive}
u3 := &domain.User{Username: "batchuser3", Password: "hash", Status: domain.UserStatusActive}
repo.Create(ctx, u1)
repo.Create(ctx, u2)
repo.Create(ctx, u3)
users, err := repo.GetByIDs(ctx, []int64{u1.ID, u3.ID})
if err != nil {
t.Fatalf("GetByIDs() error = %v", err)
}
if len(users) != 2 {
t.Errorf("len(users) = %d, want 2", len(users))
}
}
// TestUserRepository_GetByIDs_Empty 测试空ID列表
func TestUserRepository_GetByIDs_Empty(t *testing.T) {
db := setupTestDB(t)
repo := NewUserRepository(db)
ctx := context.Background()
users, err := repo.GetByIDs(ctx, []int64{})
if err != nil {
t.Fatalf("GetByIDs() error = %v", err)
}
if len(users) != 0 {
t.Errorf("len(users) = %d, want 0", len(users))
}
}
// TestUserRepository_UpdatePassword 测试更新密码
func TestUserRepository_UpdatePassword(t *testing.T) {
db := setupTestDB(t)
repo := NewUserRepository(db)
ctx := context.Background()
user := &domain.User{
Username: "pwduser",
Password: "oldpassword",
Status: domain.UserStatusActive,
}
repo.Create(ctx, user)
err := repo.UpdatePassword(ctx, user.ID, "newpasswordhash")
if err != nil {
t.Fatalf("UpdatePassword() error = %v", err)
}
found, _ := repo.GetByID(ctx, user.ID)
if found.Password != "newpasswordhash" {
t.Errorf("Password = %v, want newpasswordhash", found.Password)
}
}
// TestUserRepository_UpdateTOTP 测试更新TOTP
func TestUserRepository_UpdateTOTP(t *testing.T) {
db := setupTestDB(t)
repo := NewUserRepository(db)
ctx := context.Background()
user := &domain.User{
Username: "totpuser",
Password: "hash",
Status: domain.UserStatusActive,
}
repo.Create(ctx, user)
user.TOTPEnabled = true
user.TOTPSecret = "JBSWY3DPEHPK3PXP"
err := repo.UpdateTOTP(ctx, user)
if err != nil {
t.Fatalf("UpdateTOTP() error = %v", err)
}
found, _ := repo.GetByID(ctx, user.ID)
if !found.TOTPEnabled {
t.Error("TOTPEnabled should be true")
}
if found.TOTPSecret != "JBSWY3DPEHPK3PXP" {
t.Errorf("TOTPSecret = %v, want JBSWY3DPEHPK3PXP", found.TOTPSecret)
}
}
// TestUserRepository_ListCreatedAfter 测试查询创建时间之后的用户
func TestUserRepository_ListCreatedAfter(t *testing.T) {
db := setupTestDB(t)
repo := NewUserRepository(db)
ctx := context.Background()
user := &domain.User{
Username: "afteruser",
Password: "hash",
Status: domain.UserStatusActive,
}
repo.Create(ctx, user)
since := user.CreatedAt.Add(-1 * time.Hour)
users, total, err := repo.ListCreatedAfter(ctx, since, 0, 10)
if err != nil {
t.Fatalf("ListCreatedAfter() error = %v", err)
}
if total < 1 {
t.Errorf("total = %d, want at least 1", total)
}
_ = users
}
// TestUserRepository_ListCreatedAfter_Limited 测试带limit的查询
func TestUserRepository_ListCreatedAfter_Limited(t *testing.T) {
db := setupTestDB(t)
repo := NewUserRepository(db)
ctx := context.Background()
for i := 0; i < 5; i++ {
repo.Create(ctx, &domain.User{
Username: "limituser" + string(rune('0'+i)),
Password: "hash",
Status: domain.UserStatusActive,
})
}
since := time.Now().Add(-1 * time.Hour)
users, total, err := repo.ListCreatedAfter(ctx, since, 0, 3)
if err != nil {
t.Fatalf("ListCreatedAfter() error = %v", err)
}
if len(users) != 3 {
t.Errorf("len(users) = %d, want 3", len(users))
}
if total < 5 {
t.Errorf("total = %d, want at least 5", total)
}
}
// TestUserRepository_AdvancedSearch 测试高级搜索
func TestUserRepository_AdvancedSearch(t *testing.T) {
db := setupTestDB(t)
repo := NewUserRepository(db)
ctx := context.Background()
repo.Create(ctx, &domain.User{
Username: "searchuser1",
Nickname: "张三",
Email: domain.StrPtr("zhangsan@example.com"),
Password: "hash",
Status: domain.UserStatusActive,
})
repo.Create(ctx, &domain.User{
Username: "searchuser2",
Nickname: "李四",
Email: domain.StrPtr("lisi@example.com"),
Password: "hash",
Status: domain.UserStatusActive,
})
repo.Create(ctx, &domain.User{
Username: "searchuser3",
Nickname: "王五",
Email: domain.StrPtr("wangwu@example.com"),
Password: "hash",
Status: domain.UserStatusInactive,
})
// 按关键字搜索Status=-1 表示全部状态)
filter := &AdvancedFilter{Keyword: "searchuser1", Status: -1, Offset: 0, Limit: 10}
users, total, err := repo.AdvancedSearch(ctx, filter)
if err != nil {
t.Fatalf("AdvancedSearch() error = %v", err)
}
if len(users) != 1 {
t.Errorf("len(users) = %d, want 1", len(users))
}
if total != 1 {
t.Errorf("total = %d, want 1", total)
}
// 按状态筛选
filter2 := &AdvancedFilter{Status: int(domain.UserStatusActive), Offset: 0, Limit: 10}
users2, total2, err := repo.AdvancedSearch(ctx, filter2)
if err != nil {
t.Fatalf("AdvancedSearch() error = %v", err)
}
if len(users2) != 2 {
t.Errorf("len(users2) = %d, want 2", len(users2))
}
if total2 != 2 {
t.Errorf("total2 = %d, want 2", total2)
}
// 按状态筛选 - 禁用用户
filter3 := &AdvancedFilter{Status: int(domain.UserStatusInactive), Offset: 0, Limit: 10}
users3, total3, err := repo.AdvancedSearch(ctx, filter3)
if err != nil {
t.Fatalf("AdvancedSearch() error = %v", err)
}
if len(users3) != 1 {
t.Errorf("len(users3) = %d, want 1", len(users3))
}
if total3 != 1 {
t.Errorf("total3 = %d, want 1", total3)
}
}
// TestUserRepository_AdvancedSearch_AllStatus 测试状态为-1返回全部
func TestUserRepository_AdvancedSearch_AllStatus(t *testing.T) {
db := setupTestDB(t)
repo := NewUserRepository(db)
ctx := context.Background()
repo.Create(ctx, &domain.User{Username: "active", Password: "hash", Status: domain.UserStatusActive})
repo.Create(ctx, &domain.User{Username: "inactive", Password: "hash", Status: domain.UserStatusInactive})
filter := &AdvancedFilter{Status: -1, Offset: 0, Limit: 10}
users, total, err := repo.AdvancedSearch(ctx, filter)
if err != nil {
t.Fatalf("AdvancedSearch() error = %v", err)
}
if len(users) != 2 {
t.Errorf("len(users) = %d, want 2", len(users))
}
if total != 2 {
t.Errorf("total = %d, want 2", total)
}
}
// TestUserRepository_AdvancedSearch_LikeSpecialChars 测试搜索LIKE特殊字符转义
func TestUserRepository_AdvancedSearch_LikeSpecialChars(t *testing.T) {
db := setupTestDB(t)
repo := NewUserRepository(db)
ctx := context.Background()
repo.Create(ctx, &domain.User{
Username: "user%with%percent",
Nickname: "测试用户",
Password: "hash",
Status: domain.UserStatusActive,
})
// 搜索%应该不匹配任何记录(被转义)
filter := &AdvancedFilter{Keyword: "%", Offset: 0, Limit: 10}
users, _, err := repo.AdvancedSearch(ctx, filter)
if err != nil {
t.Fatalf("AdvancedSearch() error = %v", err)
}
if len(users) != 0 {
t.Errorf("len(users) = %d, want 0 for escaped percent", len(users))
}
}