2026-07-13 23:53:01 +08:00

143 lines
4.7 KiB
Go

package app
import (
"testing"
"time"
"hyapp/pkg/walletmq"
"hyapp/services/wallet-service/internal/config"
mysqlstorage "hyapp/services/wallet-service/internal/storage/mysql"
)
func TestWalletOutboxSingleRecordLeaseCoversPublishAndStateWrite(t *testing.T) {
if got := walletOutboxSingleRecordLease(3 * time.Second); got != 9*time.Second {
t.Fatalf("single-record lease = %s, want 9s", got)
}
if got := walletOutboxSingleRecordLease(0); got != 9*time.Second {
t.Fatalf("default single-record lease = %s, want 9s", got)
}
}
func TestWalletOutboxRocketMessageUsesConfiguredTagMode(t *testing.T) {
tests := []struct {
name string
mode config.WalletEventTagMode
wantTag string
}{
{
name: "missing config remains legacy",
mode: "",
wantTag: walletmq.TagWalletOutboxEvent,
},
{
name: "phase a publishes legacy",
mode: config.WalletEventTagModeLegacy,
wantTag: walletmq.TagWalletOutboxEvent,
},
{
name: "phase b publishes event type",
mode: config.WalletEventTagModeEventType,
wantTag: walletmq.EventTypeWalletGiftDebited,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
message, err := walletOutboxRocketMessage("hyapp_wallet_outbox", test.mode, validWalletOutboxRecord())
if err != nil {
t.Fatalf("walletOutboxRocketMessage failed: %v", err)
}
if message.Tag != test.wantTag {
t.Fatalf("message tag = %q, want %q", message.Tag, test.wantTag)
}
decoded, err := walletmq.DecodeWalletOutboxMessage(message.Body)
if err != nil {
t.Fatalf("DecodeWalletOutboxMessage failed: %v", err)
}
if decoded.EventType != walletmq.EventTypeWalletGiftDebited {
t.Fatalf("body event_type = %q", decoded.EventType)
}
if message.Topic != "hyapp_wallet_outbox" || len(message.Keys) != 3 {
t.Fatalf("message routing metadata mismatch: %+v", message)
}
})
}
}
func TestWalletOutboxRocketMessageRejectsUnsupportedTagMode(t *testing.T) {
_, err := walletOutboxRocketMessage("hyapp_wallet_outbox", config.WalletEventTagMode("all"), validWalletOutboxRecord())
if err == nil {
t.Fatal("walletOutboxRocketMessage unexpectedly accepted an unsupported tag mode")
}
}
func TestWalletOutboxRocketMessageRejectsUnsafeEventTypeTag(t *testing.T) {
record := validWalletOutboxRecord()
record.EventType = "WalletGiftDebited || *"
_, err := walletOutboxRocketMessage("hyapp_wallet_outbox", config.WalletEventTagModeLegacy, record)
if err == nil {
t.Fatal("walletOutboxRocketMessage unexpectedly accepted an unsafe event_type")
}
}
func TestWalletOutboxRocketMessageRestoresHistoricalResourceCorrelationID(t *testing.T) {
for _, eventType := range []string{
"GiftConfigChanged",
"ResourceChanged",
"ResourceGrantRevoked",
"ResourceGranted",
"ResourceGroupChanged",
"ResourceGroupGranted",
"UserResourceChanged",
"VipEffectiveChanged",
"VipTrialCardGranted",
} {
t.Run(eventType, func(t *testing.T) {
record := validWalletOutboxRecord()
record.EventID = "wev-historical-resource"
record.EventType = eventType
record.TransactionID = ""
record.AssetType = "RESOURCE"
message, err := walletOutboxRocketMessage("hyapp_wallet_outbox", config.WalletEventTagModeEventType, record)
if err != nil {
t.Fatalf("historical resource fact was rejected: %v", err)
}
decoded, err := walletmq.DecodeWalletOutboxMessage(message.Body)
if err != nil {
t.Fatalf("DecodeWalletOutboxMessage failed: %v", err)
}
if decoded.TransactionID != record.EventID || message.Keys[1] != record.EventID {
t.Fatalf("correlation id mismatch: decoded=%q keys=%v", decoded.TransactionID, message.Keys)
}
})
}
}
func TestWalletOutboxRocketMessageDoesNotInventFinancialTransactionID(t *testing.T) {
record := validWalletOutboxRecord()
record.TransactionID = ""
if _, err := walletOutboxRocketMessage("hyapp_wallet_outbox", config.WalletEventTagModeEventType, record); err == nil {
t.Fatal("financial fact without transaction_id must remain invalid")
}
record.EventType = "UnknownResourceEvent"
record.AssetType = "RESOURCE"
if _, err := walletOutboxRocketMessage("hyapp_wallet_outbox", config.WalletEventTagModeEventType, record); err == nil {
t.Fatal("unknown resource fact must not receive a synthetic transaction_id")
}
}
func validWalletOutboxRecord() mysqlstorage.WalletOutboxRecord {
return mysqlstorage.WalletOutboxRecord{
AppCode: "lalu",
EventID: "event-1",
EventType: " " + walletmq.EventTypeWalletGiftDebited + " ",
TransactionID: "transaction-1",
CommandID: "command-1",
UserID: 1001,
AssetType: "COIN",
AvailableDelta: -100,
PayloadJSON: `{"gift_id":"gift-1"}`,
CreatedAtMS: 1_700_000_000_000,
}
}