package repository import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/topfans/backend/pkg/database" "github.com/topfans/backend/pkg/models" "gorm.io/gorm" ) // setupTestDB 设置测试数据库 func setupTestDB(t *testing.T) *gorm.DB { config := database.Config{ Host: "localhost", Port: 5432, User: "haihuizhu", Password: "admin", DBName: "top-fans", SSLMode: "disable", TimeZone: "Asia/Shanghai", } if err := database.Init(config); err != nil { t.Skipf("Skipping test: failed to connect to test database: %v", err) } db := database.GetDB() // 迁移资产相关表 if err := db.AutoMigrate(&models.Asset{}, &models.MintOrder{}, &models.AssetLike{}); err != nil { t.Logf("Warning: Failed to migrate asset tables (may already exist): %v", err) } // 测试开始前先清理一次,确保测试环境干净 cleanupTestDB(t, db) return db } // cleanupTestDB 清理测试数据 func cleanupTestDB(t *testing.T, db *gorm.DB) { // 清理测试数据(注意外键约束顺序) // 先清理关联表 db.Exec("DELETE FROM asset_likes WHERE user_id IN (SELECT id FROM users WHERE mobile LIKE '199%') OR asset_id IN (SELECT id FROM assets WHERE owner_uid IN (SELECT id FROM users WHERE mobile LIKE '199%'))") db.Exec("DELETE FROM assets WHERE owner_uid IN (SELECT id FROM users WHERE mobile LIKE '199%')") db.Exec("DELETE FROM mint_orders WHERE user_id IN (SELECT id FROM users WHERE mobile LIKE '199%')") db.Exec("DELETE FROM fan_profiles WHERE user_id IN (SELECT id FROM users WHERE mobile LIKE '199%')") db.Exec("DELETE FROM users WHERE mobile LIKE '199%'") // 清理测试明星(包括 test_asset_% 和 test_mint_%) db.Exec("DELETE FROM stars WHERE identity_id LIKE 'test_asset_%' OR identity_id LIKE 'test_mint_%'") } // createTestStar 创建测试用明星 func createTestStar(t *testing.T, db *gorm.DB, identityID string) *models.Star { // 先检查是否已存在 var existingStar models.Star if err := db.Where("identity_id = ?", identityID).First(&existingStar).Error; err == nil { // 已存在,返回现有的 return &existingStar } // 不存在,创建新的 star := &models.Star{ Name: "测试明星-" + identityID, IdentityID: identityID, IsActive: true, } if err := db.Create(star).Error; err != nil { t.Fatalf("Failed to create test star: %v", err) } return star } // createTestUser 创建测试用户 func createTestUser(t *testing.T, db *gorm.DB, mobile string) *models.User { user := &models.User{ Mobile: mobile, PasswordHash: "test_hash", IsActive: true, } if err := db.Create(user).Error; err != nil { t.Fatalf("Failed to create test user: %v", err) } return user } // createTestFanProfile 创建测试粉丝档案 func createTestFanProfile(t *testing.T, db *gorm.DB, userID, starID int64, nickname string) *models.FanProfile { profile := &models.FanProfile{ UserID: userID, StarID: starID, Nickname: nickname, Level: 1, } if err := db.Create(profile).Error; err != nil { t.Fatalf("Failed to create test fan profile: %v", err) } return profile } // createTestAsset 创建测试资产 func createTestAsset(t *testing.T, db *gorm.DB, ownerUID, starID int64, name string) *models.Asset { asset := &models.Asset{ OwnerUID: ownerUID, StarID: starID, Name: name, CoverURL: "https://example.com/cover.jpg", Status: models.AssetStatusPending, } if err := db.Create(asset).Error; err != nil { t.Fatalf("Failed to create test asset: %v", err) } return asset } // TestAssetRepository_Create 测试创建资产 func TestAssetRepository_Create(t *testing.T) { db := setupTestDB(t) defer cleanupTestDB(t, db) repo := NewAssetRepository(db) // 创建测试数据 star := createTestStar(t, db, "test_asset_create") user := createTestUser(t, db, "19900000001") createTestFanProfile(t, db, user.ID, star.StarID, "测试用户") // 测试创建资产 asset := &models.Asset{ OwnerUID: user.ID, StarID: star.StarID, Name: "测试资产", CoverURL: "https://example.com/cover.jpg", Status: models.AssetStatusPending, } err := repo.Create(asset) assert.NoError(t, err) assert.NotZero(t, asset.ID) assert.NotZero(t, asset.CreatedAt) assert.NotZero(t, asset.UpdatedAt) } // TestAssetRepository_GetByID 测试根据ID查询资产 func TestAssetRepository_GetByID(t *testing.T) { db := setupTestDB(t) defer cleanupTestDB(t, db) repo := NewAssetRepository(db) // 创建测试数据 star := createTestStar(t, db, "test_asset_getbyid") user := createTestUser(t, db, "19900000002") createTestFanProfile(t, db, user.ID, star.StarID, "测试用户") asset := createTestAsset(t, db, user.ID, star.StarID, "测试资产") // 测试查询存在的资产 result, err := repo.GetByID(asset.ID) assert.NoError(t, err) assert.NotNil(t, result) assert.Equal(t, asset.ID, result.ID) assert.Equal(t, asset.Name, result.Name) // 测试查询不存在的资产 result, err = repo.GetByID(999999) assert.Error(t, err) assert.Nil(t, result) } // TestAssetRepository_GetByIDAndOwner 测试根据ID和所有者查询资产 func TestAssetRepository_GetByIDAndOwner(t *testing.T) { db := setupTestDB(t) defer cleanupTestDB(t, db) repo := NewAssetRepository(db) // 创建测试数据 star := createTestStar(t, db, "test_asset_owner") user1 := createTestUser(t, db, "19900000003") user2 := createTestUser(t, db, "19900000004") createTestFanProfile(t, db, user1.ID, star.StarID, "用户1") createTestFanProfile(t, db, user2.ID, star.StarID, "用户2") asset := createTestAsset(t, db, user1.ID, star.StarID, "用户1的资产") // 测试正确的所有者 result, err := repo.GetByIDAndOwner(asset.ID, user1.ID, star.StarID) assert.NoError(t, err) assert.NotNil(t, result) assert.Equal(t, asset.ID, result.ID) // 测试错误的所有者 result, err = repo.GetByIDAndOwner(asset.ID, user2.ID, star.StarID) assert.Error(t, err) assert.Nil(t, result) // 测试错误的 StarID result, err = repo.GetByIDAndOwner(asset.ID, user1.ID, 999999) assert.Error(t, err) assert.Nil(t, result) } // TestAssetRepository_GetByOwner 测试查询用户的资产列表 func TestAssetRepository_GetByOwner(t *testing.T) { db := setupTestDB(t) defer cleanupTestDB(t, db) repo := NewAssetRepository(db) // 创建测试数据 star := createTestStar(t, db, "test_asset_list") user := createTestUser(t, db, "19900000005") createTestFanProfile(t, db, user.ID, star.StarID, "测试用户") // 创建多个资产 for i := 1; i <= 5; i++ { createTestAsset(t, db, user.ID, star.StarID, "资产"+string(rune('0'+i))) time.Sleep(10 * time.Millisecond) // 确保创建时间不同 } // 测试查询列表(分页) assets, err := repo.GetByOwner(user.ID, star.StarID, 3, 0) assert.NoError(t, err) assert.Len(t, assets, 3) // 测试第二页 assets, err = repo.GetByOwner(user.ID, star.StarID, 3, 3) assert.NoError(t, err) assert.Len(t, assets, 2) // 验证按创建时间倒序排列 allAssets, err := repo.GetByOwner(user.ID, star.StarID, 10, 0) assert.NoError(t, err) assert.Len(t, allAssets, 5) for i := 0; i < len(allAssets)-1; i++ { assert.GreaterOrEqual(t, allAssets[i].CreatedAt, allAssets[i+1].CreatedAt) } } // TestAssetRepository_CountByOwner 测试统计用户的资产数量 func TestAssetRepository_CountByOwner(t *testing.T) { db := setupTestDB(t) defer cleanupTestDB(t, db) repo := NewAssetRepository(db) // 创建测试数据 star := createTestStar(t, db, "test_asset_count") user := createTestUser(t, db, "19900000006") createTestFanProfile(t, db, user.ID, star.StarID, "测试用户") // 初始数量应该为0 count, err := repo.CountByOwner(user.ID, star.StarID) assert.NoError(t, err) assert.Equal(t, int64(0), count) // 创建3个资产 for i := 0; i < 3; i++ { createTestAsset(t, db, user.ID, star.StarID, "资产") } // 验证数量 count, err = repo.CountByOwner(user.ID, star.StarID) assert.NoError(t, err) assert.Equal(t, int64(3), count) } // TestAssetRepository_UpdateStatus 测试更新资产状态 func TestAssetRepository_UpdateStatus(t *testing.T) { db := setupTestDB(t) defer cleanupTestDB(t, db) repo := NewAssetRepository(db) // 创建测试数据 star := createTestStar(t, db, "test_asset_status") user := createTestUser(t, db, "19900000007") createTestFanProfile(t, db, user.ID, star.StarID, "测试用户") asset := createTestAsset(t, db, user.ID, star.StarID, "测试资产") // 验证初始状态 assert.Equal(t, int32(models.AssetStatusPending), asset.Status) // 更新状态 err := repo.UpdateStatus(asset.ID, models.AssetStatusActive) assert.NoError(t, err) // 验证状态已更新 updated, err := repo.GetByID(asset.ID) assert.NoError(t, err) assert.Equal(t, int32(models.AssetStatusActive), updated.Status) } // TestAssetRepository_UpdateBlockchainInfo 测试更新上链信息 func TestAssetRepository_UpdateBlockchainInfo(t *testing.T) { db := setupTestDB(t) defer cleanupTestDB(t, db) repo := NewAssetRepository(db) // 创建测试数据 star := createTestStar(t, db, "test_asset_mint") user := createTestUser(t, db, "19900000008") createTestFanProfile(t, db, user.ID, star.StarID, "测试用户") asset := createTestAsset(t, db, user.ID, star.StarID, "测试资产") // 更新上链信息 txHash := "0x1234567890abcdef" blockNumber := int64(12345) mintedAt := time.Now().UnixMilli() err := repo.UpdateBlockchainInfo(asset.ID, txHash, blockNumber, mintedAt) assert.NoError(t, err) // 验证信息已更新 updated, err := repo.GetByID(asset.ID) assert.NoError(t, err) assert.NotNil(t, updated.TxHash) assert.Equal(t, txHash, *updated.TxHash) assert.NotNil(t, updated.BlockNumber) assert.Equal(t, blockNumber, *updated.BlockNumber) assert.NotNil(t, updated.MintedAt) assert.Equal(t, mintedAt, *updated.MintedAt) } // TestAssetRepository_IncrementLikeCount 测试原子增加点赞数 func TestAssetRepository_IncrementLikeCount(t *testing.T) { db := setupTestDB(t) defer cleanupTestDB(t, db) repo := NewAssetRepository(db) // 创建测试数据 star := createTestStar(t, db, "test_asset_like") user := createTestUser(t, db, "19900000009") createTestFanProfile(t, db, user.ID, star.StarID, "测试用户") asset := createTestAsset(t, db, user.ID, star.StarID, "测试资产") // 验证初始点赞数 assert.Equal(t, int32(0), asset.LikeCount) // 增加点赞数 err := repo.IncrementLikeCount(asset.ID) assert.NoError(t, err) // 验证点赞数已增加 updated, err := repo.GetByID(asset.ID) assert.NoError(t, err) assert.Equal(t, int32(1), updated.LikeCount) // 再次增加 err = repo.IncrementLikeCount(asset.ID) assert.NoError(t, err) updated, err = repo.GetByID(asset.ID) assert.NoError(t, err) assert.Equal(t, int32(2), updated.LikeCount) } // TestAssetRepository_DecrementLikeCount 测试原子减少点赞数 func TestAssetRepository_DecrementLikeCount(t *testing.T) { db := setupTestDB(t) defer cleanupTestDB(t, db) repo := NewAssetRepository(db) // 创建测试数据 star := createTestStar(t, db, "test_asset_unlike") user := createTestUser(t, db, "19900000010") createTestFanProfile(t, db, user.ID, star.StarID, "测试用户") asset := createTestAsset(t, db, user.ID, star.StarID, "测试资产") // 先增加点赞数到3 db.Model(&models.Asset{}).Where("id = ?", asset.ID).Update("like_count", 3) // 减少点赞数 err := repo.DecrementLikeCount(asset.ID) assert.NoError(t, err) // 验证点赞数已减少 updated, err := repo.GetByID(asset.ID) assert.NoError(t, err) assert.Equal(t, int32(2), updated.LikeCount) // 测试不会减到负数 db.Model(&models.Asset{}).Where("id = ?", asset.ID).Update("like_count", 0) err = repo.DecrementLikeCount(asset.ID) assert.NoError(t, err) updated, err = repo.GetByID(asset.ID) assert.NoError(t, err) assert.Equal(t, int32(0), updated.LikeCount) // 不应该变成负数 } // TestAssetRepository_LikeCount 测试点赞数功能 func TestAssetRepository_LikeCount(t *testing.T) { db := setupTestDB(t) defer cleanupTestDB(t, db) repo := NewAssetRepository(db) // 创建测试数据 star := createTestStar(t, db, "test_asset_likecount") user := createTestUser(t, db, "19900000011") createTestFanProfile(t, db, user.ID, star.StarID, "测试用户") asset := createTestAsset(t, db, user.ID, star.StarID, "测试资产") // 验证初始点赞数为0 result, err := repo.GetByID(asset.ID) assert.NoError(t, err) assert.Equal(t, int32(0), result.LikeCount) // 增加点赞数 err = repo.IncrementLikeCount(asset.ID) assert.NoError(t, err) result, err = repo.GetByID(asset.ID) assert.NoError(t, err) assert.Equal(t, int32(1), result.LikeCount) // 减少点赞数 err = repo.DecrementLikeCount(asset.ID) assert.NoError(t, err) result, err = repo.GetByID(asset.ID) assert.NoError(t, err) assert.Equal(t, int32(0), result.LikeCount) }