399 lines
8.0 KiB
Go
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")
|
|
}
|
|
}
|