)
const (
- // testBotToken is the bot token of the tests. It has the form of a real
- // token.
- testBotToken = "123456:TEST-secret_token"
+ // telegramTestToken is the bot token of the tests. It has the form of a
+ // real token.
+ telegramTestToken = "123456:TEST-secret_token"
- // testOwner is the user ID of the owner of the test bot.
- testOwner = 4242
+ // telegramTestOwner is the user ID of the owner of the test bot.
+ telegramTestOwner = 4242
)
func TestGetUpdatesAsksForMessagesFromTheOffset(t *testing.T) {
- server := newFakeBot(t)
- server.Add(botUpdate{
+ server := newTelegramFake(t)
+ server.Add(TelegramUpdate{
UpdateID: 7,
- Message: ownerMessage("hello"),
+ Message: telegramOwnerMessage("hello"),
})
- updates, err := newBotAPI(server.URL, testBotToken).GetUpdates(t.Context(), 7)
+ updates, err := newTelegramAPI(server.URL, telegramTestToken).GetUpdates(t.Context(), 7)
require.NoError(t, err)
require.Len(t, updates, 1)
require.Equal(t, int64(7), updates[0].UpdateID)
require.Equal(t, "getUpdates", call.method)
require.Equal(t, map[string]any{
"offset": float64(7),
- "timeout": float64(pollSeconds),
+ "timeout": float64(telegramPollSeconds),
"allowed_updates": []any{"message"},
}, call.params)
}
func TestSendMessageSendsPlainText(t *testing.T) {
- server := newFakeBot(t)
- err := newBotAPI(server.URL, testBotToken).SendMessage(t.Context(), testOwner, "*not bold*", nil)
+ server := newTelegramFake(t)
+ err := newTelegramAPI(server.URL, telegramTestToken).SendMessage(t.Context(), telegramTestOwner, "*not bold*", nil)
require.NoError(t, err)
require.Equal(t, map[string]any{
- "chat_id": float64(testOwner),
+ "chat_id": float64(telegramTestOwner),
"text": "*not bold*",
}, server.Calls()[0].params)
}
func TestSendMessageSendsTheEntities(t *testing.T) {
- server := newFakeBot(t)
- err := newBotAPI(server.URL, testBotToken).SendMessage(t.Context(), testOwner, "a link", []botEntity{
+ server := newTelegramFake(t)
+ err := newTelegramAPI(server.URL, telegramTestToken).SendMessage(t.Context(), telegramTestOwner, "a link", []TelegramEntity{
{
Type: "text_link",
Offset: 2,
}
func TestSendRichMessageSendsTheMarkdownUnchanged(t *testing.T) {
- server := newFakeBot(t)
+ server := newTelegramFake(t)
markdown := "| a | b |\n|---|---|\n| **1** | `2` |"
- err := newBotAPI(server.URL, testBotToken).SendRichMessage(t.Context(), testOwner, markdown)
+ err := newTelegramAPI(server.URL, telegramTestToken).SendRichMessage(t.Context(), telegramTestOwner, markdown)
require.NoError(t, err)
call := server.Calls()[0]
require.Equal(t, "sendRichMessage", call.method)
require.Equal(t, map[string]any{
- "chat_id": float64(testOwner),
+ "chat_id": float64(telegramTestOwner),
"rich_message": map[string]any{
"markdown": markdown,
},
func TestSendCallsTimeOutOnAStalledConnection(t *testing.T) {
testCases := []struct {
method string
- send func(ctx context.Context, api *botAPI) error
+ send func(ctx context.Context, api *TelegramAPI) error
}{
{
method: "sendMessage",
- send: func(ctx context.Context, api *botAPI) error {
- return api.SendMessage(ctx, testOwner, "hi", nil)
+ send: func(ctx context.Context, api *TelegramAPI) error {
+ return api.SendMessage(ctx, telegramTestOwner, "hi", nil)
},
},
{
method: "sendRichMessage",
- send: func(ctx context.Context, api *botAPI) error {
- return api.SendRichMessage(ctx, testOwner, "hi")
+ send: func(ctx context.Context, api *TelegramAPI) error {
+ return api.SendRichMessage(ctx, telegramTestOwner, "hi")
},
},
}
for _, tc := range testCases {
t.Run(tc.method, func(t *testing.T) {
- server := newFakeBot(t)
- server.stall = func(call fakeBotCall) bool {
+ server := newTelegramFake(t)
+ server.stall = func(call TelegramFakeCall) bool {
return call.method == tc.method
}
- api := newBotAPI(server.URL, testBotToken)
+ api := newTelegramAPI(server.URL, telegramTestToken)
api.sendTimeout = 50 * time.Millisecond
start := time.Now()
err := tc.send(t.Context(), api)
- require.ErrorIs(t, err, errBotNetwork)
+ require.ErrorIs(t, err, errTelegramNetwork)
require.Less(t, time.Since(start), 5*time.Second)
- require.NotContains(t, err.Error(), testBotToken)
+ require.NotContains(t, err.Error(), telegramTestToken)
require.NotContains(t, err.Error(), server.URL)
- retry, _ := retryable(err)
+ retry, _ := telegramRetryable(err)
require.True(t, retry)
api.sendTimeout = time.Hour
name: "token echoed in a description is hidden",
reply: func(w http.ResponseWriter) {
w.WriteHeader(http.StatusUnauthorized)
- _, _ = io.WriteString(w, `{"ok":false,"error_code":401,"description":"Unauthorized: bot`+testBotToken+`"}`)
+ _, _ = io.WriteString(w, `{"ok":false,"error_code":401,"description":"Unauthorized: bot`+telegramTestToken+`"}`)
},
retry: false,
errorText: "Unauthorized: botTOKEN",
}))
t.Cleanup(server.Close)
- err := newBotAPI(server.URL, testBotToken).SendMessage(t.Context(), testOwner, "hi", nil)
+ err := newTelegramAPI(server.URL, telegramTestToken).SendMessage(t.Context(), telegramTestOwner, "hi", nil)
require.ErrorContains(t, err, tc.errorText)
- require.NotContains(t, err.Error(), testBotToken)
+ require.NotContains(t, err.Error(), telegramTestToken)
require.NotContains(t, err.Error(), server.URL)
- retry, wait := retryable(err)
+ retry, wait := telegramRetryable(err)
require.Equal(t, tc.retry, retry)
require.Equal(t, tc.wait, wait)
- require.Equal(t, tc.rejected, rejected(err))
+ require.Equal(t, tc.rejected, telegramRejected(err))
})
}
server := httptest.NewServer(http.NotFoundHandler())
server.Close()
- _, err := newBotAPI(server.URL, testBotToken).GetUpdates(t.Context(), 0)
- require.ErrorIs(t, err, errBotNetwork)
- require.NotContains(t, err.Error(), testBotToken)
+ _, err := newTelegramAPI(server.URL, telegramTestToken).GetUpdates(t.Context(), 0)
+ require.ErrorIs(t, err, errTelegramNetwork)
+ require.NotContains(t, err.Error(), telegramTestToken)
}
-// fakeBot is a fake Bot API server for testBotToken. getUpdates returns the
-// added updates from the offset on, or waits a short time when there are none.
-// The other methods succeed, unless fail gives an error answer for a call. An
-// answer with HTTP 429 asks for a wait of one second. When stall is true for a
-// call, the call gets no answer until the client gives it up or the test ends.
-// Fail and stall run with the mutex held.
-type fakeBot struct {
- *httptest.Server
-
- mutex sync.Mutex
- updates []botUpdate
- calls []fakeBotCall
- fail func(call fakeBotCall) (int, string)
- stall func(call fakeBotCall) bool
- added chan struct{}
- stopped chan struct{}
-}
-
-// fakeBotCall is one call of the fake Bot API. OK tells if the call got a
+// TelegramFakeCall is one call of the fake Bot API. OK tells if the call got a
// success answer.
-type fakeBotCall struct {
+type TelegramFakeCall struct {
method string
params map[string]any
ok bool
}
-// newFakeBot starts a fake Bot API server that stops at the end of the test.
-func newFakeBot(t *testing.T) *fakeBot {
- t.Helper()
-
- f := &fakeBot{
- added: make(chan struct{}, 1),
- stopped: make(chan struct{}),
- }
-
- f.Server = httptest.NewServer(http.HandlerFunc(f.serve))
- t.Cleanup(f.Close)
- t.Cleanup(func() {
- close(f.stopped)
- })
+// TelegramFake is a fake Bot API server for telegramTestToken. getUpdates
+// returns the added updates from the offset on, or waits a short time when
+// there are none. The other methods succeed, unless fail gives an error answer
+// for a call. An answer with HTTP 429 asks for a wait of one second. When stall
+// is true for a call, the call gets no answer until the client gives it up or
+// the test ends. Fail and stall run with the mutex held.
+type TelegramFake struct {
+ *httptest.Server
- return f
+ mutex sync.Mutex
+ updates []TelegramUpdate
+ calls []TelegramFakeCall
+ fail func(call TelegramFakeCall) (int, string)
+ stall func(call TelegramFakeCall) bool
+ added chan struct{}
+ stopped chan struct{}
}
// Add makes update available to getUpdates.
-func (f *fakeBot) Add(update botUpdate) {
+func (f *TelegramFake) Add(update TelegramUpdate) {
f.mutex.Lock()
f.updates = append(f.updates, update)
f.mutex.Unlock()
}
// Calls returns the calls so far.
-func (f *fakeBot) Calls() []fakeBotCall {
+func (f *TelegramFake) Calls() []TelegramFakeCall {
f.mutex.Lock()
defer f.mutex.Unlock()
- return append([]fakeBotCall(nil), f.calls...)
+ return append([]TelegramFakeCall(nil), f.calls...)
}
// Sent returns the texts of the sendMessage calls and the Markdown of the
// sendRichMessage calls that succeeded, in order.
-func (f *fakeBot) Sent() []string {
+func (f *TelegramFake) Sent() []string {
var texts []string
for _, call := range f.Calls() {
switch {
return texts
}
-func (f *fakeBot) serve(w http.ResponseWriter, r *http.Request) {
- method, found := strings.CutPrefix(r.URL.Path, "/bot"+testBotToken+"/")
+func (f *TelegramFake) serve(w http.ResponseWriter, r *http.Request) {
+ method, found := strings.CutPrefix(r.URL.Path, "/bot"+telegramTestToken+"/")
if !found {
http.Error(w, `{"ok":false,"error_code":404,"description":"Not Found"}`, http.StatusNotFound)
return
return
}
- call := fakeBotCall{
+ call := TelegramFakeCall{
method: method,
params: params,
}
}
// record keeps call.
-func (f *fakeBot) record(call fakeBotCall) {
+func (f *TelegramFake) record(call TelegramFakeCall) {
f.mutex.Lock()
defer f.mutex.Unlock()
// from returns the updates from offset on, and forgets the ones before it, as
// Telegram does.
-func (f *fakeBot) from(offset int64) []botUpdate {
+func (f *TelegramFake) from(offset int64) []TelegramUpdate {
f.mutex.Lock()
defer f.mutex.Unlock()
- var updates []botUpdate
+ var updates []TelegramUpdate
kept := f.updates[:0]
for _, update := range f.updates {
if update.UpdateID >= offset {
return updates
}
-// ownerMessage returns a text message of the owner in the private chat.
-func ownerMessage(text string) *botMessage {
- return &botMessage{
- Chat: botChat{
- ID: testOwner,
+// newTelegramFake starts a fake Bot API server that stops at the end of the
+// test.
+func newTelegramFake(t *testing.T) *TelegramFake {
+ t.Helper()
+
+ f := &TelegramFake{
+ added: make(chan struct{}, 1),
+ stopped: make(chan struct{}),
+ }
+
+ f.Server = httptest.NewServer(http.HandlerFunc(f.serve))
+ t.Cleanup(f.Close)
+ t.Cleanup(func() {
+ close(f.stopped)
+ })
+
+ return f
+}
+
+// telegramOwnerMessage returns a text message of the owner in the private chat.
+func telegramOwnerMessage(text string) *TelegramMessage {
+ return &TelegramMessage{
+ Chat: TelegramChat{
+ ID: telegramTestOwner,
Type: "private",
},
- From: &botUser{
- ID: testOwner,
+ From: &TelegramUser{
+ ID: telegramTestOwner,
},
Text: text,
}