mirror of
https://github.com/HugeFrog24/go-telegram-bot.git
synced 2026-06-29 22:07:12 +00:00
Support for images
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFormatUploadFilename(t *testing.T) {
|
||||
cases := []struct {
|
||||
botID uint
|
||||
chatID int64
|
||||
tgMessageID int
|
||||
ext string
|
||||
want string
|
||||
}{
|
||||
{1, 12345, 42, "jpg", "tg-1-12345-42.jpg"},
|
||||
// Negative chat IDs are how Telegram represents groups/channels —
|
||||
// %d preserves the leading minus, no special handling needed.
|
||||
{7, -1001234567890, 1, "png", "tg-7--1001234567890-1.png"},
|
||||
{0, 0, 0, "webp", "tg-0-0-0.webp"},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
got := formatUploadFilename(tc.botID, tc.chatID, tc.tgMessageID, tc.ext)
|
||||
assert.Equal(t, tc.want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseMissingFileIDFromBody(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
body string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "canonical Anthropic file-not-found body",
|
||||
body: `{"type":"error","error":{"type":"invalid_request_error","message":"File not found: file_011CNha8iCJcU1wXNR6q4V8w"}}`,
|
||||
want: "file_011CNha8iCJcU1wXNR6q4V8w",
|
||||
},
|
||||
{
|
||||
name: "trailing punctuation after the id is excluded",
|
||||
body: `something File not found: file_abc123! more text`,
|
||||
want: "file_abc123",
|
||||
},
|
||||
{
|
||||
name: "body without the prefix yields empty",
|
||||
body: `{"type":"error","error":{"message":"Model not found: claude-foo"}}`,
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "id at the very end of the buffer",
|
||||
body: `File not found: file_xyz789`,
|
||||
want: "file_xyz789",
|
||||
},
|
||||
{
|
||||
name: "empty body",
|
||||
body: "",
|
||||
want: "",
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert.Equal(t, tc.want, parseMissingFileIDFromBody(tc.body))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStripDeadFileIDs(t *testing.T) {
|
||||
dead := map[string]struct{}{
|
||||
"file_a": {},
|
||||
"file_b": {},
|
||||
}
|
||||
cases := []struct {
|
||||
name string
|
||||
input []string
|
||||
wantSurvivors []string
|
||||
wantDirty bool
|
||||
}{
|
||||
{
|
||||
name: "no overlap returns input verbatim",
|
||||
input: []string{"file_x", "file_y"},
|
||||
wantSurvivors: []string{"file_x", "file_y"},
|
||||
wantDirty: false,
|
||||
},
|
||||
{
|
||||
name: "partial overlap returns survivors and reports dirty",
|
||||
input: []string{"file_a", "file_x", "file_b", "file_y"},
|
||||
wantSurvivors: []string{"file_x", "file_y"},
|
||||
wantDirty: true,
|
||||
},
|
||||
{
|
||||
name: "all dead returns empty survivors and dirty",
|
||||
input: []string{"file_a", "file_b"},
|
||||
wantSurvivors: []string{},
|
||||
wantDirty: true,
|
||||
},
|
||||
{
|
||||
name: "empty input is not dirty",
|
||||
input: []string{},
|
||||
wantSurvivors: []string{},
|
||||
wantDirty: false,
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
survivors, dirty := stripDeadFileIDs(tc.input, dead)
|
||||
assert.Equal(t, tc.wantSurvivors, survivors)
|
||||
assert.Equal(t, tc.wantDirty, dirty)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarkFilesPendingCleanup(t *testing.T) {
|
||||
b, _ := setupBotForTest(t, 123)
|
||||
chatID := int64(555)
|
||||
|
||||
// Row 1: has dead file_a + alive file_x → should be updated with survivors.
|
||||
row1 := Message{
|
||||
BotID: b.botID,
|
||||
ChatID: chatID,
|
||||
UserID: 777,
|
||||
Username: "u",
|
||||
UserRole: "user",
|
||||
Text: "look at these",
|
||||
Timestamp: time.Now(),
|
||||
IsUser: true,
|
||||
ImageFileIDs: []string{"file_a", "file_x"},
|
||||
}
|
||||
assert.NoError(t, b.db.Create(&row1).Error)
|
||||
|
||||
// Row 2: only dead files → ImageFileIDs should become nil.
|
||||
row2 := Message{
|
||||
BotID: b.botID,
|
||||
ChatID: chatID,
|
||||
UserID: 777,
|
||||
Username: "u",
|
||||
UserRole: "user",
|
||||
Text: "screenshot",
|
||||
Timestamp: time.Now(),
|
||||
IsUser: true,
|
||||
ImageFileIDs: []string{"file_a", "file_b"},
|
||||
}
|
||||
assert.NoError(t, b.db.Create(&row2).Error)
|
||||
|
||||
// Row 3: no dead files → should be untouched.
|
||||
row3 := Message{
|
||||
BotID: b.botID,
|
||||
ChatID: chatID,
|
||||
UserID: 777,
|
||||
Username: "u",
|
||||
UserRole: "user",
|
||||
Text: "another",
|
||||
Timestamp: time.Now(),
|
||||
IsUser: true,
|
||||
ImageFileIDs: []string{"file_x", "file_y"},
|
||||
}
|
||||
assert.NoError(t, b.db.Create(&row3).Error)
|
||||
|
||||
// Row 4: different chat → must NOT be touched even if it references a dead file.
|
||||
row4 := Message{
|
||||
BotID: b.botID,
|
||||
ChatID: 999,
|
||||
UserID: 777,
|
||||
Username: "u",
|
||||
UserRole: "user",
|
||||
Text: "other chat",
|
||||
Timestamp: time.Now(),
|
||||
IsUser: true,
|
||||
ImageFileIDs: []string{"file_a"},
|
||||
}
|
||||
assert.NoError(t, b.db.Create(&row4).Error)
|
||||
|
||||
updated, err := b.markFilesPendingCleanup(t.Context(), chatID, []string{"file_a", "file_b"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 2, updated, "rows 1 and 2 should have been updated")
|
||||
|
||||
// Row 1: only file_x should remain; FilesCleanedAt MUST stay nil because
|
||||
// file_x is still alive on Anthropic and a future death of it must remain
|
||||
// visible to the reconciliation job's `WHERE files_cleaned_at IS NULL` filter.
|
||||
var r1 Message
|
||||
assert.NoError(t, b.db.First(&r1, row1.ID).Error)
|
||||
assert.Equal(t, []string{"file_x"}, r1.ImageFileIDs)
|
||||
assert.Nil(t, r1.FilesCleanedAt)
|
||||
|
||||
// Row 2: all gone → ImageFileIDs nil/empty; FilesCleanedAt set.
|
||||
var r2 Message
|
||||
assert.NoError(t, b.db.First(&r2, row2.ID).Error)
|
||||
assert.Empty(t, r2.ImageFileIDs)
|
||||
assert.NotNil(t, r2.FilesCleanedAt)
|
||||
|
||||
// Row 3: untouched.
|
||||
var r3 Message
|
||||
assert.NoError(t, b.db.First(&r3, row3.ID).Error)
|
||||
assert.Equal(t, []string{"file_x", "file_y"}, r3.ImageFileIDs)
|
||||
assert.Nil(t, r3.FilesCleanedAt)
|
||||
|
||||
// Row 4: untouched despite referencing a dead file — scope is per-chat.
|
||||
var r4 Message
|
||||
assert.NoError(t, b.db.First(&r4, row4.ID).Error)
|
||||
assert.Equal(t, []string{"file_a"}, r4.ImageFileIDs)
|
||||
assert.Nil(t, r4.FilesCleanedAt)
|
||||
}
|
||||
Reference in New Issue
Block a user