Initial open source release

This commit is contained in:
A 2026-06-04 01:37:39 +08:00
commit 74992e893f
377 changed files with 118084 additions and 0 deletions

View file

@ -0,0 +1,154 @@
package updates
import (
"context"
"testing"
"telesrv/internal/domain"
"telesrv/internal/store/memory"
)
func seedEvent(t *testing.T, events *memory.UpdateEventStore, userID int64, pts int) {
t.Helper()
if err := events.Append(context.Background(), userID, domain.UpdateEvent{
Type: domain.UpdateEventNewMessage,
Pts: pts,
PtsCount: 1,
Date: 1700000000 + pts,
Message: domain.Message{
ID: pts,
OwnerUserID: userID,
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: domain.OfficialSystemUserID},
From: domain.Peer{Type: domain.PeerTypeUser, ID: domain.OfficialSystemUserID},
},
}); err != nil {
t.Fatalf("seed event pts=%d: %v", pts, err)
}
}
// TestGetDifferenceStopsAtHolegetDifference 只返回从 from 起连续的事件,遇在途空洞即截断,
// State.Pts 取最后连续值;补洞后下次拉取可继续,绝不跳过空洞。
func TestGetDifferenceStopsAtHole(t *testing.T) {
ctx := context.Background()
var authKeyID [8]byte
userID := int64(1000000001)
events := memory.NewUpdateEventStore()
svc := NewService(memory.NewUpdateStateStore(), events)
// pts 1,2,3 已提交4 在途5,6 已提交 → 连续只到 3。
for _, p := range []int{1, 2, 3, 5, 6} {
seedEvent(t, events, userID, p)
}
diff, err := svc.GetDifference(ctx, authKeyID, userID, domain.UpdateState{})
if err != nil {
t.Fatalf("GetDifference: %v", err)
}
if len(diff.Events) != 3 || diff.State.Pts != 3 {
t.Fatalf("diff = %d events, state.pts %d; want 3 连续事件、止于空洞(pts=3)", len(diff.Events), diff.State.Pts)
}
if diff.Partial {
t.Fatalf("Partial=truewant false被空洞而非 limit 截断)")
}
// 补上 pts=4在途事务提交从 3 继续应拿到 4,5,6。
seedEvent(t, events, userID, 4)
diff, err = svc.GetDifference(ctx, authKeyID, userID, domain.UpdateState{Pts: 3})
if err != nil {
t.Fatalf("GetDifference after fill: %v", err)
}
if len(diff.Events) != 3 || diff.State.Pts != 6 {
t.Fatalf("补洞后 diff = %d events, state.pts %d; want 4,5,6 到 pts=6", len(diff.Events), diff.State.Pts)
}
}
// TestGetDifferenceSliceOnLimit连续事件填满 limit 时返回 Partial(=differenceSlice)
// State.Pts 为中间态;客户端据此续拉,最终一页 Partial=false。
func TestGetDifferenceSliceOnLimit(t *testing.T) {
ctx := context.Background()
var authKeyID [8]byte
userID := int64(1000000002)
events := memory.NewUpdateEventStore()
svc := NewService(memory.NewUpdateStateStore(), events)
total := getDifferenceLimit + 25
for p := 1; p <= total; p++ {
seedEvent(t, events, userID, p)
}
diff, err := svc.GetDifference(ctx, authKeyID, userID, domain.UpdateState{})
if err != nil {
t.Fatalf("GetDifference: %v", err)
}
if len(diff.Events) != getDifferenceLimit || !diff.Partial || diff.State.Pts != getDifferenceLimit {
t.Fatalf("第一页 = %d events partial %v state.pts %d; want %d/true/%d",
len(diff.Events), diff.Partial, diff.State.Pts, getDifferenceLimit, getDifferenceLimit)
}
diff, err = svc.GetDifference(ctx, authKeyID, userID, domain.UpdateState{Pts: getDifferenceLimit})
if err != nil {
t.Fatalf("GetDifference page2: %v", err)
}
if len(diff.Events) != 25 || diff.Partial || diff.State.Pts != total {
t.Fatalf("第二页 = %d events partial %v state.pts %d; want 25/false/%d",
len(diff.Events), diff.Partial, diff.State.Pts, total)
}
}
// TestGetStateReportsContiguousNotMaxgetState 报告最大连续 pts而非最大已提交 pts
// 避免首次登录基线越过在途空洞而丢消息。
func TestGetStateReportsContiguousNotMax(t *testing.T) {
ctx := context.Background()
var authKeyID [8]byte
userID := int64(1000000003)
events := memory.NewUpdateEventStore()
svc := NewService(memory.NewUpdateStateStore(), events)
for _, p := range []int{1, 2, 3, 5, 6} { // 4 在途空洞,最大已提交=6
seedEvent(t, events, userID, p)
}
st, err := svc.GetState(ctx, authKeyID, userID)
if err != nil {
t.Fatalf("GetState: %v", err)
}
if st.Pts != 3 {
t.Fatalf("GetState.Pts=%d, want 3最大连续而非最大已提交 6", st.Pts)
}
}
func TestGetStateDoesNotConfirmUnfetchedEvents(t *testing.T) {
ctx := context.Background()
var authKeyID [8]byte
authKeyID[0] = 7
userID := int64(1000000004)
events := memory.NewUpdateEventStore()
states := memory.NewUpdateStateStore()
svc := NewService(states, events)
seedEvent(t, events, userID, 1)
if err := states.Save(ctx, authKeyID, userID, domain.UpdateState{Pts: 1, Date: 1700000001}); err != nil {
t.Fatalf("Save state: %v", err)
}
seedEvent(t, events, userID, 2)
st, err := svc.GetState(ctx, authKeyID, userID)
if err != nil {
t.Fatalf("GetState: %v", err)
}
if st.Pts != 1 {
t.Fatalf("GetState.Pts=%d, want existing confirmed pts=1", st.Pts)
}
diff, err := svc.GetDifference(ctx, authKeyID, userID, st)
if err != nil {
t.Fatalf("GetDifference: %v", err)
}
if len(diff.Events) != 1 || diff.Events[0].Pts != 2 || diff.State.Pts != 2 {
t.Fatalf("diff = %+v, want one event at pts=2 and confirmed state pts=2", diff)
}
st, err = svc.GetState(ctx, authKeyID, userID)
if err != nil {
t.Fatalf("GetState after difference: %v", err)
}
if st.Pts != 2 {
t.Fatalf("GetState after difference pts=%d, want confirmed pts=2", st.Pts)
}
}