package app import ( "encoding/json" "testing" "hyapp/pkg/walletmq" ) func TestUserLeaderboardGiftEventFromWalletMessage(t *testing.T) { body := mustWalletOutboxMessage(t, "WalletGiftDebited", map[string]any{ "sender_user_id": 10001, "target_user_id": 20002, "heat_value": 900, "coin_spent": 1200, "charge_amount": 1200, "gift_count": 3, }) event, ok, err := userLeaderboardGiftEventFromWalletMessage(body) if err != nil || !ok { t.Fatalf("gift event should parse: ok=%v err=%v", ok, err) } if event.AppCode != "hyapp_room" || event.EventID != "evt-1" || event.SenderUserID != 10001 || event.TargetUserID != 20002 { t.Fatalf("event identity mismatch: %+v", event) } if event.GiftValue != 900 || event.GiftCount != 3 || event.OccurredAtMS != 1782461920000 { t.Fatalf("event aggregate fields mismatch: %+v", event) } } func TestUserLeaderboardGiftEventSkipsDirectGift(t *testing.T) { body := mustWalletOutboxMessage(t, "WalletGiftDebited", map[string]any{ "sender_user_id": 10001, "target_user_id": 20002, "heat_value": 900, "gift_count": 1, "direct_gift": true, }) _, ok, err := userLeaderboardGiftEventFromWalletMessage(body) if err != nil { t.Fatalf("direct gift parse failed: %v", err) } if ok { t.Fatal("direct gift must keep the old gift_debit SQL scope and skip user leaderboard") } } func TestUserLeaderboardGiftEventKeepsSQLCoalesceSemantics(t *testing.T) { fallbackBody := mustWalletOutboxMessage(t, "WalletGiftDebited", map[string]any{ "sender_user_id": 10001, "target_user_id": 20002, "coin_spent": 1200, "charge_amount": 1500, "gift_count": 1, }) fallback, ok, err := userLeaderboardGiftEventFromWalletMessage(fallbackBody) if err != nil || !ok { t.Fatalf("fallback gift event should parse: ok=%v err=%v", ok, err) } if fallback.GiftValue != 1200 { t.Fatalf("missing heat_value should fall back to coin_spent, got %+v", fallback) } zeroHeatBody := mustWalletOutboxMessage(t, "WalletGiftDebited", map[string]any{ "sender_user_id": 10001, "target_user_id": 20002, "heat_value": 0, "coin_spent": 1200, "charge_amount": 1500, "gift_count": 1, }) _, ok, err = userLeaderboardGiftEventFromWalletMessage(zeroHeatBody) if err != nil { t.Fatalf("zero heat gift event parse failed: %v", err) } if ok { t.Fatal("heat_value=0 should not fall back to coin_spent because old SQL COALESCE treats zero as present") } } func mustWalletOutboxMessage(t *testing.T, eventType string, payload map[string]any) []byte { t.Helper() payloadBytes, err := json.Marshal(payload) if err != nil { t.Fatalf("marshal payload failed: %v", err) } body, err := walletmq.EncodeWalletOutboxMessage(walletmq.WalletOutboxMessage{ AppCode: "hyapp_room", EventID: "evt-1", EventType: eventType, TransactionID: "tx-1", CommandID: "cmd-1", UserID: 10001, AssetType: "COIN", AvailableDelta: -1200, PayloadJSON: string(payloadBytes), OccurredAtMS: 1782461920000, }) if err != nil { t.Fatalf("encode wallet outbox failed: %v", err) } return body }