topfans/backend/services/taskService/repository/like_bet_repo_test.go
2026-06-29 14:47:32 +08:00

223 lines
7.8 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 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 这个 errorcleanup_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)
}
}