Files
MeBox/internal/service/telegram_api_test.go
T
2026-06-10 20:43:42 +08:00

234 lines
6.8 KiB
Go

package service
import (
"context"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/ShukeBta/MediaStationGo/internal/model"
)
func TestTelegramMethodURLUsesCustomAPIBase(t *testing.T) {
got, err := telegramMethodURL(map[string]string{
"api_base_url": "https://tg.example.com/",
}, "123456:ABC-def", "sendMessage")
if err != nil {
t.Fatalf("telegramMethodURL returned error: %v", err)
}
want := "https://tg.example.com/bot123456:ABC-def/sendMessage"
if got != want {
t.Fatalf("got %q, want %q", got, want)
}
}
func TestSanitizeTelegramErrorRedactsBotToken(t *testing.T) {
err := sanitizeTelegramError(errors.New(`Post "https://api.telegram.org/bot123456:SECRET/sendMessage": context deadline exceeded`))
if err == nil {
t.Fatal("expected error")
}
msg := err.Error()
if strings.Contains(msg, "SECRET") || strings.Contains(msg, "123456:") {
t.Fatalf("telegram token leaked in error: %s", msg)
}
if !strings.Contains(msg, "timeout") {
t.Fatalf("expected timeout hint, got: %s", msg)
}
}
func TestValidateTelegramChannelDoesNotRequireLegacyChatID(t *testing.T) {
err := validateChannel(ChannelInput{
Name: "Telegram",
Type: "telegram",
Config: map[string]any{
"bot_token": "123456:ABC-def",
"admin_user_ids": "10001",
},
})
if err != nil {
t.Fatalf("validateChannel returned error: %v", err)
}
}
func TestTelegramTargetChatIDsFallsBackToAdmins(t *testing.T) {
got := telegramTargetChatIDs(map[string]string{
"admin_user_ids": "10001, 10002",
})
if len(got) != 2 || got[0] != "10001" || got[1] != "10002" {
t.Fatalf("got %#v, want admin user ids", got)
}
}
func TestNormalizeTelegramChannelMigratesLegacyChatID(t *testing.T) {
input := ChannelInput{
Name: "Telegram",
Type: "telegram",
Config: map[string]any{
"chat_id": "-10001",
},
}
normalizeChannelInput(&input)
if got := str(input.Config["group_chat_id"]); got != "-10001" {
t.Fatalf("group_chat_id = %q, want -10001", got)
}
}
func TestNormalizeTelegramChannelMigratesLegacyPrivateChatIDToAdmin(t *testing.T) {
cfg := map[string]string{"chat_id": "5812333517"}
normalizeTelegramConfig(cfg)
if got := cfg["admin_user_ids"]; got != "5812333517" {
t.Fatalf("admin_user_ids = %q, want legacy chat_id", got)
}
}
func TestTelegramTargetChatIDsUsesLegacyPrivateChatID(t *testing.T) {
got := telegramTargetChatIDs(map[string]string{
"chat_id": "5812333517",
})
if len(got) != 1 || got[0] != "5812333517" {
t.Fatalf("got %#v, want legacy private chat target", got)
}
}
func TestRegisterTelegramBotCommands(t *testing.T) {
var gotPath string
var payload struct {
Commands []telegramBotCommand `json:"commands"`
}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotPath = r.URL.Path
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
t.Fatalf("decode payload: %v", err)
}
_, _ = w.Write([]byte(`{"ok":true}`))
}))
defer server.Close()
err := registerTelegramBotCommands(t.Context(), map[string]string{
"bot_token": "123456:ABC",
"api_base_url": server.URL,
})
if err != nil {
t.Fatal(err)
}
if gotPath != "/bot123456:ABC/setMyCommands" {
t.Fatalf("path = %q", gotPath)
}
if len(payload.Commands) == 0 || payload.Commands[0].Command != "start" {
t.Fatalf("commands not registered: %#v", payload.Commands)
}
}
func TestTelegramProxyCandidatesDefaultLocalFallbacks(t *testing.T) {
got := telegramProxyCandidates(map[string]string{})
joined := strings.Join(got, ",")
for _, want := range []string{"host.docker.internal:20171", "socks5://172.17.0.1:20170", "127.0.0.1:10808", "172.17.0.1:7890"} {
if !strings.Contains(joined, want) {
t.Fatalf("default proxy candidates %q missing %q", joined, want)
}
}
}
func TestTelegramHTTPClientsPreferConfiguredProxy(t *testing.T) {
clients := telegramHTTPClients(time.Second, map[string]string{
"proxy_url": "http://proxy.example:7890",
})
if len(clients) == 0 {
t.Fatal("expected telegram clients")
}
if got := telegramClientProxyString(t, clients[0]); got != "http://proxy.example:7890" {
t.Fatalf("first client proxy = %q, want configured proxy", got)
}
}
func telegramClientProxyString(t *testing.T, client *http.Client) string {
t.Helper()
transport, ok := client.Transport.(*http.Transport)
if !ok || transport.Proxy == nil {
return ""
}
req, err := http.NewRequest(http.MethodGet, defaultTelegramAPIBaseURL, nil)
if err != nil {
t.Fatal(err)
}
proxyURL, err := transport.Proxy(req)
if err != nil {
t.Fatal(err)
}
if proxyURL == nil {
return ""
}
return proxyURL.String()
}
func TestTelegramReplyAutoDeletesSentMessage(t *testing.T) {
requests := make(chan string, 4)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case strings.HasSuffix(r.URL.Path, "/sendMessage"):
requests <- "sendMessage"
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"ok":true,"result":{"message_id":777}}`))
case strings.HasSuffix(r.URL.Path, "/deleteMessage"):
requests <- "deleteMessage"
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"ok":true,"result":true}`))
default:
http.NotFound(w, r)
}
}))
defer server.Close()
cfg, _ := json.Marshal(map[string]string{
"bot_token": "123456:ABC-def",
"api_base_url": server.URL,
"auto_delete_seconds": "0",
})
_, bot := newBotTestService(t)
channel := &model.NotifyChannel{Name: "Telegram", Type: "telegram", Enabled: true, Config: string(cfg)}
if err := bot.reply(context.Background(), channel, 42, telegramCommandReply{Text: "hello"}); err != nil {
t.Fatalf("reply: %v", err)
}
waitForTelegramMethod(t, requests, "sendMessage")
waitForTelegramMethod(t, requests, "deleteMessage")
}
func waitForTelegramMethod(t *testing.T, requests <-chan string, want string) {
t.Helper()
deadline := time.After(2 * time.Second)
for {
select {
case got := <-requests:
if got == want {
return
}
case <-deadline:
t.Fatalf("timed out waiting for telegram %s", want)
}
}
}
func TestTelegramCommandFiltering(t *testing.T) {
if telegramIsCommandText("今天看什么") {
t.Fatal("plain chat message should not be treated as command")
}
if !telegramIsCommandText("/start user pass") {
t.Fatal("/start should be treated as command")
}
if got := telegramCommandName("/hideadult@MediaStationGoBot on"); got != "/hideadult" {
t.Fatalf("telegramCommandName = %q, want /hideadult", got)
}
if telegramSupportedCommand("/签到") {
t.Fatal("unrelated group bot command should not be handled")
}
for _, cmd := range []string{"/signin", "/redeem", "/gencode", "/users", "/renew_user", "/delete_user", "/cleanup_rule"} {
if !telegramSupportedCommand(cmd) {
t.Fatalf("%s should be supported so group slash commands get feedback", cmd)
}
}
}