package service import ( "carrotskin/internal/model" "testing" "go.uber.org/zap" ) // TestProfileService_Validation 测试Profile服务验证逻辑 func TestProfileService_Validation(t *testing.T) { tests := []struct { name string userID int64 profileName string wantValid bool }{ { name: "有效的用户ID和角色名", userID: 1, profileName: "TestProfile", wantValid: true, }, { name: "用户ID为0时无效", userID: 0, profileName: "TestProfile", wantValid: false, }, { name: "角色名为空时无效", userID: 1, profileName: "", wantValid: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { isValid := tt.userID > 0 && tt.profileName != "" if isValid != tt.wantValid { t.Errorf("Validation failed: got %v, want %v", isValid, tt.wantValid) } }) } } // TestProfileService_StatusValidation 测试用户状态验证 func TestProfileService_StatusValidation(t *testing.T) { tests := []struct { name string status int16 wantValid bool }{ { name: "状态为1(正常)时有效", status: 1, wantValid: true, }, { name: "状态为0(禁用)时无效", status: 0, wantValid: false, }, { name: "状态为-1(删除)时无效", status: -1, wantValid: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { isValid := tt.status == 1 if isValid != tt.wantValid { t.Errorf("Status validation failed: got %v, want %v", isValid, tt.wantValid) } }) } } // TestProfileService_IsActiveDefault 测试Profile默认活跃状态 func TestProfileService_IsActiveDefault(t *testing.T) { // 新创建的档案默认为活跃状态 isActive := true if !isActive { t.Error("新创建的Profile应该默认为活跃状态") } } // TestUpdateProfile_PermissionCheck 测试更新Profile的权限检查逻辑 func TestUpdateProfile_PermissionCheck(t *testing.T) { tests := []struct { name string profileUserID int64 requestUserID int64 wantErr bool }{ { name: "用户ID匹配,允许操作", profileUserID: 1, requestUserID: 1, wantErr: false, }, { name: "用户ID不匹配,拒绝操作", profileUserID: 1, requestUserID: 2, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { hasError := tt.profileUserID != tt.requestUserID if hasError != tt.wantErr { t.Errorf("Permission check failed: got %v, want %v", hasError, tt.wantErr) } }) } } // TestUpdateProfile_NameValidation 测试更新Profile时名称验证逻辑 func TestUpdateProfile_NameValidation(t *testing.T) { tests := []struct { name string currentName string newName *string shouldCheck bool }{ { name: "名称未改变,不检查", currentName: "TestProfile", newName: stringPtr("TestProfile"), shouldCheck: false, }, { name: "名称改变,需要检查", currentName: "TestProfile", newName: stringPtr("NewProfile"), shouldCheck: true, }, { name: "名称为nil,不检查", currentName: "TestProfile", newName: nil, shouldCheck: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { shouldCheck := tt.newName != nil && *tt.newName != tt.currentName if shouldCheck != tt.shouldCheck { t.Errorf("Name validation check failed: got %v, want %v", shouldCheck, tt.shouldCheck) } }) } } // TestDeleteProfile_PermissionCheck 测试删除Profile的权限检查 func TestDeleteProfile_PermissionCheck(t *testing.T) { tests := []struct { name string profileUserID int64 requestUserID int64 wantErr bool }{ { name: "用户ID匹配,允许删除", profileUserID: 1, requestUserID: 1, wantErr: false, }, { name: "用户ID不匹配,拒绝删除", profileUserID: 1, requestUserID: 2, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { hasError := tt.profileUserID != tt.requestUserID if hasError != tt.wantErr { t.Errorf("Permission check failed: got %v, want %v", hasError, tt.wantErr) } }) } } // TestSetActiveProfile_PermissionCheck 测试设置活跃Profile的权限检查 func TestSetActiveProfile_PermissionCheck(t *testing.T) { tests := []struct { name string profileUserID int64 requestUserID int64 wantErr bool }{ { name: "用户ID匹配,允许设置", profileUserID: 1, requestUserID: 1, wantErr: false, }, { name: "用户ID不匹配,拒绝设置", profileUserID: 1, requestUserID: 2, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { hasError := tt.profileUserID != tt.requestUserID if hasError != tt.wantErr { t.Errorf("Permission check failed: got %v, want %v", hasError, tt.wantErr) } }) } } // TestCheckProfileLimit_Logic 测试Profile数量限制检查逻辑 func TestCheckProfileLimit_Logic(t *testing.T) { tests := []struct { name string count int maxProfiles int wantErr bool }{ { name: "未达到上限", count: 5, maxProfiles: 10, wantErr: false, }, { name: "达到上限", count: 10, maxProfiles: 10, wantErr: true, }, { name: "超过上限", count: 15, maxProfiles: 10, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { hasError := tt.count >= tt.maxProfiles if hasError != tt.wantErr { t.Errorf("Limit check failed: got %v, want %v", hasError, tt.wantErr) } }) } } // TestValidateProfileByUserID_InputValidation 测试ValidateProfileByUserID输入验证 func TestValidateProfileByUserID_InputValidation(t *testing.T) { tests := []struct { name string userID int64 uuid string wantErr bool }{ { name: "有效输入", userID: 1, uuid: "test-uuid", wantErr: false, }, { name: "userID为0", userID: 0, uuid: "test-uuid", wantErr: true, }, { name: "uuid为空", userID: 1, uuid: "", wantErr: true, }, { name: "两者都无效", userID: 0, uuid: "", wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { hasError := tt.userID == 0 || tt.uuid == "" if hasError != tt.wantErr { t.Errorf("Input validation failed: got %v, want %v", hasError, tt.wantErr) } }) } } // TestValidateProfileByUserID_UserIDMatching 测试用户ID匹配逻辑 func TestValidateProfileByUserID_UserIDMatching(t *testing.T) { tests := []struct { name string profileUserID int64 requestUserID int64 wantValid bool }{ { name: "用户ID匹配", profileUserID: 1, requestUserID: 1, wantValid: true, }, { name: "用户ID不匹配", profileUserID: 1, requestUserID: 2, wantValid: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { isValid := tt.profileUserID == tt.requestUserID if isValid != tt.wantValid { t.Errorf("UserID matching failed: got %v, want %v", isValid, tt.wantValid) } }) } } // TestGenerateRSAPrivateKey 测试RSA私钥生成 func TestGenerateRSAPrivateKey(t *testing.T) { tests := []struct { name string wantError bool }{ { name: "生成RSA私钥", wantError: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { privateKey, err := generateRSAPrivateKeyInternal() if (err != nil) != tt.wantError { t.Errorf("generateRSAPrivateKeyInternal() error = %v, wantError %v", err, tt.wantError) return } if !tt.wantError { if privateKey == "" { t.Error("generateRSAPrivateKeyInternal() 返回的私钥不应为空") } // 验证PEM格式 if len(privateKey) < 100 { t.Errorf("generateRSAPrivateKeyInternal() 返回的私钥长度异常: %d", len(privateKey)) } // 验证包含PEM头部 if !contains(privateKey, "BEGIN RSA PRIVATE KEY") { t.Error("generateRSAPrivateKeyInternal() 返回的私钥应包含PEM头部") } } }) } } // TestGenerateRSAPrivateKey_Uniqueness 测试RSA私钥唯一性 func TestGenerateRSAPrivateKey_Uniqueness(t *testing.T) { keys := make(map[string]bool) for i := 0; i < 10; i++ { key, err := generateRSAPrivateKeyInternal() if err != nil { t.Fatalf("generateRSAPrivateKeyInternal() 失败: %v", err) } if keys[key] { t.Errorf("第%d次生成的密钥与之前重复", i+1) } keys[key] = true } } // 辅助函数 func stringPtr(s string) *string { return &s } func contains(s, substr string) bool { return len(s) >= len(substr) && (s == substr || (len(s) > len(substr) && (s[:len(substr)] == substr || s[len(s)-len(substr):] == substr || containsMiddle(s, substr)))) } func containsMiddle(s, substr string) bool { for i := 0; i <= len(s)-len(substr); i++ { if s[i:i+len(substr)] == substr { return true } } return false } // ============================================================================ // 使用 Mock 的集成测试 // ============================================================================ // TestProfileServiceImpl_Create 测试创建Profile func TestProfileServiceImpl_Create(t *testing.T) { profileRepo := NewMockProfileRepository() userRepo := NewMockUserRepository() logger := zap.NewNop() // 预置用户 testUser := &model.User{ ID: 1, Username: "testuser", Email: "test@example.com", Status: 1, } userRepo.Create(testUser) profileService := NewProfileService(profileRepo, userRepo, logger) tests := []struct { name string userID int64 profileName string wantErr bool errMsg string setupMocks func() }{ { name: "正常创建Profile", userID: 1, profileName: "TestProfile", wantErr: false, }, { name: "用户不存在", userID: 999, profileName: "TestProfile2", wantErr: true, errMsg: "用户不存在", }, { name: "角色名已存在", userID: 1, profileName: "ExistingProfile", wantErr: true, errMsg: "角色名已被使用", setupMocks: func() { profileRepo.Create(&model.Profile{ UUID: "existing-uuid", UserID: 2, Name: "ExistingProfile", }) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if tt.setupMocks != nil { tt.setupMocks() } profile, err := profileService.Create(tt.userID, tt.profileName) if tt.wantErr { if err == nil { t.Error("期望返回错误,但实际没有错误") return } if tt.errMsg != "" && err.Error() != tt.errMsg { t.Errorf("错误信息不匹配: got %v, want %v", err.Error(), tt.errMsg) } } else { if err != nil { t.Errorf("不期望返回错误: %v", err) return } if profile == nil { t.Error("返回的Profile不应为nil") } if profile.Name != tt.profileName { t.Errorf("Profile名称不匹配: got %v, want %v", profile.Name, tt.profileName) } if profile.UUID == "" { t.Error("Profile UUID不应为空") } } }) } } // TestProfileServiceImpl_GetByUUID 测试获取Profile func TestProfileServiceImpl_GetByUUID(t *testing.T) { profileRepo := NewMockProfileRepository() userRepo := NewMockUserRepository() logger := zap.NewNop() // 预置Profile testProfile := &model.Profile{ UUID: "test-uuid-123", UserID: 1, Name: "TestProfile", } profileRepo.Create(testProfile) profileService := NewProfileService(profileRepo, userRepo, logger) tests := []struct { name string uuid string wantErr bool }{ { name: "获取存在的Profile", uuid: "test-uuid-123", wantErr: false, }, { name: "获取不存在的Profile", uuid: "non-existent-uuid", wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { profile, err := profileService.GetByUUID(tt.uuid) if tt.wantErr { if err == nil { t.Error("期望返回错误,但实际没有错误") } } else { if err != nil { t.Errorf("不期望返回错误: %v", err) return } if profile == nil { t.Error("返回的Profile不应为nil") } if profile.UUID != tt.uuid { t.Errorf("Profile UUID不匹配: got %v, want %v", profile.UUID, tt.uuid) } } }) } } // TestProfileServiceImpl_Delete 测试删除Profile func TestProfileServiceImpl_Delete(t *testing.T) { profileRepo := NewMockProfileRepository() userRepo := NewMockUserRepository() logger := zap.NewNop() // 预置Profile testProfile := &model.Profile{ UUID: "delete-test-uuid", UserID: 1, Name: "DeleteTestProfile", } profileRepo.Create(testProfile) profileService := NewProfileService(profileRepo, userRepo, logger) tests := []struct { name string uuid string userID int64 wantErr bool }{ { name: "正常删除", uuid: "delete-test-uuid", userID: 1, wantErr: false, }, { name: "用户ID不匹配", uuid: "delete-test-uuid", userID: 2, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := profileService.Delete(tt.uuid, tt.userID) if tt.wantErr { if err == nil { t.Error("期望返回错误,但实际没有错误") } } else { if err != nil { t.Errorf("不期望返回错误: %v", err) } } }) } } // TestProfileServiceImpl_GetByUserID 测试按用户获取档案列表 func TestProfileServiceImpl_GetByUserID(t *testing.T) { profileRepo := NewMockProfileRepository() userRepo := NewMockUserRepository() logger := zap.NewNop() // 为用户 1 和 2 预置不同档案 profileRepo.Create(&model.Profile{UUID: "p1", UserID: 1, Name: "P1"}) profileRepo.Create(&model.Profile{UUID: "p2", UserID: 1, Name: "P2"}) profileRepo.Create(&model.Profile{UUID: "p3", UserID: 2, Name: "P3"}) svc := NewProfileService(profileRepo, userRepo, logger) list, err := svc.GetByUserID(1) if err != nil { t.Fatalf("GetByUserID 失败: %v", err) } if len(list) != 2 { t.Fatalf("GetByUserID 返回数量错误, got=%d, want=2", len(list)) } } // TestProfileServiceImpl_Update_And_SetActive 测试 Update 与 SetActive func TestProfileServiceImpl_Update_And_SetActive(t *testing.T) { profileRepo := NewMockProfileRepository() userRepo := NewMockUserRepository() logger := zap.NewNop() profile := &model.Profile{ UUID: "u1", UserID: 1, Name: "OldName", } profileRepo.Create(profile) svc := NewProfileService(profileRepo, userRepo, logger) // 正常更新名称与皮肤/披风 newName := "NewName" var skinID int64 = 10 var capeID int64 = 20 updated, err := svc.Update("u1", 1, &newName, &skinID, &capeID) if err != nil { t.Fatalf("Update 正常情况失败: %v", err) } if updated == nil || updated.Name != newName { t.Fatalf("Update 未更新名称, got=%+v", updated) } // 用户无权限 if _, err := svc.Update("u1", 2, &newName, nil, nil); err == nil { t.Fatalf("Update 在无权限时应返回错误") } // 名称重复 profileRepo.Create(&model.Profile{ UUID: "u2", UserID: 2, Name: "Duplicate", }) if _, err := svc.Update("u1", 1, stringPtr("Duplicate"), nil, nil); err == nil { t.Fatalf("Update 在名称重复时应返回错误") } // SetActive 正常 if err := svc.SetActive("u1", 1); err != nil { t.Fatalf("SetActive 正常情况失败: %v", err) } // SetActive 无权限 if err := svc.SetActive("u1", 2); err == nil { t.Fatalf("SetActive 在无权限时应返回错误") } } // TestProfileServiceImpl_CheckLimit_And_GetByNames 测试 CheckLimit / GetByNames / GetByProfileName func TestProfileServiceImpl_CheckLimit_And_GetByNames(t *testing.T) { profileRepo := NewMockProfileRepository() userRepo := NewMockUserRepository() logger := zap.NewNop() // 为用户 1 预置 2 个档案 profileRepo.Create(&model.Profile{UUID: "a", UserID: 1, Name: "A"}) profileRepo.Create(&model.Profile{UUID: "b", UserID: 1, Name: "B"}) svc := NewProfileService(profileRepo, userRepo, logger) // CheckLimit 未达上限 if err := svc.CheckLimit(1, 3); err != nil { t.Fatalf("CheckLimit 未达到上限时不应报错: %v", err) } // CheckLimit 达到上限 if err := svc.CheckLimit(1, 2); err == nil { t.Fatalf("CheckLimit 达到上限时应报错") } // GetByNames list, err := svc.GetByNames([]string{"A", "B"}) if err != nil { t.Fatalf("GetByNames 失败: %v", err) } if len(list) != 2 { t.Fatalf("GetByNames 返回数量错误, got=%d, want=2", len(list)) } // GetByProfileName 存在 p, err := svc.GetByProfileName("A") if err != nil || p == nil || p.Name != "A" { t.Fatalf("GetByProfileName 返回错误, profile=%+v, err=%v", p, err) } }