hyapp-server/services/activity-service/internal/app/vip_rebate_notice_test.go
2026-07-23 21:38:43 +08:00

112 lines
5.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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]
wantSummary := "Your daily VIP cashback of 100000 coins is ready. Claim it before it expires."
if item.Title != "VIP Coin Cashback" || item.Summary != wantSummary || item.Body != wantSummary || item.TemplateVersion != "v2" || item.AggregateID != event.RebateID || item.ExpireAtMS != event.ExpiresAtMS || len(item.Actions) != 1 {
t.Fatalf("VIP rebate notice mismatch: %+v", item)
}
action := item.Actions[0]
if action.Action != "claim" || action.Label != "Claim" || action.Style != "primary" || action.API.Method != "POST" || action.API.Path != "/api/v1/vip/coin-rebates/viprebate_fami_2027-01-15_42/claim" || action.API.Body == nil {
t.Fatalf("VIP rebate action mismatch: %+v", action)
}
// 当天部署前已经落库的 v1 中文消息仍在有效窗口内;列表读取必须立即投影为同一英文文案。
legacyActionParam := `{"coin_amount":88,"rebate_id":"viprebate_fami_2027-01-15_43"}`
if _, err := service.CreateSystemNotice(ctx, messageservice.NoticeCommand{
TargetUserID: 43, Producer: "wallet-service", ProducerEventID: "wev-vip-rebate-legacy",
TemplateID: "vip_daily_coin_rebate_available", TemplateVersion: "v1",
Title: "VIP金币返现", Summary: "尊敬的VIP用户平台今日返现88金币请及时领取。",
Body: "尊敬的VIP用户平台今日返现88金币请及时领取。", ActionType: "vip_coin_rebate_claim",
ActionParam: legacyActionParam, SentAtMS: event.AvailableAtMS, ExpireAtMS: event.ExpiresAtMS,
}); err != nil {
t.Fatalf("create legacy system notice failed: %v", err)
}
legacyItems, _, err := service.ListInboxMessages(ctx, 43, "system", 20, "")
if err != nil || len(legacyItems) != 1 || legacyItems[0].Title != "VIP Coin Cashback" || legacyItems[0].Summary != "Your daily VIP cashback of 88 coins is ready. Claim it before it expires." || legacyItems[0].Body != legacyItems[0].Summary || legacyItems[0].ActionType != "" || len(legacyItems[0].Actions) != 1 || legacyItems[0].Actions[0].Label != "Claim" {
t.Fatalf("legacy VIP rebate notice must return English copy: items=%+v err=%v", legacyItems, err)
}
}
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
}