feat: sync ephemeral transient messages
Sync telesrv 570ccf8 (feat(ephemeral): implement Layer 228 transient messages). Skipped telesrv docs changes per public sync rules; normalized the public appearance seed label.
This commit is contained in:
parent
3f78eaa2c6
commit
f49c817def
53 changed files with 5793 additions and 112 deletions
99
internal/store/redisstore/ephemeral_integration_test.go
Normal file
99
internal/store/redisstore/ephemeral_integration_test.go
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
package redisstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
"telesrv/internal/store"
|
||||
)
|
||||
|
||||
func TestRedisEphemeralAtomicLifecycleCallbackAndBroker(t *testing.T) {
|
||||
addr := os.Getenv("TELESRV_TEST_REDIS_ADDR")
|
||||
if addr == "" {
|
||||
t.Skip("set TELESRV_TEST_REDIS_ADDR to run redis integration test")
|
||||
}
|
||||
client := redis.NewClient(&redis.Options{Addr: addr})
|
||||
defer client.Close()
|
||||
ctx := context.Background()
|
||||
storeImpl := NewEphemeralMessageStore(client)
|
||||
now := time.Now()
|
||||
seed := now.UnixNano() & 0x3fffffff
|
||||
message := domain.EphemeralMessage{
|
||||
ID: int(seed) + 1, Peer: domain.Peer{Type: domain.PeerTypeChannel, ID: seed + 2},
|
||||
SenderUserID: seed + 3, ReceiverUserID: seed + 4, Date: int(now.Unix()), RandomID: seed + 5,
|
||||
Content: domain.EphemeralContent{Message: "/private"}, PayloadHash: sha256.Sum256([]byte("payload")),
|
||||
Version: 1, CreatedAt: now, ExpiresAt: now.Add(domain.EphemeralMessageRetention),
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
_ = client.Del(context.Background(), ephemeralMessageKey(message.Peer, message.ID), ephemeralRandomKey(message), ephemeralCallbackActionKey(seed+6)).Err()
|
||||
})
|
||||
created, fresh, err := storeImpl.CreateEphemeralMessage(ctx, message)
|
||||
if err != nil || !fresh || created.ID != message.ID {
|
||||
t.Fatalf("create=%+v fresh=%v err=%v", created, fresh, err)
|
||||
}
|
||||
replay, fresh, err := storeImpl.CreateEphemeralMessage(ctx, message)
|
||||
if err != nil || fresh || replay.ID != message.ID {
|
||||
t.Fatalf("replay=%+v fresh=%v err=%v", replay, fresh, err)
|
||||
}
|
||||
edited, err := storeImpl.EditEphemeralMessage(ctx, message.Peer, message.ID, 1, domain.EphemeralContent{Message: "edited"}, message.Date+1, now)
|
||||
if err != nil || edited.Version != 2 || edited.Content.Message != "edited" {
|
||||
t.Fatalf("edit=%+v err=%v", edited, err)
|
||||
}
|
||||
deleted, changed, err := storeImpl.DeleteEphemeralMessage(ctx, message.Peer, message.ID, 2, now)
|
||||
if err != nil || !changed || !deleted.Deleted || deleted.Version != 3 {
|
||||
t.Fatalf("delete=%+v changed=%v err=%v", deleted, changed, err)
|
||||
}
|
||||
|
||||
action := domain.EphemeralCallbackAction{
|
||||
QueryID: seed + 6, BotUserID: seed + 3, UserID: seed + 4, Peer: message.Peer,
|
||||
MessageID: message.ID, TopMessageID: 42,
|
||||
Device: domain.EphemeralDevice{UserID: seed + 4, BusinessAuthKeyID: [8]byte{7}, SessionID: 8},
|
||||
CreatedAt: now, ExpiresAt: now.Add(domain.EphemeralReplyWindow),
|
||||
}
|
||||
if created, err := storeImpl.PutEphemeralCallbackAction(ctx, action); err != nil || !created {
|
||||
t.Fatalf("put callback created=%v err=%v", created, err)
|
||||
}
|
||||
got, found, err := storeImpl.GetEphemeralCallbackAction(ctx, action.BotUserID, action.QueryID, now)
|
||||
if err != nil || !found || got.TopMessageID != 42 || got.Device.BusinessAuthKeyID != action.Device.BusinessAuthKeyID {
|
||||
t.Fatalf("callback=%+v found=%v err=%v", got, found, err)
|
||||
}
|
||||
|
||||
brokerCtx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
received := make(chan store.EphemeralPush, 1)
|
||||
go func() {
|
||||
_ = storeImpl.SubscribeEphemeralPushes(brokerCtx, func(_ context.Context, event store.EphemeralPush) {
|
||||
select {
|
||||
case received <- event:
|
||||
default:
|
||||
}
|
||||
})
|
||||
}()
|
||||
event := store.EphemeralPush{
|
||||
SourceID: "redis-test", Kind: store.EphemeralPushDelete,
|
||||
TargetUserID: message.ReceiverUserID, Message: deleted, Date: int(now.Unix()),
|
||||
}
|
||||
ticker := time.NewTicker(20 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
if err := storeImpl.PublishEphemeralPush(ctx, event); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
select {
|
||||
case got := <-received:
|
||||
if got.SourceID != event.SourceID || got.Message.ID != event.Message.ID || got.Kind != event.Kind {
|
||||
t.Fatalf("broker event=%+v", got)
|
||||
}
|
||||
return
|
||||
case <-brokerCtx.Done():
|
||||
t.Fatal("redis ephemeral broker did not deliver")
|
||||
case <-ticker.C:
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue