Files

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")
}
}