172 lines
4.3 KiB
Go
172 lines
4.3 KiB
Go
package telegram
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.yeij.top/AskaEth/Cyrene_v2/internal/channel/adapter"
|
|
)
|
|
|
|
// fakeUpdate builds a minimal telegramUpdate JSON object.
|
|
func fakeUpdate(id int, text string) map[string]any {
|
|
return map[string]any{
|
|
"update_id": id,
|
|
"message": map[string]any{
|
|
"message_id": id,
|
|
"from": map[string]any{"id": 1, "first_name": "Tester"},
|
|
"chat": map[string]any{"id": 10, "type": "private"},
|
|
"date": 1700000000,
|
|
"text": text,
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestReadMessages_LongPollOffset(t *testing.T) {
|
|
t.Parallel()
|
|
var mu sync.Mutex
|
|
first := true
|
|
a, _ := newTestAdapter(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if methodOf(r) != "getUpdates" {
|
|
http.Error(w, "unexpected method", http.StatusNotFound)
|
|
return
|
|
}
|
|
mu.Lock()
|
|
firstCall := first
|
|
first = false
|
|
mu.Unlock()
|
|
if firstCall {
|
|
okResponse(w, []map[string]any{fakeUpdate(1, "one"), fakeUpdate(2, "two")})
|
|
return
|
|
}
|
|
okResponse(w, []map[string]any{})
|
|
}))
|
|
if err := a.Connect(context.Background()); err != nil {
|
|
t.Fatalf("Connect: %v", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
emitted := make(chan adapter.UnifiedMessage, 4)
|
|
done := make(chan struct{})
|
|
go func() {
|
|
a.ReadMessages(ctx, func(m adapter.UnifiedMessage) { emitted <- m })
|
|
close(done)
|
|
}()
|
|
|
|
got := make([]adapter.UnifiedMessage, 0, 2)
|
|
for i := 0; i < 2; i++ {
|
|
select {
|
|
case m := <-emitted:
|
|
got = append(got, m)
|
|
case <-time.After(3 * time.Second):
|
|
t.Fatal("timeout waiting for emitted message")
|
|
}
|
|
}
|
|
cancel()
|
|
select {
|
|
case <-done:
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("ReadMessages did not return after ctx cancel")
|
|
}
|
|
|
|
if len(got) != 2 {
|
|
t.Fatalf("emitted %d messages, want 2", len(got))
|
|
}
|
|
if got[0].Content != "one" || got[1].Content != "two" {
|
|
t.Errorf("contents = %q/%q, want one/two", got[0].Content, got[1].Content)
|
|
}
|
|
a.mu.RLock()
|
|
offset := a.offset
|
|
a.mu.RUnlock()
|
|
if offset != 3 {
|
|
t.Errorf("offset = %d, want 3", offset)
|
|
}
|
|
}
|
|
|
|
func TestReadMessages_CtxPreCanceled(t *testing.T) {
|
|
t.Parallel()
|
|
a, _ := newTestAdapter(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
http.Error(w, "no api call expected", http.StatusNotFound)
|
|
}))
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
done := make(chan struct{})
|
|
go func() {
|
|
a.ReadMessages(ctx, func(m adapter.UnifiedMessage) {})
|
|
close(done)
|
|
}()
|
|
select {
|
|
case <-done:
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("ReadMessages should return immediately on canceled ctx")
|
|
}
|
|
}
|
|
|
|
func TestReadMessages_WebhookWaits(t *testing.T) {
|
|
t.Parallel()
|
|
var calls int
|
|
var mu sync.Mutex
|
|
a, _ := newTestAdapter(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if methodOf(r) == "setWebhook" {
|
|
okResponse(w, map[string]any{"webhook_url": "https://example.com/hook"})
|
|
return
|
|
}
|
|
mu.Lock()
|
|
calls++
|
|
mu.Unlock()
|
|
http.Error(w, "no getUpdates expected in webhook mode", http.StatusNotFound)
|
|
}))
|
|
a.cfg.WebhookURL = "https://example.com/hook"
|
|
if err := a.Connect(context.Background()); err != nil {
|
|
t.Fatalf("Connect: %v", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
done := make(chan struct{})
|
|
go func() {
|
|
a.ReadMessages(ctx, func(m adapter.UnifiedMessage) {})
|
|
close(done)
|
|
}()
|
|
time.Sleep(100 * time.Millisecond)
|
|
cancel()
|
|
select {
|
|
case <-done:
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("ReadMessages should return on cancel in webhook mode")
|
|
}
|
|
mu.Lock()
|
|
if calls != 0 {
|
|
t.Errorf("getUpdates called %d times in webhook mode", calls)
|
|
}
|
|
mu.Unlock()
|
|
}
|
|
|
|
func TestReadMessages_ErrorRetriesThenCancel(t *testing.T) {
|
|
t.Parallel()
|
|
a, _ := newTestAdapter(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if methodOf(r) != "getUpdates" {
|
|
http.Error(w, "unexpected method", http.StatusNotFound)
|
|
return
|
|
}
|
|
failResponse(w, "Conflict", 409)
|
|
}))
|
|
if err := a.Connect(context.Background()); err != nil {
|
|
t.Fatalf("Connect: %v", err)
|
|
}
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
done := make(chan struct{})
|
|
go func() {
|
|
a.ReadMessages(ctx, func(m adapter.UnifiedMessage) {})
|
|
close(done)
|
|
}()
|
|
time.Sleep(150 * time.Millisecond)
|
|
cancel()
|
|
select {
|
|
case <-done:
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("ReadMessages should return on cancel despite getUpdates errors")
|
|
}
|
|
}
|