feat: support quoted message headers

This commit is contained in:
richjohnrambo
2026-09-23 17:46:51 +08:00
parent aa9f562e1e
commit 3e92021254
5 changed files with 235 additions and 1 deletions
+123
View File
@@ -302,6 +302,70 @@ func TestHandleBroadcastDataP2P(t *testing.T) {
}
}
func TestHandleBroadcastDataPreservesReplyHead(t *testing.T) {
numUsers := 2
helper := TopicTestHelper{}
helper.setUp(t, numUsers, types.TopicCatP2P, "p2p-test" /*attach=*/, true)
defer helper.tearDown()
reply := map[string]any{
"seq": int64(128),
"type": "text",
"text": "今天晚上一起吃饭吗?",
"from": helper.uids[1].UserId(),
}
helper.mm.EXPECT().Save(types.TenantID(1), gomock.Any(), gomock.Any(), gomock.Any()).
DoAndReturn(func(_ types.TenantID, msg *types.Message, _ []string, _ bool) (*types.MessageSaveResult, error) {
got, ok := msg.Head["x-im-reply"].(map[string]any)
if !ok {
t.Fatalf("stored x-im-reply: got %#v", msg.Head["x-im-reply"])
}
if got["seq"] != reply["seq"] || got["type"] != reply["type"] || got["text"] != reply["text"] || got["from"] != reply["from"] {
t.Fatalf("stored x-im-reply changed: got %#v want %#v", got, reply)
}
return &types.MessageSaveResult{SeqId: 1, MarkedReadBySender: true}, nil
})
from := helper.uids[0].UserId()
msg := &ClientComMessage{
AsUser: from,
Original: from,
Pub: &MsgClientPub{
Topic: "p2p",
Head: map[string]any{
"mime": "application/json",
"x-im-schema": 1,
"x-im-type": "text",
"x-im-client-mid": "mid-reply-1",
"x-im-reply": reply,
},
Content: map[string]any{"text": "好啊"},
NoEcho: true,
},
sess: helper.sessions[0],
}
helper.topic.handleClientMsg(msg)
helper.finish()
if errorMsgs, hasError := helper.hubMessages["__ERROR__"]; hasError {
t.Fatal(errorMsgs[0].Ctrl.Text)
}
if len(helper.results[1].messages) != 1 {
t.Fatalf("peer: expected 1 message, got %d", len(helper.results[1].messages))
}
data := helper.results[1].messages[0].(*ServerComMessage).Data
if data == nil {
t.Fatalf("peer response must have data")
}
got, ok := data.Head["x-im-reply"].(map[string]any)
if !ok {
t.Fatalf("broadcast x-im-reply: got %#v", data.Head["x-im-reply"])
}
if got["seq"] != reply["seq"] || got["type"] != reply["type"] || got["text"] != reply["text"] || got["from"] != reply["from"] {
t.Fatalf("broadcast x-im-reply changed: got %#v want %#v", got, reply)
}
}
func TestHandleBroadcastDataP2PRequiresRelationship(t *testing.T) {
numUsers := 2
helper := TopicTestHelper{}
@@ -1130,6 +1194,65 @@ func TestSaveMessageMaybeKafkaPublishesWithoutDBPersist(t *testing.T) {
}
}
func TestSaveMessageMaybeKafkaPreservesReplyHead(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
oldKafkaBus := kafkaBus
oldStore := store.Store
oldMessages := store.Messages
oldPublisher := publishKafkaMessagePersist
defer func() {
kafkaBus = oldKafkaBus
store.Store = oldStore
store.Messages = oldMessages
publishKafkaMessagePersist = oldPublisher
}()
kafkaBus = &kafkaRuntime{}
mockStore := mock_store.NewMockPersistentStorageInterface(ctrl)
mockStore.EXPECT().GetUid().Return(types.Uid(102))
store.Store = mockStore
store.Messages = mock_store.NewMockMessagesPersistenceInterface(ctrl)
var publishedPayload kafkaMessagePersistPayload
publishKafkaMessagePersist = func(_ context.Context, _, _ string, envelope kafkaEnvelope) error {
if err := json.Unmarshal(envelope.Payload, &publishedPayload); err != nil {
t.Fatalf("failed to unmarshal kafka payload: %v", err)
}
return nil
}
reply := map[string]any{
"seq": float64(12),
"type": "text",
"text": "quoted hello",
"from": types.Uid(2).UserId(),
}
msg := &types.Message{
TenantID: 1,
Topic: "grp-test",
SeqId: 7,
From: types.Uid(1).String(),
Head: types.KVMap{
"x-im-client-mid": "client-reply-1",
"x-im-type": "text",
"x-im-reply": reply,
},
Content: map[string]any{"text": "reply"},
}
if _, err := saveMessageMaybeKafka(context.Background(), 1, msg, nil, true); err != nil {
t.Fatalf("saveMessageMaybeKafka returned error: %v", err)
}
got, ok := publishedPayload.Message.Head["x-im-reply"].(map[string]any)
if !ok {
t.Fatalf("kafka payload x-im-reply: got %#v", publishedPayload.Message.Head["x-im-reply"])
}
if got["seq"] != reply["seq"] || got["type"] != reply["type"] || got["text"] != reply["text"] || got["from"] != reply["from"] {
t.Fatalf("kafka payload x-im-reply changed: got %#v want %#v", got, reply)
}
}
func TestShouldTryHotMessageFallback(t *testing.T) {
if !shouldTryHotMessageFallback(types.TopicCatGrp, 159, &types.QueryOpt{Since: 159, Limit: 1}) {
t.Fatal("expected fallback for latest group seq query")