返回 DeepSeek-Reasonix
media_test.go
根目录 / internal / bot / media_test.go
1 package bot
2
3 import (
4 "context"
5 "encoding/base64"
6 "net/http"
7 "net/http/httptest"
8 "os"
9 "path/filepath"
10 "strings"
11 "testing"
12 )
13
14 func TestSaveInboundMediaStoresWorkspaceImageAttachment(t *testing.T) {
15 raw, err := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII=")
16 if err != nil {
17 t.Fatalf("decode png: %v", err)
18 }
19 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
20 w.Header().Set("Content-Type", "application/octet-stream")
21 _, _ = w.Write(raw)
22 }))
23 defer srv.Close()
24 workspace := t.TempDir()
25
26 ref, err := saveOneInboundMedia(context.Background(), workspace, srv.URL+"/shot.png")
27 if err != nil {
28 t.Fatalf("saveOneInboundMedia: %v", err)
29 }
30 if !strings.HasPrefix(ref, ".reasonix/attachments/") || !strings.HasSuffix(ref, ".png") {
31 t.Fatalf("ref = %q, want png attachment ref", ref)
32 }
33 if _, err := os.Stat(filepath.Join(workspace, filepath.FromSlash(ref))); err != nil {
34 t.Fatalf("stored attachment missing: %v", err)
35 }
36 }
37
38 func TestSaveInboundMediaItemsStoresBytesAndReportsErrors(t *testing.T) {
39 png, err := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII=")
40 if err != nil {
41 t.Fatalf("decode png: %v", err)
42 }
43 workspace := t.TempDir()
44
45 refs, fallbacks, errs := saveInboundMediaItems(context.Background(), workspace, []InboundMedia{
46 {MIME: "image/png", Data: png},
47 {Name: "notes.txt", MIME: "text/plain", Data: []byte("hello from feishu")},
48 {Name: "empty.bin", FailureText: "[file unavailable]"}, // no data -> error
49 })
50 if len(errs) != 1 {
51 t.Fatalf("errs = %v, want exactly one for the empty item", errs)
52 }
53 if len(refs) != 2 {
54 t.Fatalf("refs = %v, want image and text attachment", refs)
55 }
56 if len(fallbacks) != 1 || fallbacks[0] != "[file unavailable]" {
57 t.Fatalf("fallbacks = %v, want the failed item's placeholder", fallbacks)
58 }
59 if !strings.HasSuffix(refs[0], ".png") {
60 t.Fatalf("image ref = %q, want .png attachment", refs[0])
61 }
62 if !strings.HasSuffix(refs[1], ".txt") {
63 t.Fatalf("text ref = %q, want .txt attachment", refs[1])
64 }
65 for _, ref := range refs {
66 if _, err := os.Stat(filepath.Join(workspace, filepath.FromSlash(ref))); err != nil {
67 t.Fatalf("stored attachment missing: %v", err)
68 }
69 }
70 }
71
72 func TestSaveInboundMediaItemsLoadsDeferredBytesAfterAdmission(t *testing.T) {
73 workspace := t.TempDir()
74 called := false
75 refs, fallbacks, errs := saveInboundMediaItems(context.Background(), workspace, []InboundMedia{{
76 FailureText: "[download failed]",
77 Load: func(context.Context) ([]byte, string, error) {
78 called = true
79 return []byte("deferred data"), "notes.txt", nil
80 },
81 }})
82 if !called {
83 t.Fatal("deferred loader was not called")
84 }
85 if len(errs) != 0 || len(fallbacks) != 0 || len(refs) != 1 || !strings.HasSuffix(refs[0], ".txt") {
86 t.Fatalf("refs/fallbacks/errs = %v/%v/%v, want one saved text attachment", refs, fallbacks, errs)
87 }
88 }
89
90 func TestInputTextWithMediaKeepsDeferredFailurePlaceholder(t *testing.T) {
91 workspace := t.TempDir()
92 adapter := newFakeAdapter(PlatformFeishu, "feishu")
93 gw := NewGateway(GatewayConfig{WorkspaceRoot: workspace}, map[Platform]Adapter{PlatformFeishu: adapter}, discardLogger())
94 input := gw.inputTextWithMedia(context.Background(), adapter, InboundMessage{
95 Platform: PlatformFeishu,
96 ChatType: ChatDM,
97 ChatID: "chat",
98 Media: []InboundMedia{{
99 FailureText: "[文件下载失败: report.pdf]",
100 Load: func(context.Context) ([]byte, string, error) {
101 return nil, "", os.ErrNotExist
102 },
103 }},
104 }, nil)
105 if input != "[文件下载失败: report.pdf]" {
106 t.Fatalf("input = %q, want deferred failure placeholder", input)
107 }
108 }
109
109 lines GO