14 "github.com/stretchr/testify/require"
17 func TestTelegramSelectsLargestPhotoAtMost1568Pixels(t *testing.T) {
18 server := newModelFake(t)
19 test := newTestTurnLoop(t, server)
20 telegram := newTelegramFake(t)
21 bot, logs := telegramStartTestBot(t, telegram)
22 picture := imageTestPNG(t, 8, 6)
23 telegram.Image("selected", picture)
25 message := telegramOwnerMessage("")
26 message.Caption = "Read this label.\n"
27 message.Photo = []TelegramPhoto{
49 require.NoError(t, bot.handle(t.Context(), test.loop, nil, TelegramUpdate{
54 test.events.WaitFor(t, "idle", 1)
56 files, err := os.ReadDir(test.agent.Images().dir)
57 require.NoError(t, err)
58 require.Len(t, files, 1)
60 logged, err := test.memory.Message(0)
61 require.NoError(t, err)
62 require.Equal(t, imageText([]string{files[0].Name()}, message.Caption), logged.Text)
63 require.True(t, strings.HasSuffix(logged.Text, "\n"))
64 require.Contains(t, string(server.Call(0).raw), "input_image")
65 require.NotContains(t, logs.String(), "base64")
66 require.NotContains(t, logs.String(), "Read this label")
68 for _, call := range telegram.Calls() {
69 if call.method == "getFile" {
70 require.Equal(t, "selected", call.params["file_id"])
75 func TestTelegramImageDocumentsUseContentFormatAndRejectOtherMedia(t *testing.T) {
76 telegram := newTelegramFake(t)
77 png := imageTestPNG(t, 2, 2)
79 var jpegBytes bytes.Buffer
81 require.NoError(t, jpeg.Encode(&jpegBytes, image.NewRGBA(image.Rect(0, 0, 2, 2)), nil))
83 // A made-up one-pixel lossless WebP image.
84 webp := []byte{'R', 'I', 'F', 'F', 22, 0, 0, 0, 'W', 'E', 'B', 'P', 'V', 'P', '8', 'L', 10, 0, 0, 0, 0x2f, 0, 0, 0, 0, 0x07, 0x10, 0x11, 0x11, 0x88}
86 telegram.Image("png", png)
87 telegram.Image("jpeg", jpegBytes.Bytes())
88 telegram.Image("webp", webp)
89 telegram.Image("invalid", []byte("not an image"))
90 telegram.Image("large", bytes.Repeat([]byte{0}, imageMaxBytes+1))
92 api := newTelegramAPI(telegram.URL, telegramTestToken)
94 store := &ImageStore{dir: filepath.Join(t.TempDir(), imageDirName)}
95 testCases := []struct {
97 document TelegramDocument
102 name: "accepts PNG documents",
103 document: TelegramDocument{
105 MimeType: "image/png",
110 name: "accepts JPEG documents",
111 document: TelegramDocument{
113 MimeType: "image/jpeg",
118 name: "accepts WebP documents",
119 document: TelegramDocument{
121 MimeType: "image/webp",
126 name: "other documents keep text answer",
127 document: TelegramDocument{
129 MimeType: "application/pdf",
131 failure: "only text messages are supported",
134 name: "other images refused",
135 document: TelegramDocument{
137 MimeType: "image/gif",
139 failure: "JPEG, PNG, or WebP",
142 name: "invalid image refused",
143 document: TelegramDocument{
145 MimeType: "image/png",
147 failure: "JPEG, PNG, or WebP",
150 name: "large download refused",
151 document: TelegramDocument{
153 MimeType: "image/png",
155 failure: "exceeds 20 MB",
158 name: "large metadata refused",
159 document: TelegramDocument{
160 FileID: "not-downloaded",
161 MimeType: "image/png",
162 FileSize: imageMaxBytes + 1,
164 failure: "exceeds 20 MB",
168 for _, tc := range testCases {
169 t.Run(tc.name, func(t *testing.T) {
170 text, err := telegramImageText(t.Context(), api, store, &TelegramMessage{Document: &tc.document})
171 if tc.failure != "" {
172 require.ErrorContains(t, err, tc.failure)
177 require.NoError(t, err)
178 require.True(t, strings.HasSuffix(text, tc.suffix))
183 func TestTelegramRetriesImageDownloadBeforeAdvancingOffset(t *testing.T) {
184 telegram := newTelegramFake(t)
185 telegram.Image("photo", imageTestPNG(t, 2, 2))
188 telegram.fail = func(call TelegramFakeCall) (int, string) {
189 if call.method == "getFile" {
192 return http.StatusServiceUnavailable, "try again"
199 bot, logs := telegramStartTestBot(t, telegram)
200 server := newModelFake(t)
201 socket := daemonStartTestWithBot(t, server, bot)
202 terminal := terminalStartTestClient(t, socket)
203 terminal.WaitFor(t, "[connected to OneChat]")
205 message := telegramOwnerMessage("")
206 message.Photo = []TelegramPhoto{{
212 telegram.Add(TelegramUpdate{
217 terminal.WaitFor(t, "[done]")
218 require.Eventually(t, func() bool {
219 offset, err := telegramLoadOffset(bot.offsetPath)
220 return err == nil && offset == 8
221 }, 10*time.Second, time.Millisecond)
223 require.Contains(t, logs.String(), "receive failed")
224 require.Contains(t, logs.String(), "receive back")
225 require.NotContains(t, logs.String(), telegramTestToken)
226 require.NotContains(t, logs.String(), "base64")
229 func TestTelegramRefusesPhotoWhenEverySizeIsTooLarge(t *testing.T) {
230 telegram := newTelegramFake(t)
231 _, err := telegramImageText(t.Context(), newTelegramAPI(telegram.URL, telegramTestToken), &ImageStore{dir: t.TempDir()}, &TelegramMessage{Photo: []TelegramPhoto{{
236 require.ErrorContains(t, err, "1568 pixels")
237 require.Empty(t, telegram.Calls())
240 func TestTelegramStorageFailureDoesNotAcceptTheImage(t *testing.T) {
241 telegram := newTelegramFake(t)
242 telegram.Image("photo", imageTestPNG(t, 2, 2))
244 test := newTestTurnLoop(t, newModelFake(t))
245 require.NoError(t, os.WriteFile(test.agent.Images().dir, []byte("blocked"), 0o600))
247 bot, _ := telegramStartTestBot(t, telegram)
248 message := telegramOwnerMessage("")
249 message.Photo = []TelegramPhoto{{
255 err := bot.handle(t.Context(), test.loop, nil, TelegramUpdate{
259 require.ErrorContains(t, err, "create image directory")
260 require.Equal(t, 0, test.memory.Len())
262 _, found := bot.next()
263 require.False(t, found)