Files
backend/internal/service/mocks_test.go
lafay 7d1c78f965
Some checks failed
Build / build (push) Successful in 7m52s
Build / build-docker (push) Has been cancelled
refactor: 移除全局单例、修复分层违规、统一错误与响应
2026-06-15 16:40:36 +08:00

886 lines
23 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"
"context"
"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(ctx context.Context, 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(ctx context.Context, 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(ctx context.Context, 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(ctx context.Context, 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(ctx context.Context, user *model.User) error {
if m.FailUpdate {
return errors.New("mock update error")
}
m.users[user.ID] = user
return nil
}
func (m *MockUserRepository) UpdateFields(ctx context.Context, 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(ctx context.Context, id int64) error {
delete(m.users, id)
return nil
}
func (m *MockUserRepository) CreateLoginLog(ctx context.Context, log *model.UserLoginLog) error {
return nil
}
func (m *MockUserRepository) CreatePointLog(ctx context.Context, log *model.UserPointLog) error {
return nil
}
func (m *MockUserRepository) UpdatePoints(ctx context.Context, userID int64, amount int, changeType, reason string) error {
return nil
}
// BatchUpdate 和 BatchDelete 仅用于满足接口,在测试中不做具体操作
func (m *MockUserRepository) BatchUpdate(ctx context.Context, ids []int64, fields map[string]interface{}) (int64, error) {
return 0, nil
}
func (m *MockUserRepository) BatchDelete(ctx context.Context, ids []int64) (int64, error) {
return 0, nil
}
// FindByIDs 批量查询用户
func (m *MockUserRepository) FindByIDs(ctx context.Context, ids []int64) ([]*model.User, error) {
var result []*model.User
for _, id := range ids {
if u, ok := m.users[id]; ok {
result = append(result, u)
}
}
return result, nil
}
// List 分页查询用户列表管理员mock 实现)
func (m *MockUserRepository) List(ctx context.Context, page, pageSize int) ([]*model.User, int64, error) {
var all []*model.User
for _, u := range m.users {
all = append(all, u)
}
total := int64(len(all))
offset := (page - 1) * pageSize
if offset >= len(all) {
return []*model.User{}, total, nil
}
end := offset + pageSize
if end > len(all) {
end = len(all)
}
return all[offset:end], total, 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(ctx context.Context, 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(ctx context.Context, 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(ctx context.Context, 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(ctx context.Context, userID int64) ([]*model.Profile, error) {
if m.FailFind {
return nil, errors.New("mock find error")
}
return m.userProfiles[userID], nil
}
func (m *MockProfileRepository) Update(ctx context.Context, profile *model.Profile) error {
if m.FailUpdate {
return errors.New("mock update error")
}
m.profiles[profile.UUID] = profile
return nil
}
func (m *MockProfileRepository) UpdateFields(ctx context.Context, uuid string, updates map[string]interface{}) error {
if m.FailUpdate {
return errors.New("mock update error")
}
return nil
}
func (m *MockProfileRepository) Delete(ctx context.Context, uuid string) error {
if m.FailDelete {
return errors.New("mock delete error")
}
delete(m.profiles, uuid)
return nil
}
func (m *MockProfileRepository) CountByUserID(ctx context.Context, userID int64) (int64, error) {
return int64(len(m.userProfiles[userID])), nil
}
func (m *MockProfileRepository) SetActive(ctx context.Context, uuid string, userID int64) error {
return nil
}
func (m *MockProfileRepository) UpdateLastUsedAt(ctx context.Context, uuid string) error {
return nil
}
func (m *MockProfileRepository) GetByNames(ctx context.Context, 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(ctx context.Context, profileId string) (*model.KeyPair, error) {
return nil, nil
}
func (m *MockProfileRepository) UpdateKeyPair(ctx context.Context, profileId string, keyPair *model.KeyPair) error {
return nil
}
// BatchUpdate / BatchDelete 仅用于满足接口
func (m *MockProfileRepository) BatchUpdate(ctx context.Context, uuids []string, updates map[string]interface{}) (int64, error) {
return 0, nil
}
func (m *MockProfileRepository) BatchDelete(ctx context.Context, uuids []string) (int64, error) {
return 0, nil
}
// FindByUUIDs 批量查询 Profile
func (m *MockProfileRepository) FindByUUIDs(ctx context.Context, uuids []string) ([]*model.Profile, error) {
var result []*model.Profile
for _, id := range uuids {
if p, ok := m.profiles[id]; ok {
result = append(result, p)
}
}
return result, 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(ctx context.Context, 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(ctx context.Context, 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(ctx context.Context, 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) FindByHashAndUploaderID(ctx context.Context, hash string, uploaderID int64) (*model.Texture, error) {
if m.FailFind {
return nil, errors.New("mock find error")
}
for _, texture := range m.textures {
if texture.Hash == hash && texture.UploaderID == uploaderID {
return texture, nil
}
}
return nil, nil
}
func (m *MockTextureRepository) FindByUploaderID(ctx context.Context, 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(ctx context.Context, 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(ctx context.Context, texture *model.Texture) error {
if m.FailUpdate {
return errors.New("mock update error")
}
m.textures[texture.ID] = texture
return nil
}
func (m *MockTextureRepository) UpdateFields(ctx context.Context, id int64, fields map[string]interface{}) error {
if m.FailUpdate {
return errors.New("mock update error")
}
return nil
}
func (m *MockTextureRepository) Delete(ctx context.Context, id int64) error {
if m.FailDelete {
return errors.New("mock delete error")
}
delete(m.textures, id)
return nil
}
func (m *MockTextureRepository) IncrementDownloadCount(ctx context.Context, id int64) error {
if texture, ok := m.textures[id]; ok {
texture.DownloadCount++
}
return nil
}
func (m *MockTextureRepository) IncrementFavoriteCount(ctx context.Context, id int64) error {
if texture, ok := m.textures[id]; ok {
texture.FavoriteCount++
}
return nil
}
func (m *MockTextureRepository) DecrementFavoriteCount(ctx context.Context, id int64) error {
if texture, ok := m.textures[id]; ok && texture.FavoriteCount > 0 {
texture.FavoriteCount--
}
return nil
}
func (m *MockTextureRepository) CreateDownloadLog(ctx context.Context, log *model.TextureDownloadLog) error {
return nil
}
func (m *MockTextureRepository) IsFavorited(ctx context.Context, userID, textureID int64) (bool, error) {
if userFavs, ok := m.favorites[userID]; ok {
return userFavs[textureID], nil
}
return false, nil
}
func (m *MockTextureRepository) AddFavorite(ctx context.Context, 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(ctx context.Context, userID, textureID int64) error {
if userFavs, ok := m.favorites[userID]; ok {
delete(userFavs, textureID)
}
return nil
}
func (m *MockTextureRepository) GetUserFavorites(ctx context.Context, 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(ctx context.Context, uploaderID int64) (int64, error) {
var count int64
for _, texture := range m.textures {
if texture.UploaderID == uploaderID {
count++
}
}
return count, nil
}
// FindByIDs 批量查询 Texture
func (m *MockTextureRepository) FindByIDs(ctx context.Context, ids []int64) ([]*model.Texture, error) {
var result []*model.Texture
for _, id := range ids {
if tex, ok := m.textures[id]; ok {
result = append(result, tex)
}
}
return result, nil
}
// BatchUpdate 仅用于满足接口
func (m *MockTextureRepository) BatchUpdate(ctx context.Context, ids []int64, fields map[string]interface{}) (int64, error) {
return 0, nil
}
// BatchDelete 仅用于满足接口
func (m *MockTextureRepository) BatchDelete(ctx context.Context, ids []int64) (int64, error) {
var deleted int64
for _, id := range ids {
if _, ok := m.textures[id]; ok {
delete(m.textures, id)
deleted++
}
}
return deleted, nil
}
// ListForAdmin 管理员列表mock 实现)
func (m *MockTextureRepository) ListForAdmin(ctx context.Context, page, pageSize int) ([]*model.Texture, int64, error) {
var all []*model.Texture
for _, t := range m.textures {
all = append(all, t)
}
total := int64(len(all))
offset := (page - 1) * pageSize
if offset >= len(all) {
return []*model.Texture{}, total, nil
}
end := offset + pageSize
if end > len(all) {
end = len(all)
}
return all[offset:end], total, nil
}
// FindByIDForAdmin 管理员查询mock 实现,无权限过滤)
func (m *MockTextureRepository) FindByIDForAdmin(ctx context.Context, id int64) (*model.Texture, error) {
if t, ok := m.textures[id]; ok {
return t, nil
}
return nil, nil
}
// ============================================================================
// 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
}
// ============================================================================
// CacheManager Mock - 使用 database.CacheManager 的内存版本
// ============================================================================
// NewMockCacheManager 创建一个内存 CacheManager 用于测试
func NewMockCacheManager() *database.CacheManager {
return database.NewCacheManager(nil, database.CacheConfig{
Prefix: "test:",
Expiration: 5 * time.Minute,
Enabled: false, // 禁用缓存,测试不依赖 Redis
})
}