package telegram import ( "context" "encoding/json" "net/http" "net/http/httptest" "strings" "sync" "testing" "time" "github.com/kamelion/ratatoskr-go/internal/chat" ) // fakeTG — тестовый сервер, имитирующий Telegram Bot API. type fakeTG struct { srv *httptest.Server mu sync.Mutex sent []sendReq updCh chan []update // следующая пачка обновлений closed bool } type sendReq struct { ChatID string `json:"chat_id"` Text string `json:"text"` } func newFakeTG(t *testing.T) *fakeTG { t.Helper() f := &fakeTG{updCh: make(chan []update, 10)} mux := http.NewServeMux() mux.HandleFunc("/botTOKEN/sendMessage", func(w http.ResponseWriter, r *http.Request) { var req sendReq json.NewDecoder(r.Body).Decode(&req) f.mu.Lock() f.sent = append(f.sent, req) f.mu.Unlock() w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]bool{"ok": true}) }) mux.HandleFunc("/botTOKEN/getUpdates", func(w http.ResponseWriter, r *http.Request) { f.mu.Lock() upds := []update{} if len(f.updCh) > 0 { upds = <-f.updCh } f.mu.Unlock() w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]any{"ok": true, "result": upds}) }) f.srv = httptest.NewServer(mux) t.Cleanup(f.srv.Close) return f } func (f *fakeTG) push(text string) { msg := message{MessageID: int64(len(f.sent) + 1), Text: text} msg.Chat.ID = 123 f.updCh <- []update{{ UpdateID: int64(len(f.sent) + 1), Message: &msg, }} } func TestSend(t *testing.T) { f := newFakeTG(t) ch := New("TOKEN", time.Second) ch.client = f.srv.Client() // подменяем baseURL на тестовый ch.apiURL = f.srv.URL + "/bot%s/" err := ch.Send(context.Background(), "tg://123", chat.Message{Text: "hello"}) if err != nil { t.Fatalf("Send: %v", err) } if len(f.sent) != 1 { t.Fatal("не отправлено") } if f.sent[0].Text != "hello" { t.Errorf("text = %q", f.sent[0].Text) } } func TestSendWithOptions(t *testing.T) { f := newFakeTG(t) ch := New("TOKEN", time.Second) ch.client = f.srv.Client() ch.apiURL = f.srv.URL + "/bot%s/" err := ch.Send(context.Background(), "tg://123", chat.Message{ Text: "Выбери:", Options: []chat.Option{ {ID: "a", Label: "Чай"}, {ID: "b", Label: "Кофе"}, }, }) if err != nil { t.Fatalf("Send: %v", err) } if !strings.Contains(f.sent[0].Text, "1. Чай") { t.Errorf("формат options: %s", f.sent[0].Text) } if !strings.Contains(f.sent[0].Text, "2. Кофе") { t.Errorf("формат options: %s", f.sent[0].Text) } } func TestIncoming(t *testing.T) { f := newFakeTG(t) ch := New("TOKEN", time.Second) ch.client = f.srv.Client() ch.apiURL = f.srv.URL + "/bot%s/" incoming := make(chan chat.Incoming, 1) ch.OnMessage(func(inc chat.Incoming) { incoming <- inc }) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() go ch.Run(ctx) f.push("тест") select { case inc := <-incoming: if inc.UserID != "123" { t.Errorf("UserID = %q, want 123", inc.UserID) } if inc.Address != "tg://123" { t.Errorf("Address = %q", inc.Address) } if inc.Msg.Text != "тест" { t.Errorf("text = %q", inc.Msg.Text) } case <-ctx.Done(): t.Fatal("timeout ожидания входящего") } }