325 lines
9.6 KiB
Go
325 lines
9.6 KiB
Go
package telegram
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"sync"
|
|
"testing"
|
|
|
|
"git.yeij.top/AskaEth/Cyrene_v2/internal/channel/adapter"
|
|
)
|
|
|
|
func TestSendMessage_TextConstruction(t *testing.T) {
|
|
t.Parallel()
|
|
var mu sync.Mutex
|
|
var body map[string]any
|
|
a, _ := newTestAdapter(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if methodOf(r) != "sendMessage" {
|
|
http.Error(w, "unexpected method: "+methodOf(r), http.StatusNotFound)
|
|
return
|
|
}
|
|
mu.Lock()
|
|
body = decodeBody(r)
|
|
mu.Unlock()
|
|
okResponse(w, map[string]any{"message_id": 100})
|
|
}))
|
|
if err := a.Connect(context.Background()); err != nil {
|
|
t.Fatalf("Connect: %v", err)
|
|
}
|
|
|
|
resp := adapter.UnifiedResponse{
|
|
ReplyTo: "42",
|
|
Messages: []adapter.UnifiedMessage{
|
|
{ChannelID: "200", ChannelType: adapter.ChannelDirect, Content: "hello", ContentType: adapter.ContentText},
|
|
},
|
|
}
|
|
if err := a.SendMessage(context.Background(), resp); err != nil {
|
|
t.Fatalf("SendMessage: %v", err)
|
|
}
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
if body["chat_id"] != "200" {
|
|
t.Errorf("chat_id = %v, want 200", body["chat_id"])
|
|
}
|
|
if body["text"] != "hello" {
|
|
t.Errorf("text = %v, want hello", body["text"])
|
|
}
|
|
if body["parse_mode"] != "Markdown" {
|
|
t.Errorf("parse_mode = %v, want Markdown", body["parse_mode"])
|
|
}
|
|
if body["reply_to_message_id"] != float64(42) {
|
|
t.Errorf("reply_to_message_id = %v, want 42", body["reply_to_message_id"])
|
|
}
|
|
}
|
|
|
|
func TestSendMessage_ImageByFileID(t *testing.T) {
|
|
t.Parallel()
|
|
var mu sync.Mutex
|
|
var body map[string]any
|
|
var method string
|
|
a, _ := newTestAdapter(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
m := methodOf(r)
|
|
if m != "sendPhoto" {
|
|
http.Error(w, "unexpected method: "+m, http.StatusNotFound)
|
|
return
|
|
}
|
|
mu.Lock()
|
|
method = m
|
|
body = decodeBody(r)
|
|
mu.Unlock()
|
|
okResponse(w, map[string]any{"message_id": 101})
|
|
}))
|
|
if err := a.Connect(context.Background()); err != nil {
|
|
t.Fatalf("Connect: %v", err)
|
|
}
|
|
resp := adapter.UnifiedResponse{
|
|
Messages: []adapter.UnifiedMessage{
|
|
{
|
|
ChannelID: "200",
|
|
ChannelType: adapter.ChannelDirect,
|
|
Content: "a caption",
|
|
ContentType: adapter.ContentImage,
|
|
Media: []adapter.Media{{Kind: adapter.MediaImage, URL: "file-abc"}},
|
|
},
|
|
},
|
|
}
|
|
if err := a.SendMessage(context.Background(), resp); err != nil {
|
|
t.Fatalf("SendMessage: %v", err)
|
|
}
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
if method != "sendPhoto" {
|
|
t.Errorf("method = %q, want sendPhoto", method)
|
|
}
|
|
if body["chat_id"] != "200" || body["photo"] != "file-abc" {
|
|
t.Errorf("body = %v, want chat_id 200 and photo file-abc", body)
|
|
}
|
|
if body["caption"] != "a caption" || body["parse_mode"] != "Markdown" {
|
|
t.Errorf("caption/parse_mode = %v/%v", body["caption"], body["parse_mode"])
|
|
}
|
|
}
|
|
|
|
func TestSendMessage_ImageNoFileIDFallsBackToText(t *testing.T) {
|
|
t.Parallel()
|
|
var mu sync.Mutex
|
|
var method string
|
|
a, _ := newTestAdapter(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
m := methodOf(r)
|
|
mu.Lock()
|
|
method = m
|
|
mu.Unlock()
|
|
if m != "sendMessage" {
|
|
http.Error(w, "unexpected method: "+m, http.StatusNotFound)
|
|
return
|
|
}
|
|
okResponse(w, map[string]any{"message_id": 102})
|
|
}))
|
|
if err := a.Connect(context.Background()); err != nil {
|
|
t.Fatalf("Connect: %v", err)
|
|
}
|
|
resp := adapter.UnifiedResponse{
|
|
Messages: []adapter.UnifiedMessage{
|
|
{ChannelID: "200", ContentType: adapter.ContentImage, Content: "no file_id so text"},
|
|
},
|
|
}
|
|
if err := a.SendMessage(context.Background(), resp); err != nil {
|
|
t.Fatalf("SendMessage: %v", err)
|
|
}
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
if method != "sendMessage" {
|
|
t.Errorf("method = %q, want fallback sendMessage", method)
|
|
}
|
|
}
|
|
|
|
func TestSendMessage_VoiceAndDocument(t *testing.T) {
|
|
t.Parallel()
|
|
for _, tc := range []struct {
|
|
name string
|
|
ct adapter.ContentType
|
|
media adapter.Media
|
|
wantMethod string
|
|
wantField string
|
|
}{
|
|
{name: "voice", ct: adapter.ContentVoice, media: adapter.Media{Kind: adapter.MediaVoice, URL: "voice-1"}, wantMethod: "sendVoice", wantField: "voice"},
|
|
{name: "document", ct: adapter.ContentFile, media: adapter.Media{Kind: adapter.MediaFile, URL: "doc-1", Name: "x.pdf"}, wantMethod: "sendDocument", wantField: "document"},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
var mu sync.Mutex
|
|
var body map[string]any
|
|
var method string
|
|
a, _ := newTestAdapter(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
m := methodOf(r)
|
|
mu.Lock()
|
|
method = m
|
|
body = decodeBody(r)
|
|
mu.Unlock()
|
|
if m != tc.wantMethod {
|
|
http.Error(w, "unexpected method: "+m, http.StatusNotFound)
|
|
return
|
|
}
|
|
okResponse(w, map[string]any{"message_id": 103})
|
|
}))
|
|
if err := a.Connect(context.Background()); err != nil {
|
|
t.Fatalf("Connect: %v", err)
|
|
}
|
|
resp := adapter.UnifiedResponse{
|
|
Messages: []adapter.UnifiedMessage{
|
|
{ChannelID: "200", ChannelType: adapter.ChannelDirect, ContentType: tc.ct, Media: []adapter.Media{tc.media}},
|
|
},
|
|
}
|
|
if err := a.SendMessage(context.Background(), resp); err != nil {
|
|
t.Fatalf("SendMessage: %v", err)
|
|
}
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
if method != tc.wantMethod {
|
|
t.Errorf("method = %q, want %q", method, tc.wantMethod)
|
|
}
|
|
field := tc.media.URL
|
|
if body[tc.wantField] != field {
|
|
t.Errorf("%s = %v, want %v", tc.wantField, body[tc.wantField], field)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSendProactive(t *testing.T) {
|
|
t.Parallel()
|
|
var mu sync.Mutex
|
|
var body map[string]any
|
|
a, _ := newTestAdapter(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if methodOf(r) != "sendMessage" {
|
|
http.Error(w, "unexpected method", http.StatusNotFound)
|
|
return
|
|
}
|
|
mu.Lock()
|
|
body = decodeBody(r)
|
|
mu.Unlock()
|
|
okResponse(w, map[string]any{"message_id": 104})
|
|
}))
|
|
if err := a.Connect(context.Background()); err != nil {
|
|
t.Fatalf("Connect: %v", err)
|
|
}
|
|
msg := adapter.UnifiedMessage{
|
|
ChannelID: "300", ChannelType: adapter.ChannelDirect,
|
|
Content: "reminder", ContentType: adapter.ContentText,
|
|
}
|
|
if err := a.SendProactive(context.Background(), msg); err != nil {
|
|
t.Fatalf("SendProactive: %v", err)
|
|
}
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
if body["chat_id"] != "300" || body["text"] != "reminder" {
|
|
t.Errorf("body = %v, want chat_id 300 text reminder", body)
|
|
}
|
|
if _, ok := body["reply_to_message_id"]; ok {
|
|
t.Errorf("proactive message should not carry reply_to_message_id, got %v", body)
|
|
}
|
|
}
|
|
|
|
func TestSendMessage_NotConnected(t *testing.T) {
|
|
t.Parallel()
|
|
a := New(Config{BotToken: "x"})
|
|
err := a.SendMessage(context.Background(), adapter.UnifiedResponse{})
|
|
if !errors.Is(err, adapter.ErrNotConnected) {
|
|
t.Fatalf("err = %v, want ErrNotConnected", err)
|
|
}
|
|
if err := a.SendProactive(context.Background(), adapter.UnifiedMessage{}); !errors.Is(err, adapter.ErrNotConnected) {
|
|
t.Fatalf("SendProactive err = %v, want ErrNotConnected", err)
|
|
}
|
|
}
|
|
|
|
func TestSendMessage_NoTargetChat(t *testing.T) {
|
|
t.Parallel()
|
|
a, _ := newTestAdapter(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
http.Error(w, "unexpected method", http.StatusNotFound)
|
|
}))
|
|
if err := a.Connect(context.Background()); err != nil {
|
|
t.Fatalf("Connect: %v", err)
|
|
}
|
|
err := a.SendMessage(context.Background(), adapter.UnifiedResponse{
|
|
Messages: []adapter.UnifiedMessage{{ContentType: adapter.ContentText, Content: "hi"}},
|
|
})
|
|
if !errors.Is(err, ErrNoChatTarget) {
|
|
t.Fatalf("err = %v, want ErrNoChatTarget", err)
|
|
}
|
|
}
|
|
|
|
func TestSendMessage_APIError(t *testing.T) {
|
|
t.Parallel()
|
|
a, _ := newTestAdapter(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if methodOf(r) != "sendMessage" {
|
|
http.Error(w, "unexpected method", http.StatusNotFound)
|
|
return
|
|
}
|
|
failResponse(w, "Bad Request: chat not found", 400)
|
|
}))
|
|
if err := a.Connect(context.Background()); err != nil {
|
|
t.Fatalf("Connect: %v", err)
|
|
}
|
|
err := a.SendMessage(context.Background(), adapter.UnifiedResponse{
|
|
Messages: []adapter.UnifiedMessage{{ChannelID: "999", ContentType: adapter.ContentText, Content: "hi"}},
|
|
})
|
|
if !errors.Is(err, ErrBadResponse) {
|
|
t.Fatalf("err = %v, want ErrBadResponse", err)
|
|
}
|
|
}
|
|
|
|
func TestProcessUpdate_DeliversToHandler(t *testing.T) {
|
|
t.Parallel()
|
|
a, _ := newTestAdapter(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
http.Error(w, "unexpected method", http.StatusNotFound)
|
|
}))
|
|
if err := a.Connect(context.Background()); err != nil {
|
|
t.Fatalf("Connect: %v", err)
|
|
}
|
|
|
|
var mu sync.Mutex
|
|
var got *adapter.UnifiedMessage
|
|
a.SetMessageHandler(func(ctx context.Context, m adapter.UnifiedMessage) {
|
|
mu.Lock()
|
|
got = &m
|
|
mu.Unlock()
|
|
})
|
|
raw := []byte(`{"update_id":1,"message":{"message_id":5,"from":{"id":1,"first_name":"Ada"},"chat":{"id":10,"type":"private"},"date":1700000000,"text":"webhook hi"}}`)
|
|
n, err := a.ProcessUpdate(context.Background(), raw)
|
|
if err != nil {
|
|
t.Fatalf("ProcessUpdate: %v", err)
|
|
}
|
|
if n != 1 {
|
|
t.Errorf("delivered %d messages, want 1", n)
|
|
}
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
if got == nil {
|
|
t.Fatal("handler was not called")
|
|
}
|
|
if got.Content != "webhook hi" || got.Platform != "telegram" {
|
|
t.Errorf("message = %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestProcessUpdate_NonMessageIgnored(t *testing.T) {
|
|
t.Parallel()
|
|
a := New(Config{BotToken: "x"})
|
|
raw := []byte(`{"update_id":2,"callback_query":{"id":"cq","from":{"id":1},"data":"btn"}}`)
|
|
n, err := a.ProcessUpdate(context.Background(), raw)
|
|
if err != nil {
|
|
t.Fatalf("ProcessUpdate: %v", err)
|
|
}
|
|
if n != 0 {
|
|
t.Errorf("delivered %d messages, want 0 for callback query", n)
|
|
}
|
|
}
|
|
|
|
func TestProcessUpdate_InvalidJSON(t *testing.T) {
|
|
t.Parallel()
|
|
a := New(Config{BotToken: "x"})
|
|
if _, err := a.ProcessUpdate(context.Background(), []byte("{not json")); err == nil {
|
|
t.Fatal("expected error decoding invalid JSON")
|
|
}
|
|
}
|