feat: add redis cache and fanout support

This commit is contained in:
richjohnrambo
2026-08-25 20:54:03 +08:00
parent 07376c70e3
commit f900a08a01
68 changed files with 3949 additions and 5696 deletions
+61 -7
View File
@@ -861,11 +861,11 @@ func (s *testCallsStore) Create(_ types.TenantID, _ *types.CallRecord) error {
return nil
}
func (s *testCallsStore) Accept(_ types.TenantID, _ string, _ int, _ time.Time) error {
func (s *testCallsStore) Accept(_ types.TenantID, _ string, _ int64, _ time.Time) error {
return nil
}
func (s *testCallsStore) Finish(_ types.TenantID, _ string, _ int, _ string, _ int64, _ time.Time) error {
func (s *testCallsStore) Finish(_ types.TenantID, _ string, _ int64, _ string, _ int64, _ time.Time) error {
return nil
}
@@ -885,6 +885,7 @@ func (s *testConversationsStore) EnsureP2P(_ types.TenantID, uid, peer types.Uid
}
func (s *testConversationsStore) GetSettings(_ types.TenantID, _ types.Uid, topic string) (*types.ConversationSettings, error) {
s.topic = topic
if s.settings == nil {
now := time.Now().UTC().Round(time.Millisecond)
s.settings = &types.ConversationSettings{Topic: topic, Pinned: false, Muted: false, MessageRetention: "default", Version: 1, CreatedAt: now, UpdatedAt: now}
@@ -3770,7 +3771,7 @@ func TestServeIMFavoriteBatchCreateCountsExisting(t *testing.T) {
store.Messages = oldMessages
})
messages.EXPECT().GetOne(types.TenantID(7), "grpAlpha", 3).Return(&types.Message{
messages.EXPECT().GetOne(types.TenantID(7), "grpAlpha", int64(3)).Return(&types.Message{
TenantID: 7,
Topic: "grpAlpha",
SeqId: 3,
@@ -4307,7 +4308,7 @@ func TestServeIMMessageRecall(t *testing.T) {
ModeWant: types.ModeCFull,
ModeGiven: types.ModeCFull,
}, nil)
messages.EXPECT().GetOne(types.TenantID(7), "grpRecall", 9).Return(&types.Message{
messages.EXPECT().GetOne(types.TenantID(7), "grpRecall", int64(9)).Return(&types.Message{
TenantID: 7,
SeqId: 9,
Topic: "grpRecall",
@@ -4315,7 +4316,7 @@ func TestServeIMMessageRecall(t *testing.T) {
Version: 1,
ObjHeader: types.ObjHeader{CreatedAt: time.Now().Add(-time.Second)},
}, nil)
messages.EXPECT().Recall(types.TenantID(7), "grpRecall", 9, types.Uid(42), 1, gomock.Any()).Return(&types.Message{
messages.EXPECT().Recall(types.TenantID(7), "grpRecall", int64(9), types.Uid(42), 1, gomock.Any()).Return(&types.Message{
TenantID: 7,
SeqId: 9,
Topic: "grpRecall",
@@ -4363,7 +4364,7 @@ func TestServeIMMessageRevision(t *testing.T) {
ModeWant: types.ModeCFull,
ModeGiven: types.ModeCFull,
}, nil)
messages.EXPECT().GetOne(types.TenantID(7), "grpEdit", 3).Return(&types.Message{
messages.EXPECT().GetOne(types.TenantID(7), "grpEdit", int64(3)).Return(&types.Message{
TenantID: 7,
SeqId: 3,
Topic: "grpEdit",
@@ -4373,7 +4374,7 @@ func TestServeIMMessageRevision(t *testing.T) {
Content: "old",
ObjHeader: types.ObjHeader{CreatedAt: time.Now().Add(-time.Second)},
}, nil)
messages.EXPECT().Edit(types.TenantID(7), "grpEdit", 3, types.Uid(42), 1, gomock.Any(), "new", "", gomock.Any()).Return(&types.Message{
messages.EXPECT().Edit(types.TenantID(7), "grpEdit", int64(3), types.Uid(42), 1, gomock.Any(), "new", "", gomock.Any()).Return(&types.Message{
TenantID: 7,
SeqId: 3,
Topic: "grpEdit",
@@ -4431,6 +4432,31 @@ func TestServeIMConversationContext(t *testing.T) {
}
}
func TestServeIMConversationContextAcceptsUserTopic(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
expectIMBearer(t, ctrl, "token-1", 7, 1)
oldRelations := store.Relations
peer := types.Uid(2)
topic := types.Uid(1).P2PName(peer)
store.Relations = &testRelationsStore{}
defer func() { store.Relations = oldRelations }()
req := httptest.NewRequest(http.MethodGet, "/v1/im/conversations/"+peer.UserId()+"/context", nil)
req.Header.Set("Authorization", "Bearer token-1")
rec := httptest.NewRecorder()
serveIMConversationContext(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("status: got %d, want %d; body=%s", rec.Code, http.StatusOK, rec.Body.String())
}
data := decodeIMHTTPResponse(t, rec).Data.(map[string]any)
if data["topic"] != topic || data["peer_user_id"] != peer.UserId() {
t.Fatalf("unexpected context response: %#v", data)
}
}
func TestServeIMP2PConversation(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
@@ -4487,6 +4513,34 @@ func TestServeIMConversationSettings(t *testing.T) {
}
}
func TestServeIMConversationSettingsNormalizesUserTopic(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
expectIMBearer(t, ctrl, "token-1", 7, 1)
oldConversations := store.Conversations
peer := types.Uid(2)
topic := types.Uid(1).P2PName(peer)
conversations := &testConversationsStore{}
store.Conversations = conversations
defer func() { store.Conversations = oldConversations }()
req := httptest.NewRequest(http.MethodGet, "/v1/im/conversations/"+peer.UserId()+"/settings", nil)
req.Header.Set("Authorization", "Bearer token-1")
rec := httptest.NewRecorder()
serveIMConversationContext(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("status: got %d, want %d; body=%s", rec.Code, http.StatusOK, rec.Body.String())
}
if conversations.topic != topic {
t.Fatalf("settings topic: got %q, want %q", conversations.topic, topic)
}
data := decodeIMHTTPResponse(t, rec).Data.(map[string]any)
if data["topic"] != topic {
t.Fatalf("unexpected settings response: %#v", data)
}
}
func TestServeIMTopicDraftPutAndGet(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()