package main
import (
+ "context"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"testing"
+ "testing/synctest"
"time"
"github.com/stretchr/testify/require"
})
telegramWaitForSent(t, telegram, "ok")
- require.Contains(t, logs.String(), "<4>receive failed retry=1ms: 502")
+ require.Contains(t, logs.String(), "<4>receive failed retry=1s: 502")
require.Contains(t, logs.String(), "<6>receive back after=")
require.Contains(t, logs.String(), "<4>send failed retry=1s: 429")
- require.Contains(t, logs.String(), "<6>send back after=")
+
+ // The bot logs the end of the outage after the send returns, so the line
+ // can come after the fake server has the message.
+ require.Eventually(t, func() bool {
+ return strings.Contains(logs.String(), "<6>send back after=")
+ }, 10*time.Second, time.Millisecond)
+
require.NotContains(t, logs.String(), telegramTestToken)
}
func TestDeliverTriesAgainAfterATimeout(t *testing.T) {
- telegram := newTelegramFake(t)
- stalls := 1
- telegram.stall = func(call TelegramFakeCall) bool {
- if call.method != "sendMessage" || stalls == 0 {
- return false
- }
+ synctest.Test(t, func(t *testing.T) {
+ telegram := newTelegramLocalFake(t)
+ stalls := 1
+ telegram.stall = func(call TelegramFakeCall) bool {
+ if call.method != "sendMessage" || stalls == 0 {
+ return false
+ }
- stalls--
+ stalls--
- return true
- }
+ return true
+ }
- bot, logs := telegramStartTestBot(t, telegram)
- bot.api.sendTimeout = 50 * time.Millisecond
- bot.deliver(t.Context(), TelegramPart{
- text: "hello",
- })
+ bot, logs := telegramStartTestBot(t, telegram)
+ bot.api.http.Transport = telegram
+ bot.deliver(t.Context(), TelegramPart{
+ text: "hello",
+ })
- require.Equal(t, []string{"hello"}, telegram.Sent())
- require.Len(t, telegram.Calls(), 2)
- require.Contains(t, logs.String(), "<4>send failed retry=1ms: no answer in 50ms\n")
- require.Contains(t, logs.String(), "<6>send back after=")
- require.Equal(t, 2, strings.Count(logs.String(), "\n"))
- require.NotContains(t, logs.String(), telegramTestToken)
+ require.Equal(t, []string{"hello"}, telegram.Sent())
+ require.Len(t, telegram.Calls(), 2)
+ require.Contains(t, logs.String(), "<4>send failed retry=1s: no answer in 30s\n")
+ require.Contains(t, logs.String(), "<6>send back after=1s\n")
+ require.Equal(t, 2, strings.Count(logs.String(), "\n"))
+ require.NotContains(t, logs.String(), telegramTestToken)
+ })
}
func TestRepliesGoAsRichMessages(t *testing.T) {
methods: []string{"sendRichMessage", "sendRichMessage"},
sent: []string{"a **secret** word"},
lines: 2,
- logText: "<4>send failed retry=1ms: 500",
+ logText: "<4>send failed retry=1s: 500",
},
{
name: "other refusal drops the reply",
daemonStartTestWithBot(t, newModelFake(t), bot)
require.Eventually(t, func() bool {
- return strings.Contains(logs.String(), "<4>receive failed retry=1ms: no answer: ")
+ return strings.Contains(logs.String(), "<4>receive failed retry=1s: no answer: ")
}, 10*time.Second, time.Millisecond)
require.NotContains(t, logs.String(), telegramTestToken)
}
func TestBotLogsAnOutageOnlyAtItsStartAndEnd(t *testing.T) {
- server := newModelFake(t)
- server.reply = func(w http.ResponseWriter, _ ModelFakeCall) {
- modelWriteEvents(w, modelCompletedEvent([]any{modelMessageItem("ok")}, 100, 0, 5))
- }
+ synctest.Test(t, func(t *testing.T) {
+ telegram := newTelegramLocalFake(t)
+ failures := 5
+ telegram.fail = func(call TelegramFakeCall) (int, string) {
+ if call.method != "getUpdates" || failures == 0 {
+ return 0, ""
+ }
- telegram := newTelegramFake(t)
- failures := 5
- telegram.fail = func(call TelegramFakeCall) (int, string) {
- if call.method != "getUpdates" || failures == 0 {
- return 0, ""
- }
+ failures--
- failures--
+ return http.StatusBadGateway, "Bad Gateway"
+ }
- return http.StatusBadGateway, "Bad Gateway"
- }
+ bot, logs := telegramStartTestBot(t, telegram)
+ bot.api.http.Transport = telegram
+ whatsapp, _, _ := whatsappStartTest(t, t.TempDir(), newTestClock())
+ loop, _ := newTestIdleTurnLoop(t, whatsapp)
- bot, logs := telegramStartTestBot(t, telegram)
- daemonStartTestWithBot(t, server, bot)
+ telegram.Add(TelegramUpdate{
+ UpdateID: 1,
+ Message: telegramOwnerMessage("a secret word"),
+ })
- telegram.Add(TelegramUpdate{
- UpdateID: 1,
- Message: telegramOwnerMessage("a secret word"),
+ ctx, cancel := context.WithCancel(t.Context())
+ defer cancel()
+
+ done := make(chan error, 1)
+ go func() {
+ done <- bot.poll(ctx, loop, whatsapp)
+ }()
+
+ time.Sleep(31 * time.Second)
+ synctest.Wait()
+ require.Equal(t, 1, loop.memory.Len())
+
+ message, err := loop.memory.Message(0)
+ require.NoError(t, err)
+ require.Equal(t, "a secret word", message.Text)
+
+ lines := strings.Split(strings.TrimSuffix(logs.String(), "\n"), "\n")
+ require.Equal(t, []string{
+ "<4>receive failed retry=1s: 502 Bad Gateway",
+ "<6>receive back after=31s",
+ }, lines)
+ require.NotContains(t, logs.String(), "secret word")
+ require.NotContains(t, logs.String(), strconv.FormatInt(telegramTestOwner, 10))
+ cancel()
+ require.NoError(t, <-done)
})
-
- telegramWaitForSent(t, telegram, "ok")
-
- lines := strings.Split(strings.TrimSuffix(logs.String(), "\n"), "\n")
- require.Len(t, lines, 2)
- require.Equal(t, "<4>receive failed retry=1ms: 502 Bad Gateway", lines[0])
- require.Regexp(t, `^<6>receive back after=[0-9]+m?s$`, lines[1])
- require.NotContains(t, logs.String(), "secret word")
- require.NotContains(t, logs.String(), strconv.FormatInt(telegramTestOwner, 10))
}
func TestSplitMessage(t *testing.T) {
}
// telegramStartTestBot returns a bot of telegramTestOwner on server, with its
-// offset file in a temporary directory and short waits, and the buffer of its
-// log.
+// offset file in a temporary directory and the buffer of its log.
func telegramStartTestBot(t *testing.T, server *TelegramFake) (*TelegramBot, *SyncBuffer) {
t.Helper()
logs := &SyncBuffer{}
bot := newTelegramBot(newTelegramAPI(server.URL, telegramTestToken), telegramTestOwner, filepath.Join(t.TempDir(), telegramOffsetFileName), daemonTestLogger(logs))
- bot.minWait = time.Millisecond
- bot.maxWait = 10 * time.Millisecond
return bot, logs
}