X-Git-Url: https://git.giacomolli.uk/onechat/blobdiff_plain/de170709599e849c6b409d247b800dc68b0c7ab7..0018014ab36f3de61a4b8a5515009dab803d6c38:/telegram_test.go diff --git a/telegram_test.go b/telegram_test.go index cf46106..5e50ec1 100644 --- a/telegram_test.go +++ b/telegram_test.go @@ -127,6 +127,56 @@ func TestStopFromTelegramInterruptsTheTurn(t *testing.T) { }) telegramWaitForSent(t, telegram, "Error: interrupted", "No turn runs.") + + telegram.Add(TelegramUpdate{ + UpdateID: 4, + Message: telegramOwnerMessage(stopCommand + " BlueFox"), + }) + + telegramWaitForSent(t, telegram, "No turn runs.", "No subagent BlueFox runs.") +} + +func TestBotShowsOnlyTheStartAndTheEndOfASubagent(t *testing.T) { + telegram := newTelegramFake(t) + server := newModelFake(t) + server.reply = func(w http.ResponseWriter, call ModelFakeCall) { + last := subagentTestLastItem(call) + switch { + case subagentTestCall(call): + // 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) + modelWriteEvents(w, modelCompletedEvent([]any{modelMessageItem("There are 3 files.")}, 100, 0, 5)) + case last["type"] == "function_call_output": + modelWriteEvents(w, modelCompletedEvent([]any{modelMessageItem("Started.")}, 100, 0, 5)) + case strings.HasPrefix(subagentTestText(last), "["): + modelWriteEvents(w, modelCompletedEvent([]any{modelMessageItem("It counted 3 files.")}, 100, 0, 5)) + default: + modelWriteEvents(w, modelCompletedEvent([]any{modelFunctionCallItem("fc_1", "call_1", "spawn", `{"tasks":["Count the files."],"effort":null}`)}, 100, 0, 5)) + } + } + + bot, _ := telegramStartTestBot(t, telegram) + daemonStartTestWithBot(t, server, bot) + telegram.Add(TelegramUpdate{ + UpdateID: 1, + Message: telegramOwnerMessage("Count the files in a subagent."), + }) + + require.Eventually(t, func() bool { + return slices.Contains(telegram.Sent(), "It counted 3 files.") + }, 10*time.Second, time.Millisecond) + + sent := telegram.Sent() + name := strings.TrimSuffix(strings.TrimPrefix(sent[0], "["), "] started") + require.True(t, subagentNameValid(name), sent[0]) + require.Equal(t, []string{ + "[" + name + "] started", + "Started.", + "[" + name + "] finished", + "It counted 3 files.", + }, sent) } func TestBotStartsAtTheStoredOffset(t *testing.T) { @@ -398,12 +448,12 @@ func TestReplyTagsGoAsText(t *testing.T) { }{ { name: "rich message escapes the tag outside code", - sent: []string{"a \\
tag and ``"}, + sent: []string{"a <details> & tag and ``"}, }, { name: "entity fallback shows the tag as written", status: http.StatusBadRequest, - sent: []string{"a
tag and "}, + sent: []string{"a
& tag and "}, }, } @@ -421,7 +471,7 @@ func TestReplyTagsGoAsText(t *testing.T) { bot, _ := telegramStartTestBot(t, telegram) bot.post(t.Context(), Event{ Kind: "reply", - Text: "a
tag and ``", + Text: "a
& tag and ``", }) require.Equal(t, tc.sent, telegram.Sent()) @@ -451,6 +501,50 @@ func TestLongReplySplitCountsTheEscapedTags(t *testing.T) { } } +func TestRefusedRichPartGoesWithTheTextAsWritten(t *testing.T) { + paragraph := "A & c.\n\n" + reply := strings.Repeat(paragraph, telegramRichBytes/len(paragraph)) + + telegram := newTelegramFake(t) + refused := false + telegram.fail = func(call TelegramFakeCall) (int, string) { + if call.method != "sendRichMessage" || refused { + return 0, "" + } + + refused = true + + return http.StatusBadRequest, "Bad Request" + } + + bot, _ := telegramStartTestBot(t, telegram) + bot.post(t.Context(), Event{ + Kind: "reply", + Text: reply, + }) + + var fallback, rich []string + for _, call := range telegram.Calls() { + switch { + case !call.ok: + case call.method == "sendMessage": + fallback = append(fallback, call.params["text"].(string)) + case call.method == "sendRichMessage": + rich = append(rich, call.params["rich_message"].(map[string]any)["markdown"].(string)) + } + } + + require.Greater(t, len(fallback), 1) + require.Len(t, rich, 1) + require.True(t, strings.HasPrefix(reply, strings.Join(fallback, "")), "fallback text is not the reply as written") + + for _, text := range fallback { + require.LessOrEqual(t, telegramUTF16Units(text), telegramMessageUnits) + } + + require.True(t, reply == strings.Join(fallback, "")+telegramUnescapeTags(rich[0]), "reply is not sent once and whole") +} + func TestRefusedEntitiesGoAgainAsPlainText(t *testing.T) { telegram := newTelegramFake(t) telegram.fail = func(call TelegramFakeCall) (int, string) { @@ -629,6 +723,12 @@ func TestSplitRich(t *testing.T) { limit: 3, parts: []string{"ab", "\u00e9c", "d"}, }, + { + name: "long line is not cut inside an entity", + markdown: "abc&def", + limit: 6, + parts: []string{"abc", "&d", "ef"}, + }, } for _, tc := range testCases {