2026-05-12 09:53:20 +08:00

106 lines
4.5 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 broadcast
import (
"context"
"encoding/json"
"testing"
"time"
roomeventsv1 "hyapp.local/api/proto/events/room/v1"
"hyapp/pkg/appcode"
"hyapp/pkg/tencentim"
broadcastdomain "hyapp/services/activity-service/internal/domain/broadcast"
"hyapp/services/activity-service/internal/testutil/mysqltest"
)
func TestBroadcastServiceUsesRealMySQLOutbox(t *testing.T) {
// 这个测试使用生产 initdb 创建隔离 MySQL schema验证真实 JSON 列、主键幂等、claim 锁和 delivered 更新。
repository := mysqltest.NewRepository(t)
publisher := &fakePublisher{}
service := New(Config{NodeID: "node-real", SuperGiftMinValue: 100}, repository.Repository, publisher, nil)
service.SetClock(func() time.Time { return time.UnixMilli(1_700_000_123_456) })
ctx := appcode.WithContext(context.Background(), "lalu")
envelope := mustGiftEnvelope(t, &roomeventsv1.RoomGiftSent{
SenderUserId: 42,
TargetUserId: 43,
GiftId: "super_rocket",
GiftCount: 3,
GiftValue: 300000,
VisibleRegionId: 210,
CommandId: "cmd-real-send-gift-1",
})
created, err := service.HandleRoomEvent(ctx, envelope)
if err != nil {
t.Fatalf("HandleRoomEvent with real mysql failed: %v", err)
}
if !created.BroadcastCreated || created.BroadcastEventID != "gift_broadcast:room-1001:cmd-real-send-gift-1" {
t.Fatalf("unexpected first consume result: %+v", created)
}
t.Logf("real mysql enqueue: room_id=%s region_id=%d gift_value=%d broadcast_event_id=%s", envelope.GetRoomId(), int64(210), int64(300000), created.BroadcastEventID)
// 同一个 room outbox 事实再次消费必须命中 MySQL 主键幂等,不能产生第二条播报。
again, err := service.HandleRoomEvent(ctx, envelope)
if err != nil {
t.Fatalf("second HandleRoomEvent with real mysql failed: %v", err)
}
if again.BroadcastCreated {
t.Fatalf("duplicate room event should not create another broadcast: %+v", again)
}
record, exists, err := repository.Repository.GetBroadcastOutbox(ctx, created.BroadcastEventID)
if err != nil {
t.Fatalf("read real mysql outbox failed: %v", err)
}
if !exists || record.Status != broadcastdomain.StatusPending || record.GroupID != "hy_lalu_bc_r_210" {
t.Fatalf("unexpected pending outbox record: exists=%v record=%+v", exists, record)
}
t.Logf("real mysql pending record: event_id=%s group_id=%s status=%s", record.EventID, record.GroupID, record.Status)
assertJSONField(t, record.PayloadJSON, "room_id", "room-1001")
assertJSONField(t, record.PayloadJSON, "gift_id", "super_rocket")
assertJSONField(t, record.PayloadJSON, "scope", broadcastdomain.ScopeRegion)
result, err := service.ProcessPendingBroadcasts(ctx, WorkerOptions{WorkerID: "worker-real", BatchSize: 1, PollInterval: time.Second})
if err != nil {
t.Fatalf("ProcessPendingBroadcasts with real mysql failed: %v", err)
}
if result.ClaimedCount != 1 || result.SuccessCount != 1 || result.FailureCount != 0 {
t.Fatalf("unexpected process result: %+v", result)
}
if len(publisher.messages) != 1 {
t.Fatalf("expected one Tencent message, got %d", len(publisher.messages))
}
assertPublishedMessage(t, publisher.messages[0], "hy_lalu_bc_r_210", created.BroadcastEventID)
delivered, exists, err := repository.Repository.GetBroadcastOutbox(ctx, created.BroadcastEventID)
if err != nil {
t.Fatalf("read delivered real mysql outbox failed: %v", err)
}
if !exists || delivered.Status != broadcastdomain.StatusDelivered || delivered.LockedBy != "" || delivered.LockedUntilMS != 0 {
t.Fatalf("unexpected delivered outbox record: exists=%v record=%+v", exists, delivered)
}
t.Logf("real mysql delivered record: event_id=%s group_id=%s status=%s", delivered.EventID, delivered.GroupID, delivered.Status)
}
func assertJSONField(t *testing.T, payloadJSON string, key string, want any) {
t.Helper()
var payload map[string]any
if err := json.Unmarshal([]byte(payloadJSON), &payload); err != nil {
t.Fatalf("payload is not valid json: %v\npayload=%s", err, payloadJSON)
}
if got := payload[key]; got != want {
t.Fatalf("json field %s mismatch: got=%v want=%v payload=%+v", key, got, want, payload)
}
}
func assertPublishedMessage(t *testing.T, message tencentim.CustomGroupMessage, groupID string, eventID string) {
t.Helper()
if message.GroupID != groupID || message.EventID != eventID || message.Desc != broadcastdomain.TypeSuperGift || message.Ext != "im_broadcast" {
t.Fatalf("unexpected Tencent custom message: %+v", message)
}
assertJSONField(t, string(message.PayloadJSON), "event_id", eventID)
}