154 lines
5.2 KiB
Go
154 lines
5.2 KiB
Go
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)
|
||
}
|
||
}
|
||
|
||
// TestGetDifferenceStopsAtHole:getDifference 只返回从 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=true,want 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)
|
||
}
|
||
}
|
||
|
||
// TestGetStateReportsContiguousNotMax:getState 报告最大连续 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)
|
||
}
|
||
}
|