owpengram-server/internal/store/postgres/botapi_update_integration_test.go

122 lines
4.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package postgres
import (
"context"
"testing"
"time"
"telesrv/internal/domain"
)
// TestBotAPIUpdateRetention 锁定 H1 场景矩阵:
// - 已确认 + 超宽限 → 删;已确认 + 宽限内 → 留;
// - 未确认 + date 超保留期 → 删(含无 state 行的 MTProto-only bot
// - 未确认 + date 在保留期内 → 留;
// - 删除后 getUpdates 读路径fromID > confirmed不受影响。
func TestBotAPIUpdateRetention(t *testing.T) {
pool := testPool(t)
ctx := context.Background()
suffix := randomSuffix(t)
users := NewUserStore(pool)
newBot := func(phoneTail, name string) int64 {
t.Helper()
u, err := users.Create(ctx, domain.User{
AccessHash: 920,
Phone: "+1920" + suffix + phoneTail,
FirstName: name,
})
if err != nil {
t.Fatalf("create bot user %s: %v", name, err)
}
if _, err := pool.Exec(ctx, `
INSERT INTO bots (bot_user_id, owner_user_id, token_secret)
VALUES ($1, $1, 'retention-test-secret')
ON CONFLICT (bot_user_id) DO NOTHING`, u.ID); err != nil {
t.Fatalf("seed bot %s: %v", name, err)
}
return u.ID
}
confirmedBot := newBot("01", "RetentionConfirmedBot")
mtprotoOnlyBot := newBot("02", "RetentionMTOnlyBot")
t.Cleanup(func() {
_, _ = pool.Exec(ctx, "DELETE FROM bot_api_updates WHERE bot_user_id IN ($1, $2)", confirmedBot, mtprotoOnlyBot)
_, _ = pool.Exec(ctx, "DELETE FROM bot_api_update_states WHERE bot_user_id IN ($1, $2)", confirmedBot, mtprotoOnlyBot)
_, _ = pool.Exec(ctx, "DELETE FROM bots WHERE bot_user_id IN ($1, $2)", confirmedBot, mtprotoOnlyBot)
})
s := NewBotAPIUpdateStore(pool)
now := time.Now().Unix()
stale := now - int64((48 * time.Hour).Seconds())
enqueue := func(botID int64, messageID int, date int64) domain.BotAPIUpdate {
t.Helper()
row, created, err := s.EnqueueBotAPIUpdate(ctx, domain.EnqueueBotAPIUpdateRequest{
BotUserID: botID,
Kind: domain.BotAPIUpdateMessage,
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: 1},
MessageID: messageID,
SourcePts: messageID,
Date: int(date),
})
if err != nil || !created {
t.Fatalf("enqueue bot=%d msg=%d: created=%v err=%v", botID, messageID, created, err)
}
return row
}
confirmedOld := enqueue(confirmedBot, 1, now) // 已确认 + created_at 回拨超宽限 → 删
confirmedFresh := enqueue(confirmedBot, 2, now) // 已确认 + 宽限内 → 留
unconfirmedFresh := enqueue(confirmedBot, 3, now)
expiredNoState := enqueue(mtprotoOnlyBot, 4, stale) // 无 state 行 + date 超保留期 → 删
freshNoState := enqueue(mtprotoOnlyBot, 5, now)
if err := s.ConfirmBotAPIUpdates(ctx, confirmedBot, confirmedFresh.ID); err != nil {
t.Fatalf("confirm: %v", err)
}
if _, err := pool.Exec(ctx,
"UPDATE bot_api_updates SET created_at = now() - interval '1 hour' WHERE id = $1", confirmedOld.ID); err != nil {
t.Fatalf("backdate confirmed row: %v", err)
}
deleted, err := s.DeleteDeliveredOrExpired(ctx, 15*time.Minute, 24*time.Hour, 1000)
if err != nil {
t.Fatalf("DeleteDeliveredOrExpired: %v", err)
}
// 共享测试库可能有其它历史行同被回收,只要求至少删掉本测试的 2 行;
// 精确归属由下方 remaining 断言保证。
if deleted < 2 {
t.Fatalf("deleted = %d, want >= 2 (confirmed+grace expired, date expired)", deleted)
}
remaining := map[int64]bool{}
rows, err := pool.Query(ctx, "SELECT id FROM bot_api_updates WHERE bot_user_id IN ($1, $2)", confirmedBot, mtprotoOnlyBot)
if err != nil {
t.Fatalf("list remaining: %v", err)
}
for rows.Next() {
var id int64
if err := rows.Scan(&id); err != nil {
t.Fatalf("scan remaining: %v", err)
}
remaining[id] = true
}
rows.Close()
if remaining[confirmedOld.ID] {
t.Fatal("confirmed row past grace was not deleted")
}
if remaining[expiredNoState.ID] {
t.Fatal("expired row of state-less bot was not deleted")
}
if !remaining[confirmedFresh.ID] || !remaining[unconfirmedFresh.ID] || !remaining[freshNoState.ID] {
t.Fatalf("fresh rows were deleted, remaining=%v", remaining)
}
// 读路径回归:确认水位之后的未确认行仍可被 getUpdates 读到。
items, err := s.ListBotAPIUpdates(ctx, confirmedBot, confirmedFresh.ID+1, 100)
if err != nil {
t.Fatalf("list after retention: %v", err)
}
if len(items) != 1 || items[0].ID != unconfirmedFresh.ID {
t.Fatalf("post-retention list = %+v, want only unconfirmed fresh row %d", items, unconfirmedFresh.ID)
}
}