mirror of
https://github.com/lukaszraczylo/go-telegram.git
synced 2026-07-04 17:45:26 +00:00
Initial release of go-telegram
A fully-generated, strongly-typed Go client for the Telegram Bot API. * 176 methods + 301 types generated from Bot API v10.0 * 1408 auto-generated tests (8 scenarios per method) * Typed unions throughout — no 'any' in the public surface * Pluggable HTTP transport and JSON codec (default goccy/go-json) * Built-in retry middleware honouring Telegram's retry_after * Generic dispatcher with filters and conversation handlers * Self-verifying codegen pipeline (regen → audit → emit → run tests) * 14 example bots covering common patterns
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
package transport
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/lukaszraczylo/go-telegram/client"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestWebhook_DeliversUpdate(t *testing.T) {
|
||||
b := client.New("t")
|
||||
w := NewWebhookServer(b)
|
||||
w.SecretToken = "secret"
|
||||
|
||||
srv := httptest.NewServer(w)
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
body := `{"update_id":1,"message":{"message_id":1,"date":0,"chat":{"id":1,"type":"private"},"text":"hi"}}`
|
||||
req, _ := http.NewRequest(http.MethodPost, srv.URL, strings.NewReader(body))
|
||||
req.Header.Set("X-Telegram-Bot-Api-Secret-Token", "secret")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
_ = resp.Body.Close()
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
select {
|
||||
case u := <-w.Updates():
|
||||
require.Equal(t, int64(1), u.UpdateID)
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("update not delivered")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebhook_RejectsBadSecret(t *testing.T) {
|
||||
b := client.New("t")
|
||||
w := NewWebhookServer(b)
|
||||
w.SecretToken = "secret"
|
||||
|
||||
srv := httptest.NewServer(w)
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
req, _ := http.NewRequest(http.MethodPost, srv.URL, strings.NewReader(`{}`))
|
||||
req.Header.Set("X-Telegram-Bot-Api-Secret-Token", "wrong")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
_ = resp.Body.Close()
|
||||
require.Equal(t, http.StatusUnauthorized, resp.StatusCode)
|
||||
}
|
||||
|
||||
func TestWebhook_RejectsNonPOST(t *testing.T) {
|
||||
w := NewWebhookServer(client.New("t"))
|
||||
srv := httptest.NewServer(w)
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
resp, err := http.Get(srv.URL)
|
||||
require.NoError(t, err)
|
||||
_ = resp.Body.Close()
|
||||
require.Equal(t, http.StatusMethodNotAllowed, resp.StatusCode)
|
||||
}
|
||||
|
||||
func TestWebhook_RejectsBadJSON(t *testing.T) {
|
||||
w := NewWebhookServer(client.New("t"))
|
||||
srv := httptest.NewServer(w)
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
resp, err := http.Post(srv.URL, "application/json", bytes.NewBufferString("not json"))
|
||||
require.NoError(t, err)
|
||||
_ = resp.Body.Close()
|
||||
require.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
||||
}
|
||||
|
||||
func TestWebhook_StopExitsRun(t *testing.T) {
|
||||
w := NewWebhookServer(client.New("t"))
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() { _ = w.Run(context.Background()); close(done) }()
|
||||
|
||||
require.NoError(t, w.Stop(context.Background()))
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("Run did not exit after Stop")
|
||||
}
|
||||
}
|
||||
|
||||
// TestWebhook_ConcurrentStopNoPanic fires many concurrent requests while
|
||||
// simultaneously calling Stop, and asserts no panic (send on closed channel).
|
||||
// Run under -race to verify mutex and WaitGroup correctness.
|
||||
func TestWebhook_ConcurrentStopNoPanic(t *testing.T) {
|
||||
body := `{"update_id":1,"message":{"message_id":1,"date":0,"chat":{"id":1,"type":"private"},"text":"hi"}}`
|
||||
|
||||
for range 20 {
|
||||
w := NewWebhookServer(client.New("t"), WithBufferSize(256))
|
||||
srv := httptest.NewServer(w)
|
||||
|
||||
// Drain updates so ServeHTTP doesn't block on a full channel.
|
||||
go func() {
|
||||
for range w.Updates() {
|
||||
}
|
||||
}()
|
||||
|
||||
// Run in background.
|
||||
go func() { _ = w.Run(context.Background()) }()
|
||||
|
||||
// Fire concurrent requests.
|
||||
const goroutines = 20
|
||||
ready := make(chan struct{})
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < goroutines; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
<-ready
|
||||
for j := 0; j < 5; j++ {
|
||||
req, _ := http.NewRequest(http.MethodPost, srv.URL, strings.NewReader(body))
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err == nil {
|
||||
_ = resp.Body.Close()
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
close(ready)
|
||||
time.Sleep(5 * time.Millisecond) // let some requests land before Stop
|
||||
srv.Close()
|
||||
_ = w.Stop(context.Background())
|
||||
wg.Wait()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user