feat: enhance Safew error handling and polling mechanism
- Added error_msg field to safewAPIResponse for better error descriptions. - Updated safewErrorDescription function to include error_msg in the output. - Introduced a new test to verify that error messages are included in Safew error descriptions. - Improved the polling mechanism in the Watcher to handle conflicts and ensure no overlapping polls occur.
This commit is contained in:
@@ -3,6 +3,7 @@ package safew
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -13,7 +14,7 @@ func TestRefreshMergesAndAdvancesOffset(t *testing.T) {
|
||||
st := NewMemStore()
|
||||
poll := func(token string, offset int64, timeout int) ([]adapter.SafewChat, int64, error) {
|
||||
if timeout != 0 {
|
||||
t.Fatalf("timeout=%d", timeout)
|
||||
return nil, offset, nil
|
||||
}
|
||||
if offset != 0 {
|
||||
t.Fatalf("offset=%d", offset)
|
||||
@@ -36,6 +37,7 @@ func TestRefreshMergesAndAdvancesOffset(t *testing.T) {
|
||||
if off != 11 {
|
||||
t.Fatalf("offset=%d", off)
|
||||
}
|
||||
w.Stop()
|
||||
}
|
||||
|
||||
func TestRefreshAuthError(t *testing.T) {
|
||||
@@ -50,6 +52,44 @@ func TestRefreshAuthError(t *testing.T) {
|
||||
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) {
|
||||
@@ -57,7 +97,7 @@ func TestEnsurePollsInBackground(t *testing.T) {
|
||||
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, errors.New("not background")
|
||||
return nil, offset, nil
|
||||
}
|
||||
select {
|
||||
case got <- timeout:
|
||||
@@ -74,3 +114,10 @@ func TestEnsurePollsInBackground(t *testing.T) {
|
||||
}
|
||||
w.Stop()
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user