]> git.giacomolli.uk - onechat/blobdiff - telegram_test.go
Send the Telegram replies as rich messages
[onechat] / telegram_test.go
index 30b2fa1bb686f730963c2426bdb88e3761949d5b..df35e0df6430490778a523cbbdace02a09a7eabb 100644 (file)
@@ -171,8 +171,8 @@ func TestBotTriesAgainAfterFailures(t *testing.T) {
 
        telegram := newFakeBot(t)
        failures := map[string]int{
 
        telegram := newFakeBot(t)
        failures := map[string]int{
-               "getUpdates":  2,
-               "sendMessage": 1,
+               "getUpdates":      2,
+               "sendRichMessage": 1,
        }
 
        telegram.fail = func(call fakeBotCall) (int, string) {
        }
 
        telegram.fail = func(call fakeBotCall) (int, string) {
@@ -181,7 +181,7 @@ func TestBotTriesAgainAfterFailures(t *testing.T) {
                }
 
                failures[call.method]--
                }
 
                failures[call.method]--
-               if call.method == "sendMessage" {
+               if call.method == "sendRichMessage" {
                        return http.StatusTooManyRequests, "Too Many Requests: retry after 1"
                }
 
                        return http.StatusTooManyRequests, "Too Many Requests: retry after 1"
                }
 
@@ -227,10 +227,11 @@ func TestDeliverTriesAgainAfterATimeout(t *testing.T) {
        require.NotContains(t, logs.String(), testBotToken)
 }
 
        require.NotContains(t, logs.String(), testBotToken)
 }
 
-func TestOnlyRepliesGetEntities(t *testing.T) {
+func TestRepliesGoAsRichMessages(t *testing.T) {
+       reply := "**Done.** The table:\n\n| Name | Size |\n|:-----|-----:|\n| `a.go` | 10 |\n\n- one\n- two\n\n```go\nx := 1\n```"
        server := newFakeResponses(t)
        server.reply = func(w http.ResponseWriter, call fakeCall) {
        server := newFakeResponses(t)
        server.reply = func(w http.ResponseWriter, call fakeCall) {
-               writeEvents(w, completedEvent([]any{messageItem("**Done** with `x`.")}, 100, 0, 5))
+               writeEvents(w, completedEvent([]any{messageItem(reply)}, 100, 0, 5))
        }
 
        telegram := newFakeBot(t)
        }
 
        telegram := newFakeBot(t)
@@ -240,7 +241,7 @@ func TestOnlyRepliesGetEntities(t *testing.T) {
        terminal.WaitFor(t, "[connected to OneChat]")
 
        terminal.Type("make **it** so")
        terminal.WaitFor(t, "[connected to OneChat]")
 
        terminal.Type("make **it** so")
-       waitForSent(t, telegram, "[terminal] make **it** so", "Done with x.")
+       waitForSent(t, telegram, "[terminal] make **it** so", reply)
 
        telegram.Add(botUpdate{
                UpdateID: 1,
 
        telegram.Add(botUpdate{
                UpdateID: 1,
@@ -249,29 +250,144 @@ func TestOnlyRepliesGetEntities(t *testing.T) {
 
        waitForSent(t, telegram, textOnly)
 
 
        waitForSent(t, telegram, textOnly)
 
-       entities := map[string]any{}
+       var sends []fakeBotCall
        for _, call := range telegram.Calls() {
        for _, call := range telegram.Calls() {
-               if call.method == "sendMessage" {
-                       entities[call.params["text"].(string)] = call.params["entities"]
+               if call.method == "sendMessage" || call.method == "sendRichMessage" {
+                       sends = append(sends, call)
                }
        }
 
                }
        }
 
-       require.Equal(t, map[string]any{
-               "[terminal] make **it** so": nil,
-               "Done with x.": []any{
-                       map[string]any{
-                               "type":   "bold",
-                               "offset": float64(0),
-                               "length": float64(4),
+       require.Equal(t, []fakeBotCall{
+               {
+                       method: "sendMessage",
+                       params: map[string]any{
+                               "chat_id": float64(testOwner),
+                               "text":    "[terminal] make **it** so",
                        },
                        },
-                       map[string]any{
-                               "type":   "code",
-                               "offset": float64(10),
-                               "length": float64(1),
+                       ok: true,
+               },
+               {
+                       method: "sendRichMessage",
+                       params: map[string]any{
+                               "chat_id": float64(testOwner),
+                               "rich_message": map[string]any{
+                                       "markdown": reply,
+                               },
                        },
                        },
+                       ok: true,
+               },
+               {
+                       method: "sendMessage",
+                       params: map[string]any{
+                               "chat_id": float64(testOwner),
+                               "text":    textOnly,
+                       },
+                       ok: true,
+               },
+       }, sends)
+}
+
+func TestRichMessageFailures(t *testing.T) {
+       testCases := []struct {
+               name    string
+               status  int
+               always  bool
+               methods []string
+               sent    []string
+               logText string
+       }{
+               {
+                       name:    "bad request sends the reply once with entities",
+                       status:  http.StatusBadRequest,
+                       always:  true,
+                       methods: []string{"sendRichMessage", "sendMessage"},
+                       sent:    []string{"a secret word"},
+                       logText: "rich message refused",
+               },
+               {
+                       name:    "unknown method sends the reply once with entities",
+                       status:  http.StatusNotFound,
+                       always:  true,
+                       methods: []string{"sendRichMessage", "sendMessage"},
+                       sent:    []string{"a secret word"},
+                       logText: "rich message refused",
+               },
+               {
+                       name:    "rate limit tries the rich message again",
+                       status:  http.StatusTooManyRequests,
+                       methods: []string{"sendRichMessage", "sendRichMessage"},
+                       sent:    []string{"a **secret** word"},
+                       logText: "answered 429",
                },
                },
-               textOnly: nil,
-       }, entities)
+               {
+                       name:    "server error tries the rich message again",
+                       status:  http.StatusInternalServerError,
+                       methods: []string{"sendRichMessage", "sendRichMessage"},
+                       sent:    []string{"a **secret** word"},
+                       logText: "answered 500",
+               },
+               {
+                       name:    "other refusal drops the reply",
+                       status:  http.StatusForbidden,
+                       always:  true,
+                       methods: []string{"sendRichMessage"},
+                       logText: "message dropped",
+               },
+       }
+
+       for _, tc := range testCases {
+               t.Run(tc.name, func(t *testing.T) {
+                       telegram := newFakeBot(t)
+                       failed := false
+                       telegram.fail = func(call fakeBotCall) (int, string) {
+                               if call.method != "sendRichMessage" || failed && !tc.always {
+                                       return 0, ""
+                               }
+
+                               failed = true
+
+                               return tc.status, "refused for bot" + testBotToken
+                       }
+
+                       bot, logs := startTestBot(t, telegram)
+                       bot.post(t.Context(), Event{
+                               Kind: "reply",
+                               Text: "a **secret** word",
+                       })
+
+                       var methods []string
+                       for _, call := range telegram.Calls() {
+                               methods = append(methods, call.method)
+                       }
+
+                       require.Equal(t, tc.methods, methods)
+                       require.Equal(t, tc.sent, telegram.Sent())
+                       require.Equal(t, 1, strings.Count(logs.String(), "\n"))
+                       require.Contains(t, logs.String(), tc.logText)
+                       require.NotContains(t, logs.String(), testBotToken)
+                       require.NotContains(t, logs.String(), "secret word")
+
+                       calls := telegram.Calls()
+                       if calls[len(calls)-1].method == "sendMessage" {
+                               require.NotNil(t, calls[len(calls)-1].params["entities"])
+                       }
+               })
+       }
+}
+
+func TestLongReplyGoesAsRichMessagesSplitBetweenBlocks(t *testing.T) {
+       paragraphs := strings.Repeat("A paragraph of the reply.\n\n", richMessageBytes/len("A paragraph of the reply.\n\n"))
+       table := "| a | b |\n|---|---|\n| 1 | 2 |\n"
+       reply := paragraphs + table
+
+       telegram := newFakeBot(t)
+       bot, _ := startTestBot(t, telegram)
+       bot.post(t.Context(), Event{
+               Kind: "reply",
+               Text: reply,
+       })
+
+       require.Equal(t, []string{paragraphs, table}, telegram.Sent())
 }
 
 func TestRefusedEntitiesGoAgainAsPlainText(t *testing.T) {
 }
 
 func TestRefusedEntitiesGoAgainAsPlainText(t *testing.T) {