251 lines
5.6 KiB
Go
251 lines
5.6 KiB
Go
package telegram
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/armon/go-socks5"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type mockTelegramServer struct {
|
|
mu sync.Mutex
|
|
messages []mockMessage
|
|
updateID int64
|
|
}
|
|
|
|
type mockMessage struct {
|
|
chatID int64
|
|
text string
|
|
}
|
|
|
|
func (m *mockTelegramServer) addMessage(chatID int64, text string) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
m.messages = append(m.messages, mockMessage{chatID: chatID, text: text})
|
|
}
|
|
|
|
func (m *mockTelegramServer) handler() http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.URL.Path {
|
|
case "/bottest_token/getMe":
|
|
json.NewEncoder(w).Encode(map[string]any{
|
|
"ok": true,
|
|
"result": map[string]string{"username": "test_bot"},
|
|
})
|
|
case "/bottest_token/sendMessage":
|
|
json.NewEncoder(w).Encode(map[string]any{
|
|
"ok": true,
|
|
"result": map[string]any{"message_id": 1},
|
|
})
|
|
case "/bottest_token/getUpdates":
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
updates := []map[string]any{}
|
|
for _, msg := range m.messages {
|
|
m.updateID++
|
|
updates = append(updates, map[string]any{
|
|
"update_id": m.updateID,
|
|
"message": map[string]any{
|
|
"message_id": m.updateID,
|
|
"chat": map[string]any{"id": msg.chatID},
|
|
"text": msg.text,
|
|
},
|
|
})
|
|
}
|
|
m.messages = nil
|
|
|
|
json.NewEncoder(w).Encode(map[string]any{
|
|
"ok": true,
|
|
"result": updates,
|
|
})
|
|
default:
|
|
w.WriteHeader(http.StatusNotFound)
|
|
}
|
|
}
|
|
}
|
|
|
|
func startMockSocks5Server(t *testing.T) (string, func()) {
|
|
conf := &socks5.Config{}
|
|
server, err := socks5.New(conf)
|
|
require.NoError(t, err)
|
|
|
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
|
require.NoError(t, err)
|
|
|
|
port := listener.Addr().(*net.TCPAddr).Port
|
|
proxyURL := fmt.Sprintf("socks5://127.0.0.1:%d", port)
|
|
|
|
go func() {
|
|
_ = server.Serve(listener)
|
|
}()
|
|
|
|
return proxyURL, func() {
|
|
listener.Close()
|
|
}
|
|
}
|
|
|
|
func newMockServer() (*httptest.Server, *mockTelegramServer) {
|
|
mock := &mockTelegramServer{}
|
|
return httptest.NewServer(mock.handler()), mock
|
|
}
|
|
|
|
func newTestBot(t *testing.T, server *httptest.Server) *Bot {
|
|
t.Helper()
|
|
bot, err := New("test_token", server.URL+"/bot%s/%s", "")
|
|
require.NoError(t, err)
|
|
return bot
|
|
}
|
|
|
|
func TestNew_MockAPI(t *testing.T) {
|
|
server, _ := newMockServer()
|
|
defer server.Close()
|
|
|
|
bot := newTestBot(t, server)
|
|
assert.NotNil(t, bot)
|
|
}
|
|
|
|
func TestNew_Error(t *testing.T) {
|
|
server, _ := newMockServer()
|
|
defer server.Close()
|
|
|
|
_, err := New("test_token", server.URL+"/invalid", "")
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestNew_GetMeError(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path == "/bottest_token/getMe" {
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
w.Write([]byte(`{"ok":false,"description":"Unauthorized"}`))
|
|
return
|
|
}
|
|
json.NewEncoder(w).Encode(map[string]any{"ok": true, "result": map[string]string{}})
|
|
}))
|
|
defer server.Close()
|
|
|
|
_, err := New("test_token", server.URL+"/bot%s/%s", "")
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "failed to connect to telegram")
|
|
}
|
|
|
|
func TestSend_MockAPI(t *testing.T) {
|
|
server, _ := newMockServer()
|
|
defer server.Close()
|
|
|
|
bot := newTestBot(t, server)
|
|
err := bot.Send(t.Context(), "123456789", "hello")
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestSend_InvalidID(t *testing.T) {
|
|
server, _ := newMockServer()
|
|
defer server.Close()
|
|
|
|
bot := newTestBot(t, server)
|
|
err := bot.Send(t.Context(), "not_a_number", "test")
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestReceive_MockAPI(t *testing.T) {
|
|
server, mock := newMockServer()
|
|
defer server.Close()
|
|
|
|
bot := newTestBot(t, server)
|
|
mock.addMessage(123456789, "hello world")
|
|
|
|
ctx2, cancel := context.WithCancel(t.Context())
|
|
defer cancel()
|
|
|
|
msgs := bot.Receive(ctx2)
|
|
|
|
select {
|
|
case msg := <-msgs:
|
|
assert.Equal(t, "123456789", msg.ID)
|
|
assert.Equal(t, "hello world", msg.Text)
|
|
assert.Equal(t, "telegram", msg.Chat)
|
|
case <-time.After(time.Second):
|
|
t.Fatal("timeout waiting for message")
|
|
}
|
|
}
|
|
|
|
func TestReceive_ContextCancel(t *testing.T) {
|
|
server, _ := newMockServer()
|
|
defer server.Close()
|
|
|
|
bot := newTestBot(t, server)
|
|
|
|
ctx, cancel := context.WithCancel(t.Context())
|
|
msgs := bot.Receive(ctx)
|
|
cancel()
|
|
|
|
select {
|
|
case _, ok := <-msgs:
|
|
assert.False(t, ok)
|
|
case <-time.After(time.Second):
|
|
t.Fatal("channel not closed after context cancel")
|
|
}
|
|
}
|
|
|
|
func TestReceive_ContextCancelDuringSend(t *testing.T) {
|
|
server, mock := newMockServer()
|
|
defer server.Close()
|
|
|
|
bot := newTestBot(t, server)
|
|
mock.addMessage(123456789, "hello world")
|
|
|
|
ctx, cancel := context.WithCancel(t.Context())
|
|
msgs := bot.Receive(ctx)
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
cancel()
|
|
|
|
select {
|
|
case _, ok := <-msgs:
|
|
assert.False(t, ok)
|
|
case <-time.After(time.Second):
|
|
t.Fatal("channel not closed")
|
|
}
|
|
}
|
|
|
|
func TestNew_WithProxy(t *testing.T) {
|
|
proxyURL, cleanup := startMockSocks5Server(t)
|
|
defer cleanup()
|
|
|
|
server, _ := newMockServer()
|
|
defer server.Close()
|
|
|
|
bot, err := New("test_token", server.URL+"/bot%s/%s", proxyURL)
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, bot)
|
|
}
|
|
|
|
func TestNew_WithProxy_InvalidURL(t *testing.T) {
|
|
_, err := New("test_token", "http://api.telegram.org", "invalid-url")
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestNew_WithProxy_ConnectionRefused(t *testing.T) {
|
|
_, err := New("test_token", "http://api.telegram.org", "socks5://localhost:1081")
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestNew_WithoutProxy(t *testing.T) {
|
|
server, _ := newMockServer()
|
|
defer server.Close()
|
|
|
|
bot, err := New("test_token", server.URL+"/bot%s/%s", "")
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, bot)
|
|
}
|