5e783adf7a
- Introduced a new function `mergeAndLog` in the Watcher to handle merging Safew chats and logging newly discovered chats. - Added a test `TestNewSafewChatsSkipsKnownIDs` to ensure that known chat IDs are skipped during the merging process. - Enhanced the polling mechanism to improve chat management and reduce duplicates.
136 lines
3.5 KiB
Go
136 lines
3.5 KiB
Go
package safew
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"aiaa-notification-service/internal/adapter"
|
|
)
|
|
|
|
func TestRefreshMergesAndAdvancesOffset(t *testing.T) {
|
|
st := NewMemStore()
|
|
poll := func(token string, offset int64, timeout int) ([]adapter.SafewChat, int64, error) {
|
|
if timeout != 0 {
|
|
return nil, offset, nil
|
|
}
|
|
if offset != 0 {
|
|
t.Fatalf("offset=%d", offset)
|
|
}
|
|
return []adapter.SafewChat{{ID: "10000778141", Type: "group", Title: "测试AI"}}, 11, nil
|
|
}
|
|
w := NewWatcher(st, poll)
|
|
ctx := context.Background()
|
|
if err := w.Refresh(ctx, "tok"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
list, err := w.List(ctx, "tok", "测试")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(list) != 1 || list[0].ID != "10000778141" {
|
|
t.Fatalf("%#v", list)
|
|
}
|
|
off, _ := st.GetSafewOffset(ctx, "tok")
|
|
if off != 11 {
|
|
t.Fatalf("offset=%d", off)
|
|
}
|
|
w.Stop()
|
|
}
|
|
|
|
func TestRefreshAuthError(t *testing.T) {
|
|
st := NewMemStore()
|
|
w := NewWatcher(st, func(string, int64, int) ([]adapter.SafewChat, int64, error) {
|
|
return nil, 0, &adapter.SafewAuthError{Description: "BOT_TOKEN_INVALID"}
|
|
})
|
|
err := w.Refresh(context.Background(), "bad")
|
|
if err == nil {
|
|
t.Fatal("expected auth error")
|
|
}
|
|
if _, ok := err.(*adapter.SafewAuthError); !ok {
|
|
t.Fatalf("%T", err)
|
|
}
|
|
w.Stop()
|
|
}
|
|
|
|
type alwaysLockStore struct{ *MemStore }
|
|
|
|
func (a *alwaysLockStore) TrySafewPollLock(context.Context, string, time.Duration) (bool, error) {
|
|
return true, nil
|
|
}
|
|
|
|
func (a *alwaysLockStore) UnlockSafewPoll(context.Context, string) error { return nil }
|
|
|
|
func TestRefreshAndEnsureDoNotOverlapPolls(t *testing.T) {
|
|
st := &alwaysLockStore{MemStore: NewMemStore()}
|
|
var inflight int32
|
|
var overlap int32
|
|
poll := func(token string, offset int64, timeout int) ([]adapter.SafewChat, int64, error) {
|
|
n := atomic.AddInt32(&inflight, 1)
|
|
if n > 1 {
|
|
atomic.AddInt32(&overlap, 1)
|
|
}
|
|
time.Sleep(40 * time.Millisecond)
|
|
atomic.AddInt32(&inflight, -1)
|
|
return []adapter.SafewChat{{ID: "1", Type: "group", Title: "g"}}, offset + 1, nil
|
|
}
|
|
w := NewWatcher(st, poll)
|
|
w.bgIdle = 20 * time.Millisecond
|
|
ctx := context.Background()
|
|
errCh := make(chan error, 1)
|
|
go func() { errCh <- w.Refresh(ctx, "tok") }()
|
|
w.Ensure("tok")
|
|
if err := <-errCh; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
time.Sleep(80 * time.Millisecond)
|
|
w.Stop()
|
|
if atomic.LoadInt32(&overlap) > 0 {
|
|
t.Fatalf("overlapping getUpdates: %d", overlap)
|
|
}
|
|
}
|
|
|
|
func TestEnsurePollsInBackground(t *testing.T) {
|
|
st := NewMemStore()
|
|
got := make(chan int, 1)
|
|
w := NewWatcher(st, func(token string, offset int64, timeout int) ([]adapter.SafewChat, int64, error) {
|
|
if timeout != 30 {
|
|
return nil, offset, nil
|
|
}
|
|
select {
|
|
case got <- timeout:
|
|
default:
|
|
}
|
|
return []adapter.SafewChat{{ID: "1", Type: "group", Title: "g"}}, offset + 1, nil
|
|
})
|
|
w.bgIdle = 10 * time.Millisecond
|
|
w.Ensure("tok")
|
|
select {
|
|
case <-got:
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("background poll not called")
|
|
}
|
|
w.Stop()
|
|
}
|
|
|
|
func TestNewSafewChatsSkipsKnownIDs(t *testing.T) {
|
|
known := map[string]struct{}{"1": {}}
|
|
got := newSafewChats(known, []adapter.SafewChat{
|
|
{ID: "1", Title: "old"},
|
|
{ID: "2", Title: "测试AI", Type: "group"},
|
|
{ID: "2", Title: "dup"},
|
|
})
|
|
if len(got) != 1 || got[0].ID != "2" || got[0].Title != "测试AI" {
|
|
t.Fatalf("%#v", got)
|
|
}
|
|
}
|
|
|
|
func TestPollConflictIsRetryable(t *testing.T) {
|
|
err := errors.New("safew getUpdates status 400: BAD_REQUEST: Conflict: terminated by other getUpdates request; make sure that only one bot instance is running")
|
|
if !isGetUpdatesConflict(err) {
|
|
t.Fatal("expected conflict")
|
|
}
|
|
}
|