X-Git-Url: https://git.giacomolli.uk/onechat/blobdiff_plain/b473b56ebf00e6b4d6cf16664f93e1958fbe2080..c781f79f3c8cb9a7577ee1574d196988da2719b3:/telegram_test.go?ds=sidebyside diff --git a/telegram_test.go b/telegram_test.go index 60e9e86..8d033ab 100644 --- a/telegram_test.go +++ b/telegram_test.go @@ -1,6 +1,7 @@ package main import ( + "context" "net/http" "os" "path/filepath" @@ -8,6 +9,7 @@ import ( "strconv" "strings" "testing" + "testing/synctest" "time" "github.com/stretchr/testify/require" @@ -143,7 +145,8 @@ func TestBotShowsOnlyTheStartAndTheEndOfASubagent(t *testing.T) { last := subagentTestLastItem(call) switch { case subagentTestCall(call): - // The report comes after the reply of the turn of the spawn. + // The report comes after the reply of the turn of the + // spawn. require.Eventually(t, func() bool { return slices.Contains(telegram.Sent(), "Started.") }, 10*time.Second, time.Millisecond) @@ -189,11 +192,13 @@ func TestBotSkipsTheWorkOfWhatsAppAndTheSchedules(t *testing.T) { Source: whatsappSource, Text: "[WhatsApp] Ana Souza", }) + bot.Notify(Event{ Kind: "work", Source: scheduleSource, Text: "[Schedule 1] Sunday 2026-11-08 20:05 -03:00, once", }) + require.Empty(t, bot.queue) } @@ -265,12 +270,12 @@ func TestBotTriesAgainAfterFailures(t *testing.T) { }) 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") - // The bot logs the end of the outage after the send returns, so the line - // can come after the fake server has the message. + // 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) @@ -279,30 +284,32 @@ func TestBotTriesAgainAfterFailures(t *testing.T) { } 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) { @@ -408,7 +415,7 @@ func TestRichMessageFailures(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", @@ -639,45 +646,63 @@ func TestBotLogsNoTokenWhenTheNetworkFails(t *testing.T) { 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-- + + return http.StatusBadGateway, "Bad Gateway" } - failures-- + bot, logs := telegramStartTestBot(t, telegram) + bot.api.http.Transport = telegram + whatsapp, _, _ := whatsappStartTest(t, t.TempDir(), newTestClock()) + loop, _ := newTestIdleTurnLoop(t, whatsapp) - return http.StatusBadGateway, "Bad Gateway" - } + telegram.Add(TelegramUpdate{ + UpdateID: 1, + Message: telegramOwnerMessage("a secret word"), + }) - bot, logs := telegramStartTestBot(t, telegram) - daemonStartTestWithBot(t, server, bot) + ctx, cancel := context.WithCancel(t.Context()) + defer cancel() - telegram.Add(TelegramUpdate{ - UpdateID: 1, - Message: telegramOwnerMessage("a secret word"), - }) + done := make(chan error, 1) + go func() { + done <- bot.poll(ctx, loop, whatsapp) + }() - telegramWaitForSent(t, telegram, "ok") + time.Sleep(31 * time.Second) + synctest.Wait() + require.Equal(t, 1, loop.memory.Len()) - 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)) + 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) + }) } func TestSplitMessage(t *testing.T) { @@ -906,15 +931,12 @@ func TestLoadBotConfig(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 }