package app import ( "context" "testing" "time" "hyapp/pkg/appcode" "hyapp/pkg/walletmq" messageservice "hyapp/services/activity-service/internal/service/message" "hyapp/services/activity-service/internal/testutil/mysqltest" ) func TestVIPRebateAvailableEventFromWalletMessage(t *testing.T) { body := vipRebateWalletMessage(t, "VipDailyCoinRebateAvailable", 42, `{ "rebate_id":"viprebate_fami_2027-01-15_42", "user_id":42, "task_day":"2027-01-15", "vip_level":9, "vip_name":"VIP9", "coin_amount":100000, "available_at_ms":1800000000000, "expires_at_ms":1800086400000, "config_version":7, "created_at_ms":1800000000100 }`) event, ok, err := vipRebateAvailableEventFromWalletMessage(body) if err != nil || !ok { t.Fatalf("parse VIP rebate event failed: ok=%v event=%+v err=%v", ok, event, err) } if event.AppCode != "fami" || event.EventID != "wev-vip-rebate" || event.RebateID != "viprebate_fami_2027-01-15_42" || event.UserID != 42 || event.TaskDay != "2027-01-15" || event.VIPLevel != 9 || event.CoinAmount != 100000 || event.ExpiresAtMS != 1800086400000 || event.ConfigVersion != 7 { t.Fatalf("VIP rebate event mismatch: %+v", event) } } func TestVIPRebateWalletEventSkipsOtherFactsAndRejectsCorruptTarget(t *testing.T) { other := vipRebateWalletMessage(t, "WalletRechargeRecorded", 42, `{}`) if event, ok, err := vipRebateAvailableEventFromWalletMessage(other); err != nil || ok || event.EventID != "" { t.Fatalf("non-target event should be acknowledged: ok=%v event=%+v err=%v", ok, event, err) } corrupt := vipRebateWalletMessage(t, "VipDailyCoinRebateAvailable", 42, `{"rebate_id":"r1","user_id":99}`) if _, ok, err := vipRebateAvailableEventFromWalletMessage(corrupt); err == nil || ok { t.Fatalf("corrupt target event must fail for MQ retry: ok=%v err=%v", ok, err) } } func TestVIPRebateWalletEventCreatesOneIdempotentSystemNotice(t *testing.T) { ctx := appcode.WithContext(context.Background(), "fami") repository := mysqltest.NewRepository(t) service := messageservice.New(messageservice.Config{NodeID: "activity-test"}, repository) service.SetClock(func() time.Time { return time.UnixMilli(1800000000200) }) event := vipRebateAvailableEvent{ AppCode: "fami", EventID: "wev-vip-rebate", RebateID: "viprebate_fami_2027-01-15_42", UserID: 42, TaskDay: "2027-01-15", VIPLevel: 9, VIPName: "VIP9", CoinAmount: 100000, AvailableAtMS: 1800000000000, ExpiresAtMS: 1800086400000, ConfigVersion: 7, CreatedAtMS: 1800000000100, } if err := createVIPRebateSystemNotice(ctx, service, event); err != nil { t.Fatalf("create system notice failed: %v", err) } if err := createVIPRebateSystemNotice(ctx, service, event); err != nil { t.Fatalf("duplicate system notice should be idempotent: %v", err) } items, _, err := service.ListInboxMessages(ctx, 42, "system", 20, "") if err != nil || len(items) != 1 { t.Fatalf("system inbox mismatch: items=%+v err=%v", items, err) } item := items[0] if item.Title != "VIP金币返现" || item.ActionType != "vip_coin_rebate_claim" || item.AggregateID != event.RebateID || item.ExpireAtMS != event.ExpiresAtMS || item.ActionParam == "" { t.Fatalf("VIP rebate notice mismatch: %+v", item) } } func vipRebateWalletMessage(t *testing.T, eventType string, userID int64, payload string) []byte { t.Helper() body, err := walletmq.EncodeWalletOutboxMessage(walletmq.WalletOutboxMessage{ AppCode: "fami", EventID: "wev-vip-rebate", EventType: eventType, TransactionID: "viprebate_fami_2027-01-15_42", CommandID: "vip-rebate-generate:fami:2027-01-15:42", UserID: userID, AssetType: "COIN", PayloadJSON: payload, OccurredAtMS: 1800000000100, }) if err != nil { t.Fatalf("encode wallet outbox message: %v", err) } return body }