218 lines
6.0 KiB
Go
218 lines
6.0 KiB
Go
package telegram
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"sync"
|
|
"testing"
|
|
|
|
"git.yeij.top/AskaEth/Cyrene_v2/internal/channel/adapter"
|
|
)
|
|
|
|
func TestName(t *testing.T) {
|
|
t.Parallel()
|
|
a := New(Config{BotToken: "x"})
|
|
if got := a.Name(); got != "telegram" {
|
|
t.Fatalf("Name() = %q, want telegram", got)
|
|
}
|
|
}
|
|
|
|
func TestConfig(t *testing.T) {
|
|
t.Parallel()
|
|
a := New(Config{BotToken: "abc123", WebhookURL: "https://example.com/hook"})
|
|
got := a.Config()
|
|
if got["bot_token"] != "abc123" {
|
|
t.Fatalf("bot_token = %v, want abc123", got["bot_token"])
|
|
}
|
|
if got["webhook_url"] != "https://example.com/hook" {
|
|
t.Fatalf("webhook_url = %v, want https://example.com/hook", got["webhook_url"])
|
|
}
|
|
// Round-trip through the shared decoder to prove the map is decodable.
|
|
var round Config
|
|
if err := adapter.DecodeConfig(got, &round); err != nil {
|
|
t.Fatalf("DecodeConfig: %v", err)
|
|
}
|
|
if round.BotToken != "abc123" || round.WebhookURL != "https://example.com/hook" {
|
|
t.Fatalf("round trip mismatch: %+v", round)
|
|
}
|
|
}
|
|
|
|
func TestCapabilities(t *testing.T) {
|
|
t.Parallel()
|
|
a := New(Config{BotToken: "x"})
|
|
caps := a.Capabilities()
|
|
if !caps.Proactive {
|
|
t.Error("Proactive should be true for telegram")
|
|
}
|
|
if !caps.Markdown || !caps.Image || !caps.Voice {
|
|
t.Errorf("expected Markdown/Image/Voice supported, got %+v", caps)
|
|
}
|
|
if caps.Reaction || caps.TypingHint {
|
|
t.Errorf("expected Reaction/TypingHint unsupported, got %+v", caps)
|
|
}
|
|
if caps.MaxMessageLength != 4096 {
|
|
t.Errorf("MaxMessageLength = %d, want 4096", caps.MaxMessageLength)
|
|
}
|
|
if caps.RecommendBurstMax <= 0 {
|
|
t.Errorf("RecommendBurstMax should be positive, got %d", caps.RecommendBurstMax)
|
|
}
|
|
}
|
|
|
|
func TestListChannels_Empty(t *testing.T) {
|
|
t.Parallel()
|
|
a := New(Config{BotToken: "x"})
|
|
got, err := a.ListChannels(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("ListChannels: %v", err)
|
|
}
|
|
if got == nil || len(got) != 0 {
|
|
t.Fatalf("expected empty non-nil slice, got %#v", got)
|
|
}
|
|
}
|
|
|
|
func TestConnect_MissingToken(t *testing.T) {
|
|
t.Parallel()
|
|
a := New(Config{})
|
|
err := a.Connect(context.Background())
|
|
if !errors.Is(err, ErrMissingToken) {
|
|
t.Fatalf("err = %v, want ErrMissingToken", err)
|
|
}
|
|
if a.IsConnected() {
|
|
t.Fatal("should not be connected")
|
|
}
|
|
}
|
|
|
|
func TestConnect_WebhookIdempotent(t *testing.T) {
|
|
t.Parallel()
|
|
var mu sync.Mutex
|
|
calls := 0
|
|
a, _ := newTestAdapter(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if methodOf(r) != "setWebhook" {
|
|
http.Error(w, "unexpected method", http.StatusNotFound)
|
|
return
|
|
}
|
|
mu.Lock()
|
|
calls++
|
|
mu.Unlock()
|
|
okResponse(w, map[string]any{"webhook_url": "https://example.com/hook"})
|
|
}))
|
|
a.cfg.WebhookURL = "https://example.com/hook"
|
|
|
|
if err := a.Connect(context.Background()); err != nil {
|
|
t.Fatalf("Connect: %v", err)
|
|
}
|
|
if !a.IsConnected() {
|
|
t.Fatal("expected connected")
|
|
}
|
|
if err := a.Connect(context.Background()); err != nil {
|
|
t.Fatalf("second Connect should be idempotent: %v", err)
|
|
}
|
|
mu.Lock()
|
|
if calls != 1 {
|
|
t.Fatalf("expected 1 setWebhook call, got %d", calls)
|
|
}
|
|
mu.Unlock()
|
|
}
|
|
|
|
func TestConnect_PollingMode(t *testing.T) {
|
|
t.Parallel()
|
|
a, _ := newTestAdapter(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
http.Error(w, "no api call expected in poll mode", http.StatusNotFound)
|
|
}))
|
|
if err := a.Connect(context.Background()); err != nil {
|
|
t.Fatalf("Connect: %v", err)
|
|
}
|
|
if !a.IsConnected() {
|
|
t.Fatal("expected connected")
|
|
}
|
|
if a.isWebhookMode() {
|
|
t.Fatal("expected polling mode")
|
|
}
|
|
}
|
|
|
|
func TestDisconnect_Idempotent(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)
|
|
}))
|
|
if err := a.Connect(context.Background()); err != nil {
|
|
t.Fatalf("Connect: %v", err)
|
|
}
|
|
if err := a.Disconnect(context.Background()); err != nil {
|
|
t.Fatalf("Disconnect: %v", err)
|
|
}
|
|
if a.IsConnected() {
|
|
t.Fatal("expected disconnected")
|
|
}
|
|
if err := a.Disconnect(context.Background()); err != nil {
|
|
t.Fatalf("second Disconnect should be idempotent: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestClose_Terminal(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)
|
|
}))
|
|
if err := a.Connect(context.Background()); err != nil {
|
|
t.Fatalf("Connect: %v", err)
|
|
}
|
|
if err := a.Close(); err != nil {
|
|
t.Fatalf("Close: %v", err)
|
|
}
|
|
if a.IsConnected() {
|
|
t.Fatal("expected disconnected after close")
|
|
}
|
|
if err := a.Connect(context.Background()); !errors.Is(err, adapter.ErrClosed) {
|
|
t.Fatalf("Connect after close err = %v, want ErrClosed", err)
|
|
}
|
|
if err := a.HealthCheck(context.Background()); !errors.Is(err, adapter.ErrClosed) {
|
|
t.Fatalf("HealthCheck after close err = %v, want ErrClosed", err)
|
|
}
|
|
}
|
|
|
|
func TestHealthCheck_OK(t *testing.T) {
|
|
t.Parallel()
|
|
a, _ := newTestAdapter(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if methodOf(r) != "getMe" {
|
|
http.Error(w, "unexpected method", http.StatusNotFound)
|
|
return
|
|
}
|
|
okResponse(w, map[string]any{"id": 123, "username": "cyrene_bot"})
|
|
}))
|
|
if err := a.Connect(context.Background()); err != nil {
|
|
t.Fatalf("Connect: %v", err)
|
|
}
|
|
if err := a.HealthCheck(context.Background()); err != nil {
|
|
t.Fatalf("HealthCheck: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestHealthCheck_NotConnected(t *testing.T) {
|
|
t.Parallel()
|
|
a := New(Config{BotToken: "x"})
|
|
err := a.HealthCheck(context.Background())
|
|
if !errors.Is(err, adapter.ErrNotConnected) {
|
|
t.Fatalf("err = %v, want ErrNotConnected", err)
|
|
}
|
|
}
|
|
|
|
func TestHealthCheck_APIError(t *testing.T) {
|
|
t.Parallel()
|
|
a, _ := newTestAdapter(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if methodOf(r) != "getMe" {
|
|
http.Error(w, "unexpected method", http.StatusNotFound)
|
|
return
|
|
}
|
|
failResponse(w, "Unauthorized", 401)
|
|
}))
|
|
if err := a.Connect(context.Background()); err != nil {
|
|
t.Fatalf("Connect: %v", err)
|
|
}
|
|
err := a.HealthCheck(context.Background())
|
|
if !errors.Is(err, ErrBadResponse) {
|
|
t.Fatalf("err = %v, want ErrBadResponse", err)
|
|
}
|
|
}
|