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{
53 test.events.WaitFor(t, "idle", 1)
55 files, err := os.ReadDir(test.agent.Images().dir)
56 require.NoError(t, err)
57 require.Len(t, files, 1)
59 logged, err := test.memory.Message(0)
60 require.NoError(t, err)
61 require.Equal(t, imageText([]string{files[0].Name()}, message.Caption), logged.Text)
62 require.True(t, strings.HasSuffix(logged.Text, "\n"))
63 require.Contains(t, string(server.Call(0).raw), "input_image")
64 require.NotContains(t, logs.String(), "base64")
65 require.NotContains(t, logs.String(), "Read this label")
67 for _, call := range telegram.Calls() {
68 if call.method == "getFile" {
69 require.Equal(t, "selected", call.params["file_id"])
74 func TestTelegramImageDocumentsUseContentFormatAndRejectOtherMedia(t *testing.T) {
75 telegram := newTelegramFake(t)
76 png := imageTestPNG(t, 2, 2)
78 var jpegBytes bytes.Buffer
80 require.NoError(t, jpeg.Encode(&jpegBytes, image.NewRGBA(image.Rect(0, 0, 2, 2)), nil))
82 // A made-up one-pixel lossless WebP image.
83 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}
85 telegram.Image("png", png)
86 telegram.Image("jpeg", jpegBytes.Bytes())
87 telegram.Image("webp", webp)
88 telegram.Image("invalid", []byte("not an image"))
89 telegram.Image("large", bytes.Repeat([]byte{0}, imageMaxBytes+1))
91 api := newTelegramAPI(telegram.URL, telegramTestToken)
93 store := &ImageStore{dir: filepath.Join(t.TempDir(), imageDirName)}
94 testCases := []struct {
96 document TelegramDocument
101 name: "accepts PNG documents",
102 document: TelegramDocument{
104 MimeType: "image/png",
109 name: "accepts JPEG documents",
110 document: TelegramDocument{
112 MimeType: "image/jpeg",
117 name: "accepts WebP documents",
118 document: TelegramDocument{
120 MimeType: "image/webp",
125 name: "other documents keep text answer",
126 document: TelegramDocument{
128 MimeType: "application/pdf",
130 failure: "only text messages are supported",
133 name: "other images refused",
134 document: TelegramDocument{
136 MimeType: "image/gif",
138 failure: "JPEG, PNG, or WebP",
141 name: "invalid image refused",
142 document: TelegramDocument{
144 MimeType: "image/png",
146 failure: "JPEG, PNG, or WebP",
149 name: "large download refused",
150 document: TelegramDocument{
152 MimeType: "image/png",
154 failure: "exceeds 20 MB",
157 name: "large metadata refused",
158 document: TelegramDocument{
159 FileID: "not-downloaded",
160 MimeType: "image/png",
161 FileSize: imageMaxBytes + 1,
163 failure: "exceeds 20 MB",
166 for _, tc := range testCases {
167 t.Run(tc.name, func(t *testing.T) {
168 text, err := telegramImageText(t.Context(), api, store, &TelegramMessage{Document: &tc.document})
169 if tc.failure != "" {
170 require.ErrorContains(t, err, tc.failure)
175 require.NoError(t, err)
176 require.True(t, strings.HasSuffix(text, tc.suffix))
181 func TestTelegramRetriesImageDownloadBeforeAdvancingOffset(t *testing.T) {
182 telegram := newTelegramFake(t)
183 telegram.Image("photo", imageTestPNG(t, 2, 2))
186 telegram.fail = func(call TelegramFakeCall) (int, string) {
187 if call.method == "getFile" {
190 return http.StatusServiceUnavailable, "try again"
196 bot, logs := telegramStartTestBot(t, telegram)
197 bot.minWait = time.Millisecond
198 bot.maxWait = 5 * time.Millisecond
199 server := newModelFake(t)
200 socket := daemonStartTestWithBot(t, server, bot)
201 terminal := terminalStartTestClient(t, socket)
202 terminal.WaitFor(t, "[connected to OneChat]")
204 message := telegramOwnerMessage("")
205 message.Photo = []TelegramPhoto{{
210 telegram.Add(TelegramUpdate{
214 terminal.WaitFor(t, "[done]")
215 require.Eventually(t, func() bool {
216 offset, err := telegramLoadOffset(bot.offsetPath)
217 return err == nil && offset == 8
218 }, 10*time.Second, time.Millisecond)
219 require.Contains(t, logs.String(), "receive failed")
220 require.Contains(t, logs.String(), "receive back")
221 require.NotContains(t, logs.String(), telegramTestToken)
222 require.NotContains(t, logs.String(), "base64")
225 func TestTelegramRefusesPhotoWhenEverySizeIsTooLarge(t *testing.T) {
226 telegram := newTelegramFake(t)
227 _, err := telegramImageText(t.Context(), newTelegramAPI(telegram.URL, telegramTestToken), &ImageStore{dir: t.TempDir()}, &TelegramMessage{Photo: []TelegramPhoto{{
232 require.ErrorContains(t, err, "1568 pixels")
233 require.Empty(t, telegram.Calls())
236 func TestTelegramStorageFailureDoesNotAcceptTheImage(t *testing.T) {
237 telegram := newTelegramFake(t)
238 telegram.Image("photo", imageTestPNG(t, 2, 2))
240 test := newTestTurnLoop(t, newModelFake(t))
241 require.NoError(t, os.WriteFile(test.agent.Images().dir, []byte("blocked"), 0o600))
243 bot, _ := telegramStartTestBot(t, telegram)
244 message := telegramOwnerMessage("")
245 message.Photo = []TelegramPhoto{{
250 err := bot.handle(t.Context(), test.loop, nil, TelegramUpdate{
254 require.ErrorContains(t, err, "create image directory")
255 require.Equal(t, 0, test.memory.Len())
257 _, found := bot.next()
258 require.False(t, found)