131 lines
4.4 KiB
Go

package firstrecharge
import (
"context"
"testing"
"google.golang.org/grpc"
walletv1 "hyapp.local/api/proto/wallet/v1"
"hyapp/pkg/appcode"
domain "hyapp/services/activity-service/internal/domain/firstrecharge"
"hyapp/services/activity-service/internal/testutil/mysqltest"
)
func TestFirstRechargeRewardConsumesRechargeFactWithMySQL(t *testing.T) {
repository := mysqltest.NewRepository(t)
wallet := &fakeWallet{}
svc := New(repository, wallet)
ctx := appcode.WithContext(context.Background(), "lalu")
config, err := svc.UpdateConfig(ctx, domain.Config{
Enabled: true,
UpdatedByAdminID: 90001,
Tiers: []domain.Tier{{
TierCode: "first_099",
TierName: "首冲 $0.99",
USDMinorAmount: 99,
GoogleProductID: "first_recharge_google_099",
DiscountPercent: 70,
ResourceGroupID: 88001,
Status: domain.TierStatusActive,
SortOrder: 1,
}, {
TierCode: "first_199",
TierName: "首冲 $1.99",
USDMinorAmount: 199,
GoogleProductID: "first_recharge_google_199",
DiscountPercent: 60,
ResourceGroupID: 88002,
Status: domain.TierStatusActive,
SortOrder: 2,
}},
})
if err != nil {
t.Fatalf("UpdateConfig failed: %v", err)
}
if len(config.Tiers) != 2 || config.Tiers[0].TierID <= 0 || config.Tiers[1].TierID <= 0 {
t.Fatalf("tier id was not generated: %+v", config.Tiers)
}
event := domain.RechargeEvent{
AppCode: "lalu",
EventID: "evt-recharge-1",
EventType: domain.RechargeEventWalletRecorded,
TransactionID: "wtx-recharge-1",
CommandID: "seller-transfer-1",
UserID: 42001,
RechargeCoinAmount: 0,
RechargeSequence: 2,
RechargeType: "google",
RechargeUSDMinor: 99,
GoogleProductID: "first_recharge_google_099",
OccurredAtMS: 1710000000000,
}
err = svc.ConsumeWalletRechargeEvent(ctx, event)
if err != nil {
t.Fatalf("ConsumeWalletRechargeEvent failed: %v", err)
}
claim, exists, err := repository.GetFirstRechargeRewardClaimByUser(ctx, 42001)
if err != nil {
t.Fatalf("GetFirstRechargeRewardClaimByUser failed: %v", err)
}
if !exists || claim.Status != domain.StatusGranted || claim.ResourceGroupID != 88001 || claim.GoogleProductID != "first_recharge_google_099" || claim.RechargeCoinAmount != 0 {
t.Fatalf("consume result mismatch: exists=%v claim=%+v", exists, claim)
}
if len(wallet.grants) != 1 || wallet.grants[0].GroupId != 88001 || wallet.grants[0].TargetUserId != 42001 {
t.Fatalf("wallet grant mismatch: %+v", wallet.grants)
}
status, err := svc.GetStatus(ctx, 42001)
if err != nil {
t.Fatalf("GetStatus failed: %v", err)
}
if status.Rewarded || len(status.Tiers) != 1 || status.Tiers[0].GoogleProductID != "first_recharge_google_199" || len(status.Claims) != 1 {
t.Fatalf("status should keep the unpurchased tier visible: %+v", status)
}
err = svc.ConsumeWalletRechargeEvent(ctx, event)
if err != nil {
t.Fatalf("retry ConsumeWalletRechargeEvent failed: %v", err)
}
if len(wallet.grants) != 1 {
t.Fatalf("retry should be idempotent, grants=%d", len(wallet.grants))
}
event.EventID = "evt-recharge-2"
event.TransactionID = "wtx-recharge-2"
event.CommandID = "seller-transfer-2"
event.RechargeUSDMinor = 199
event.GoogleProductID = "first_recharge_google_199"
err = svc.ConsumeWalletRechargeEvent(ctx, event)
if err != nil {
t.Fatalf("second recharge ConsumeWalletRechargeEvent failed: %v", err)
}
if len(wallet.grants) != 2 || wallet.grants[1].GroupId != 88002 {
t.Fatalf("second tier should be granted, grants=%+v", wallet.grants)
}
status, err = svc.GetStatus(ctx, 42001)
if err != nil {
t.Fatalf("GetStatus after all tiers failed: %v", err)
}
if !status.Rewarded || len(status.Tiers) != 0 || len(status.Claims) != 2 {
t.Fatalf("status should complete after all tiers are purchased: %+v", status)
}
}
type fakeWallet struct {
grants []*walletv1.GrantResourceGroupRequest
}
func (f *fakeWallet) GrantResourceGroup(_ context.Context, req *walletv1.GrantResourceGroupRequest, _ ...grpc.CallOption) (*walletv1.ResourceGrantResponse, error) {
f.grants = append(f.grants, req)
return &walletv1.ResourceGrantResponse{Grant: &walletv1.ResourceGrant{
AppCode: req.GetAppCode(),
GrantId: "grant-first-recharge",
CommandId: req.GetCommandId(),
TargetUserId: req.GetTargetUserId(),
GrantSource: req.GetGrantSource(),
Status: "granted",
}}, nil
}