251 lines
9.9 KiB
Go
251 lines
9.9 KiB
Go
package service
|
|
|
|
import (
|
|
"slices"
|
|
"testing"
|
|
"time"
|
|
|
|
"google.golang.org/protobuf/proto"
|
|
roomeventsv1 "hyapp.local/api/proto/events/room/v1"
|
|
roomv1 "hyapp.local/api/proto/room/v1"
|
|
walletv1 "hyapp.local/api/proto/wallet/v1"
|
|
"hyapp/services/room-service/internal/room/command"
|
|
"hyapp/services/room-service/internal/room/state"
|
|
)
|
|
|
|
func TestBuildRoomGiftSentRecordsPreservesDurableFactFields(t *testing.T) {
|
|
now := time.Date(2026, 6, 27, 10, 0, 0, 0, time.UTC)
|
|
cmd := command.SendGift{
|
|
Base: command.Base{
|
|
CommandID: "cmd-gift-fact",
|
|
ActorID: 101,
|
|
Room: "room-gift-fact",
|
|
},
|
|
TargetType: "user",
|
|
TargetUserID: 202,
|
|
TargetUserIDs: []int64{202},
|
|
GiftID: "gift-rose",
|
|
PoolID: "pool-lucky",
|
|
GiftCount: 3,
|
|
SenderCountryID: 86,
|
|
SenderRegionID: 9001,
|
|
SenderDisplayProfile: command.GiftDisplayProfile{
|
|
UserID: 101,
|
|
Username: " sender ",
|
|
Avatar: " avatar-sender ",
|
|
DisplayUserID: "S101",
|
|
PrettyDisplayUserID: "PS101",
|
|
},
|
|
TargetDisplayProfiles: []command.GiftDisplayProfile{{
|
|
UserID: 202,
|
|
Username: " receiver ",
|
|
Avatar: " avatar-receiver ",
|
|
DisplayUserID: "R202",
|
|
PrettyDisplayUserID: "PR202",
|
|
}},
|
|
RobotWalletGift: true,
|
|
SyntheticLuckyGift: true,
|
|
}
|
|
billing := &walletv1.DebitGiftResponse{
|
|
BillingReceiptId: "receipt-target-202",
|
|
CoinSpent: 300,
|
|
HeatValue: 450,
|
|
GiftTypeCode: "lucky",
|
|
CpRelationType: "couple",
|
|
GiftName: "Rose",
|
|
GiftIconUrl: "https://asset/icon.png",
|
|
GiftAnimationUrl: "https://asset/anim.svga",
|
|
GiftEffectTypes: []string{"room_banner", "global_broadcast"},
|
|
}
|
|
|
|
records, err := buildRoomGiftSentRecords("room-gift-fact", 7, now, cmd, RoomMeta{VisibleRegionID: 8008}, []giftTargetBilling{{
|
|
TargetUserID: 202,
|
|
CommandID: "cmd-gift-fact:202",
|
|
Billing: billing,
|
|
}}, map[int64]int64{202: 999})
|
|
if err != nil {
|
|
t.Fatalf("build RoomGiftSent record failed: %v", err)
|
|
}
|
|
if len(records) != 1 {
|
|
t.Fatalf("RoomGiftSent must be split per target, got %d records", len(records))
|
|
}
|
|
record := records[0]
|
|
if record.EventType != "RoomGiftSent" || record.RoomID != "room-gift-fact" || record.Envelope.GetRoomVersion() != 7 {
|
|
t.Fatalf("outbox envelope mismatch: %+v", record)
|
|
}
|
|
|
|
var event roomeventsv1.RoomGiftSent
|
|
if err := proto.Unmarshal(record.Envelope.GetBody(), &event); err != nil {
|
|
t.Fatalf("unmarshal RoomGiftSent failed: %v", err)
|
|
}
|
|
if event.GetSenderUserId() != 101 || event.GetTargetUserId() != 202 || event.GetGiftId() != "gift-rose" || event.GetGiftCount() != 3 {
|
|
t.Fatalf("basic gift fields mismatch: %+v", &event)
|
|
}
|
|
if event.GetBillingReceiptId() != "receipt-target-202" || event.GetCoinSpent() != 300 || event.GetGiftValue() != 450 || event.GetTargetGiftValue() != 999 {
|
|
t.Fatalf("billing fields mismatch: %+v", &event)
|
|
}
|
|
if event.GetVisibleRegionId() != 8008 || event.GetCountryId() != 86 || event.GetRegionId() != 9001 || event.GetCommandId() != "cmd-gift-fact:202" {
|
|
t.Fatalf("routing/stat fields mismatch: %+v", &event)
|
|
}
|
|
if event.GetGiftTypeCode() != "lucky" || event.GetCpRelationType() != "couple" || event.GetGiftName() != "Rose" {
|
|
t.Fatalf("gift config fields mismatch: %+v", &event)
|
|
}
|
|
if event.GetDisplayMode() != "" {
|
|
t.Fatalf("single-target durable fact must keep legacy display mode empty: %+v", &event)
|
|
}
|
|
if !slices.Equal(event.GetGiftEffectTypes(), []string{"room_banner", "global_broadcast"}) {
|
|
t.Fatalf("gift effect types mismatch: %+v", event.GetGiftEffectTypes())
|
|
}
|
|
if !event.GetIsRobotGift() || !event.GetSyntheticLuckyGift() || !event.GetRealRoomRobotGift() {
|
|
t.Fatalf("robot/lucky flags mismatch: %+v", &event)
|
|
}
|
|
if event.GetSenderName() != "sender" || event.GetReceiverNickname() != "receiver" || event.GetReceiverAvatar() != "avatar-receiver" {
|
|
t.Fatalf("display profile fields mismatch: %+v", &event)
|
|
}
|
|
}
|
|
|
|
func TestBuildRoomGiftSentRecordsKeepsBatchDisplayFactsSplitPerTarget(t *testing.T) {
|
|
now := time.Date(2026, 6, 27, 10, 30, 0, 0, time.UTC)
|
|
cmd := command.SendGift{
|
|
Base: command.Base{
|
|
CommandID: "cmd-gift-batch-facts",
|
|
ActorID: 101,
|
|
Room: "room-gift-batch-facts",
|
|
},
|
|
TargetType: "user",
|
|
TargetUserID: 202,
|
|
TargetUserIDs: []int64{202, 203},
|
|
GiftID: "gift-clover",
|
|
GiftCount: 1,
|
|
DisplayMode: "batch",
|
|
}
|
|
billing := &walletv1.DebitGiftResponse{
|
|
BillingReceiptId: "receipt-target",
|
|
CoinSpent: 100,
|
|
HeatValue: 100,
|
|
}
|
|
|
|
records, err := buildRoomGiftSentRecords("room-gift-batch-facts", 9, now, cmd, RoomMeta{}, []giftTargetBilling{
|
|
{TargetUserID: 202, CommandID: "cmd-gift-batch-facts:202", Billing: billing},
|
|
{TargetUserID: 203, CommandID: "cmd-gift-batch-facts:203", Billing: billing},
|
|
}, nil)
|
|
if err != nil {
|
|
t.Fatalf("build batch RoomGiftSent records failed: %v", err)
|
|
}
|
|
if len(records) != 2 {
|
|
t.Fatalf("batch gift facts must stay split per target, got %d records", len(records))
|
|
}
|
|
for _, record := range records {
|
|
var event roomeventsv1.RoomGiftSent
|
|
if err := proto.Unmarshal(record.Envelope.GetBody(), &event); err != nil {
|
|
t.Fatalf("unmarshal batch RoomGiftSent failed: %v", err)
|
|
}
|
|
if event.GetTargetUserId() <= 0 || event.GetCommandId() == "" {
|
|
t.Fatalf("batch RoomGiftSent fact must keep per-target fields: %+v", &event)
|
|
}
|
|
if event.GetDisplayMode() != "batch" {
|
|
t.Fatalf("batch RoomGiftSent fact must be marked for display suppression: %+v", &event)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRoomGiftBatchSentEventFromDisplayPreservesTargetsAndTotals(t *testing.T) {
|
|
cmd := command.SendGift{
|
|
Base: command.Base{
|
|
CommandID: "cmd-batch-fact",
|
|
ActorID: 301,
|
|
Room: "room-batch-fact",
|
|
},
|
|
TargetUserID: 401,
|
|
TargetUserIDs: []int64{401, 402},
|
|
GiftID: "gift-star",
|
|
PoolID: "pool-batch",
|
|
GiftCount: 2,
|
|
DisplayMode: "batch",
|
|
SenderDisplayProfile: command.GiftDisplayProfile{
|
|
UserID: 301,
|
|
Username: "batch sender",
|
|
Avatar: "sender-avatar",
|
|
DisplayUserID: "S301",
|
|
PrettyDisplayUserID: "PS301",
|
|
},
|
|
TargetDisplayProfiles: []command.GiftDisplayProfile{
|
|
{UserID: 401, Username: "target-one", Avatar: "avatar-one", DisplayUserID: "T401", PrettyDisplayUserID: "PT401"},
|
|
{UserID: 402, Username: "target-two", Avatar: "avatar-two", DisplayUserID: "T402", PrettyDisplayUserID: "PT402"},
|
|
},
|
|
}
|
|
billing := &walletv1.DebitGiftResponse{
|
|
BillingReceiptId: "receipt-batch-root",
|
|
HeatValue: 330,
|
|
CoinSpent: 220,
|
|
GiftTypeCode: "normal",
|
|
CpRelationType: "brother",
|
|
GiftName: "Star",
|
|
GiftIconUrl: "icon-star",
|
|
GiftAnimationUrl: "anim-star",
|
|
GiftEffectTypes: []string{"room_banner"},
|
|
}
|
|
targetBillings := []giftTargetBilling{
|
|
{TargetUserID: 401, CommandID: "cmd-batch-fact:401", Billing: &walletv1.DebitGiftResponse{BillingReceiptId: "receipt-401", HeatValue: 110, CoinSpent: 70}},
|
|
{TargetUserID: 402, CommandID: "cmd-batch-fact:402", Billing: &walletv1.DebitGiftResponse{BillingReceiptId: "receipt-402", HeatValue: 220, CoinSpent: 150}},
|
|
}
|
|
luckyGifts := []*roomv1.LuckyGiftDrawResult{{
|
|
Enabled: true,
|
|
DrawId: "draw-402",
|
|
CommandId: "cmd-batch-fact:402",
|
|
PoolId: "pool-batch",
|
|
GiftId: "gift-star",
|
|
MultiplierPpm: 2000000,
|
|
EffectiveRewardCoins: 50,
|
|
TargetUserId: 402,
|
|
}}
|
|
|
|
display := buildSendGiftBatchDisplay(cmd, billing, targetBillings, map[int64]int64{401: 1001, 402: 1002}, luckyGifts, 8888)
|
|
event := roomGiftBatchSentEventFromDisplay(display, 6006)
|
|
if event.GetSenderUserId() != 301 || event.GetGiftId() != "gift-star" || event.GetGiftCount() != 2 || event.GetCommandId() != "cmd-batch-fact" {
|
|
t.Fatalf("batch root fields mismatch: %+v", event)
|
|
}
|
|
if event.GetTotalGiftValue() != 330 || event.GetTotalCoinSpent() != 220 || event.GetBillingReceiptId() != "receipt-batch-root" || event.GetRoomHeat() != 8888 {
|
|
t.Fatalf("batch total fields mismatch: %+v", event)
|
|
}
|
|
if event.GetVisibleRegionId() != 6006 || !slices.Equal(event.GetTargetUserIds(), []int64{401, 402}) || !slices.Equal(event.GetGiftEffectTypes(), []string{"room_banner"}) {
|
|
t.Fatalf("batch routing/effect fields mismatch: %+v", event)
|
|
}
|
|
if len(event.GetTargets()) != 2 {
|
|
t.Fatalf("batch targets mismatch: %+v", event.GetTargets())
|
|
}
|
|
if target := event.GetTargets()[0]; target.GetTargetUserId() != 401 || target.GetGiftValue() != 110 || target.GetTargetGiftValue() != 1001 || target.GetBillingReceiptId() != "receipt-401" {
|
|
t.Fatalf("first batch target mismatch: %+v", target)
|
|
}
|
|
if target := event.GetTargets()[1]; target.GetTargetUserId() != 402 || target.GetGiftValue() != 220 || target.GetTargetGiftValue() != 1002 || target.GetLuckyGift().GetDrawId() != "draw-402" {
|
|
t.Fatalf("second batch target mismatch: %+v", target)
|
|
}
|
|
}
|
|
|
|
func TestRoomHeatAndRankChangedRecordsPreserveSnapshotFields(t *testing.T) {
|
|
now := time.Date(2026, 6, 27, 11, 0, 0, 0, time.UTC)
|
|
heatRecord, err := buildRoomHeatChangedRecord("room-heat-rank", 12, now, 77, 1007)
|
|
if err != nil {
|
|
t.Fatalf("build RoomHeatChanged failed: %v", err)
|
|
}
|
|
var heat roomeventsv1.RoomHeatChanged
|
|
if err := proto.Unmarshal(heatRecord.Envelope.GetBody(), &heat); err != nil {
|
|
t.Fatalf("unmarshal RoomHeatChanged failed: %v", err)
|
|
}
|
|
if heatRecord.EventType != "RoomHeatChanged" || heat.GetDelta() != 77 || heat.GetCurrentHeat() != 1007 {
|
|
t.Fatalf("heat event mismatch: record=%+v event=%+v", heatRecord, &heat)
|
|
}
|
|
|
|
rankRecord, err := buildRoomRankChangedRecord("room-heat-rank", 13, now, state.RankItem{UserID: 501, Score: 700, GiftValue: 700})
|
|
if err != nil {
|
|
t.Fatalf("build RoomRankChanged failed: %v", err)
|
|
}
|
|
var rank roomeventsv1.RoomRankChanged
|
|
if err := proto.Unmarshal(rankRecord.Envelope.GetBody(), &rank); err != nil {
|
|
t.Fatalf("unmarshal RoomRankChanged failed: %v", err)
|
|
}
|
|
if rankRecord.EventType != "RoomRankChanged" || rank.GetUserId() != 501 || rank.GetScore() != 700 || rank.GetGiftValue() != 700 {
|
|
t.Fatalf("rank event mismatch: record=%+v event=%+v", rankRecord, &rank)
|
|
}
|
|
}
|