owpengram-server/internal/store/postgres/channel_top_message_cache_test.go
2026-09-01 12:06:31 +03:00

90 lines
2.8 KiB
Go

package postgres
import (
"context"
"testing"
"telesrv/internal/domain"
)
func TestChannelTopMessageCacheBatchesClonesAndInvalidates(t *testing.T) {
cache := NewChannelTopMessageCache(8)
if cache == nil {
t.Fatal("NewChannelTopMessageCache returned nil")
}
keys := []channelMessageLookupKey{{channelID: 7, id: 11}, {channelID: 7, id: 12}, {channelID: 8, id: 21}}
loads := 0
load := func(_ context.Context, missing []channelMessageLookupKey) (map[channelMessageLookupKey]domain.ChannelMessage, error) {
loads++
out := make(map[channelMessageLookupKey]domain.ChannelMessage, len(missing))
for _, key := range missing {
out[key] = domain.ChannelMessage{
ChannelID: key.channelID,
ID: key.id,
Entities: []domain.MessageEntity{{Offset: 1, Length: 2}},
Action: &domain.ChannelMessageAction{UserIDs: []int64{3}},
RichMessage: &domain.MessageRichMessage{
Blocks: []byte{4, 5},
},
}
}
return out, nil
}
first, err := cache.getOrLoadBatch(context.Background(), keys, load)
if err != nil {
t.Fatal(err)
}
if loads != 1 {
t.Fatalf("cold batch loads = %d, want 1", loads)
}
first[keys[0]].Entities[0].Offset = 99
first[keys[0]].Action.UserIDs[0] = 99
first[keys[0]].RichMessage.Blocks[0] = 99
second, err := cache.getOrLoadBatch(context.Background(), keys, load)
if err != nil {
t.Fatal(err)
}
if loads != 1 {
t.Fatalf("warm batch loads = %d, want 1", loads)
}
if got := second[keys[0]]; got.Entities[0].Offset != 1 || got.Action.UserIDs[0] != 3 || got.RichMessage.Blocks[0] != 4 {
t.Fatalf("cached message alias-mutated: %+v", got)
}
listener := NewReadModelChangeListener("", ReadModelCacheSet{ChannelTopMessages: cache}, nil)
cache.reactionPresence.Store(keys[0], channelTopReactionPresence{Normal: true})
listener.handlePayload(`{"model":"channel_base","owner_user_id":0,"peer_type":"channel","peer_id":7}`)
if _, ok := cache.reactionPresence.Peek(keys[0]); ok {
t.Fatal("channel_base did not invalidate top reaction presence")
}
if _, err := cache.getOrLoadBatch(context.Background(), keys, load); err != nil {
t.Fatal(err)
}
if loads != 2 {
t.Fatalf("channel invalidation loads = %d, want 2", loads)
}
}
func TestChannelTopMessageCacheNegativeResultIsCached(t *testing.T) {
cache := NewChannelTopMessageCache(4)
key := channelMessageLookupKey{channelID: 9, id: 1}
loads := 0
load := func(context.Context, []channelMessageLookupKey) (map[channelMessageLookupKey]domain.ChannelMessage, error) {
loads++
return map[channelMessageLookupKey]domain.ChannelMessage{}, nil
}
for i := 0; i < 2; i++ {
got, err := cache.getOrLoadBatch(context.Background(), []channelMessageLookupKey{key}, load)
if err != nil {
t.Fatal(err)
}
if got[key].ID != 0 {
t.Fatalf("negative result = %+v", got[key])
}
}
if loads != 1 {
t.Fatalf("negative cache loads = %d, want 1", loads)
}
}