223 lines
7.8 KiB
Go
223 lines
7.8 KiB
Go
package repository
|
||
|
||
import (
|
||
"os"
|
||
"strings"
|
||
"testing"
|
||
"time"
|
||
|
||
"github.com/topfans/backend/pkg/database"
|
||
"github.com/topfans/backend/pkg/logger"
|
||
"github.com/topfans/backend/services/taskService/model"
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// TestMain 初始化 logger + DB(避免 logger.Logger nil + 测试可跳过连不上 DB 的环境)
|
||
func TestMain(m *testing.M) {
|
||
_ = logger.Init(logger.Config{
|
||
ServiceName: "task-service-repo-test",
|
||
Environment: "test",
|
||
LogLevel: "error",
|
||
})
|
||
os.Exit(m.Run())
|
||
}
|
||
|
||
// setupTestDB 连接 dev 库(与后端 .env.dev 配置一致:15432 / postgres / 123456)
|
||
// 注意:不要连 5432 / haihuizhu,那是另一个本机 PostgreSQL 实例(无 uk_like_bet_unique 约束)
|
||
func setupTestDB(t *testing.T) *gorm.DB {
|
||
config := database.Config{
|
||
Host: "localhost",
|
||
Port: 15432,
|
||
User: "postgres",
|
||
Password: "123456",
|
||
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(&model.LikeBetRevenueRecord{}); err != nil {
|
||
t.Logf("Warning: AutoMigrate like_bet_revenue_records: %v", err)
|
||
}
|
||
return db
|
||
}
|
||
|
||
// cleanupTestRecords 删除本测试产生的记录(用 user_id < 0 的 sentinel 避免污染线上数据)
|
||
func cleanupTestRecords(t *testing.T, db *gorm.DB) {
|
||
if err := db.Where("user_id < ?", 0).Delete(&model.LikeBetRevenueRecord{}).Error; err != nil {
|
||
t.Logf("Warning: cleanup like_bet_revenue_records: %v", err)
|
||
}
|
||
}
|
||
|
||
func TestLikeBetRevenueRepository_BatchCreate(t *testing.T) {
|
||
db := setupTestDB(t)
|
||
defer cleanupTestRecords(t, db)
|
||
repo := NewLikeBetRevenueRepository(db)
|
||
|
||
now := time.Now().UnixMilli()
|
||
records := []*model.LikeBetRevenueRecord{
|
||
{UserID: -1, StarID: 87, ExhibitionID: 90001, AssetID: 100, LikeID: 8001, BetOrder: 1, TotalLikes: 3, CrystalAmount: 3, CycleStartTime: now, CycleEndTime: now, Status: "claimable", CreatedAt: now},
|
||
{UserID: -2, StarID: 87, ExhibitionID: 90001, AssetID: 100, LikeID: 8002, BetOrder: 2, TotalLikes: 3, CrystalAmount: 2, CycleStartTime: now, CycleEndTime: now, Status: "claimable", CreatedAt: now},
|
||
{UserID: -3, StarID: 87, ExhibitionID: 90001, AssetID: 100, LikeID: 8003, BetOrder: 3, TotalLikes: 3, CrystalAmount: 1, CycleStartTime: now, CycleEndTime: now, Status: "claimable", CreatedAt: now},
|
||
}
|
||
|
||
if err := repo.BatchCreate(records); err != nil {
|
||
t.Fatalf("BatchCreate failed: %v", err)
|
||
}
|
||
|
||
// 验证
|
||
var got int64
|
||
if err := db.Model(&model.LikeBetRevenueRecord{}).Where("user_id IN ? AND exhibition_id = ?", []int64{-1, -2, -3}, 90001).Count(&got).Error; err != nil {
|
||
t.Fatalf("Count failed: %v", err)
|
||
}
|
||
if got != 3 {
|
||
t.Errorf("Expected 3 records, got %d", got)
|
||
}
|
||
}
|
||
|
||
func TestLikeBetRevenueRepository_ListByUser(t *testing.T) {
|
||
db := setupTestDB(t)
|
||
defer cleanupTestRecords(t, db)
|
||
repo := NewLikeBetRevenueRepository(db)
|
||
|
||
now := time.Now().UnixMilli()
|
||
// 给 user_id=-10, star_id=87 写 3 条
|
||
records := []*model.LikeBetRevenueRecord{
|
||
{UserID: -10, StarID: 87, ExhibitionID: 90010, AssetID: 110, LikeID: 8101, BetOrder: 1, TotalLikes: 5, CrystalAmount: 5, CycleStartTime: now, CycleEndTime: now, Status: "claimable", CreatedAt: now},
|
||
{UserID: -10, StarID: 87, ExhibitionID: 90011, AssetID: 111, LikeID: 8102, BetOrder: 2, TotalLikes: 5, CrystalAmount: 4, CycleStartTime: now, CycleEndTime: now, Status: "claimed", CreatedAt: now},
|
||
{UserID: -10, StarID: 87, ExhibitionID: 90012, AssetID: 112, LikeID: 8103, BetOrder: 3, TotalLikes: 5, CrystalAmount: 3, CycleStartTime: now, CycleEndTime: now, Status: "claimable", CreatedAt: now},
|
||
}
|
||
if err := repo.BatchCreate(records); err != nil {
|
||
t.Fatalf("BatchCreate failed: %v", err)
|
||
}
|
||
|
||
// 查全部
|
||
all, total, err := repo.ListByUser(-10, 87, "", 1, 10)
|
||
if err != nil {
|
||
t.Fatalf("ListByUser (all) failed: %v", err)
|
||
}
|
||
if total != 3 {
|
||
t.Errorf("Expected total=3, got %d", total)
|
||
}
|
||
if len(all) != 3 {
|
||
t.Errorf("Expected 3 items, got %d", len(all))
|
||
}
|
||
|
||
// 查 claimable
|
||
claimable, totalClaimable, err := repo.ListByUser(-10, 87, "claimable", 1, 10)
|
||
if err != nil {
|
||
t.Fatalf("ListByUser (claimable) failed: %v", err)
|
||
}
|
||
if totalClaimable != 2 {
|
||
t.Errorf("Expected total=2 claimable, got %d", totalClaimable)
|
||
}
|
||
if len(claimable) != 2 {
|
||
t.Errorf("Expected 2 claimable items, got %d", len(claimable))
|
||
}
|
||
|
||
// 跨 star_id 隔离:换 star_id 查不到
|
||
other, _, err := repo.ListByUser(-10, 999, "", 1, 10)
|
||
if err != nil {
|
||
t.Fatalf("ListByUser (other star) failed: %v", err)
|
||
}
|
||
if len(other) != 0 {
|
||
t.Errorf("Expected 0 items for other star_id, got %d", len(other))
|
||
}
|
||
}
|
||
|
||
func TestLikeBetRevenueRepository_ClaimRecord_OptimisticLock(t *testing.T) {
|
||
db := setupTestDB(t)
|
||
defer cleanupTestRecords(t, db)
|
||
repo := NewLikeBetRevenueRepository(db)
|
||
|
||
now := time.Now().UnixMilli()
|
||
rec := &model.LikeBetRevenueRecord{
|
||
UserID: -20, StarID: 87, ExhibitionID: 90020, AssetID: 120,
|
||
LikeID: 8201, BetOrder: 1, TotalLikes: 3, CrystalAmount: 3,
|
||
CycleStartTime: now, CycleEndTime: now, Status: "claimable", CreatedAt: now,
|
||
}
|
||
if err := repo.BatchCreate([]*model.LikeBetRevenueRecord{rec}); err != nil {
|
||
t.Fatalf("BatchCreate failed: %v", err)
|
||
}
|
||
|
||
// 第一次领取:成功
|
||
ok, err := repo.ClaimRecord(rec.ID, -20)
|
||
if err != nil {
|
||
t.Fatalf("ClaimRecord first time failed: %v", err)
|
||
}
|
||
if !ok {
|
||
t.Fatal("First claim should succeed")
|
||
}
|
||
|
||
// 第二次领取:乐观锁拦截(RowsAffected=0)
|
||
ok2, err2 := repo.ClaimRecord(rec.ID, -20)
|
||
if err2 != nil {
|
||
t.Fatalf("ClaimRecord second time returned error: %v", err2)
|
||
}
|
||
if ok2 {
|
||
t.Error("Second claim should be blocked by optimistic lock")
|
||
}
|
||
|
||
// 错误 user_id:不应成功
|
||
ok3, err3 := repo.ClaimRecord(rec.ID, -999)
|
||
if err3 != nil {
|
||
t.Fatalf("ClaimRecord wrong user returned error: %v", err3)
|
||
}
|
||
if ok3 {
|
||
t.Error("Wrong user claim should be blocked")
|
||
}
|
||
|
||
// 验证最终状态
|
||
got, err := repo.GetRecord(rec.ID)
|
||
if err != nil {
|
||
t.Fatalf("GetRecord failed: %v", err)
|
||
}
|
||
if got.Status != "claimed" {
|
||
t.Errorf("Expected status=claimed, got %s", got.Status)
|
||
}
|
||
}
|
||
|
||
func TestLikeBetRevenueRepository_UniqueConstraint(t *testing.T) {
|
||
// 验证 (exhibition_id, like_id) 唯一约束:
|
||
// - 第一次 BatchCreate 成功
|
||
// - 第二次同样 (exhibition_id, like_id) 的 BatchCreate 应报错
|
||
// - 报错之后 dev 库里只应有 1 条记录(因为 BatchCreate 内部用了事务,第一条失败则回滚)
|
||
//
|
||
// 注:这意味着 RecordLikeBetRevenue 内部需要 tolerate 这个 error(cleanup_worker 重跑会触发)。
|
||
db := setupTestDB(t)
|
||
defer cleanupTestRecords(t, db)
|
||
repo := NewLikeBetRevenueRepository(db)
|
||
|
||
now := time.Now().UnixMilli()
|
||
rec := &model.LikeBetRevenueRecord{
|
||
UserID: -30, StarID: 87, ExhibitionID: 90030, AssetID: 130,
|
||
LikeID: 8301, BetOrder: 1, TotalLikes: 1, CrystalAmount: 1,
|
||
CycleStartTime: now, CycleEndTime: now, Status: "claimable", CreatedAt: now,
|
||
}
|
||
if err := repo.BatchCreate([]*model.LikeBetRevenueRecord{rec}); err != nil {
|
||
t.Fatalf("First BatchCreate failed: %v", err)
|
||
}
|
||
|
||
// 重跑相同 (exhibition_id, like_id) —— 应当返回唯一约束错误
|
||
rec2 := &model.LikeBetRevenueRecord{
|
||
UserID: -30, StarID: 87, ExhibitionID: 90030, AssetID: 130,
|
||
LikeID: 8301, BetOrder: 1, TotalLikes: 1, CrystalAmount: 1,
|
||
CycleStartTime: now, CycleEndTime: now, Status: "claimable", CreatedAt: now,
|
||
}
|
||
err := repo.BatchCreate([]*model.LikeBetRevenueRecord{rec2})
|
||
if err == nil {
|
||
t.Fatal("Second BatchCreate should fail with unique constraint violation")
|
||
}
|
||
if !strings.Contains(err.Error(), "uk_like_bet_unique") {
|
||
t.Errorf("Expected uk_like_bet_unique error, got: %v", err)
|
||
}
|
||
|
||
// 验证:DB 里只能保留 1 条记录
|
||
var c int64
|
||
db.Model(&model.LikeBetRevenueRecord{}).Where("exhibition_id = ? AND like_id = ?", 90030, 8301).Count(&c)
|
||
if c != 1 {
|
||
t.Errorf("Expected 1 record, got %d (unique constraint broken?)", c)
|
||
}
|
||
} |