1
bot/bot_test.go
negasus 6317bb0bb5 - add method bot.ID() int64
- add method `bot.Token() string`
2025-01-10 12:00:32 +03:00

399 lines
8.0 KiB
Go

package bot
import (
"context"
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"strings"
"sync"
"testing"
"time"
"github.com/go-telegram/bot/models"
)
type serverMock struct {
s *httptest.Server
custom map[string]any
hooks map[string]func(body []byte) any
hooksCalls map[string]int
updateIdx int
updates []*models.Update
token string
}
type getUpdatesResponse struct {
OK bool `json:"ok"`
Result []*models.Update `json:"result"`
}
func (s *serverMock) handler(rw http.ResponseWriter, req *http.Request) {
if req.URL.String() == "/bot"+s.token+"/getMe" {
_, err := rw.Write([]byte(`{"ok":true,"result":{}}`))
if err != nil {
panic(err)
}
return
}
if req.URL.String() == "/bot"+s.token+"/getUpdates" {
s.handlerGetUpdates(rw)
return
}
reqBody, errReadBody := io.ReadAll(req.Body)
if errReadBody != nil {
panic(errReadBody)
}
defer func() {
if err := req.Body.Close(); err != nil {
panic(err)
}
}()
hook, okHook := s.hooks[req.URL.String()]
if okHook {
s.hooksCalls[req.URL.String()]++
resp, errData := json.Marshal(hook(reqBody))
if errData != nil {
panic(errData)
}
_, err := rw.Write(resp)
if err != nil {
panic(err)
}
return
}
d, ok := s.custom[req.URL.String()]
if !ok {
panic("answer not found for request: " + req.URL.String())
}
resp, errData := json.Marshal(d)
if errData != nil {
panic(errData)
}
_, err := rw.Write(resp)
if err != nil {
panic(err)
}
}
func (s *serverMock) Close() {
s.s.Close()
}
func (s *serverMock) handlerGetUpdates(rw http.ResponseWriter) {
if s.updateIdx >= len(s.updates) {
_, err := rw.Write([]byte(`{"ok":true,"result":[]}`))
if err != nil {
panic(err)
}
return
}
s.updates[s.updateIdx].ID = int64(s.updateIdx + 1)
r := getUpdatesResponse{
OK: true,
Result: []*models.Update{s.updates[s.updateIdx]},
}
s.updateIdx++
d, err := json.Marshal(r)
if err != nil {
panic(err)
}
_, err = rw.Write(d)
if err != nil {
panic(err)
}
}
func (s *serverMock) URL() string {
return s.s.URL
}
func newServerMock(token string) *serverMock {
s := &serverMock{
token: token,
custom: map[string]any{},
hooks: map[string]func([]byte) any{},
hooksCalls: map[string]int{},
}
s.s = httptest.NewServer(http.HandlerFunc(s.handler))
return s
}
func TestNew_error_getMe(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
_, err := rw.Write([]byte(`{"ok":false,"description":"err1","error_code":400}`))
if err != nil {
panic(err)
}
}))
defer server.Close()
_, err := New("xxx", WithServerURL(server.URL))
if err == nil {
t.Fatal("unexpected nil error")
}
if err.Error() != "error call getMe, bad request, err1" {
t.Fatalf("wrong error message %q", err.Error())
}
}
func TestNew_emptyToken(t *testing.T) {
_, err := New("")
if err == nil {
t.Fatalf("expect error %q", err)
}
if err.Error() != "empty token" {
t.Fatalf("exexpected error %q", err)
}
}
func TestNew(t *testing.T) {
s := newServerMock("xxx")
defer s.Close()
_, err := New("xxx", WithServerURL(s.URL()))
if err != nil {
t.Fatalf("unexpected error %q", err)
}
}
func TestBot_StartWebhookWithCorrectSecret(t *testing.T) {
s := newServerMock("xxx")
defer s.Close()
opts := []Option{WithServerURL(s.URL()), WithWebhookSecretToken("zzzz")}
b, err := New("xxx", opts...)
if err != nil {
t.Fatalf("unexpected error %q", err)
}
mx := sync.Mutex{}
var called bool
b.defaultHandlerFunc = func(ctx context.Context, bot *Bot, update *models.Update) {
if update.Message.ID != 1 {
t.Errorf("unexpected message id")
}
mx.Lock()
called = true
mx.Unlock()
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go b.StartWebhook(ctx)
time.Sleep(time.Millisecond * 100)
req, errReq := http.NewRequest(http.MethodPost, "", strings.NewReader(`{"update_id":1,"message":{"message_id":1}}`))
if errReq != nil {
t.Error(errReq)
return
}
req.Header.Set("X-Telegram-Bot-Api-Secret-Token", "zzzz")
b.WebhookHandler().ServeHTTP(nil, req)
cancel()
time.Sleep(time.Millisecond * 100)
mx.Lock()
if !called {
t.Errorf("not called default handler")
}
mx.Unlock()
}
func TestBot_StartWebhookWithNoSecret(t *testing.T) {
s := newServerMock("xxx")
defer s.Close()
opts := []Option{WithServerURL(s.URL())}
b, err := New("xxx", opts...)
if err != nil {
t.Fatalf("unexpected error %q", err)
}
mx := sync.Mutex{}
var called bool
b.defaultHandlerFunc = func(ctx context.Context, bot *Bot, update *models.Update) {
if update.Message.ID != 1 {
t.Errorf("unexpected message id")
}
mx.Lock()
called = true
mx.Unlock()
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go b.StartWebhook(ctx)
time.Sleep(time.Millisecond * 100)
req, errReq := http.NewRequest(http.MethodPost, "", strings.NewReader(`{"update_id":1,"message":{"message_id":1}}`))
if errReq != nil {
t.Error(errReq)
return
}
b.WebhookHandler().ServeHTTP(nil, req)
cancel()
time.Sleep(time.Millisecond * 100)
mx.Lock()
if !called {
t.Errorf("not called default handler")
}
mx.Unlock()
}
func TestBot_StartWebhookWithWrongSecret(t *testing.T) {
s := newServerMock("xxx")
defer s.Close()
opts := []Option{WithServerURL(s.URL()), WithWebhookSecretToken("zzzz")}
b, err := New("xxx", opts...)
if err != nil {
t.Fatalf("unexpected error %q", err)
}
mx := sync.Mutex{}
var called bool
b.defaultHandlerFunc = func(ctx context.Context, bot *Bot, update *models.Update) {
if update.Message.ID != 1 {
t.Errorf("unexpected message id")
}
mx.Lock()
called = true
mx.Unlock()
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go b.StartWebhook(ctx)
time.Sleep(time.Millisecond * 100)
req, errReq := http.NewRequest(http.MethodPost, "", strings.NewReader(`{"update_id":1,"message":{"message_id":1}}`))
if errReq != nil {
t.Error(errReq)
return
}
req.Header.Set("X-Telegram-Bot-Api-Secret-Token", "wrong_secret")
b.WebhookHandler().ServeHTTP(nil, req)
cancel()
time.Sleep(time.Millisecond * 100)
mx.Lock()
if called {
t.Errorf("not supposed to call the default handler with wrong token")
}
mx.Unlock()
}
func TestBot_Start(t *testing.T) {
s := newServerMock("xxx")
defer s.Close()
s.updates = append(s.updates, &models.Update{Message: &models.Message{ID: 42}})
b, err := New("xxx", WithServerURL(s.URL()))
if err != nil {
t.Fatalf("unexpected error %q", err)
}
var called bool
b.defaultHandlerFunc = func(ctx context.Context, bot *Bot, update *models.Update) {
if update.Message.ID != 42 {
t.Errorf("unexpected message id %d", update.Message.ID)
}
called = true
}
ctx, cancel := context.WithCancel(context.Background())
go b.Start(ctx)
time.Sleep(time.Millisecond * 100)
cancel()
time.Sleep(time.Millisecond * 100)
if !called {
t.Errorf("not called default handler")
}
}
func TestBot_ID(t *testing.T) {
type fields struct {
token string
}
tests := []struct {
name string
fields fields
want int64
}{
{name: "empty token", fields: fields{token: ""}, want: 0},
{name: "no colon", fields: fields{token: "xxx"}, want: 0},
{name: "bad value", fields: fields{token: "123xxx:xxx"}, want: 0},
{name: "bad value", fields: fields{token: ":xxx"}, want: 0},
{name: "ok", fields: fields{token: "123456:xxx"}, want: 123456},
{name: "two colon", fields: fields{token: "123456:5678:xxx"}, want: 123456},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b := &Bot{
token: tt.fields.token,
}
if got := b.ID(); got != tt.want {
t.Errorf("ID() = %v, want %v", got, tt.want)
}
})
}
}
func TestBot_Token(t *testing.T) {
b := &Bot{token: "123456:xxx"}
token := b.Token()
if token != "123456:xxx" {
t.Errorf("Token() = %s, want %s", token, "123456:xxx")
}
}
func TestBot_SetToken(t *testing.T) {
b := &Bot{}
b.SetToken("123456:xxx")
if b.token != "123456:xxx" {
t.Errorf("SetToken() = %s, want %s", b.token, "123456:xxx")
}
}