mirror of
https://github.com/lukaszraczylo/go-telegram.git
synced 2026-06-05 22:43:59 +00:00
ac7cae8fa7
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
154 lines
4.2 KiB
Go
154 lines
4.2 KiB
Go
package dispatch
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/lukaszraczylo/go-telegram/api"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// makeMsg returns a minimal *api.Message for use in handler tests.
|
|
func makeMsg() *api.Message {
|
|
return &api.Message{MessageID: 1, Chat: api.Chat{ID: 1, Type: "private"}}
|
|
}
|
|
|
|
// makeCtx returns a minimal *Context (nil bot is fine for unit tests).
|
|
func makeCtx() *Context {
|
|
return NewContext(context.Background(), nil, &api.Update{})
|
|
}
|
|
|
|
func TestNamedHandlers_SetAndHas(t *testing.T) {
|
|
n := NewNamedHandlers[*api.Message]()
|
|
require.False(t, n.Has("a"))
|
|
n.Set("a", func(c *Context, m *api.Message) error { return nil })
|
|
require.True(t, n.Has("a"))
|
|
}
|
|
|
|
func TestNamedHandlers_Names_RegistrationOrder(t *testing.T) {
|
|
n := NewNamedHandlers[*api.Message]()
|
|
n.Set("first", func(c *Context, m *api.Message) error { return nil })
|
|
n.Set("second", func(c *Context, m *api.Message) error { return nil })
|
|
n.Set("third", func(c *Context, m *api.Message) error { return nil })
|
|
require.Equal(t, []string{"first", "second", "third"}, n.Names())
|
|
}
|
|
|
|
func TestNamedHandlers_Remove(t *testing.T) {
|
|
n := NewNamedHandlers[*api.Message]()
|
|
n.Set("a", func(c *Context, m *api.Message) error { return nil })
|
|
n.Set("b", func(c *Context, m *api.Message) error { return nil })
|
|
|
|
removed := n.Remove("a")
|
|
require.True(t, removed)
|
|
require.False(t, n.Has("a"))
|
|
require.Equal(t, []string{"b"}, n.Names())
|
|
|
|
// Remove non-existent returns false.
|
|
require.False(t, n.Remove("nonexistent"))
|
|
}
|
|
|
|
func TestNamedHandlers_Replacement_SameOrderSlot(t *testing.T) {
|
|
n := NewNamedHandlers[*api.Message]()
|
|
n.Set("a", func(c *Context, m *api.Message) error { return nil })
|
|
n.Set("b", func(c *Context, m *api.Message) error { return nil })
|
|
|
|
var called string
|
|
n.Set("a", func(c *Context, m *api.Message) error {
|
|
called = "replaced-a"
|
|
return nil
|
|
})
|
|
|
|
// Order must not change; "a" stays first.
|
|
require.Equal(t, []string{"a", "b"}, n.Names())
|
|
|
|
h := n.Handler()
|
|
_ = h(makeCtx(), makeMsg())
|
|
require.Equal(t, "replaced-a", called)
|
|
}
|
|
|
|
func TestNamedHandlers_Handler_RunsInOrder(t *testing.T) {
|
|
n := NewNamedHandlers[*api.Message]()
|
|
var calls []string
|
|
|
|
n.Set("first", func(c *Context, m *api.Message) error {
|
|
calls = append(calls, "first")
|
|
return nil
|
|
})
|
|
n.Set("second", func(c *Context, m *api.Message) error {
|
|
calls = append(calls, "second")
|
|
return nil
|
|
})
|
|
|
|
h := n.Handler()
|
|
require.NoError(t, h(makeCtx(), makeMsg()))
|
|
require.Equal(t, []string{"first", "second"}, calls)
|
|
}
|
|
|
|
func TestNamedHandlers_Handler_ErrorWrappedAndStops(t *testing.T) {
|
|
n := NewNamedHandlers[*api.Message]()
|
|
sentinel := errors.New("boom")
|
|
|
|
n.Set("ok", func(c *Context, m *api.Message) error { return nil })
|
|
n.Set("fail", func(c *Context, m *api.Message) error { return sentinel })
|
|
n.Set("never", func(c *Context, m *api.Message) error {
|
|
t.Fatal("should not be called after an error")
|
|
return nil
|
|
})
|
|
|
|
h := n.Handler()
|
|
err := h(makeCtx(), makeMsg())
|
|
require.Error(t, err)
|
|
require.True(t, errors.Is(err, sentinel))
|
|
require.Contains(t, err.Error(), `named handler "fail"`)
|
|
}
|
|
|
|
func TestNamedHandlers_Concurrent_SetRemove(t *testing.T) {
|
|
n := NewNamedHandlers[*api.Message]()
|
|
|
|
// Pre-populate so Handler() has something to iterate.
|
|
for i := range 5 {
|
|
name := fmt.Sprintf("h%d", i)
|
|
n.Set(name, func(c *Context, m *api.Message) error { return nil })
|
|
}
|
|
|
|
h := n.Handler()
|
|
var wg sync.WaitGroup
|
|
|
|
// Concurrent readers (invoke handler).
|
|
for range 20 {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
_ = h(makeCtx(), makeMsg())
|
|
}()
|
|
}
|
|
|
|
// Concurrent writers.
|
|
for i := range 5 {
|
|
wg.Add(1)
|
|
go func(i int) {
|
|
defer wg.Done()
|
|
name := fmt.Sprintf("new%d", i)
|
|
n.Set(name, func(c *Context, m *api.Message) error { return nil })
|
|
n.Remove(fmt.Sprintf("h%d", i))
|
|
}(i)
|
|
}
|
|
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestNamedHandlers_RemoveAndReinstate(t *testing.T) {
|
|
n := NewNamedHandlers[*api.Message]()
|
|
n.Set("a", func(c *Context, m *api.Message) error { return nil })
|
|
n.Remove("a")
|
|
require.False(t, n.Has("a"))
|
|
|
|
// Re-register after removal; should be added at end.
|
|
n.Set("b", func(c *Context, m *api.Message) error { return nil })
|
|
n.Set("a", func(c *Context, m *api.Message) error { return nil })
|
|
require.Equal(t, []string{"b", "a"}, n.Names())
|
|
}
|