Files
backend/internal/service/mocks_test.go
lan 034e02e93a feat: Enhance dependency injection and service integration
- Updated main.go to initialize email service and include it in the dependency injection container.
- Refactored handlers to utilize context in service method calls, improving consistency and error handling.
- Introduced new service options for upload, security, and captcha services, enhancing modularity and testability.
- Removed unused repository implementations to streamline the codebase.

This commit continues the effort to improve the architecture by ensuring all services are properly injected and utilized across the application.
2025-12-02 22:52:33 +08:00

981 lines
24 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
"carrotskin/internal/model"
"carrotskin/pkg/database"
"errors"
"time"
)
// ============================================================================
// Repository Mocks
// ============================================================================
// MockUserRepository 模拟UserRepository
type MockUserRepository struct {
users map[int64]*model.User
// 用于模拟错误的标志
FailCreate bool
FailFindByID bool
FailFindByUsername bool
FailFindByEmail bool
FailUpdate bool
}
func NewMockUserRepository() *MockUserRepository {
return &MockUserRepository{
users: make(map[int64]*model.User),
}
}
func (m *MockUserRepository) Create(user *model.User) error {
if m.FailCreate {
return errors.New("mock create error")
}
if user.ID == 0 {
user.ID = int64(len(m.users) + 1)
}
m.users[user.ID] = user
return nil
}
func (m *MockUserRepository) FindByID(id int64) (*model.User, error) {
if m.FailFindByID {
return nil, errors.New("mock find error")
}
if user, ok := m.users[id]; ok {
return user, nil
}
return nil, nil
}
func (m *MockUserRepository) FindByUsername(username string) (*model.User, error) {
if m.FailFindByUsername {
return nil, errors.New("mock find by username error")
}
for _, user := range m.users {
if user.Username == username {
return user, nil
}
}
return nil, nil
}
func (m *MockUserRepository) FindByEmail(email string) (*model.User, error) {
if m.FailFindByEmail {
return nil, errors.New("mock find by email error")
}
for _, user := range m.users {
if user.Email == email {
return user, nil
}
}
return nil, nil
}
func (m *MockUserRepository) Update(user *model.User) error {
if m.FailUpdate {
return errors.New("mock update error")
}
m.users[user.ID] = user
return nil
}
func (m *MockUserRepository) UpdateFields(id int64, fields map[string]interface{}) error {
if m.FailUpdate {
return errors.New("mock update fields error")
}
_, ok := m.users[id]
if !ok {
return errors.New("user not found")
}
return nil
}
func (m *MockUserRepository) Delete(id int64) error {
delete(m.users, id)
return nil
}
func (m *MockUserRepository) CreateLoginLog(log *model.UserLoginLog) error {
return nil
}
func (m *MockUserRepository) CreatePointLog(log *model.UserPointLog) error {
return nil
}
func (m *MockUserRepository) UpdatePoints(userID int64, amount int, changeType, reason string) error {
return nil
}
// MockProfileRepository 模拟ProfileRepository
type MockProfileRepository struct {
profiles map[string]*model.Profile
userProfiles map[int64][]*model.Profile
nextID int64
FailCreate bool
FailFind bool
FailUpdate bool
FailDelete bool
}
func NewMockProfileRepository() *MockProfileRepository {
return &MockProfileRepository{
profiles: make(map[string]*model.Profile),
userProfiles: make(map[int64][]*model.Profile),
nextID: 1,
}
}
func (m *MockProfileRepository) Create(profile *model.Profile) error {
if m.FailCreate {
return errors.New("mock create error")
}
m.profiles[profile.UUID] = profile
m.userProfiles[profile.UserID] = append(m.userProfiles[profile.UserID], profile)
return nil
}
func (m *MockProfileRepository) FindByUUID(uuid string) (*model.Profile, error) {
if m.FailFind {
return nil, errors.New("mock find error")
}
if profile, ok := m.profiles[uuid]; ok {
return profile, nil
}
return nil, errors.New("profile not found")
}
func (m *MockProfileRepository) FindByName(name string) (*model.Profile, error) {
if m.FailFind {
return nil, errors.New("mock find error")
}
for _, profile := range m.profiles {
if profile.Name == name {
return profile, nil
}
}
return nil, nil
}
func (m *MockProfileRepository) FindByUserID(userID int64) ([]*model.Profile, error) {
if m.FailFind {
return nil, errors.New("mock find error")
}
return m.userProfiles[userID], nil
}
func (m *MockProfileRepository) Update(profile *model.Profile) error {
if m.FailUpdate {
return errors.New("mock update error")
}
m.profiles[profile.UUID] = profile
return nil
}
func (m *MockProfileRepository) UpdateFields(uuid string, updates map[string]interface{}) error {
if m.FailUpdate {
return errors.New("mock update error")
}
return nil
}
func (m *MockProfileRepository) Delete(uuid string) error {
if m.FailDelete {
return errors.New("mock delete error")
}
delete(m.profiles, uuid)
return nil
}
func (m *MockProfileRepository) CountByUserID(userID int64) (int64, error) {
return int64(len(m.userProfiles[userID])), nil
}
func (m *MockProfileRepository) SetActive(uuid string, userID int64) error {
return nil
}
func (m *MockProfileRepository) UpdateLastUsedAt(uuid string) error {
return nil
}
func (m *MockProfileRepository) GetByNames(names []string) ([]*model.Profile, error) {
var result []*model.Profile
for _, name := range names {
for _, profile := range m.profiles {
if profile.Name == name {
result = append(result, profile)
}
}
}
return result, nil
}
func (m *MockProfileRepository) GetKeyPair(profileId string) (*model.KeyPair, error) {
return nil, nil
}
func (m *MockProfileRepository) UpdateKeyPair(profileId string, keyPair *model.KeyPair) error {
return nil
}
// MockTextureRepository 模拟TextureRepository
type MockTextureRepository struct {
textures map[int64]*model.Texture
favorites map[int64]map[int64]bool // userID -> textureID -> favorited
nextID int64
FailCreate bool
FailFind bool
FailUpdate bool
FailDelete bool
}
func NewMockTextureRepository() *MockTextureRepository {
return &MockTextureRepository{
textures: make(map[int64]*model.Texture),
favorites: make(map[int64]map[int64]bool),
nextID: 1,
}
}
func (m *MockTextureRepository) Create(texture *model.Texture) error {
if m.FailCreate {
return errors.New("mock create error")
}
if texture.ID == 0 {
texture.ID = m.nextID
m.nextID++
}
m.textures[texture.ID] = texture
return nil
}
func (m *MockTextureRepository) FindByID(id int64) (*model.Texture, error) {
if m.FailFind {
return nil, errors.New("mock find error")
}
if texture, ok := m.textures[id]; ok {
return texture, nil
}
return nil, errors.New("texture not found")
}
func (m *MockTextureRepository) FindByHash(hash string) (*model.Texture, error) {
if m.FailFind {
return nil, errors.New("mock find error")
}
for _, texture := range m.textures {
if texture.Hash == hash {
return texture, nil
}
}
return nil, nil
}
func (m *MockTextureRepository) FindByUploaderID(uploaderID int64, page, pageSize int) ([]*model.Texture, int64, error) {
if m.FailFind {
return nil, 0, errors.New("mock find error")
}
var result []*model.Texture
for _, texture := range m.textures {
if texture.UploaderID == uploaderID {
result = append(result, texture)
}
}
return result, int64(len(result)), nil
}
func (m *MockTextureRepository) Search(keyword string, textureType model.TextureType, publicOnly bool, page, pageSize int) ([]*model.Texture, int64, error) {
if m.FailFind {
return nil, 0, errors.New("mock find error")
}
var result []*model.Texture
for _, texture := range m.textures {
if publicOnly && !texture.IsPublic {
continue
}
result = append(result, texture)
}
return result, int64(len(result)), nil
}
func (m *MockTextureRepository) Update(texture *model.Texture) error {
if m.FailUpdate {
return errors.New("mock update error")
}
m.textures[texture.ID] = texture
return nil
}
func (m *MockTextureRepository) UpdateFields(id int64, fields map[string]interface{}) error {
if m.FailUpdate {
return errors.New("mock update error")
}
return nil
}
func (m *MockTextureRepository) Delete(id int64) error {
if m.FailDelete {
return errors.New("mock delete error")
}
delete(m.textures, id)
return nil
}
func (m *MockTextureRepository) IncrementDownloadCount(id int64) error {
if texture, ok := m.textures[id]; ok {
texture.DownloadCount++
}
return nil
}
func (m *MockTextureRepository) IncrementFavoriteCount(id int64) error {
if texture, ok := m.textures[id]; ok {
texture.FavoriteCount++
}
return nil
}
func (m *MockTextureRepository) DecrementFavoriteCount(id int64) error {
if texture, ok := m.textures[id]; ok && texture.FavoriteCount > 0 {
texture.FavoriteCount--
}
return nil
}
func (m *MockTextureRepository) CreateDownloadLog(log *model.TextureDownloadLog) error {
return nil
}
func (m *MockTextureRepository) IsFavorited(userID, textureID int64) (bool, error) {
if userFavs, ok := m.favorites[userID]; ok {
return userFavs[textureID], nil
}
return false, nil
}
func (m *MockTextureRepository) AddFavorite(userID, textureID int64) error {
if m.favorites[userID] == nil {
m.favorites[userID] = make(map[int64]bool)
}
m.favorites[userID][textureID] = true
return nil
}
func (m *MockTextureRepository) RemoveFavorite(userID, textureID int64) error {
if userFavs, ok := m.favorites[userID]; ok {
delete(userFavs, textureID)
}
return nil
}
func (m *MockTextureRepository) GetUserFavorites(userID int64, page, pageSize int) ([]*model.Texture, int64, error) {
var result []*model.Texture
if userFavs, ok := m.favorites[userID]; ok {
for textureID := range userFavs {
if texture, exists := m.textures[textureID]; exists {
result = append(result, texture)
}
}
}
return result, int64(len(result)), nil
}
func (m *MockTextureRepository) CountByUploaderID(uploaderID int64) (int64, error) {
var count int64
for _, texture := range m.textures {
if texture.UploaderID == uploaderID {
count++
}
}
return count, nil
}
// MockTokenRepository 模拟TokenRepository
type MockTokenRepository struct {
tokens map[string]*model.Token
userTokens map[int64][]*model.Token
FailCreate bool
FailFind bool
FailDelete bool
}
func NewMockTokenRepository() *MockTokenRepository {
return &MockTokenRepository{
tokens: make(map[string]*model.Token),
userTokens: make(map[int64][]*model.Token),
}
}
func (m *MockTokenRepository) Create(token *model.Token) error {
if m.FailCreate {
return errors.New("mock create error")
}
m.tokens[token.AccessToken] = token
m.userTokens[token.UserID] = append(m.userTokens[token.UserID], token)
return nil
}
func (m *MockTokenRepository) FindByAccessToken(accessToken string) (*model.Token, error) {
if m.FailFind {
return nil, errors.New("mock find error")
}
if token, ok := m.tokens[accessToken]; ok {
return token, nil
}
return nil, errors.New("token not found")
}
func (m *MockTokenRepository) GetByUserID(userId int64) ([]*model.Token, error) {
if m.FailFind {
return nil, errors.New("mock find error")
}
return m.userTokens[userId], nil
}
func (m *MockTokenRepository) GetUUIDByAccessToken(accessToken string) (string, error) {
if m.FailFind {
return "", errors.New("mock find error")
}
if token, ok := m.tokens[accessToken]; ok {
return token.ProfileId, nil
}
return "", errors.New("token not found")
}
func (m *MockTokenRepository) GetUserIDByAccessToken(accessToken string) (int64, error) {
if m.FailFind {
return 0, errors.New("mock find error")
}
if token, ok := m.tokens[accessToken]; ok {
return token.UserID, nil
}
return 0, errors.New("token not found")
}
func (m *MockTokenRepository) DeleteByAccessToken(accessToken string) error {
if m.FailDelete {
return errors.New("mock delete error")
}
delete(m.tokens, accessToken)
return nil
}
func (m *MockTokenRepository) DeleteByUserID(userId int64) error {
if m.FailDelete {
return errors.New("mock delete error")
}
for _, token := range m.userTokens[userId] {
delete(m.tokens, token.AccessToken)
}
m.userTokens[userId] = nil
return nil
}
func (m *MockTokenRepository) BatchDelete(accessTokens []string) (int64, error) {
if m.FailDelete {
return 0, errors.New("mock delete error")
}
var count int64
for _, accessToken := range accessTokens {
if _, ok := m.tokens[accessToken]; ok {
delete(m.tokens, accessToken)
count++
}
}
return count, nil
}
// MockSystemConfigRepository 模拟SystemConfigRepository
type MockSystemConfigRepository struct {
configs map[string]*model.SystemConfig
}
func NewMockSystemConfigRepository() *MockSystemConfigRepository {
return &MockSystemConfigRepository{
configs: make(map[string]*model.SystemConfig),
}
}
func (m *MockSystemConfigRepository) GetByKey(key string) (*model.SystemConfig, error) {
if config, ok := m.configs[key]; ok {
return config, nil
}
return nil, nil
}
func (m *MockSystemConfigRepository) GetPublic() ([]model.SystemConfig, error) {
var result []model.SystemConfig
for _, v := range m.configs {
result = append(result, *v)
}
return result, nil
}
func (m *MockSystemConfigRepository) GetAll() ([]model.SystemConfig, error) {
var result []model.SystemConfig
for _, v := range m.configs {
result = append(result, *v)
}
return result, nil
}
func (m *MockSystemConfigRepository) Update(config *model.SystemConfig) error {
m.configs[config.Key] = config
return nil
}
func (m *MockSystemConfigRepository) UpdateValue(key, value string) error {
if config, ok := m.configs[key]; ok {
config.Value = value
return nil
}
return errors.New("config not found")
}
// ============================================================================
// Service Mocks
// ============================================================================
// MockUserService 模拟UserService
type MockUserService struct {
users map[int64]*model.User
maxProfilesPerUser int
maxTexturesPerUser int
FailRegister bool
FailLogin bool
FailGetByID bool
FailUpdate bool
}
func NewMockUserService() *MockUserService {
return &MockUserService{
users: make(map[int64]*model.User),
maxProfilesPerUser: 5,
maxTexturesPerUser: 50,
}
}
func (m *MockUserService) Register(username, password, email, avatar string) (*model.User, string, error) {
if m.FailRegister {
return nil, "", errors.New("mock register error")
}
user := &model.User{
ID: int64(len(m.users) + 1),
Username: username,
Email: email,
Avatar: avatar,
Status: 1,
}
m.users[user.ID] = user
return user, "mock-token", nil
}
func (m *MockUserService) Login(usernameOrEmail, password, ipAddress, userAgent string) (*model.User, string, error) {
if m.FailLogin {
return nil, "", errors.New("mock login error")
}
for _, user := range m.users {
if user.Username == usernameOrEmail || user.Email == usernameOrEmail {
return user, "mock-token", nil
}
}
return nil, "", errors.New("user not found")
}
func (m *MockUserService) GetByID(id int64) (*model.User, error) {
if m.FailGetByID {
return nil, errors.New("mock get by id error")
}
if user, ok := m.users[id]; ok {
return user, nil
}
return nil, nil
}
func (m *MockUserService) GetByEmail(email string) (*model.User, error) {
for _, user := range m.users {
if user.Email == email {
return user, nil
}
}
return nil, nil
}
func (m *MockUserService) UpdateInfo(user *model.User) error {
if m.FailUpdate {
return errors.New("mock update error")
}
m.users[user.ID] = user
return nil
}
func (m *MockUserService) UpdateAvatar(userID int64, avatarURL string) error {
if m.FailUpdate {
return errors.New("mock update error")
}
if user, ok := m.users[userID]; ok {
user.Avatar = avatarURL
}
return nil
}
func (m *MockUserService) ChangePassword(userID int64, oldPassword, newPassword string) error {
return nil
}
func (m *MockUserService) ResetPassword(email, newPassword string) error {
return nil
}
func (m *MockUserService) ChangeEmail(userID int64, newEmail string) error {
if user, ok := m.users[userID]; ok {
user.Email = newEmail
}
return nil
}
func (m *MockUserService) ValidateAvatarURL(avatarURL string) error {
return nil
}
func (m *MockUserService) GetMaxProfilesPerUser() int {
return m.maxProfilesPerUser
}
func (m *MockUserService) GetMaxTexturesPerUser() int {
return m.maxTexturesPerUser
}
// MockProfileService 模拟ProfileService
type MockProfileService struct {
profiles map[string]*model.Profile
FailCreate bool
FailGet bool
FailUpdate bool
FailDelete bool
}
func NewMockProfileService() *MockProfileService {
return &MockProfileService{
profiles: make(map[string]*model.Profile),
}
}
func (m *MockProfileService) Create(userID int64, name string) (*model.Profile, error) {
if m.FailCreate {
return nil, errors.New("mock create error")
}
profile := &model.Profile{
UUID: "mock-uuid-" + name,
UserID: userID,
Name: name,
}
m.profiles[profile.UUID] = profile
return profile, nil
}
func (m *MockProfileService) GetByUUID(uuid string) (*model.Profile, error) {
if m.FailGet {
return nil, errors.New("mock get error")
}
if profile, ok := m.profiles[uuid]; ok {
return profile, nil
}
return nil, errors.New("profile not found")
}
func (m *MockProfileService) GetByUserID(userID int64) ([]*model.Profile, error) {
if m.FailGet {
return nil, errors.New("mock get error")
}
var result []*model.Profile
for _, profile := range m.profiles {
if profile.UserID == userID {
result = append(result, profile)
}
}
return result, nil
}
func (m *MockProfileService) Update(uuid string, userID int64, name *string, skinID, capeID *int64) (*model.Profile, error) {
if m.FailUpdate {
return nil, errors.New("mock update error")
}
if profile, ok := m.profiles[uuid]; ok {
if name != nil {
profile.Name = *name
}
if skinID != nil {
profile.SkinID = skinID
}
if capeID != nil {
profile.CapeID = capeID
}
return profile, nil
}
return nil, errors.New("profile not found")
}
func (m *MockProfileService) Delete(uuid string, userID int64) error {
if m.FailDelete {
return errors.New("mock delete error")
}
delete(m.profiles, uuid)
return nil
}
func (m *MockProfileService) SetActive(uuid string, userID int64) error {
return nil
}
func (m *MockProfileService) CheckLimit(userID int64, maxProfiles int) error {
count := 0
for _, profile := range m.profiles {
if profile.UserID == userID {
count++
}
}
if count >= maxProfiles {
return errors.New("达到档案数量上限")
}
return nil
}
func (m *MockProfileService) GetByNames(names []string) ([]*model.Profile, error) {
var result []*model.Profile
for _, name := range names {
for _, profile := range m.profiles {
if profile.Name == name {
result = append(result, profile)
}
}
}
return result, nil
}
func (m *MockProfileService) GetByProfileName(name string) (*model.Profile, error) {
for _, profile := range m.profiles {
if profile.Name == name {
return profile, nil
}
}
return nil, errors.New("profile not found")
}
// MockTextureService 模拟TextureService
type MockTextureService struct {
textures map[int64]*model.Texture
nextID int64
FailCreate bool
FailGet bool
FailUpdate bool
FailDelete bool
}
func NewMockTextureService() *MockTextureService {
return &MockTextureService{
textures: make(map[int64]*model.Texture),
nextID: 1,
}
}
func (m *MockTextureService) Create(uploaderID int64, name, description, textureType, url, hash string, size int, isPublic, isSlim bool) (*model.Texture, error) {
if m.FailCreate {
return nil, errors.New("mock create error")
}
texture := &model.Texture{
ID: m.nextID,
UploaderID: uploaderID,
Name: name,
Description: description,
URL: url,
Hash: hash,
Size: size,
IsPublic: isPublic,
IsSlim: isSlim,
}
m.textures[texture.ID] = texture
m.nextID++
return texture, nil
}
func (m *MockTextureService) GetByID(id int64) (*model.Texture, error) {
if m.FailGet {
return nil, errors.New("mock get error")
}
if texture, ok := m.textures[id]; ok {
return texture, nil
}
return nil, errors.New("texture not found")
}
func (m *MockTextureService) GetByUserID(uploaderID int64, page, pageSize int) ([]*model.Texture, int64, error) {
if m.FailGet {
return nil, 0, errors.New("mock get error")
}
var result []*model.Texture
for _, texture := range m.textures {
if texture.UploaderID == uploaderID {
result = append(result, texture)
}
}
return result, int64(len(result)), nil
}
func (m *MockTextureService) Search(keyword string, textureType model.TextureType, publicOnly bool, page, pageSize int) ([]*model.Texture, int64, error) {
if m.FailGet {
return nil, 0, errors.New("mock get error")
}
var result []*model.Texture
for _, texture := range m.textures {
if publicOnly && !texture.IsPublic {
continue
}
result = append(result, texture)
}
return result, int64(len(result)), nil
}
func (m *MockTextureService) Update(textureID, uploaderID int64, name, description string, isPublic *bool) (*model.Texture, error) {
if m.FailUpdate {
return nil, errors.New("mock update error")
}
if texture, ok := m.textures[textureID]; ok {
if name != "" {
texture.Name = name
}
if description != "" {
texture.Description = description
}
if isPublic != nil {
texture.IsPublic = *isPublic
}
return texture, nil
}
return nil, errors.New("texture not found")
}
func (m *MockTextureService) Delete(textureID, uploaderID int64) error {
if m.FailDelete {
return errors.New("mock delete error")
}
delete(m.textures, textureID)
return nil
}
func (m *MockTextureService) ToggleFavorite(userID, textureID int64) (bool, error) {
return true, nil
}
func (m *MockTextureService) GetUserFavorites(userID int64, page, pageSize int) ([]*model.Texture, int64, error) {
return nil, 0, nil
}
func (m *MockTextureService) CheckUploadLimit(uploaderID int64, maxTextures int) error {
count := 0
for _, texture := range m.textures {
if texture.UploaderID == uploaderID {
count++
}
}
if count >= maxTextures {
return errors.New("达到材质数量上限")
}
return nil
}
// MockTokenService 模拟TokenService
type MockTokenService struct {
tokens map[string]*model.Token
FailCreate bool
FailValidate bool
FailRefresh bool
}
func NewMockTokenService() *MockTokenService {
return &MockTokenService{
tokens: make(map[string]*model.Token),
}
}
func (m *MockTokenService) Create(userID int64, uuid, clientToken string) (*model.Profile, []*model.Profile, string, string, error) {
if m.FailCreate {
return nil, nil, "", "", errors.New("mock create error")
}
accessToken := "mock-access-token"
if clientToken == "" {
clientToken = "mock-client-token"
}
token := &model.Token{
AccessToken: accessToken,
ClientToken: clientToken,
UserID: userID,
ProfileId: uuid,
Usable: true,
}
m.tokens[accessToken] = token
return nil, nil, accessToken, clientToken, nil
}
func (m *MockTokenService) Validate(accessToken, clientToken string) bool {
if m.FailValidate {
return false
}
if token, ok := m.tokens[accessToken]; ok {
if clientToken == "" || token.ClientToken == clientToken {
return token.Usable
}
}
return false
}
func (m *MockTokenService) Refresh(accessToken, clientToken, selectedProfileID string) (string, string, error) {
if m.FailRefresh {
return "", "", errors.New("mock refresh error")
}
return "new-access-token", clientToken, nil
}
func (m *MockTokenService) Invalidate(accessToken string) {
delete(m.tokens, accessToken)
}
func (m *MockTokenService) InvalidateUserTokens(userID int64) {
for key, token := range m.tokens {
if token.UserID == userID {
delete(m.tokens, key)
}
}
}
func (m *MockTokenService) GetUUIDByAccessToken(accessToken string) (string, error) {
if token, ok := m.tokens[accessToken]; ok {
return token.ProfileId, nil
}
return "", errors.New("token not found")
}
func (m *MockTokenService) GetUserIDByAccessToken(accessToken string) (int64, error) {
if token, ok := m.tokens[accessToken]; ok {
return token.UserID, nil
}
return 0, errors.New("token not found")
}
// ============================================================================
// CacheManager Mock - uses database.CacheManager with nil redis
// ============================================================================
// NewMockCacheManager 创建一个禁用的 CacheManager 用于测试
// 通过设置 Enabled = false缓存操作会被跳过测试不依赖 Redis
func NewMockCacheManager() *database.CacheManager {
return database.NewCacheManager(nil, database.CacheConfig{
Prefix: "test:",
Expiration: 5 * time.Minute,
Enabled: false, // 禁用缓存,测试不依赖 Redis
})
}