- 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.
847 lines
19 KiB
Go
847 lines
19 KiB
Go
package service
|
||
|
||
import (
|
||
"carrotskin/internal/model"
|
||
"context"
|
||
"testing"
|
||
|
||
"go.uber.org/zap"
|
||
)
|
||
|
||
// TestTextureService_TypeValidation 测试材质类型验证
|
||
func TestTextureService_TypeValidation(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
textureType string
|
||
wantValid bool
|
||
}{
|
||
{
|
||
name: "SKIN类型有效",
|
||
textureType: "SKIN",
|
||
wantValid: true,
|
||
},
|
||
{
|
||
name: "CAPE类型有效",
|
||
textureType: "CAPE",
|
||
wantValid: true,
|
||
},
|
||
{
|
||
name: "无效类型",
|
||
textureType: "INVALID",
|
||
wantValid: false,
|
||
},
|
||
{
|
||
name: "空类型无效",
|
||
textureType: "",
|
||
wantValid: false,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
isValid := tt.textureType == "SKIN" || tt.textureType == "CAPE"
|
||
if isValid != tt.wantValid {
|
||
t.Errorf("Texture type validation failed: got %v, want %v", isValid, tt.wantValid)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestTextureService_DefaultValues 测试材质默认值
|
||
func TestTextureService_DefaultValues(t *testing.T) {
|
||
// 测试默认状态
|
||
defaultStatus := 1
|
||
if defaultStatus != 1 {
|
||
t.Errorf("默认状态应为1,实际为%d", defaultStatus)
|
||
}
|
||
|
||
// 测试默认下载数
|
||
defaultDownloadCount := 0
|
||
if defaultDownloadCount != 0 {
|
||
t.Errorf("默认下载数应为0,实际为%d", defaultDownloadCount)
|
||
}
|
||
|
||
// 测试默认收藏数
|
||
defaultFavoriteCount := 0
|
||
if defaultFavoriteCount != 0 {
|
||
t.Errorf("默认收藏数应为0,实际为%d", defaultFavoriteCount)
|
||
}
|
||
}
|
||
|
||
// TestTextureService_StatusValidation 测试材质状态验证
|
||
func TestTextureService_StatusValidation(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
status int16
|
||
wantValid bool
|
||
}{
|
||
{
|
||
name: "状态为1(正常)时有效",
|
||
status: 1,
|
||
wantValid: true,
|
||
},
|
||
{
|
||
name: "状态为-1(删除)时无效",
|
||
status: -1,
|
||
wantValid: false,
|
||
},
|
||
{
|
||
name: "状态为0时可能有效(取决于业务逻辑)",
|
||
status: 0,
|
||
wantValid: true, // 状态为0(禁用)时,材质仍然存在,只是不可用,但查询时不会返回错误
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
// 材质状态为-1时表示已删除,无效
|
||
isValid := tt.status != -1
|
||
if isValid != tt.wantValid {
|
||
t.Errorf("Status validation failed: got %v, want %v", isValid, tt.wantValid)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestGetUserTextures_Pagination 测试分页逻辑
|
||
func TestGetUserTextures_Pagination(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
page int
|
||
pageSize int
|
||
wantPage int
|
||
wantSize int
|
||
}{
|
||
{
|
||
name: "有效的分页参数",
|
||
page: 2,
|
||
pageSize: 20,
|
||
wantPage: 2,
|
||
wantSize: 20,
|
||
},
|
||
{
|
||
name: "page小于1,应该设为1",
|
||
page: 0,
|
||
pageSize: 20,
|
||
wantPage: 1,
|
||
wantSize: 20,
|
||
},
|
||
{
|
||
name: "pageSize小于1,应该设为20",
|
||
page: 1,
|
||
pageSize: 0,
|
||
wantPage: 1,
|
||
wantSize: 20,
|
||
},
|
||
{
|
||
name: "pageSize超过100,应该设为20",
|
||
page: 1,
|
||
pageSize: 200,
|
||
wantPage: 1,
|
||
wantSize: 20,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
page := tt.page
|
||
pageSize := tt.pageSize
|
||
|
||
if page < 1 {
|
||
page = 1
|
||
}
|
||
if pageSize < 1 || pageSize > 100 {
|
||
pageSize = 20
|
||
}
|
||
|
||
if page != tt.wantPage {
|
||
t.Errorf("Page = %d, want %d", page, tt.wantPage)
|
||
}
|
||
if pageSize != tt.wantSize {
|
||
t.Errorf("PageSize = %d, want %d", pageSize, tt.wantSize)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestSearchTextures_Pagination 测试搜索分页逻辑
|
||
func TestSearchTextures_Pagination(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
page int
|
||
pageSize int
|
||
wantPage int
|
||
wantSize int
|
||
}{
|
||
{
|
||
name: "有效的分页参数",
|
||
page: 1,
|
||
pageSize: 10,
|
||
wantPage: 1,
|
||
wantSize: 10,
|
||
},
|
||
{
|
||
name: "page小于1,应该设为1",
|
||
page: -1,
|
||
pageSize: 20,
|
||
wantPage: 1,
|
||
wantSize: 20,
|
||
},
|
||
{
|
||
name: "pageSize超过100,应该设为20",
|
||
page: 1,
|
||
pageSize: 150,
|
||
wantPage: 1,
|
||
wantSize: 20,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
page := tt.page
|
||
pageSize := tt.pageSize
|
||
|
||
if page < 1 {
|
||
page = 1
|
||
}
|
||
if pageSize < 1 || pageSize > 100 {
|
||
pageSize = 20
|
||
}
|
||
|
||
if page != tt.wantPage {
|
||
t.Errorf("Page = %d, want %d", page, tt.wantPage)
|
||
}
|
||
if pageSize != tt.wantSize {
|
||
t.Errorf("PageSize = %d, want %d", pageSize, tt.wantSize)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestUpdateTexture_PermissionCheck 测试更新材质的权限检查
|
||
func TestUpdateTexture_PermissionCheck(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
uploaderID int64
|
||
requestID int64
|
||
wantErr bool
|
||
}{
|
||
{
|
||
name: "上传者ID匹配,允许更新",
|
||
uploaderID: 1,
|
||
requestID: 1,
|
||
wantErr: false,
|
||
},
|
||
{
|
||
name: "上传者ID不匹配,拒绝更新",
|
||
uploaderID: 1,
|
||
requestID: 2,
|
||
wantErr: true,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
hasError := tt.uploaderID != tt.requestID
|
||
if hasError != tt.wantErr {
|
||
t.Errorf("Permission check failed: got %v, want %v", hasError, tt.wantErr)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestUpdateTexture_FieldUpdates 测试更新字段逻辑
|
||
func TestUpdateTexture_FieldUpdates(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
nameValue string
|
||
descValue string
|
||
isPublic *bool
|
||
wantUpdates int
|
||
}{
|
||
{
|
||
name: "更新所有字段",
|
||
nameValue: "NewName",
|
||
descValue: "NewDesc",
|
||
isPublic: boolPtr(true),
|
||
wantUpdates: 3,
|
||
},
|
||
{
|
||
name: "只更新名称",
|
||
nameValue: "NewName",
|
||
descValue: "",
|
||
isPublic: nil,
|
||
wantUpdates: 1,
|
||
},
|
||
{
|
||
name: "只更新描述",
|
||
nameValue: "",
|
||
descValue: "NewDesc",
|
||
isPublic: nil,
|
||
wantUpdates: 1,
|
||
},
|
||
{
|
||
name: "只更新公开状态",
|
||
nameValue: "",
|
||
descValue: "",
|
||
isPublic: boolPtr(false),
|
||
wantUpdates: 1,
|
||
},
|
||
{
|
||
name: "没有更新",
|
||
nameValue: "",
|
||
descValue: "",
|
||
isPublic: nil,
|
||
wantUpdates: 0,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
updates := 0
|
||
if tt.nameValue != "" {
|
||
updates++
|
||
}
|
||
if tt.descValue != "" {
|
||
updates++
|
||
}
|
||
if tt.isPublic != nil {
|
||
updates++
|
||
}
|
||
|
||
if updates != tt.wantUpdates {
|
||
t.Errorf("Updates count = %d, want %d", updates, tt.wantUpdates)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestDeleteTexture_PermissionCheck 测试删除材质的权限检查
|
||
func TestDeleteTexture_PermissionCheck(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
uploaderID int64
|
||
requestID int64
|
||
wantErr bool
|
||
}{
|
||
{
|
||
name: "上传者ID匹配,允许删除",
|
||
uploaderID: 1,
|
||
requestID: 1,
|
||
wantErr: false,
|
||
},
|
||
{
|
||
name: "上传者ID不匹配,拒绝删除",
|
||
uploaderID: 1,
|
||
requestID: 2,
|
||
wantErr: true,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
hasError := tt.uploaderID != tt.requestID
|
||
if hasError != tt.wantErr {
|
||
t.Errorf("Permission check failed: got %v, want %v", hasError, tt.wantErr)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestToggleTextureFavorite_Logic 测试切换收藏状态的逻辑
|
||
func TestToggleTextureFavorite_Logic(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
isFavorited bool
|
||
wantResult bool
|
||
}{
|
||
{
|
||
name: "已收藏,取消收藏",
|
||
isFavorited: true,
|
||
wantResult: false,
|
||
},
|
||
{
|
||
name: "未收藏,添加收藏",
|
||
isFavorited: false,
|
||
wantResult: true,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
result := !tt.isFavorited
|
||
if result != tt.wantResult {
|
||
t.Errorf("Toggle favorite failed: got %v, want %v", result, tt.wantResult)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestGetUserTextureFavorites_Pagination 测试收藏列表分页
|
||
func TestGetUserTextureFavorites_Pagination(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
page int
|
||
pageSize int
|
||
wantPage int
|
||
wantSize int
|
||
}{
|
||
{
|
||
name: "有效的分页参数",
|
||
page: 1,
|
||
pageSize: 20,
|
||
wantPage: 1,
|
||
wantSize: 20,
|
||
},
|
||
{
|
||
name: "page小于1,应该设为1",
|
||
page: 0,
|
||
pageSize: 20,
|
||
wantPage: 1,
|
||
wantSize: 20,
|
||
},
|
||
{
|
||
name: "pageSize超过100,应该设为20",
|
||
page: 1,
|
||
pageSize: 200,
|
||
wantPage: 1,
|
||
wantSize: 20,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
page := tt.page
|
||
pageSize := tt.pageSize
|
||
|
||
if page < 1 {
|
||
page = 1
|
||
}
|
||
if pageSize < 1 || pageSize > 100 {
|
||
pageSize = 20
|
||
}
|
||
|
||
if page != tt.wantPage {
|
||
t.Errorf("Page = %d, want %d", page, tt.wantPage)
|
||
}
|
||
if pageSize != tt.wantSize {
|
||
t.Errorf("PageSize = %d, want %d", pageSize, tt.wantSize)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestCheckTextureUploadLimit_Logic 测试上传限制检查逻辑
|
||
func TestCheckTextureUploadLimit_Logic(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
count int64
|
||
maxTextures int
|
||
wantErr bool
|
||
}{
|
||
{
|
||
name: "未达到上限",
|
||
count: 5,
|
||
maxTextures: 10,
|
||
wantErr: false,
|
||
},
|
||
{
|
||
name: "达到上限",
|
||
count: 10,
|
||
maxTextures: 10,
|
||
wantErr: true,
|
||
},
|
||
{
|
||
name: "超过上限",
|
||
count: 15,
|
||
maxTextures: 10,
|
||
wantErr: true,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
hasError := tt.count >= int64(tt.maxTextures)
|
||
if hasError != tt.wantErr {
|
||
t.Errorf("Limit check failed: got %v, want %v", hasError, tt.wantErr)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// 辅助函数
|
||
func boolPtr(b bool) *bool {
|
||
return &b
|
||
}
|
||
|
||
// ============================================================================
|
||
// 使用 Mock 的集成测试
|
||
// ============================================================================
|
||
|
||
// TestTextureServiceImpl_Create 测试创建Texture
|
||
func TestTextureServiceImpl_Create(t *testing.T) {
|
||
textureRepo := NewMockTextureRepository()
|
||
userRepo := NewMockUserRepository()
|
||
logger := zap.NewNop()
|
||
|
||
// 预置用户
|
||
testUser := &model.User{
|
||
ID: 1,
|
||
Username: "testuser",
|
||
Email: "test@example.com",
|
||
Status: 1,
|
||
}
|
||
userRepo.Create(testUser)
|
||
|
||
cacheManager := NewMockCacheManager()
|
||
textureService := NewTextureService(textureRepo, userRepo, cacheManager, logger)
|
||
|
||
tests := []struct {
|
||
name string
|
||
uploaderID int64
|
||
textureName string
|
||
textureType string
|
||
hash string
|
||
wantErr bool
|
||
errContains string
|
||
setupMocks func()
|
||
}{
|
||
{
|
||
name: "正常创建SKIN材质",
|
||
uploaderID: 1,
|
||
textureName: "TestSkin",
|
||
textureType: "SKIN",
|
||
hash: "unique-hash-1",
|
||
wantErr: false,
|
||
},
|
||
{
|
||
name: "正常创建CAPE材质",
|
||
uploaderID: 1,
|
||
textureName: "TestCape",
|
||
textureType: "CAPE",
|
||
hash: "unique-hash-2",
|
||
wantErr: false,
|
||
},
|
||
{
|
||
name: "用户不存在",
|
||
uploaderID: 999,
|
||
textureName: "TestTexture",
|
||
textureType: "SKIN",
|
||
hash: "unique-hash-3",
|
||
wantErr: true,
|
||
},
|
||
{
|
||
name: "材质Hash已存在",
|
||
uploaderID: 1,
|
||
textureName: "DuplicateTexture",
|
||
textureType: "SKIN",
|
||
hash: "existing-hash",
|
||
wantErr: true,
|
||
errContains: "已存在",
|
||
setupMocks: func() {
|
||
textureRepo.Create(&model.Texture{
|
||
ID: 100,
|
||
UploaderID: 1,
|
||
Name: "ExistingTexture",
|
||
Hash: "existing-hash",
|
||
})
|
||
},
|
||
},
|
||
{
|
||
name: "无效的材质类型",
|
||
uploaderID: 1,
|
||
textureName: "InvalidTypeTexture",
|
||
textureType: "INVALID",
|
||
hash: "unique-hash-4",
|
||
wantErr: true,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
if tt.setupMocks != nil {
|
||
tt.setupMocks()
|
||
}
|
||
|
||
ctx := context.Background()
|
||
texture, err := textureService.Create(
|
||
ctx,
|
||
tt.uploaderID,
|
||
tt.textureName,
|
||
"Test description",
|
||
tt.textureType,
|
||
"http://example.com/texture.png",
|
||
tt.hash,
|
||
1024,
|
||
true,
|
||
false,
|
||
)
|
||
|
||
if tt.wantErr {
|
||
if err == nil {
|
||
t.Error("期望返回错误,但实际没有错误")
|
||
return
|
||
}
|
||
if tt.errContains != "" && !containsString(err.Error(), tt.errContains) {
|
||
t.Errorf("错误信息应包含 %q, 实际为: %v", tt.errContains, err.Error())
|
||
}
|
||
} else {
|
||
if err != nil {
|
||
t.Errorf("不期望返回错误: %v", err)
|
||
return
|
||
}
|
||
if texture == nil {
|
||
t.Error("返回的Texture不应为nil")
|
||
}
|
||
if texture.Name != tt.textureName {
|
||
t.Errorf("Texture名称不匹配: got %v, want %v", texture.Name, tt.textureName)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestTextureServiceImpl_GetByID 测试获取Texture
|
||
func TestTextureServiceImpl_GetByID(t *testing.T) {
|
||
textureRepo := NewMockTextureRepository()
|
||
userRepo := NewMockUserRepository()
|
||
logger := zap.NewNop()
|
||
|
||
// 预置Texture
|
||
testTexture := &model.Texture{
|
||
ID: 1,
|
||
UploaderID: 1,
|
||
Name: "TestTexture",
|
||
Hash: "test-hash",
|
||
}
|
||
textureRepo.Create(testTexture)
|
||
|
||
cacheManager := NewMockCacheManager()
|
||
textureService := NewTextureService(textureRepo, userRepo, cacheManager, logger)
|
||
|
||
tests := []struct {
|
||
name string
|
||
id int64
|
||
wantErr bool
|
||
}{
|
||
{
|
||
name: "获取存在的Texture",
|
||
id: 1,
|
||
wantErr: false,
|
||
},
|
||
{
|
||
name: "获取不存在的Texture",
|
||
id: 999,
|
||
wantErr: true,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
ctx := context.Background()
|
||
texture, err := textureService.GetByID(ctx, tt.id)
|
||
|
||
if tt.wantErr {
|
||
if err == nil {
|
||
t.Error("期望返回错误,但实际没有错误")
|
||
}
|
||
} else {
|
||
if err != nil {
|
||
t.Errorf("不期望返回错误: %v", err)
|
||
return
|
||
}
|
||
if texture == nil {
|
||
t.Error("返回的Texture不应为nil")
|
||
}
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestTextureServiceImpl_GetByUserID_And_Search 测试 GetByUserID 与 Search 分页封装
|
||
func TestTextureServiceImpl_GetByUserID_And_Search(t *testing.T) {
|
||
textureRepo := NewMockTextureRepository()
|
||
userRepo := NewMockUserRepository()
|
||
logger := zap.NewNop()
|
||
|
||
// 预置多条 Texture
|
||
for i := int64(1); i <= 5; i++ {
|
||
textureRepo.Create(&model.Texture{
|
||
ID: i,
|
||
UploaderID: 1,
|
||
Name: "T",
|
||
IsPublic: i%2 == 0,
|
||
})
|
||
}
|
||
|
||
cacheManager := NewMockCacheManager()
|
||
textureService := NewTextureService(textureRepo, userRepo, cacheManager, logger)
|
||
|
||
ctx := context.Background()
|
||
|
||
// GetByUserID 应按上传者过滤并调用 NormalizePagination
|
||
textures, total, err := textureService.GetByUserID(ctx, 1, 0, 0)
|
||
if err != nil {
|
||
t.Fatalf("GetByUserID 失败: %v", err)
|
||
}
|
||
if total != int64(len(textures)) {
|
||
t.Fatalf("GetByUserID 返回数量与总数不一致, total=%d, len=%d", total, len(textures))
|
||
}
|
||
|
||
// Search 仅验证能够正常调用并返回结果
|
||
searchResult, searchTotal, err := textureService.Search(ctx, "", model.TextureTypeSkin, true, -1, 200)
|
||
if err != nil {
|
||
t.Fatalf("Search 失败: %v", err)
|
||
}
|
||
if searchTotal != int64(len(searchResult)) {
|
||
t.Fatalf("Search 返回数量与总数不一致, total=%d, len=%d", searchTotal, len(searchResult))
|
||
}
|
||
}
|
||
|
||
// TestTextureServiceImpl_Update_And_Delete 测试 Update / Delete 权限与字段更新
|
||
func TestTextureServiceImpl_Update_And_Delete(t *testing.T) {
|
||
textureRepo := NewMockTextureRepository()
|
||
userRepo := NewMockUserRepository()
|
||
logger := zap.NewNop()
|
||
|
||
texture := &model.Texture{
|
||
ID: 1,
|
||
UploaderID: 1,
|
||
Name: "Old",
|
||
Description: "OldDesc",
|
||
IsPublic: false,
|
||
}
|
||
textureRepo.Create(texture)
|
||
|
||
cacheManager := NewMockCacheManager()
|
||
textureService := NewTextureService(textureRepo, userRepo, cacheManager, logger)
|
||
|
||
ctx := context.Background()
|
||
|
||
// 更新成功
|
||
newName := "NewName"
|
||
newDesc := "NewDesc"
|
||
public := boolPtr(true)
|
||
updated, err := textureService.Update(ctx, 1, 1, newName, newDesc, public)
|
||
if err != nil {
|
||
t.Fatalf("Update 正常情况失败: %v", err)
|
||
}
|
||
// 由于 MockTextureRepository.UpdateFields 不会真正修改结构体字段,这里只验证不会返回 nil 即可
|
||
if updated == nil {
|
||
t.Fatalf("Update 返回结果不应为 nil")
|
||
}
|
||
|
||
// 无权限更新
|
||
if _, err := textureService.Update(ctx, 1, 2, "X", "Y", nil); err == nil {
|
||
t.Fatalf("Update 在无权限时应返回错误")
|
||
}
|
||
|
||
// 删除成功
|
||
if err := textureService.Delete(ctx, 1, 1); err != nil {
|
||
t.Fatalf("Delete 正常情况失败: %v", err)
|
||
}
|
||
|
||
// 无权限删除
|
||
if err := textureService.Delete(ctx, 1, 2); err == nil {
|
||
t.Fatalf("Delete 在无权限时应返回错误")
|
||
}
|
||
}
|
||
|
||
// TestTextureServiceImpl_FavoritesAndLimit 测试 GetUserFavorites 与 CheckUploadLimit
|
||
func TestTextureServiceImpl_FavoritesAndLimit(t *testing.T) {
|
||
textureRepo := NewMockTextureRepository()
|
||
userRepo := NewMockUserRepository()
|
||
logger := zap.NewNop()
|
||
|
||
// 预置若干 Texture 与收藏关系
|
||
for i := int64(1); i <= 3; i++ {
|
||
textureRepo.Create(&model.Texture{
|
||
ID: i,
|
||
UploaderID: 1,
|
||
Name: "T",
|
||
})
|
||
_ = textureRepo.AddFavorite(1, i)
|
||
}
|
||
|
||
cacheManager := NewMockCacheManager()
|
||
textureService := NewTextureService(textureRepo, userRepo, cacheManager, logger)
|
||
|
||
ctx := context.Background()
|
||
|
||
// GetUserFavorites
|
||
favs, total, err := textureService.GetUserFavorites(ctx, 1, -1, -1)
|
||
if err != nil {
|
||
t.Fatalf("GetUserFavorites 失败: %v", err)
|
||
}
|
||
if int64(len(favs)) != total || total != 3 {
|
||
t.Fatalf("GetUserFavorites 数量不正确, total=%d, len=%d", total, len(favs))
|
||
}
|
||
|
||
// CheckUploadLimit 未超过上限
|
||
if err := textureService.CheckUploadLimit(ctx, 1, 10); err != nil {
|
||
t.Fatalf("CheckUploadLimit 在未达到上限时不应报错: %v", err)
|
||
}
|
||
|
||
// CheckUploadLimit 超过上限
|
||
if err := textureService.CheckUploadLimit(ctx, 1, 2); err == nil {
|
||
t.Fatalf("CheckUploadLimit 在超过上限时应返回错误")
|
||
}
|
||
}
|
||
|
||
// TestTextureServiceImpl_ToggleFavorite 测试收藏功能
|
||
func TestTextureServiceImpl_ToggleFavorite(t *testing.T) {
|
||
textureRepo := NewMockTextureRepository()
|
||
userRepo := NewMockUserRepository()
|
||
logger := zap.NewNop()
|
||
|
||
// 预置用户和Texture
|
||
testUser := &model.User{ID: 1, Username: "testuser", Status: 1}
|
||
userRepo.Create(testUser)
|
||
|
||
testTexture := &model.Texture{
|
||
ID: 1,
|
||
UploaderID: 1,
|
||
Name: "TestTexture",
|
||
Hash: "test-hash",
|
||
}
|
||
textureRepo.Create(testTexture)
|
||
|
||
cacheManager := NewMockCacheManager()
|
||
textureService := NewTextureService(textureRepo, userRepo, cacheManager, logger)
|
||
|
||
ctx := context.Background()
|
||
|
||
// 第一次收藏
|
||
isFavorited, err := textureService.ToggleFavorite(ctx, 1, 1)
|
||
if err != nil {
|
||
t.Errorf("第一次收藏失败: %v", err)
|
||
}
|
||
if !isFavorited {
|
||
t.Error("第一次操作应该是添加收藏")
|
||
}
|
||
|
||
// 第二次取消收藏
|
||
isFavorited, err = textureService.ToggleFavorite(ctx, 1, 1)
|
||
if err != nil {
|
||
t.Errorf("取消收藏失败: %v", err)
|
||
}
|
||
if isFavorited {
|
||
t.Error("第二次操作应该是取消收藏")
|
||
}
|
||
}
|
||
|
||
// 辅助函数
|
||
func containsString(s, substr string) bool {
|
||
return len(s) >= len(substr) && (s == substr ||
|
||
(len(s) > len(substr) && (findSubstring(s, substr) != -1)))
|
||
}
|
||
|
||
func findSubstring(s, substr string) int {
|
||
for i := 0; i <= len(s)-len(substr); i++ {
|
||
if s[i:i+len(substr)] == substr {
|
||
return i
|
||
}
|
||
}
|
||
return -1
|
||
}
|