fix im forwarding and message list regressions
This commit is contained in:
+136
-1
@@ -1625,6 +1625,21 @@ func TestServeIMTenantResolveRequiresAPIKey(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteIMRelationErrorUsesResourceNotFoundReason(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/im/test", nil)
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
writeIMRelationError(rec, req, types.ErrNotFound)
|
||||
|
||||
if rec.Code != http.StatusNotFound {
|
||||
t.Fatalf("status: got %d, want %d; body=%s", rec.Code, http.StatusNotFound, rec.Body.String())
|
||||
}
|
||||
resp := decodeIMHTTPResponse(t, rec)
|
||||
if resp.Reason != imReasonResourceNotFound {
|
||||
t.Fatalf("reason: got %q, want %q", resp.Reason, imReasonResourceNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServeIMTenantResolveRejectsMalformedTenantCode(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodPost, "/v1/im/tenants/resolve",
|
||||
bytes.NewBufferString(`{"tenant_code":"`+strings.Repeat("a", 65)+`"}`))
|
||||
@@ -5288,6 +5303,10 @@ func TestServeIMFavoriteBatchCreateNormalizesP2PUserTopic(t *testing.T) {
|
||||
if favorites.created == nil || favorites.created.SourceTopic != normalizedTopic {
|
||||
t.Fatalf("favorite source topic was not normalized: %#v", favorites.created)
|
||||
}
|
||||
snapshot := favorites.created.Snapshot.(map[string]any)
|
||||
if snapshot["from"] != peer.UserId() {
|
||||
t.Fatalf("favorite snapshot from must use full user_id: %#v", favorites.created.Snapshot)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServeIMFavoriteForwardSavesMessages(t *testing.T) {
|
||||
@@ -5679,6 +5698,100 @@ func TestServeIMMessageForwardKafkaAssignsSeqAndBroadcasts(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestServeIMMessageForwardKafkaPublishUnknownReturnsPending(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
expectIMBearer(t, ctrl, "token-1", 7, 42)
|
||||
|
||||
oldKafkaBus := kafkaBus
|
||||
oldPublisher := publishKafkaMessagePersist
|
||||
oldHub := globals.hub
|
||||
oldSubs := store.Subs
|
||||
oldTopics := store.Topics
|
||||
oldMessages := store.Messages
|
||||
subs := mock_store.NewMockSubsPersistenceInterface(ctrl)
|
||||
topics := mock_store.NewMockTopicsPersistenceInterface(ctrl)
|
||||
messages := mock_store.NewMockMessagesPersistenceInterface(ctrl)
|
||||
kafkaBus = &kafkaRuntime{}
|
||||
globals.hub = &Hub{routeSrv: make(chan *ServerComMessage, 3)}
|
||||
store.Subs = subs
|
||||
store.Topics = topics
|
||||
store.Messages = messages
|
||||
t.Cleanup(func() {
|
||||
kafkaBus = oldKafkaBus
|
||||
publishKafkaMessagePersist = oldPublisher
|
||||
globals.hub = oldHub
|
||||
store.Subs = oldSubs
|
||||
store.Topics = oldTopics
|
||||
store.Messages = oldMessages
|
||||
})
|
||||
|
||||
targetUser := types.Uid(99)
|
||||
targetTopic := types.Uid(42).P2PName(targetUser)
|
||||
subs.EXPECT().Get(types.TenantID(7), targetTopic, types.Uid(42), false).Return(&types.Subscription{
|
||||
TenantID: 7,
|
||||
User: types.Uid(42).String(),
|
||||
Topic: targetTopic,
|
||||
ModeWant: types.ModeCPublic,
|
||||
ModeGiven: types.ModeCPublic,
|
||||
}, nil)
|
||||
subs.EXPECT().Get(types.TenantID(7), "grpSource", types.Uid(42), false).Return(&types.Subscription{
|
||||
TenantID: 7,
|
||||
User: types.Uid(42).String(),
|
||||
Topic: "grpSource",
|
||||
ModeWant: types.ModeCPublic,
|
||||
ModeGiven: types.ModeCPublic,
|
||||
}, nil)
|
||||
topics.EXPECT().Get(types.TenantID(7), targetTopic).Return(&types.Topic{
|
||||
TenantID: 7,
|
||||
SeqId: 40,
|
||||
}, nil)
|
||||
messages.EXPECT().GetOne(types.TenantID(7), "grpSource", int64(14)).Return(&types.Message{
|
||||
TenantID: 7,
|
||||
Topic: "grpSource",
|
||||
SeqId: 14,
|
||||
From: targetUser.String(),
|
||||
Head: types.KVMap{"x-im-type": "text"},
|
||||
Content: map[string]any{"text": "hello"},
|
||||
ObjHeader: types.ObjHeader{
|
||||
CreatedAt: time.Date(2026, 8, 13, 9, 0, 0, 0, time.UTC),
|
||||
},
|
||||
}, nil)
|
||||
|
||||
publishKafkaMessagePersist = func(_ context.Context, _, _ string, _ kafkaEnvelope) error {
|
||||
return context.DeadlineExceeded
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/v1/im/messages/forward", bytes.NewBufferString(`{"mode":"separate","sources":[{"topic":"grpSource","seq":14}],"target_topics":["`+targetUser.UserId()+`"]}`))
|
||||
req.Header.Set("Authorization", "Bearer token-1")
|
||||
req.Header.Set("Idempotency-Key", "idem-forward-1")
|
||||
rec := httptest.NewRecorder()
|
||||
serveIMMessageForward(rec, req)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("forward status: got %d body=%s", rec.Code, rec.Body.String())
|
||||
}
|
||||
data := decodeIMHTTPResponse(t, rec).Data.(map[string]any)
|
||||
items := data["items"].([]any)
|
||||
if len(items) != 1 {
|
||||
t.Fatalf("unexpected forward response: %#v", data)
|
||||
}
|
||||
got := items[0].(map[string]any)
|
||||
if got["status"] != "pending" || got["reason"] != "MESSAGE_PERSIST_PENDING" || got["seq"].(float64) != 41 {
|
||||
t.Fatalf("unexpected pending item: %#v", got)
|
||||
}
|
||||
if got["client_mid"] != "fwd_"+stableIMEventID("forward", "idem-forward-1", "separate", targetTopic, "grpSource", int64(14)) {
|
||||
t.Fatalf("unexpected stable client mid: %#v", got)
|
||||
}
|
||||
if failed := data["failed"].([]any); len(failed) != 0 {
|
||||
t.Fatalf("unexpected failed items: %#v", failed)
|
||||
}
|
||||
select {
|
||||
case delivered := <-globals.hub.routeSrv:
|
||||
t.Fatalf("pending forward should not broadcast immediately: %#v", delivered)
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func TestServeIMMessageForwardBundleSavesForwardBundle(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
@@ -6024,6 +6137,15 @@ func TestServeIMMessageSearchContextAndMedia(t *testing.T) {
|
||||
if searchRec.Code != http.StatusOK {
|
||||
t.Fatalf("search status: got %d body=%s", searchRec.Code, searchRec.Body.String())
|
||||
}
|
||||
searchData := decodeIMHTTPResponse(t, searchRec).Data.(map[string]any)
|
||||
searchItems := searchData["items"].([]any)
|
||||
if len(searchItems) != 1 || searchItems[0].(map[string]any)["from"] != types.Uid(42).UserId() {
|
||||
t.Fatalf("search from must use full user_id: %#v", searchData)
|
||||
}
|
||||
searchSnapshot := searchItems[0].(map[string]any)["snapshot"].(map[string]any)
|
||||
if searchSnapshot["from"] != types.Uid(42).UserId() {
|
||||
t.Fatalf("search snapshot from must use full user_id: %#v", searchSnapshot)
|
||||
}
|
||||
|
||||
contextReq := httptest.NewRequest(http.MethodGet, "/v1/im/topics/grpAlpha/messages/3/context", nil)
|
||||
contextReq.Header.Set("Authorization", "Bearer token-1")
|
||||
@@ -6032,6 +6154,11 @@ func TestServeIMMessageSearchContextAndMedia(t *testing.T) {
|
||||
if contextRec.Code != http.StatusOK {
|
||||
t.Fatalf("context status: got %d body=%s", contextRec.Code, contextRec.Body.String())
|
||||
}
|
||||
contextData := decodeIMHTTPResponse(t, contextRec).Data.(map[string]any)
|
||||
contextItems := contextData["items"].([]any)
|
||||
if len(contextItems) != 1 || contextItems[0].(map[string]any)["from"] != types.Uid(42).UserId() {
|
||||
t.Fatalf("context from must use full user_id: %#v", contextData)
|
||||
}
|
||||
|
||||
mediaReq := httptest.NewRequest(http.MethodGet, "/v1/im/topics/grpAlpha/media?type=image", nil)
|
||||
mediaReq.Header.Set("Authorization", "Bearer token-1")
|
||||
@@ -6040,6 +6167,11 @@ func TestServeIMMessageSearchContextAndMedia(t *testing.T) {
|
||||
if mediaRec.Code != http.StatusOK {
|
||||
t.Fatalf("media status: got %d body=%s", mediaRec.Code, mediaRec.Body.String())
|
||||
}
|
||||
mediaData := decodeIMHTTPResponse(t, mediaRec).Data.(map[string]any)
|
||||
mediaItems := mediaData["items"].([]any)
|
||||
if len(mediaItems) != 1 || mediaItems[0].(map[string]any)["from"] != types.Uid(42).UserId() {
|
||||
t.Fatalf("media from must use full user_id: %#v", mediaData)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServeIMTopicReadActionsNormalizeP2PUserTopic(t *testing.T) {
|
||||
@@ -7318,7 +7450,7 @@ func TestServeIMUserProfile(t *testing.T) {
|
||||
users.EXPECT().Get(types.TenantID(7), peer).Return(&types.User{
|
||||
ObjHeader: types.ObjHeader{Id: peer.String()},
|
||||
TenantID: 7,
|
||||
Public: map[string]any{"display_name": "Alice", "avatar_file_id": "file-1", "signature": "hello"},
|
||||
Public: map[string]any{"display_name": "Alice", "nickname": "Alice", "avatar_file_id": "file-1", "signature": "hello"},
|
||||
}, nil)
|
||||
store.Users = users
|
||||
store.Relations = &testRelationsStore{context: &types.RelationshipContext{
|
||||
@@ -7353,6 +7485,9 @@ func TestServeIMUserProfile(t *testing.T) {
|
||||
if data["display_name"] != "老同事" || data["remark"] != "老同事" {
|
||||
t.Fatalf("profile display name should use friend remark: %#v", data)
|
||||
}
|
||||
if data["nickname"] != "Alice" {
|
||||
t.Fatalf("profile should include original nickname: %#v", data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServeIMUserQRCodes(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user