package rentergrowth import ( "testing" "hfb_sys/backend/internal/database" "hfb_sys/backend/internal/model" ) func TestLevelForPointsStartsFromNormal(t *testing.T) { cfg := DefaultConfig() cases := []struct { points int64 want string }{ {points: 0, want: DefaultLevelCode}, {points: 299, want: DefaultLevelCode}, {points: 300, want: "platinum"}, {points: 1000, want: "diamond"}, {points: 5000, want: "peak"}, } for _, tc := range cases { got := LevelForPoints(cfg, tc.points) if got.Code != tc.want { t.Fatalf("LevelForPoints(%d) = %q, want %q", tc.points, got.Code, tc.want) } } } func TestCalculateDiscountCentCapsAtPlatformFee(t *testing.T) { if got := CalculateDiscountCent(10000, 60, 9900); got != 60 { t.Fatalf("discount = %d, want capped platform fee 60", got) } if got := CalculateDiscountCent(10000, 1000, 9900); got != 100 { t.Fatalf("discount = %d, want 100", got) } if got := CalculateDiscountCent(10000, 1000, DefaultDiscountBps); got != 0 { t.Fatalf("normal discount = %d, want 0", got) } } func TestAwardOrderCompletedUpdatesUserAndIsIdempotent(t *testing.T) { db := database.NewTestDB() if err := db.AutoMigrate( &model.User{}, &model.RentalOrder{}, &model.SystemConfig{}, &model.RenterGrowthLedger{}, ); err != nil { t.Fatalf("migrate failed: %v", err) } user := model.User{ Phone: "13800008888", RenterGrowthPoints: 290, RenterGrowthLevel: DefaultLevelCode, } if err := db.Create(&user).Error; err != nil { t.Fatalf("create user failed: %v", err) } order := model.RentalOrder{ OrderNo: "RG202607260001", RenterID: user.ID, RentAmountCent: 2000, } if err := db.Create(&order).Error; err != nil { t.Fatalf("create order failed: %v", err) } if err := AwardOrderCompleted(db, &order, 2000, SourceOrderCompleted); err != nil { t.Fatalf("AwardOrderCompleted() error = %v", err) } if order.GrowthPointsAwarded != 20 || order.GrowthPointsAwardedAt == nil { t.Fatalf("order awarded = %d at %v, want 20 with time", order.GrowthPointsAwarded, order.GrowthPointsAwardedAt) } var savedUser model.User if err := db.First(&savedUser, user.ID).Error; err != nil { t.Fatalf("find user failed: %v", err) } if savedUser.RenterGrowthPoints != 310 || savedUser.RenterGrowthLevel != "platinum" { t.Fatalf("user growth = %d/%s, want 310/platinum", savedUser.RenterGrowthPoints, savedUser.RenterGrowthLevel) } freshOrder := order freshOrder.GrowthPointsAwarded = 0 freshOrder.GrowthPointsAwardedAt = nil if err := AwardOrderCompleted(db, &freshOrder, 2000, SourceOrderCompleted); err != nil { t.Fatalf("second AwardOrderCompleted() error = %v", err) } if err := db.First(&savedUser, user.ID).Error; err != nil { t.Fatalf("find user after second award failed: %v", err) } if savedUser.RenterGrowthPoints != 310 { t.Fatalf("user points after second award = %d, want unchanged 310", savedUser.RenterGrowthPoints) } var ledgerCount int64 if err := db.Model(&model.RenterGrowthLedger{}).Where("order_id = ?", order.ID).Count(&ledgerCount).Error; err != nil { t.Fatalf("count ledger failed: %v", err) } if ledgerCount != 1 { t.Fatalf("ledger count = %d, want 1", ledgerCount) } }